06 - CI/CD with GitHub Actions¶
Automate build and deployment so every commit can produce a new Container App revision. This tutorial uses GitHub Actions, ACR, and Azure Container Apps deploy actions.
Infrastructure Context
Service: Container Apps (Consumption) | Network: VNet integrated | VNet: ✅
This tutorial assumes a production-ready Container Apps deployment with a custom VNet, ACR with managed identity pull, and private endpoints for backend services.
flowchart TD
INET[Internet] -->|HTTPS| CA["Container App\nConsumption\nLinux Python 3.11"]
subgraph VNET["VNet 10.0.0.0/16"]
subgraph ENV_SUB["Environment Subnet 10.0.0.0/23\nDelegation: Microsoft.App/environments"]
CAE[Container Apps Environment]
CA
end
subgraph PE_SUB["Private Endpoint Subnet 10.0.2.0/24"]
PE_ACR[PE: ACR]
PE_KV[PE: Key Vault]
PE_ST[PE: Storage]
end
end
PE_ACR --> ACR[Azure Container Registry]
PE_KV --> KV[Key Vault]
PE_ST --> ST[Storage Account]
subgraph DNS[Private DNS Zones]
DNS_ACR[privatelink.azurecr.io]
DNS_KV[privatelink.vaultcore.azure.net]
DNS_ST[privatelink.blob.core.windows.net]
end
PE_ACR -.-> DNS_ACR
PE_KV -.-> DNS_KV
PE_ST -.-> DNS_ST
CA -.->|System-Assigned MI| ENTRA[Microsoft Entra ID]
CAE --> LOG[Log Analytics]
CA --> AI[Application Insights]
style CA fill:#107c10,color:#fff
style VNET fill:#E8F5E9,stroke:#4CAF50
style DNS fill:#E3F2FD CI/CD Pipeline Flow¶
graph TD
PUSH[Push to main] --> GHA[GitHub Actions]
GHA --> ACR[ACR Build]
ACR --> DEPLOY[Deploy Revision]
DEPLOY --> HEALTH[Health Check] Prerequisites¶
- Completed 05 - Infrastructure as Code with Bicep
- GitHub repository with Actions enabled
- Azure service principal stored as GitHub secret
Never expose credentials in workflow logs
Keep all credential material in GitHub Secrets and avoid printing secret-derived values in shell steps. Use masked placeholders in documentation and workflow examples.
Step-by-step¶
-
Configure repository variables and secrets
-
Variables:
RESOURCE_GROUP,APP_NAME,ACR_NAME - Secrets:
AZURE_CREDENTIALS,REGISTRY_USERNAME,REGISTRY_PASSWORD
Example AZURE_CREDENTIALS JSON (masked):
{
"clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"clientSecret": "<client-secret>",
"subscriptionId": "<subscription-id>",
"tenantId": "<tenant-id>"
}
- Create workflow file
name: Deploy to Azure Container Apps
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: ACR Login
uses: azure/docker-login@v2
with:
login-server: ${{ vars.ACR_NAME }}.azurecr.io
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and push image
run: |
docker build --tag ${{ vars.ACR_NAME }}.azurecr.io/${{ vars.APP_NAME }}:${{ github.sha }} .
docker push ${{ vars.ACR_NAME }}.azurecr.io/${{ vars.APP_NAME }}:${{ github.sha }}
- name: Deploy Container App
uses: azure/container-apps-deploy-action@v1
with:
imageToDeploy: ${{ vars.ACR_NAME }}.azurecr.io/${{ vars.APP_NAME }}:${{ github.sha }}
resourceGroup: ${{ vars.RESOURCE_GROUP }}
containerAppName: ${{ vars.APP_NAME }}
- Add infrastructure deployment (optional but recommended)
- name: Deploy Infrastructure
uses: azure/arm-deploy@v2
with:
resourceGroupName: ${{ vars.RESOURCE_GROUP }}
template: ./infra/main.bicep
parameters: appName=${{ vars.APP_NAME }} acrName=${{ vars.ACR_NAME }}
-
Validate rollout behavior
- Trigger workflow from a commit to
main. - Confirm a new revision was created.
- Confirm traffic moved to healthy revision.
az containerapp revision list \ --name "$APP_NAME" \ --resource-group "$RG" \ --query "[].{name:name,active:properties.active,trafficWeight:properties.trafficWeight,replicas:properties.replicas,healthState:properties.healthState,runningState:properties.runningState}"Command Why it is used az containerapp revision list ...Lists revisions so rollout state, traffic, and health can be verified. Expected output
Command Why it is used az containerapp ingress show ...Reads ingress configuration such as exposure, target port, transport, and affinity. - Trigger workflow from a commit to
Verify in Azure Portal¶

[Observed] ca-sample-d38538. Container App. Activity log. Activity. Edit columns. Refresh. Export Activity Logs. Download as CSV. Insights. Feedback. Pin current filters. Reset filters. Looking for Log Analytics?. Visit Log Analytics. Search. Quick Insights. Management Group. None. Subscription. Visual Studio Enterprise Subscription. All. Timespan. Last 6 hours. Resource group. rg-aca-basics-d38538. Resource. ca-sample-d38538. Add Filter. 1 item.. Operation name. Status. Time. Time stamp. Subscription. Event initiated by. Auth Token for Container App Dev APIs. Succeeded. 23 minutes .... Thu Jun 04 .... user@example.com. Overview. Activity log. Access control (IAM). Tags. Diagnose and solve problems. Resource visualizer. Application. Revisions and replicas. Containers. Scale. Volumes. Settings. Networking. Ingress. Custom domains. CORS. Security. Monitoring. Log stream. Logs. Console. Alerts. Metrics. Dashboards with Grafana. Advisor recommendations.
[Inferred] The resource-scoped filter chips Resource group and Resource appear to map to the same ${{ vars.RESOURCE_GROUP }} and ${{ vars.APP_NAME }} values passed to resourceGroup and containerAppName in Step-by-step Step 2. The Activity log blade appears consistent with the same container app remaining the deployment target when Step-by-step Step 3 runs azure/arm-deploy@v2 with resourceGroupName: ${{ vars.RESOURCE_GROUP }} and template: ./infra/main.bicep. The single Succeeded status appears consistent with the healthy rollout checks in Step-by-step Step 4, where az containerapp revision list is expected to show healthState: "Healthy" and runningState: "Running". The recent Time and Time stamp columns appear to map to the post-commit verification window described in Step-by-step Step 4, which tells you to trigger the workflow from a commit to main, confirm a new revision was created, and confirm traffic moved to the healthy revision.
[Not Proven] Additional GitHub Actions workflow detail, deployment template detail, and CLI command output are not visible on this view.
Advanced Topics¶
- Add pre-deploy tests and security scanning gates.
- Implement staged deployment with manual approval.
- Use multiple active revisions for canary rollout in pipeline.
Use immutable image tags in pipelines
Prefer commit SHA or release-based tags for image versions. Immutable tags make revision-to-commit tracing straightforward during incident response.