Skip to content

09 Jobs & Background Processing

Goal

Run batch workloads with Azure Container Apps Jobs. Create a scheduled job that batch-processes the triage queue and a manual-trigger job for on-demand report generation.

Estimated time

15 minutes.

Official references

Key concepts

Concept Purpose
Container Apps Job A container that runs to completion (not a long-running service).
Scheduled trigger Runs on a cron schedule (e.g. nightly batch).
Manual trigger Runs on demand via CLI or API.
Event trigger Runs in response to events (queue messages, etc.).
Execution A single run of a job — has its own logs and exit code.

Exercise

Step 1 — Create a scheduled job

Deploy the batch worker as a scheduled job that runs at 2 AM UTC daily:

source .env

# Enable admin credentials on ACR (needed for job registry auth)
az acr update --name $ACR_NAME --admin-enabled true

az containerapp job create \
  --name triage-batch-job \
  --resource-group $RESOURCE_GROUP \
  --environment $CONTAINERAPPS_ENVIRONMENT \
  --image $ACR_NAME.azurecr.io/triage-worker:v1 \
  --registry-server $ACR_NAME.azurecr.io \
  --registry-username $ACR_NAME \
  --registry-password "$(az acr credential show --name $ACR_NAME --query 'passwords[0].value' -o tsv)" \
  --trigger-type Schedule \
  --cron-expression "0 2 * * *" \
  --replica-timeout 600 \
  --replica-retry-limit 1 \
  --cpu 0.5 \
  --memory 1Gi \
  --secrets "project-endpoint=$AZURE_AI_PROJECT_ENDPOINT" \
  --env-vars "AZURE_AI_PROJECT_ENDPOINT=secretref:project-endpoint" \
             "AZURE_AI_MODEL_DEPLOYMENT=$AZURE_AI_MODEL_DEPLOYMENT"

Step 2 — Assign managed identity and grant AI access

The job uses DefaultAzureCredential to call the AI model. Assign a system-assigned identity and grant it the required role:

az containerapp job identity assign \
  --name triage-batch-job \
  --resource-group $RESOURCE_GROUP \
  --system-assigned

JOB_PRINCIPAL_ID=$(az containerapp job show \
  --name triage-batch-job \
  --resource-group $RESOURCE_GROUP \
  --query identity.principalId -o tsv)

AI_RESOURCE_ID=$(az resource list --resource-group $RESOURCE_GROUP \
  --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].id" -o tsv)

az role assignment create \
  --assignee $JOB_PRINCIPAL_ID \
  --role "Cognitive Services OpenAI User" \
  --scope "$AI_RESOURCE_ID"

Info

Role assignments can take 1–2 minutes to propagate. Wait a moment before triggering the job.

Step 3 — Trigger the scheduled job manually (for testing)

Don't wait until 2 AM — start an execution now:

az containerapp job start \
  --name triage-batch-job \
  --resource-group $RESOURCE_GROUP

Step 4 — View execution history

az containerapp job execution list \
  --name triage-batch-job \
  --resource-group $RESOURCE_GROUP \
  -o table

Step 5 — View job logs

az containerapp job logs show \
  --name triage-batch-job \
  --resource-group $RESOURCE_GROUP \
  --container triage-batch-job \
  --follow

You should see the batch triage report with urgency classifications for the sample patients.

Step 6 — Create a manual-trigger job

Create a second job that runs only when explicitly triggered — useful for on-demand reports:

az containerapp job create \
  --name triage-report-job \
  --resource-group $RESOURCE_GROUP \
  --environment $CONTAINERAPPS_ENVIRONMENT \
  --image $ACR_NAME.azurecr.io/triage-worker:v1 \
  --registry-server $ACR_NAME.azurecr.io \
  --registry-username $ACR_NAME \
  --registry-password "$(az acr credential show --name $ACR_NAME --query 'passwords[0].value' -o tsv)" \
  --trigger-type Manual \
  --replica-timeout 600 \
  --replica-retry-limit 1 \
  --cpu 0.5 \
  --memory 1Gi \
  --secrets "project-endpoint=$AZURE_AI_PROJECT_ENDPOINT" \
  --env-vars "AZURE_AI_PROJECT_ENDPOINT=secretref:project-endpoint" \
             "AZURE_AI_MODEL_DEPLOYMENT=$AZURE_AI_MODEL_DEPLOYMENT"

# Assign identity and grant AI access (same as the batch job)
az containerapp job identity assign \
  --name triage-report-job \
  --resource-group $RESOURCE_GROUP \
  --system-assigned

REPORT_PRINCIPAL_ID=$(az containerapp job show \
  --name triage-report-job \
  --resource-group $RESOURCE_GROUP \
  --query identity.principalId -o tsv)

az role assignment create \
  --assignee $REPORT_PRINCIPAL_ID \
  --role "Cognitive Services OpenAI User" \
  --scope "$AI_RESOURCE_ID"

Step 7 — Run the manual job

az containerapp job start \
  --name triage-report-job \
  --resource-group $RESOURCE_GROUP

Step 8 — Compare execution history

# Scheduled job
az containerapp job execution list \
  --name triage-batch-job \
  --resource-group $RESOURCE_GROUP \
  -o table

# Manual job
az containerapp job execution list \
  --name triage-report-job \
  --resource-group $RESOURCE_GROUP \
  -o table

Step 9 — List all jobs in the environment

az containerapp job list \
  --resource-group $RESOURCE_GROUP \
  -o table

Jobs vs Container Apps

Aspect Container App Container Apps Job
Lifecycle Long-running (always on or scaled to zero) Runs to completion, then exits
Trigger HTTP requests, TCP, events Schedule, manual, event
Scaling 0–N replicas based on load 0–N executions based on trigger
Use case APIs, web apps, microservices Batch processing, ETL, reports
Cost Per-second while running Per-second per execution

What this lab demonstrates

  1. Creating scheduled and manual-trigger Container Apps Jobs.
  2. Running a batch AI workload (triage classification).
  3. Viewing execution history and logs.
  4. Understanding when to use Jobs vs always-on Container Apps.

Expected result

Two jobs exist in the environment. The scheduled job runs nightly (and can be triggered manually). The manual job runs on demand. Both process patient records through Microsoft Foundry and output a triage report.

Verification

  • [ ] az containerapp job list --resource-group $RESOURCE_GROUP -o table shows both jobs.
  • [ ] Manual execution of triage-batch-job completes successfully.
  • [ ] Job logs show the batch triage report with patient classifications.
  • [ ] triage-report-job can be triggered and shows execution history.