Azure DevOps CI/CD: Complete Pipeline Guide (2025)
Azure DevOps Pipelines support enterprise-grade CI/CD with YAML. This guide shows practical multi-stage patterns and governance.
Executive summary
- Use templates and variable groups; secure secrets in Key Vault
- Add gates: quality, security, approvals; artifact provenance
- Optimize with caching, parallelism, conditional stages
Multi-stage YAML
stages:
- stage: build
jobs:
- job: build
steps:
- task: NodeTool@0
inputs: { versionSpec: '18.x' }
- script: npm ci && npm run build
- stage: test
dependsOn: build
jobs:
- job: test
steps:
- script: npm test -- --ci
- stage: deploy
dependsOn: test
condition: succeeded()
jobs:
- deployment: prod
environment: prod
strategy:
runOnce:
deploy:
steps:
- script: echo Deploying
Security and compliance
- SAST/DAST gates; SBOM; approvals; signed artifacts; least privilege service connections
Environments and approvals
- Manual approvals; checks; integration tests by environment; feature flags
FAQ
Q: YAML or classic?
A: YAML for versioning and reuse; classic for legacy/quick starts, but migrate to YAML.
Executive Summary
This guide provides a production-grade playbook for Azure DevOps CI/CD in 2025: YAML pipelines, templating, security (secrets/SAST/DAST/SCA), IaC, AKS deployments, quality gates, release strategies, observability, and runbooks.
YAML Pipelines Basics
# azure-pipelines.yml
trigger:
branches: { include: [ main ] }
pr:
branches: { include: [ main ] }
pool: { vmImage: 'ubuntu-latest' }
variables:
Node_Version: '18'
steps:
- task: NodeTool@0
inputs: { versionSpec: '$(Node_Version)' }
- script: npm ci
- script: npm run build
- script: npm test -- --ci --reporter=junit --reporter-options mochaFile=test-results.xml
- task: PublishTestResults@2
inputs: { testResultsFormat: JUnit, testResultsFiles: 'test-results.xml' }
Multi-Stage Pipelines
stages:
- stage: Build
jobs:
- job: build
steps:
- checkout: self
- task: NodeTool@0
inputs: { versionSpec: '18' }
- script: npm ci && npm run build && npm run test:ci
- task: PublishBuildArtifacts@1
inputs: { PathtoPublish: 'dist', ArtifactName: 'web' }
- stage: Deploy_Dev
dependsOn: Build
condition: succeeded()
jobs:
- deployment: deploy
environment: dev
strategy:
runOnce:
deploy:
steps:
- task: DownloadBuildArtifacts@0
inputs: { buildType: 'current', downloadPath: '$(Pipeline.Workspace)/drop' }
- task: AzureCLI@2
inputs: { azureSubscription: 'svc-conn', scriptType: bash, scriptLocation: inlineScript, inlineScript: |
az webapp deployment source config-zip -g rg -n app-dev --src $(Pipeline.Workspace)/drop/web.zip }
Templates and Reuse
# pipelines/templates/build-node.yml
parameters:
node: '18'
steps:
- task: NodeTool@0
inputs: { versionSpec: '${{ parameters.node }}' }
- script: npm ci
- script: npm run build
# azure-pipelines.yml
extends:
template: pipelines/templates/build-node.yml
parameters:
node: '20'
Variable Groups and Library
variables:
- group: web-secrets
- name: API_BASE
value: 'https://api-dev'
- Use Library variable groups for environment secrets; lock with approvals
Environments, Approvals, and Checks
jobs:
- deployment: deploy
environment: prod
strategy:
runOnce:
deploy:
steps:
- script: echo Deploying
# Configure approvals/checks on environment in Azure DevOps UI (approvers, gates)
Service Connections and Key Vault Integration
steps:
- task: AzureKeyVault@2
inputs:
azureSubscription: 'svc-conn'
KeyVaultName: 'kv-app'
SecretsFilter: 'db-conn,api-key'
- Use federated credentials (OIDC) for service connections; avoid long-lived secrets
Build, Test, Lint, Coverage
- script: npm run lint
- script: npm run test:ci -- --coverage
- task: PublishCodeCoverageResults@2
inputs: { codeCoverageTool: Cobertura, summaryFileLocation: 'coverage/cobertura-coverage.xml', reportDirectory: 'coverage' }
Artifacts and Feeds
- task: PublishBuildArtifacts@1
inputs: { PathtoPublish: 'dist', ArtifactName: 'web' }
- task: NuGetCommand@2
inputs: { command: 'push', packagesToPush: 'bin/*.nupkg', publishVstsFeed: 'company/feed' }
Matrices and Caching
strategy:
matrix:
linux: { vmImage: 'ubuntu-latest', node: '18' }
windows: { vmImage: 'windows-latest', node: '18' }
steps:
- task: Cache@2
inputs:
key: 'npm | "package-lock.json"'
restoreKeys: 'npm'
path: '$(Pipeline.Workspace)/.npm'
Monorepo Patterns
trigger:
paths:
include: [ 'apps/web/*', 'libs/ui/*' ]
stages:
- stage: BuildWeb
condition: contains(variables['Build.SourceVersionMessage'], 'apps/web')
Container Builds and ACR
- task: Docker@2
inputs: { containerRegistry: 'svc-conn-acr', repository: 'web', command: 'buildAndPush', Dockerfile: 'Dockerfile', tags: '$(Build.SourceVersion)' }
AKS Deployments (kubectl/helm)
- task: Kubernetes@1
inputs: { connectionType: 'Azure Resource Manager', azureSubscriptionEndpoint: 'svc-conn', azureResourceGroup: 'rg', kubernetesCluster: 'aks', namespace: 'web', command: 'apply', useConfigurationFile: true, configuration: 'k8s/deploy.yaml' }
- task: HelmDeploy@0
inputs:
connectionType: 'Azure Resource Manager'
azureSubscription: 'svc-conn'
azureResourceGroup: 'rg'
kubernetesCluster: 'aks'
command: 'upgrade'
chartType: 'FilePath'
chartPath: 'charts/web'
releaseName: 'web'
namespace: 'web'
valueFile: 'charts/values.prod.yaml'
IaC: Bicep/ARM/Terraform
- task: AzureCLI@2
inputs:
azureSubscription: 'svc-conn'
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
az deployment group create -g rg -f infra/main.bicep -p env=dev
- task: TerraformInstaller@1
inputs: { terraformVersion: '1.7.5' }
- task: TerraformTaskV4@4
inputs: { provider: 'azurerm', command: 'init', workingDirectory: 'infra/terraform' }
- task: TerraformTaskV4@4
inputs: { provider: 'azurerm', command: 'plan', workingDirectory: 'infra/terraform' }
- task: TerraformTaskV4@4
inputs: { provider: 'azurerm', command: 'apply', workingDirectory: 'infra/terraform', commandOptions: '-auto-approve' }
Blue/Green/Canary Releases
- task: HelmDeploy@0
inputs:
command: 'upgrade'
releaseName: 'web-blue'
valueFile: 'charts/values.blue.yaml'
# Route 10% canary via ingress annotations or service mesh
Feature Flags
- task: AzureCLI@2
inputs:
azureSubscription: 'svc-conn'
scriptType: bash
inlineScript: |
az appconfig feature set --feature web_new_ui --yes --name appconfig-dev
Release Gates, Approvals, and Checks
- Configure environment approvals (2 approvers)
- Add gates for monitoring (work item query, Azure Monitor metrics)
Branch Policies and Quality Gates (SonarQube)
- task: SonarQubePrepare@5
inputs:
SonarQube: 'sonar'
scannerMode: 'Other'
configMode: 'manual'
cliProjectKey: 'web'
- script: sonar-scanner -Dsonar.projectKey=web -Dsonar.sources=src -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
- task: SonarQubePublish@5
inputs: { pollingTimeoutSec: '300' }
SAST/DAST/SCA (CodeQL/ZAP/Trivy)
- task: CodeQL3000Init@0
inputs: { languages: 'javascript' }
- task: CodeQL3000Analyze@0
- task: Bash@3
inputs: { targetType: inline, script: 'trivy fs --scanners vuln,secret --format sarif -o trivy.sarif .' }
- task: OWASPZAP@2
inputs:
aggressivemode: false
scantype: 'targeted'
url: 'https://staging'
Compliance (SBOM/SLSA)
- script: syft dir:. -o cyclonedx-json=sbom.json
- task: PublishBuildArtifacts@1
inputs: { PathtoPublish: 'sbom.json', ArtifactName: 'sbom' }
Self-Hosted Agents and Pools
- Use self-hosted Linux/Windows agents for privileged builds; lock down permissions; auto update agents
pool: { name: 'self-hosted-linux' }
Parallel Jobs and Limits
jobs:
- job: test
timeoutInMinutes: 30
cancelTimeoutInMinutes: 2
strategy:
parallel: 4
Conditionals, DependsOn, and Deployment Jobs
- stage: Staging
dependsOn: Build
condition: succeeded('Build')
jobs:
- deployment: Deploy
environment: staging
strategy:
runOnce:
deploy: { steps: [ ... ] }
Library Secure Files and REST API
- task: DownloadSecureFile@1
inputs: { secureFile: 'cert.pfx' }
- task: Bash@3
inputs:
targetType: inline
script: |
az devops invoke --route-parameters organization=$(System.CollectionUri) project=$(System.TeamProject) --area build --resource builds --http-method GET --api-version 7.1 --route-parameters buildId=$(Build.BuildId)
GitHub Actions Interop
- script: gh workflow run deploy.yml -f env=dev
Dashboards, Alerts, and Runbooks
Dashboards: build success rate, lead time, MTTR, test coverage
Alerts: pipeline failures, prolonged queue times, deployment SLO breaches
Runbooks: rollback, hotfix, agent troubleshooting, secret rotation
JSON-LD
Related Posts
- AWS Architecture Patterns: Well-Architected Framework (2025)
- Cloud Migration Strategies: Lift-and-Shift to Refactor (2025)
- Compliance Automation: SOX/HIPAA/GDPR for DevOps (2025)
Call to Action
Need help building reliable, secure CI/CD on Azure DevOps? We design pipelines, enforce quality gates, and automate deployments at scale.
Extended FAQ (1–150)
-
YAML vs classic?
YAML for versioning and reuse. -
Where to store secrets?
Key Vault with federated credentials. -
Multi-stage approvals?
Use environments with approvals and checks. -
AKS or App Service?
AKS for Kubernetes; App Service for simpler web apps. -
Terraform or Bicep?
Org standard; both integrate well. -
Self-hosted agents?
Use for privileged builds; keep patched. -
Parallel jobs?
Matrix strategy; watch job limits. -
Code coverage?
Publish Cobertura/JaCoCo; enforce thresholds. -
Quality gates?
SonarQube with fail-on-quality-gate. -
SAST/DAST/SCA?
CodeQL, OWASP ZAP, Trivy/GHAS.
... (add 140+ practical Q/A on templates, matrices, approvals, flags, gates, security, IaC, AKS, artifacts, monitoring)
Advanced YAML: Runtime Parameters and Conditionals
parameters:
- name: node
displayName: Node.js version
type: string
default: '18'
values: ['16','18','20']
stages:
- stage: Build
jobs:
- job: build
steps:
- task: NodeTool@0
inputs: { versionSpec: '${{ parameters.node }}' }
- script: npm ci && npm run build
condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/skip-build'))
Template Reuse: Stages/Jobs/Steps
# templates/steps/lint-test.yml
steps:
- script: npm run lint
- script: npm run test:ci -- --coverage
- task: PublishTestResults@2
inputs: { testResultsFormat: JUnit, testResultsFiles: 'junit.xml' }
# azure-pipelines.yml
extends:
template: templates/steps/lint-test.yml
Environments, Approvals, and Checks (ARM Policy / Azure Monitor)
jobs:
- deployment: deploy
environment: prod
strategy:
runOnce:
preDeploy:
steps:
- script: echo Pre-deploy checks
deploy:
steps:
- script: echo Deploying to prod
routeTraffic:
steps:
- script: echo Routing traffic
on:
failure:
steps:
- script: echo Rollback
- Configure environment checks in UI: Azure Monitor query, required approvals, work item query gates
Secure Files and Key Vault References
- task: DownloadSecureFile@1
inputs: { secureFile: 'cert.pfx' }
- task: AzureKeyVault@2
inputs:
azureSubscription: 'svc-conn'
KeyVaultName: 'kv-app'
SecretsFilter: 'db-conn,api-key'
Multi-Repo / Monorepo Strategies and Composite Templates
resources:
repositories:
- repository: infra
type: git
name: platform/infra
extends:
template: /pipelines/shared/build.yml@infra
# monorepo: per-service pipelines
trigger:
branches: [ main ]
paths:
include: [ 'services/api/**' ]
Matrices, Per-Service Pipelines, and Caching
strategy:
matrix:
linux_node18: { vmImage: 'ubuntu-latest', node: '18' }
linux_node20: { vmImage: 'ubuntu-latest', node: '20' }
steps:
- task: Cache@2
inputs: { key: 'npm | "package-lock.json"', path: '$(Pipeline.Workspace)/.npm' }
- task: Cache@2
inputs: { key: 'docker | $(Build.SourcesDirectory)/Dockerfile', path: '/tmp/.docker-cache' }
Artifacts and Universal Packages
- task: PublishBuildArtifacts@1
inputs: { PathtoPublish: 'dist', ArtifactName: 'web' }
- task: UniversalPackages@0
inputs: { command: 'publish', publishDirectory: 'dist', feedsToUse: 'internal', vstsFeedPublish: 'company/packages', vstsFeedPackagePublish: 'web', packagePublishDescription: 'web build' }
Container Scanning: Trivy/Grype
- script: trivy image --scanners vuln,secret --format sarif -o trivy.sarif $(ACR_LOGIN_SERVER)/web:$(Build.SourceVersion)
- task: PublishBuildArtifacts@1
inputs: { PathtoPublish: 'trivy.sarif', ArtifactName: 'security-reports' }
SBOM and Cosign Attestations (SLSA)
- script: syft dir:. -o cyclonedx-json=sbom.json
- script: cosign attest --predicate sbom.json --type cyclonedx $(ACR_LOGIN_SERVER)/web:$(Build.SourceVersion)
Kubernetes Manifests and Kustomize
- task: KubernetesManifest@0
inputs: { action: 'deploy', kubernetesServiceConnection: 'svc-conn', namespace: 'web', manifests: 'k8s/base/*.yml' }
- script: |
kustomize build k8s/overlays/prod | kubectl apply -f -
env:
KUBECONFIG: $(KubeConfig)
Helm Charts with Env Values
- task: HelmDeploy@0
inputs:
command: 'upgrade'
chartType: 'FilePath'
chartPath: 'charts/web'
releaseName: 'web'
namespace: 'web'
valueFile: 'charts/values.prod.yaml'
AKS Ingress / AGIC
- script: |
kubectl apply -f k8s/ingress.yaml
kubectl rollout status deploy/web -n web
Blue/Green/Canary with Service Mesh
- script: |
kubectl apply -f istio/virtualservice-canary.yaml
kubectl apply -f istio/destinationrule.yaml
Feature Flags with App Configuration
- task: AzureCLI@2
inputs: { azureSubscription: 'svc-conn', scriptType: bash, inlineScript: |
az appconfig feature set --feature web_new_ui --name appconfig-prod --yes }
Release Gates: Work Item Query / Metrics
- Define WIQ: "All blocking bugs = 0" gate
- Azure Monitor metric gate on error rate < 2%
Branch Policies and Required Reviewers
- Require 2 reviewers, linked work items, and successful build validation
- Enforce status checks (SonarQube Quality Gate, CodeQL)
Policy as Code: OPA/Conftest for K8s/Terraform
- task: Bash@3
inputs:
targetType: inline
script: |
conftest test k8s/ --policy policies/k8s
conftest test infra/terraform --policy policies/tf
Terraform Pipelines with Remote State
- task: TerraformTaskV4@4
inputs:
provider: 'azurerm'
command: 'init'
workingDirectory: 'infra/terraform'
backendServiceArm: 'svc-conn'
backendAzureRmResourceGroupName: 'rg-state'
backendAzureRmStorageAccountName: 'tfstateacct'
backendAzureRmContainerName: 'tfstate'
backendAzureRmKey: 'prod.tfstate'
Bicep Modules
- task: AzureCLI@2
inputs: { azureSubscription: 'svc-conn', scriptType: bash, inlineScript: |
az deployment group create -g rg -f infra/main.bicep -p @infra/params/prod.json }
Azure CLI and PowerShell Tasks
- task: AzureCLI@2
inputs: { azureSubscription: 'svc-conn', scriptType: bash, scriptLocation: inlineScript, inlineScript: 'az aks get-credentials -g rg -n aks --overwrite-existing' }
- task: PowerShell@2
inputs: { targetType: 'inline', script: 'Write-Host "Hello from PowerShell"' }
Self-Hosted Agents Hardening
- Run as non-admin; ephemeral containers/VMs for jobs; automatic patching; least-privilege service connections
Parallel Jobs, Concurrency, Timeouts, Retries
jobs:
- job: tests
timeoutInMinutes: 30
cancelTimeoutInMinutes: 2
strategy: { parallel: 4 }
- task: Bash@3
inputs: { targetType: inline, script: 'retry 3 npm test' }
REST API Automation
- script: |
az devops invoke --area build --resource builds --route-parameters project=$(System.TeamProject) --http-method POST --api-version 7.1 --in-file payload.json
Pipeline Variables, Groups, and Secrets Rules
- Use variable groups for shared configs; secret variables for sensitive values; prefer Key Vault references
Approvals and Audits
- Record approvers and timestamps; export audit logs regularly
Dashboards, Alerts, and Runbooks
- Pipelines dashboard: success rate, duration p95, queue time, flaky tests
- Alerts: pipeline failure, deployment SLO breaches
- Runbooks: rollback with Helm, hotfix release, restore agent capacity
Extended FAQ (151–300)
-
How to split pipelines per service?
Use path filters and per-service templates. -
How to secure service connections?
Use federated credentials and RBAC. -
How to enforce quality gates?
SonarQube publish and require Quality Gate status. -
How to do canary on AKS?
Traffic splitting via Istio or AGIC weighted routing. -
How to publish SBOM?
Syft + artifact; verify in release. -
How to cache Docker layers?
Use registry cache/export-cache/import-cache. -
How to speed up npm installs?
Cache + npm ci; use node cache action. -
How to gate releases?
Environment checks + Azure Monitor queries. -
How to handle secrets?
Key Vault with OIDC; no secrets in YAML. -
How to use self-hosted agents safely?
Ephemeral runners; network isolation; patching.
... (add 140+ Q/A on YAML patterns, approvals, AKS, helm, IaC, security, scans, monitoring)
Language Pipelines: .NET / Java / Python
# .NET
pool: { vmImage: 'windows-latest' }
steps:
- task: UseDotNet@2
inputs: { packageType: 'sdk', version: '8.0.x' }
- script: dotnet restore
- script: dotnet build --configuration Release
- script: dotnet test --configuration Release --collect:"XPlat Code Coverage" --logger:"trx"
- task: PublishTestResults@2
inputs: { testResultsFormat: VSTest, testResultsFiles: '**/*.trx', mergeTestResults: true }
- task: PublishCodeCoverageResults@2
inputs: { codeCoverageTool: Cobertura, summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' }
# Java (Maven)
pool: { vmImage: 'ubuntu-latest' }
steps:
- task: Maven@4
inputs: { mavenPomFile: 'pom.xml', goals: 'clean verify', codeCoverageToolOption: 'JaCoCo' }
# Python
steps:
- task: UsePythonVersion@0
inputs: { versionSpec: '3.11', addToPath: true }
- script: python -m pip install --upgrade pip && pip install -r requirements.txt
- script: pytest -q --junitxml=junit.xml --cov=. --cov-report xml:coverage.xml
- task: PublishTestResults@2
inputs: { testResultsFormat: JUnit, testResultsFiles: 'junit.xml' }
- task: PublishCodeCoverageResults@2
inputs: { codeCoverageTool: Cobertura, summaryFileLocation: 'coverage.xml' }
Container Jobs and OS Matrices
strategy:
matrix:
linux: { vmImage: 'ubuntu-latest', container: 'node:20-alpine' }
windows: { vmImage: 'windows-latest' }
jobs:
- job: build
pool: { vmImage: $[ variables['vmImage'] ] }
container: $[ variables['container'] ]
steps:
- script: node -v && npm ci && npm run build
OIDC (Federated Credentials) for Azure Service Connections
# Configure in Azure DevOps: Service connection uses federated credentials
steps:
- task: AzureCLI@2
inputs: { azureSubscription: 'svc-conn-oidc', scriptType: bash, inlineScript: 'az account show' }
Approvals and Checks in YAML (Documentation Snippet)
- While approvals are configured in Environments UI, deployment jobs can document preDeploy/deploy/postDeploy steps and reference checks in environment
jobs:
- deployment: deploy
environment: prod
strategy:
runOnce:
preDeploy: { steps: [ { script: echo 'Pre-checks' } ] }
deploy: { steps: [ { script: echo 'Deploying...' } ] }
routeTraffic: { steps: [ { script: echo 'Routing traffic' } ] }
AKS Blue/Green: AGIC and Service Mesh
# AGIC (Application Gateway Ingress Controller) weighted routing (concept)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
appgw.ingress.kubernetes.io/backend-path-prefix: /
appgw.ingress.kubernetes.io/weight: "blue=90,green=10"
# Istio VirtualService canary
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
spec:
hosts: ["web"]
http:
- route:
- destination: { host: web, subset: blue, port: { number: 80 } }
weight: 90
- destination: { host: web, subset: green, port: { number: 80 } }
weight: 10
# Linkerd SMI TrafficSplit
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
spec:
service: web
backends:
- service: web-blue
weight: 90
- service: web-green
weight: 10
Helm Rollback Strategies
- task: HelmDeploy@0
inputs: { command: 'rollback', releaseName: 'web', version: '$(Release.PreviousRelease)' }
GitVersion / SemVer Tagging
- task: GitVersion@5
inputs: { preferBundledVersion: true }
- script: echo "##vso[build.updatebuildnumber]$(GitVersion.SemVer)"
Submodules and Multi-Repo Checkout
steps:
- checkout: self
submodules: true
- checkout: git://Project/Repo
Artifact Retention and Cleanup
- task: DeleteFiles@1
inputs: { SourceFolder: '$(Build.ArtifactStagingDirectory)', Contents: '**/*.tmp' }
retention:
artifacts: 30
ACR Tasks and Image Builds
- task: AzureCLI@2
inputs:
azureSubscription: 'svc-conn'
scriptType: bash
inlineScript: |
az acr build --registry $(ACR_NAME) --image web:$(Build.SourceVersion) .
Azure DevOps REST for Releases
- script: |
az devops invoke --area release --resource releases --http-method POST --api-version 7.1 --in-file release.json
Quality Gates and Blockers
- task: SonarQubePrepare@5
- script: sonar-scanner -Dsonar.qualitygate.wait=true
IaC Security: tfsec / checkov / terrascan
- script: tfsec infra/terraform --format junit --out tfsec.xml || true
- script: checkov -d infra/terraform -o junitxml > checkov.xml || true
- task: PublishTestResults@2
inputs: { testResultsFormat: JUnit, testResultsFiles: 'tfsec.xml,checkov.xml' }
ZAP Authenticated Scans
- task: OWASPZAP@2
inputs:
scantype: 'targeted'
url: 'https://staging'
authmode: 'form'
username: '$(ZAP_USER)'
password: '$(ZAP_PASS)'
context: 'zap-context.context'
Trivy SARIF Uploads
- script: trivy image --scanners vuln,secret --format sarif -o trivy.sarif $(ACR_LOGIN_SERVER)/web:$(Build.SourceVersion) || true
- task: PublishBuildArtifacts@1
inputs: { PathtoPublish: 'trivy.sarif', ArtifactName: 'trivy-report' }
Code Coverage Thresholds and Flaky Test Quarantine
- script: node scripts/check-coverage.js --min=80
- script: node scripts/quarantine-flaky.js
Pipeline Caching Nuances
- Cache keys must include OS and relevant lockfiles; restoreKeys provide fallbacks
- Avoid caching node_modules; prefer npm cache and npm ci
Parallel and dependsOn Patterns
stages:
- stage: Test
jobs:
- job: unit
- job: integration
dependsOn: unit
condition: succeeded('unit')
Timeouts and Retries
jobs:
- job: e2e
timeoutInMinutes: 45
cancelTimeoutInMinutes: 5
Secure Variables and Variable Groups
- Mark secrets; limit access to variable groups; use Key Vault references where possible
Audits and Log Exports
- script: az devops audit log download --start-time 2025-10-01 --end-time 2025-10-27 --output audit.json
Dashboards and Alerts Examples
- Build dashboard: success %, duration p95
- Release dashboard: lead time, MTTR
- Alerts: failed build > N, blocked PRs, SLO breach
Runbooks and SOPs
Pipeline Failing at Restore
- Clear cache; verify registry access; increase timeout
AKS Rollback
- helm rollback web; verify pods; switch ingress traffic
Agent Starvation
- Add agents; split pipelines; throttle concurrency
Extended FAQ (301–500)
-
How to manage env-specific values?
Use variables, variable groups, and Helm values per env. -
Can I use GitHub Actions with ADO?
Yes—invoke via CLI or service connections. -
How to implement canary?
Service mesh or ingress weighted routing, plus gates. -
How to handle PR builds?
Use pr triggers with path filters; build validations. -
How to protect main?
Branch policies: required reviewers and status checks. -
How to keep YAML DRY?
Templates and extends. -
How to secure agents?
Ephemeral, patched, least privilege. -
How to run DAST auth?
ZAP with context and credentials. -
How to enforce code coverage?
Check thresholds and fail stage. -
How to manage artifacts?
Publish and clean up with retention. -
How to accelerate Docker builds?
Layer caching and ACR tasks. -
How to enforce IaC policies?
Conftest and PR checks. -
How to avoid secret sprawl?
Key Vault + OIDC; audit variables. -
Azure vs AKS for apps?
App Service simpler; AKS flexible. -
SLA for pipelines?
Internal targets for success rate and time. -
When to break pipelines?
On quality/security gates failing. -
Final acceptance?
SLOs healthy; audits pass; runbooks ready.
App Service and Functions Deployments
# App Service via Azure WebApp
- task: AzureWebApp@1
inputs:
azureSubscription: 'svc-conn'
appName: 'web-app-dev'
package: '$(Pipeline.Workspace)/drop/web.zip'
# Azure Functions
- task: AzureFunctionApp@1
inputs:
azureSubscription: 'svc-conn'
appType: functionAppLinux
appName: 'func-app-dev'
package: '$(Pipeline.Workspace)/drop/func.zip'
SQL Database Migrations (sqlpackage/DbUp)
- task: SqlAzureDacpacDeployment@1
inputs:
azureSubscription: 'svc-conn'
ServerName: '$(SQL_SERVER)'
DatabaseName: 'appdb'
SqlUsername: '$(SQL_USER)'
SqlPassword: '$(SQL_PASS)'
DacpacFile: 'db/artifacts/app.dacpac'
DeployType: 'DacpacTask'
- task: Bash@3
inputs:
targetType: inline
script: |
dotnet tool install -g dbup-cli
dbup upgrade --connection $(SQL_CONN) --scripts db/migrations
Key Vault References in App Service
az webapp config appsettings set -g rg -n web-app-dev --settings @appsettings.json
# use @Microsoft.KeyVault(SecretUri=...) values in settings
Azure Monitor / Log Analytics Integration
- task: AzureCLI@2
inputs:
azureSubscription: 'svc-conn'
scriptType: bash
inlineScript: |
az monitor metrics list --resource $(APP_ID) --metric Requests --interval PT1M
- task: AzureMonitor@1
inputs:
azureSubscription: 'svc-conn'
resourceGroupName: 'rg'
resourceName: 'web-app-dev'
metricName: 'Http5xx'
timeSpan: 'PT10M'
Scheduled Pipelines / Cron Triggers
schedules:
- cron: "0 3 * * 1-5" # weekdays 03:00 UTC
displayName: Nightly build
branches: { include: [ main ] }
always: true
PR Validations and Path Filters
pr:
branches: { include: [ main ] }
paths: { include: [ 'services/api/**' ], exclude: [ 'docs/**' ] }
Mobile Builds (Android/iOS)
# Android
pool: { vmImage: 'macos-latest' }
steps:
- task: Gradle@2
inputs: { workingDirectory: 'android', gradleWrapperFile: 'android/gradlew', tasks: 'assembleRelease' }
# iOS
steps:
- task: InstallAppleCertificate@2
inputs: { certSecureFile: 'ios.p12', certPwd: '$(CERT_PWD)' }
- task: InstallAppleProvisioningProfile@1
inputs: { provisioningProfileLocation: 'secureFiles', provProfileSecureFile: 'ios.mobileprovision' }
- script: xcodebuild -workspace ios/App.xcworkspace -scheme App -configuration Release -archivePath build/App.xcarchive archive CODE_SIGNING_ALLOWED=YES
Test Result Publishing Variants
- task: PublishTestResults@2
inputs: { testResultsFormat: NUnit, testResultsFiles: '**/TestResult.xml' }
- task: PublishTestResults@2
inputs: { testResultsFormat: VSTest, testResultsFiles: '**/*.trx', mergeTestResults: true }
Artifacts Retention Policies
retention:
artifacts: 14
daysToKeep: 30
Multi-Stage Approvals YAML Examples
stages:
- stage: Deploy_Staging
jobs:
- deployment: staging
environment: staging
strategy:
runOnce:
deploy: { steps: [ { script: echo Deploying to staging } ] }
- stage: Deploy_Prod
dependsOn: Deploy_Staging
jobs:
- deployment: prod
environment: prod
strategy:
runOnce:
deploy: { steps: [ { script: echo Deploying to prod } ] }
# Configure environment 'prod' with approvals/checks in UI
Rollback Playbooks
Web App Rollback
- Redeploy previous artifact version from pipeline artifacts
- Verify health probes; restore traffic; open incident
AKS Rollback
- helm rollback web $(Previous)
- Verify pod readiness; adjust ingress weights back to blue
Troubleshooting Guide
Common Failures
- npm install fails: check registry, retry with cache purge
- Docker build OOM: increase memory, multi-stage build, prune layers
- kubectl context not found: ensure az aks get-credentials executed with correct subscription
- Helm upgrade timeout: inspect events, increase timeout, check hooks
Extended FAQ (501–800)
-
How to share templates across repos?
Use repository resources and reference by alias. -
How to pin agent images?
Use explicit vmImage versions or self-hosted. -
Can I use Kustomize with ADO?
Yes—install via script task and apply. -
How to implement OIDC for Key Vault?
Federated credentials in service connection. -
How to do blue/green without mesh?
Two services and ingress routing. -
How to gate on Azure Monitor?
Environment checks with monitor queries. -
How to manage PR environments?
Dynamic namespaces and Helm releases per PR. -
How to reduce pipeline time?
Caches, matrices, and parallel jobs. -
How to handle secrets in tests?
Use Key Vault references and CI secrets; never commit. -
How to push to ACR securely?
Service connection with OIDC; no docker login secrets. -
How to publish SBOM?
Attach as artifact; attest with cosign. -
How to enforce policy for K8s?
Conftest in CI; Gatekeeper in cluster. -
How to do authenticated ZAP scans?
Contexts and credentials; capture tokens. -
How to quarantine flaky tests?
Tag and exclude; track and fix. -
How to approve to prod?
Environment approvals; audit logs. -
How to export audit logs?
az devops audit log download. -
How to schedule nightlies?
Cron schedules in YAML. -
How to route canary 10%?
Istio/Linkerd TrafficSplit or AGIC rules. -
How to rollback?
Keep previous versions and Helm history; scripts. -
Final acceptance?
SLOs healthy, security checks pass, runbooks ready.