DevOps on Azure
Azure provides two paths for CI/CD and DevOps: Azure DevOps (a full platform with repos, pipelines, boards, and artifacts) and GitHub (with GitHub Actions deploying to Azure). Many teams use a mix of both.
Azure DevOps Services
Section titled “Azure DevOps Services”Azure DevOps is a suite of five tools at dev.azure.com:
| Service | What It Does | Comparable To |
|---|---|---|
| Azure Repos | Git repository hosting | GitHub, GitLab |
| Azure Pipelines | CI/CD pipelines | GitHub Actions, Jenkins |
| Azure Boards | Work item tracking (agile boards) | Jira, GitHub Issues |
| Azure Artifacts | Package feeds (npm, NuGet, pip, Maven) | GitHub Packages, Artifactory |
| Azure Test Plans | Manual and exploratory testing | TestRail |
You can use any combination — e.g. GitHub for repos + Azure Pipelines for CI/CD, or Azure Repos + GitHub Actions.
Azure Pipelines
Section titled “Azure Pipelines”Pipelines define your CI/CD workflow in YAML (or the visual editor). They run on Microsoft-hosted agents (managed VMs) or self-hosted agents (your own machines).
Pipeline Structure
Section titled “Pipeline Structure”trigger: branches: include: - main paths: exclude: - docs/**
pool: vmImage: 'ubuntu-latest' # Microsoft-hosted agent
variables: imageName: 'my-app' registryUrl: 'myregistry.azurecr.io'
stages: - stage: Build jobs: - job: BuildAndTest steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.12'
- script: | pip install -r requirements.txt pytest --junitxml=results.xml displayName: 'Install and test'
- task: PublishTestResults@2 inputs: testResultsFiles: 'results.xml'
- task: Docker@2 inputs: containerRegistry: 'myACRConnection' repository: $(imageName) command: 'buildAndPush' Dockerfile: '**/Dockerfile' tags: | $(Build.BuildId) latest
- stage: Deploy dependsOn: Build condition: succeeded() jobs: - deployment: DeployToProduction environment: 'production' strategy: runOnce: deploy: steps: - task: AzureWebAppContainer@1 inputs: azureSubscription: 'my-azure-connection' appName: 'my-webapp' containers: '$(registryUrl)/$(imageName):$(Build.BuildId)'Key Concepts
Section titled “Key Concepts”| Concept | What It Is |
|---|---|
| Trigger | What starts the pipeline (branch push, PR, schedule, manual) |
| Pool | The agent that runs the pipeline (Microsoft-hosted or self-hosted) |
| Stage | A logical group of jobs (e.g. Build, Test, Deploy to Staging, Deploy to Prod) |
| Job | A set of steps that run on one agent |
| Step | A single task or script |
| Task | A pre-built action (Docker build, deploy to App Service, run tests) |
| Environment | A deployment target with approvals and checks |
| Variable | Pipeline-level or stage-level variables |
| Service connection | Stored credentials for connecting to Azure, Docker registries, etc. |
Triggers
Section titled “Triggers”# Branch triggertrigger: branches: include: [main, release/*] exclude: [feature/experimental]
# PR triggerpr: branches: include: [main]
# Scheduled triggerschedules: - cron: "0 2 * * *" displayName: "Nightly build" branches: include: [main]
# Manual trigger (no trigger key, run from UI)trigger: noneEnvironments and Approvals
Section titled “Environments and Approvals”Environments add gates and approvals to deployments:
- deployment: DeployToProduction environment: 'production' # requires approval configured in Azure DevOps strategy: runOnce: deploy: steps: - script: echo "Deploying to production"Configure approvals in Azure DevOps: Pipelines → Environments → production → Approvals and checks.
| Check Type | What It Does |
|---|---|
| Manual approval | A person must approve before deployment continues |
| Branch protection | Only allow deployments from specific branches |
| Business hours | Only deploy during specified hours |
| Template | Require the pipeline to extend a specific template |
Variables and Secrets
Section titled “Variables and Secrets”# Inline variablesvariables: environment: production region: eastus
# Variable groups (shared across pipelines, stored in Azure DevOps)variables: - group: production-secrets # contains DB_PASSWORD, API_KEY, etc.
# Secret variables (masked in logs)variables: - name: DB_PASSWORD value: $(dbPassword) # from variable group or pipeline settingsNever hardcode secrets in YAML. Use variable groups linked to Azure Key Vault:
variables: - group: production-secrets # linked to Key Vault — auto-syncs secretsDeployment Strategies
Section titled “Deployment Strategies”strategy: # Simple: deploy once runOnce: deploy: steps: [...]
# Rolling: deploy to N% of targets at a time rolling: maxParallel: 25% # 25% of targets at a time deploy: steps: [...]
# Canary: route a percentage of traffic, then proceed canary: increments: [10, 20] # 10%, then 20%, then full deploy: steps: [...]Templates (Reusable Pipelines)
Section titled “Templates (Reusable Pipelines)”parameters: - name: runtime type: string default: 'python:3.12'
steps: - script: | pip install -r requirements.txt pytest displayName: 'Build and test'stages: - stage: Build jobs: - job: Build steps: - template: templates/build.yml parameters: runtime: 'python:3.12'Microsoft-Hosted vs Self-Hosted Agents
Section titled “Microsoft-Hosted vs Self-Hosted Agents”| Microsoft-Hosted | Self-Hosted | |
|---|---|---|
| Setup | Zero (managed by Microsoft) | You install and maintain |
| OS | Ubuntu, Windows, macOS | Any OS you control |
| Clean environment | Fresh VM for every job | Persistent (faster, but state leaks) |
| Cost | 1,800 free minutes/month (private projects) | Free (your infrastructure cost) |
| Use case | Most pipelines | Need GPU, specific software, VNet access |
Azure Repos
Section titled “Azure Repos”Git repositories hosted in Azure DevOps. Supports branch policies, pull requests, and code reviews.
Branch Policies
Section titled “Branch Policies”| Policy | What It Does |
|---|---|
| Required reviewers | PR must be approved by N reviewers |
| Build validation | PR must pass a CI pipeline before merge |
| Comment resolution | All PR comments must be resolved |
| Merge type | Enforce squash merge, rebase, etc. |
| Linked work items | PR must be linked to a Board work item |
Azure Container Registry (ACR)
Section titled “Azure Container Registry (ACR)”ACR is Azure’s container registry — like AWS ECR:
# Create a registryaz acr create --resource-group myapp-rg --name myregistry --sku Standard
# Loginaz acr login --name myregistry
# Build and push (ACR can build images for you — no local Docker needed)az acr build --registry myregistry --image my-app:v1 .
# List imagesaz acr repository list --name myregistry --output tableACR Tasks can build images in the cloud on push or on a schedule — no Docker on your CI agent.
GitHub + Azure
Section titled “GitHub + Azure”Many teams use GitHub for code and GitHub Actions for CI/CD, deploying to Azure.
GitHub Actions Deploying to Azure
Section titled “GitHub Actions Deploying to Azure”name: Deploy to Azure App Service
on: push: branches: [main]
permissions: id-token: write contents: read
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Azure Login (OIDC — no secrets) uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Build and push to ACR run: | az acr login --name myregistry docker build -t myregistry.azurecr.io/my-app:${{ github.sha }} . docker push myregistry.azurecr.io/my-app:${{ github.sha }}
- name: Deploy to App Service uses: azure/webapps-deploy@v3 with: app-name: my-webapp images: myregistry.azurecr.io/my-app:${{ github.sha }}OIDC Authentication (No Secrets)
Section titled “OIDC Authentication (No Secrets)”Use federated credentials instead of storing client secrets:
- Create an app registration in Entra ID.
- Add a federated credential for GitHub.
- Assign the app a role (Contributor) on the resource group.
- Use
azure/loginwithclient-id,tenant-id,subscription-id.
No secrets to rotate or leak.
Azure DevOps vs GitHub
Section titled “Azure DevOps vs GitHub”| Feature | Azure DevOps | GitHub |
|---|---|---|
| Repos | Azure Repos | GitHub Repos |
| CI/CD | Azure Pipelines (YAML) | GitHub Actions |
| Work tracking | Azure Boards (rich, Jira-like) | GitHub Issues + Projects (simpler) |
| Package management | Azure Artifacts | GitHub Packages |
| Container registry | ACR (separate service) | GitHub Container Registry / ACR |
| Approvals | Environments + approvals | Environments + required reviewers |
| Best for | Enterprise, complex pipelines, Microsoft shops | Open source, startups, GitHub-centric teams |
Both integrate with Azure equally well. The choice often comes down to where your code already lives.
Key Takeaways
Section titled “Key Takeaways”- Azure Pipelines supports multi-stage YAML pipelines with environments, approvals, and deployment strategies (rolling, canary).
- Use variable groups linked to Key Vault for secrets — never hardcode them in YAML.
- Environments with approval gates protect production deployments.
- ACR stores container images. Use ACR Tasks to build images in the cloud without local Docker.
- GitHub Actions with OIDC is a great alternative — use federated credentials for secret-free Azure authentication.
- Use branch policies (Azure Repos) or branch protection rules (GitHub) to enforce code review and CI checks before merging.