DevOps Peach

DevOps & CI/CD · CI/CD with Azure DevOps · Lesson 4 of 4

Deploying to Azure — Stages, Environments, Approvals, Service Connections

Multi-stage pipelines use dependsOn and condition; deployment jobs record history on an Environment, where approvals and checks live; service connections use workload identity federation; internal packages go to Azure Artifacts, images to ACR.

Practice interactively →

stages:
  - stage: Build
    jobs:
      - job: build
        steps:
          - script: npm ci && npm run build
          - publish: dist
            artifact: web
  - stage: DeployProd
    dependsOn: Build
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: web
        environment: prod
        strategy:
          runOnce:
            deploy:
              steps:
                - task: AzureWebApp@1
                  inputs:
                    azureSubscription: sc-devops-lab
                    appName: app-devops-lab-01
                    package: $(Pipeline.Workspace)/web
flowchart LR
  B["Build<br/>test, publish artifact"] --> DD["DeployDev<br/>environment dev"]
  DD --> AP{"Approval<br/>on Environment prod"}
  AP --> DP["DeployProd<br/>AzureWebApp@1"]
  DP -- "service connection sc-devops-lab<br/>(workload identity federation)" --> APP["app-devops-lab-01"]
Concept Key points
dependsOn / condition Stage order; for example deploy to prod only from main
Deployment job - deployment: + environment: + strategy: runOnce/rolling/canary; records deployment history
Approvals and checks Configured on the Environment (UI/REST): approvers, business hours… — not written in YAML
Service connection Lets the pipeline call Azure/AWS/K8s/registries; for Azure Resource Manager use workload identity federation (no stored client secret), scoped to a resource group
Deploy tasks AzureWebApp@1 (optionally into a slot, then AzureAppServiceManage@0 to swap), Docker@2 build & push to ACR → KubernetesManifest@1 to AKS, AzureCLI@2
Azure Artifacts Internal package feeds, upstream sources proxying npmjs/NuGet.org; not for Docker images
Classic Releases The old way; multi-stage YAML is the main path

Traps: putting approvals in YAML; pasting a client secret into a variable; giving a service connection Contributor on the whole subscription.

Command Meaning
az pipelines run --name my-app-ci --branch main Run a pipeline
az pipelines runs list -o table / az pipelines runs show --id 42 Inspect runs
az devops service-endpoint list -o table List service connections
az artifacts universal publish --feed my-feed --name web-dist --version 1.0.0 --path ./dist --description "web build" Publish a Universal package

A deployment job in YAML

- stage: DeployProd
  dependsOn: Build
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
  jobs:
  - deployment: Web
    environment: prod
    pool: { vmImage: ubuntu-latest }
    strategy:
      runOnce:
        deploy:
          steps:
          - download: current
            artifact: webapp
          - task: AzureWebApp@1
            inputs:
              azureSubscription: sc-prod
              appName: app-devops-lab-01
              package: $(Pipeline.Workspace)/webapp/*.zip

A deployment job downloads artifacts published earlier with PublishPipelineArtifact@1, so production gets exactly the build that passed tests. runOnce also supports preDeploy, routeTraffic, postRouteTraffic, and on: failure hooks for smoke tests and rollbacks.

Environments and checks

An environment records every deployment to it, linking runs, commits, and work items. Checks configured on it include manual approvals, business hours, required template (only pipelines extending an approved template may deploy), exclusive lock (one deployment at a time), and invoke REST API or Azure Function for automated gates such as "no open incidents". Kubernetes and VM resources can be added to an environment for health views.

Infrastructure in the same pipeline

A stage can run terraform plan and apply (through AzureCLI@2 or a Terraform task) before deploying the app, with the plan stage requiring approval. Keep state in a remote backend such as an Azure Storage account, and use separate service connections per environment.

Promoting through environments

A common layout is Build → Dev → Test → Prod, each a stage with its own environment and variable group. The same artifact moves forward; only configuration changes. Failed smoke tests stop promotion, and redeploying the previous run is the fastest rollback.

Practice interactively →