Azure DevOps Pipelines
How YAML pipelines move code from commit to production through agents, artifacts, environments, approvals, and deployment gates.
Azure DevOps Pipelines is a CI/CD orchestration system. A pipeline run starts from a commit or manual trigger, executes jobs on agents, publishes immutable artifacts, and promotes those artifacts through environments. The important internal idea is separation of concerns: build creates the package, release promotes the same package, and environments apply checks before production changes happen.
A commit, pull request, scheduled trigger, or manual queue creates an immutable pipeline run. The run captures the YAML version, source commit SHA, variables, and permissions at that moment. That snapshot matters: changing YAML after a run starts does not change the active run.
Each job runs on an agent: Microsoft-hosted, self-hosted, or containerized. The agent checks out source, restores tools, runs steps, and streams logs back to Azure DevOps. Jobs do not share filesystem state unless you explicitly publish artifacts, cache dependencies, or use pipeline variables.
The build stage compiles and tests the application, then publishes a versioned artifact. Later stages should deploy that exact artifact instead of rebuilding from source. Rebuilding per environment creates drift: staging and production may not actually run the same bits.
Deployment jobs bind a release to an environment such as staging or production. Environments can enforce approvals, business-hour checks, branch controls, and resource history. They also give an audit trail of what version went where and who approved it.
Quality gates stop bad changes before production: unit tests, vulnerability scans, infrastructure validation, smoke tests, approval checks, and health probes. A good pipeline fails early for code issues and blocks late for human or operational risk.
Key Concepts
A major phase of the pipeline such as Build, Staging, or Production. Stages define dependency order and are the natural place for approvals and promotion boundaries.
A set of steps that runs on one agent. Jobs can run in parallel, but each job gets its own workspace unless artifacts or caches are used.
The packaged output of a build. Production should deploy the same artifact that passed tests in earlier stages, not a fresh rebuild.
A deployment target with history, approvals, checks, and resource metadata. Use environments for staging and production instead of plain script steps.
The identity and credentials Azure DevOps uses to deploy into Azure. Prefer workload identity federation or managed service principals over long-lived secrets.
An App Service staging slot lets you deploy, warm, test, and then swap to production with minimal downtime.
1# azure-pipelines.yml - build once, promote the same artifact2trigger:3 branches:4 include:5 - main67variables:8 buildConfiguration: Release9 artifactName: webapp1011stages:12- stage: Build13 jobs:14 - job: BuildAndTest15 pool:16 vmImage: ubuntu-latest17 steps:18 - task: UseDotNet@219 inputs:20 packageType: sdk21 version: 8.x22 - script: dotnet restore23 - script: dotnet build --configuration $(buildConfiguration) --no-restore24 - script: dotnet test --configuration $(buildConfiguration) --no-build25 - task: DotNetCoreCLI@226 inputs:27 command: publish28 publishWebProjects: true29 arguments: --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)30 zipAfterPublish: true31 - publish: $(Build.ArtifactStagingDirectory)32 artifact: $(artifactName)3334- stage: Staging35 dependsOn: Build36 jobs:37 - deployment: DeployStaging38 environment: app-staging39 strategy:40 runOnce:41 deploy:42 steps:43 - download: current44 artifact: $(artifactName)45 - task: AzureWebApp@146 inputs:47 azureSubscription: sc-prod48 appName: app-orders-prod49 deployToSlotOrASE: true50 slotName: staging51 package: $(Pipeline.Workspace)/$(artifactName)/**/*.zip5253- stage: Production54 dependsOn: Staging55 jobs:56 - deployment: SwapProduction57 environment: app-production # approval checks live here58 strategy:59 runOnce:60 deploy:61 steps:62 - task: AzureAppServiceManage@063 inputs:64 azureSubscription: sc-prod65 WebAppName: app-orders-prod66 SourceSlot: staging67 SwapWithProduction: true
Pipelines are production control planes. A small YAML mistake can bypass approvals, deploy different code than the version tested in staging, leak secrets through logs, or swap the wrong App Service settings. Understanding agents, artifacts, environments, service connections, and deployment jobs turns CI/CD from a pile of scripts into a reliable release system.
Common Pitfalls
1Production Ran Different Code Than Staging
A team had separate staging and production stages, but each stage rebuilt the app from source before deploying. Staging passed smoke tests. Production failed with a dependency mismatch 20 minutes later.
The production stage restored a newer transitive package version because the dependency lock was not enforced. Staging and production looked like the same commit, but they were not the same artifact.
Changed the pipeline to build once, publish a zipped artifact, and deploy that artifact to every environment. Added lock-file restore and artifact checksum logging.
Takeaway: A release pipeline should promote an artifact, not a Git branch. Build once, verify once, promote the same bits.
2Approval Gate Bypassed by a Script Task
Production deployments required approval in Azure DevOps, but one YAML path used a regular job with an Azure CLI task instead of a deployment job.
Environment approvals only apply to deployment jobs targeting that environment. The script had production credentials through a service connection, so it deployed without triggering the approval gate.
Moved all production changes into deployment jobs targeting the production environment. Scoped the production service connection to only those jobs and enabled branch controls.
Takeaway: Approvals protect environments, not arbitrary scripts. Use deployment jobs for anything that changes production.
3Slot Swap Sent Staging Settings to Production
A pipeline deployed to an App Service staging slot and swapped to production. After the swap, the app started calling staging dependencies from production traffic.
Several app settings were not marked slot-sticky. During the swap, environment-specific settings moved with the app code.
Marked database connections, API endpoints, and feature flags as deployment slot settings. Added a pre-swap validation step that compares slot-sticky settings against an allowlist.
Takeaway: Deployment automation must know platform behavior. Slot swaps are safe only when slot-specific settings stay pinned to the slot.
Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.