Build and release pipelines

AZ-400 notes, 50–55% of the exam, updated November 2026

This is 50-55% of AZ-400, more than every other domain together, so most of your study time belongs here. Expect case studies and scenario questions on YAML pipeline structure, GitHub Actions workflows, agent choices, deployment strategies and IaC. Many questions ask you to complete or fix YAML, so read the syntax closely.

Package management

Azure Artifacts and GitHub Packages

Azure Artifacts GitHub Packages
Package types NuGet, npm, Maven, Python (PyPI), Cargo, Universal Packages npm, NuGet, Maven/Gradle, RubyGems, and container images (Container registry, ghcr.io)
Organisation Feeds (organisation or project scoped), with views Packages attached to a repo or an organisation/user
Upstream sources Yes: public (nuget.org, npmjs, Maven Central, PyPI) and other Azure Artifacts feeds No upstream feature for most types
Auth PAT, Entra ID credential provider, pipeline’s build service identity GITHUB_TOKEN in Actions, PAT with read:packages/write:packages
Fit Azure DevOps shops, internal libraries, caching public packages GitHub-centric teams, publish alongside source

Pick Azure Artifacts when you need upstream sources, views or Universal Packages (any file set). Pick GitHub Packages when the code and workflows live on GitHub and you want permissions that follow the repo.

Feeds, views and upstream sources

  • Feed scope: project-scoped feeds follow the project’s visibility; organisation-scoped feeds can be shared. A feed has its own permissions: Reader, Collaborator (can also save packages from upstream), Contributor (publish), Owner.
  • Views are filtered, read-only windows on a feed. Every feed has @Local, @Prerelease and @Release. Promote a package version to a view (for example @Release) when it passes quality gates, so consumers subscribe to the view and only see approved packages. Promotion changes the view membership, not the package, and doesn’t rebuild.
  • Upstream sources let a feed resolve packages from public registries or other feeds. When you install a package via upstream, a copy is saved to the feed (“package caching”), so the build keeps working if the public source disappears. Upstream order and precedence: local packages win over upstream, and public package names that clash with private names are the dependency confusion risk. Mitigate by using an upstream behaviour setting that blocks the externally sourced version of a package name you publish internally.
  • Retention policies on a feed delete old versions (maximum versions per package, days to keep recently downloaded packages).
  • Connect to a feed: nuget.config, .npmrc, settings.xml/pom.xml and pip.conf carry the feed URL. In pipelines use the task NuGetAuthenticate@1, npmAuthenticate@0, MavenAuthenticate@0, TwineAuthenticate@1 or PipAuthenticate@1, which injects credentials from the build identity. The project’s Build Service account needs Contributor on the feed to publish.
- task: NuGetCommand@2
  inputs:
    command: push
    packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg'
    nuGetFeedType: internal
    publishVstsFeed: 'MyProject/MyFeed'

Universal Packages carry any set of files with a name and SemVer version: az artifacts universal publish or the UniversalPackages@0 task.

Versioning strategy

Scheme Format When
SemVer MAJOR.MINOR.PATCH (optional -prerelease and +build). Major for breaking changes, minor for backward-compatible features, patch for backward-compatible fixes Libraries and APIs where consumers need to judge compatibility. Pre-release: 1.4.0-beta.2
CalVer Date-based, e.g. 2026.11.3 or 26.11 Products released on a schedule, where the date matters more than compatibility
Build number / run number Increasing integer or $(Build.BuildId) Internal artifacts and traceability
  • Dependency version ranges: ^1.2.0 (npm) accepts minor and patch updates, ~1.2.0 only patches, NuGet [1.0,2.0) is a range. Pin exact versions or use a lock file (package-lock.json, packages.lock.json) for reproducible builds.
  • Azure Pipelines name (run number) can be a format such as $(Date:yyyyMMdd)$(Rev:.r) or 1.0$(Rev:.r). $(Rev:.r) increments for repeats of the same prefix.
  • Tools such as GitVersion and Nerdbank.GitVersioning compute SemVer from branches and tags, so version comes from Git history rather than hand-edited files.
  • Pipeline artifact versioning: give each pipeline artifact a version linked to the run (run number, commit SHA and tag), so every deployed artifact is traceable to a build. For Docker images, tag with both the immutable SHA or version and a moving tag (latest, 1.4). Deploy by digest for strict immutability.

Testing strategy

Types of tests

Level Scope Run where
Local / developer Unit tests, linters, analysers, pre-commit hooks, test containers Dev machine and IDE
Unit One function or class, no external dependencies, fast CI build on every push
Integration Components with real dependencies (database, queue, API) CI against ephemeral services or a test environment
UI/end-to-end Whole system through the UI or API (Playwright, Selenium) Test stage after deploy
Load / performance Throughput and latency under load (Azure Load Testing, JMeter, Locust) Pre-production stage, a scheduled run
Security SAST, DAST, dependency scan See the security notes
Smoke A few critical checks after each deployment Right after deploy
Chaos / resilience Inject faults (Azure Chaos Studio) Staging

Follow the test pyramid: many fast unit tests, fewer integration tests, few end-to-end tests. Shift tests left (earlier) where they are cheaper to fix.

Implementing tests in a pipeline

  • Azure Pipelines: VSTest@3, DotNetCoreCLI@2 with command: test, Npm@1/script steps with JUnit output, PublishTestResults@2 for results in JUnit, NUnit, xUnit, VSTest or TRX formats, shown in the Tests tab with trends and flaky test detection. Test tasks can run in parallel across agents using the multi-agent job strategy (parallel:) with VSTest test slicing (distribute by number of tests, past running time or assembly).
  • GitHub Actions: run the test command in a step; publish results with an action such as dorny/test-reporter or upload them as artifacts. Use a services: block to start database containers alongside a job.
  • Test agents: run UI tests on an agent with a display (a Microsoft-hosted windows-latest agent works, or a self-hosted agent in interactive mode). Use Azure Test Plans for manual and exploratory testing.
  • Azure Load Testing runs JMeter scripts (or URL tests) at scale, with a pipeline task and fail criteria (for example average response time over 500 ms) that fail the run.

Code coverage

  • Collect coverage with --collect:"XPlat Code Coverage" (Coverlet) or VSTest settings, jacoco (Java), istanbul/nyc (JavaScript) and coverage.py.
  • Publish with PublishCodeCoverageResults@2 (supports Cobertura and JaCoCo). Azure Pipelines shows a Code Coverage tab, and PRs can show a coverage comment.
  • Enforce minimum thresholds in the build (a failing test command) or with a quality gate in SonarCloud/SonarQube or Codecov. In Azure Repos, a status check or build validation policy blocks a PR when coverage drops.

Quality and release gates

  • Quality gates block progress based on measured quality: tests pass, coverage, static analysis (SonarQube quality gate), security scan results, a zero-critical-vulnerabilities rule.
  • Release gates (classic releases) and checks on environments (YAML) test automatically before or after a deployment: Invoke Azure Function, Invoke REST API, Query Azure Monitor alerts, Query work items, plus Required template and Evaluate artifact checks. Gates re-evaluate on an interval until they pass or time out.
  • Approvals, branch control, business hours and exclusive lock checks add governance. In GitHub use environment protection rules: required reviewers, wait timer, deployment branches and tags, and custom protection rules (GitHub Apps).
  • Governance and security gates: require a Defender for Cloud / Advanced Security scan, a signed artifact or an SBOM before production.

Choosing and building pipelines

Azure Pipelines vs GitHub Actions

Azure Pipelines GitHub Actions
Config azure-pipelines.yml, or classic editor .github/workflows/*.yml
Hierarchy Pipeline > stages > jobs > steps Workflow > jobs > steps (jobs ordered by needs)
Triggers trigger, pr, schedules, resources on: (push, pull_request, schedule, workflow_dispatch, workflow_call, repository_dispatch, release…)
Reuse Templates (stage, job, step, variable) Reusable workflows (workflow_call), composite actions, Marketplace actions
Environments Environments with checks/approvals, deployment history, resource targets (VMs, Kubernetes) Environments with protection rules and secrets
Agents Microsoft-hosted, self-hosted, scale set agents GitHub-hosted, self-hosted, larger runners
Strengths Mature release management, test plans, boards, classic releases, deployment groups Close to the code, vast action ecosystem, OIDC, fork and community friendliness

Choose by where the code lives and what the team already uses. GitHub repo + Azure DevOps boards and release governance is common, so know how to bridge them.

GitHub and Azure Pipelines integration

  • Install the Azure Pipelines GitHub App (from Marketplace) and create a pipeline that builds a GitHub repo. A GitHub service connection (OAuth, PAT or the GitHub App) authenticates.
  • The app posts checks on pull requests and commits, so branch protection can require the Azure Pipelines check.
  • PRs from forks are not given secrets by default. Use Settings > Triggers > Pull request validation options to control forks.
  • Use resources: repositories: - repository: self and type: github with endpoint: to check out other GitHub repos.
  • The reverse: trigger GitHub Actions workflows with repository_dispatch from Azure Pipelines, or run Azure Pipelines from Actions using the Azure DevOps CLI or a REST call.

Agents and runners infrastructure

Option Notes
Microsoft-hosted agents Fresh VM per job, patched by Microsoft, images such as ubuntu-latest, windows-latest, macos-latest. Time-limited (60 minutes for private projects on free tier, up to 360 minutes on paid parallel jobs, and no limit for self-hosted). No access to private networks
Self-hosted agents You install the agent on a VM, container or physical machine. Use for private network access, special software or hardware, caching between runs, long jobs, no job time limit. You maintain patching and security. Agents connect outbound over HTTPS (443) to Azure DevOps, so no inbound firewall opening
Azure Virtual Machine Scale Set agents Self-hosted agents that autoscale on a scale set, with ephemeral machines. Good balance of control and elasticity, cost only when running
Managed DevOps Pools Microsoft-managed scale with your own VNet, images and sizes, and spot capacity. Less operational burden than scale set agents
GitHub-hosted runners Ephemeral VMs, ubuntu-latest, windows-latest, macos-latest. Larger runners (more CPU/RAM, static IPs, Arm, GPU) cost more and are billed per minute
Self-hosted runners Install the runner app, label it (runs-on: [self-hosted, linux, gpu]). Runner groups control which repos use them at organisation level. Actions Runner Controller (ARC) runs ephemeral runners in Kubernetes
Azure Private Networking for GitHub-hosted runners GitHub-hosted runners injected into your Azure virtual network

Design factors the exam lists:

  • Cost: hosted agents are billed per parallel job (Azure DevOps) or per minute (GitHub). Self-hosted agents cost infrastructure but no per-minute licence fee beyond the free self-hosted parallel job (each self-hosted parallel job needs a licence beyond the first free one). Use scale sets or spot capacity for bursts.
  • Tool selection: pre-installed software lists exist for hosted images. If you need something rare, install in the pipeline (UseDotNet@2, NodeTool@0, actions/setup-node) or use a custom image or container job.
  • Licences: Azure DevOps has parallel jobs: one free Microsoft-hosted job (limited minutes per month; public projects get more) and one free self-hosted job, additional ones purchased per parallel job. Basic users license is 5 free per organisation.
  • Connectivity: need to reach a private database, on-premises system or a private Azure endpoint -> self-hosted agents in the VNet or VNet-injected hosted agents. Firewalls allow only the Azure DevOps and GitHub service endpoints outbound.
  • Maintainability: prefer ephemeral, image-built agents (scale sets, ARC) over long-lived pets. Automate image builds with Packer, patch the image, and reimage.

Agent pools group agents. An agent pool can be organisation-wide with project-level pools; use demands (pool: demands:) to pick an agent with certain capabilities. A deployment group (classic) installs an agent on target servers for deployment.

pool:
  vmImage: ubuntu-latest        # Microsoft-hosted

pool:
  name: MyPool                  # self-hosted
  demands:
    - Agent.OS -equals Linux

Triggers

Azure Pipelines:

trigger:
  branches:
    include: [main, release/*]
    exclude: [experimental/*]
  paths:
    include: [src/*]
    exclude: [docs/*]
  tags:
    include: [v*]
  batch: true                    # one run for several quick pushes

pr:
  branches:
    include: [main]
  autoCancel: true

schedules:
  - cron: "0 3 * * 1-5"          # UTC
    displayName: Weeknight build
    branches:
      include: [main]
    always: false                # only if changed

resources:
  pipelines:
    - pipeline: upstream
      source: Build-Pipeline
      trigger:
        branches: [main]
  • trigger: none disables CI triggers. Pipeline resource triggers chain pipelines. Scheduled runs use UTC cron and always: true to run even without changes. For Azure Repos Git, PR validation is done through a branch policy (build validation), not the pr: keyword; pr: only works with GitHub and Bitbucket repos.
  • Path filters limit runs to changes in a folder (monorepos).
  • Commit message [skip ci] or ***NO_CI*** skips a run.

GitHub Actions:

on:
  push:
    branches: [main]
    paths: ['src/**']
    tags: ['v*']
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 3 * * 1-5'
  workflow_dispatch:
    inputs:
      environment:
        type: choice
        options: [dev, prod]
  workflow_call:
  workflow_run:
    workflows: [Build]
    types: [completed]

workflow_dispatch is a manual trigger, workflow_call makes the workflow reusable, workflow_run chains workflows, repository_dispatch accepts external API events.

YAML pipeline structure (Azure Pipelines)

trigger: [main]

variables:
  - group: shared-vars          # variable group
  - name: buildConfiguration
    value: Release

stages:
  - stage: Build
    jobs:
      - job: BuildJob
        pool: { vmImage: ubuntu-latest }
        steps:
          - script: dotnet build -c $(buildConfiguration)
          - publish: $(Build.ArtifactStagingDirectory)
            artifact: drop

  - stage: DeployDev
    dependsOn: Build
    condition: succeeded()
    jobs:
      - deployment: DeployWeb
        environment: dev
        strategy:
          runOnce:
            deploy:
              steps:
                - download: current
                  artifact: drop
                - task: AzureWebApp@1
                  inputs: { azureSubscription: 'svc-conn', appName: 'myapp', package: '$(Pipeline.Workspace)/drop/*.zip' }
  • Stages are major divisions (Build, Test, Deploy) that run sequentially by default. dependsOn creates a graph, dependsOn: [] makes it run in parallel with others.
  • Jobs in a stage run in parallel by default (subject to parallel job capacity) and each gets its own agent and clean workspace. Use dependsOn for order.
  • Steps run in order on one agent: script, bash, pwsh, task, checkout, download, publish.
  • A deployment job (deployment:) targets an environment, records deployment history and supports strategies runOnce, rolling, canary, each with lifecycle hooks preDeploy, deploy, routeTraffic, postRouteTraffic, on: failure/success.
  • Job types: agent job, container job (container: image to run steps in), server job (pool: server, runs on Azure DevOps itself with tasks such as Manual Validation, Invoke REST API, Azure Function), and deployment job.
  • Conditions: condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')). Defaults to succeeded(). always(), failed(), canceled() and succeededOrFailed() are the others.
  • Strategies: matrix runs a job several times with variable sets, parallel: N runs N copies, maxParallel caps. This is used for multi-platform and sharded tests.
  • Execution order and parallelism: design with dependsOn for fan-out (parallel test jobs) and fan-in (a final job that dependsOn all of them).
  • Predefined variables: Build.BuildId, Build.SourceBranch, Build.SourceVersion, Pipeline.Workspace, Build.ArtifactStagingDirectory, System.AccessToken.
  • Runtime parameters (parameters:) are typed and evaluated at queue time with ${{ }}. Variables use macro $(var) at runtime, ${{ variables.x }} at compile time, and $[ variables.x ] runtime expression. Compile-time expressions can’t see runtime values.
  • Output variables between jobs/stages: echo "##vso[task.setvariable variable=x;isOutput=true]value" then refer via dependencies.<job>.outputs['<step>.x'] (or stageDependencies across stages).
  • Pipeline artifacts (publish/download, PublishPipelineArtifact@1) are for passing files within and across runs. Build artifacts is the older mechanism. Caching dependencies uses the Cache@2 task.

GitHub Actions workflow structure

name: ci
on: [push, pull_request]
permissions:
  contents: read
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node: [20, 22]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: ${{ matrix.node }}, cache: npm }
      - run: npm ci && npm test
  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    steps:
      - run: echo deploy
  • Jobs run in parallel unless needs orders them. A job’s outputs flow to dependants through needs.<job>.outputs.
  • permissions limit the GITHUB_TOKEN (set the least needed; default may be read-only).
  • concurrency groups prevent overlapping runs and cancel-in-progress cancels older ones (typical for deployments to one environment).
  • Artifacts: actions/upload-artifact and download-artifact pass files between jobs; cache with actions/cache or the cache: option of setup actions.
  • Matrix strategy with include/exclude runs combinations.
  • Pin actions to a full commit SHA for supply-chain safety.
  • Action types: JavaScript, Docker container, composite (bundle steps in action.yml).
  • Reusable workflows: on: workflow_call in the callee; the caller uses uses: org/repo/.github/workflows/build.yml@main with with: and secrets: inherit. A reusable workflow is a whole job set; a composite action is a bundle of steps inside one job.
  • Workflow commands: $GITHUB_OUTPUT, $GITHUB_ENV, $GITHUB_STEP_SUMMARY.

Complex pipelines

  • Hybrid pipelines: deploy to on-premises and cloud in one pipeline. Self-hosted agents inside the datacentre reach local servers, while hosted agents deploy to Azure; or use environments with VM resources (register a server in an Environment) to deploy to it from a pipeline. For Kubernetes, register a cluster namespace as an environment resource.
  • VM templates / images: build golden images with Packer (or Azure VM Image Builder and Azure Compute Gallery) in a pipeline so scale set agents and VMs start identical and patched.
  • Self-hosted runners/agents: install, register with a PAT or Entra identity, run as a service, use ephemeral mode (--ephemeral for runners; one job then removed), keep images patched, and place them in the network that needs reaching.
  • Multi-repo: resources: repositories plus checkout: self and checkout: other.
  • Container jobs run steps inside a specified image, so the toolchain is consistent.
  • Matrix and parallel jobs for multi-version testing and sharding.

Reusable pipeline elements

Mechanism Description
YAML templates (Azure Pipelines) template: references include step, job, stage and variable templates, with parameters. Extends templates (extends: template: ...) enforce a required structure. Combined with an environment Required template check, they force every pipeline to use approved steps
Task groups Classic pipelines only: bundle a set of tasks reused across classic definitions. Not available in YAML (use templates)
Variables Defined in YAML, in the UI, or via templates. Secret variables are masked and not passed to forks or scripts automatically (map into env:)
Variable groups Library items shared across pipelines, optionally linked to Azure Key Vault to fetch secrets at run time. Pipelines need permission to use the group. Reference with - group: name
Secure files Library files (certificates, provisioning profiles)
Template expressions ${{ if eq(parameters.env, 'prod') }}: conditional insertion, ${{ each x in parameters.list }} loops
GitHub reusable workflows / composite actions As above
GitHub variables and secrets Organisation, repository and environment levels. Environment-level overrides repository, which overrides organisation
# templates/build-steps.yml
parameters:
  - name: configuration
    default: Release
steps:
  - script: dotnet build -c ${{ parameters.configuration }}

# azure-pipelines.yml
steps:
  - template: templates/build-steps.yml
    parameters:
      configuration: Debug

Templates can live in another repository (template: file.yml@repoAlias) so a platform team shares them.

Checks and approvals with environments

An Environment is a named target for deployments (Kubernetes namespace, VM set, or just a logical stage). Environments give deployment history, traceability to commits and work items, and checks.

Checks (set on the environment, resource or service connection, secure file or variable group):

  • Approvals (named users or groups, one or all must approve, timeout, instructions).
  • Branch control (only allow from matching branches, such as refs/heads/main).
  • Business hours.
  • Invoke Azure Function / REST API, Query Azure Monitor alerts.
  • Required template: the pipeline must extend a given template.
  • Exclusive lock: one run at a time on the resource, others queue (or only the latest proceeds with lockBehavior: sequential or runLatest in the pipeline).
  • Evaluate artifact (check image policy).

A check on the service connection or agent pool runs before the job even starts using it. Checks run in parallel, with the exception that approvals and branch control are evaluated in a defined order (static checks first, then dynamic ones). Environment approvals are for the deployment job, and a Manual Validation task (server job) is a different, in-pipeline pause.

GitHub environments: required reviewers (up to 6), wait timer, deployment branches and tags, environment secrets and variables, custom deployment protection rules. Environment secrets are only released to the job after the approvals.

Deployments

Deployment strategies

Strategy How Risk and rollback Best for
Recreate / big bang Stop old, start new Downtime, rollback = redeploy Dev, tolerant systems
Rolling Update instances in batches (maxParallel) Mixed versions during rollout, slow rollback Stateless VM sets and Kubernetes
Blue-green Two identical environments. Deploy to idle (green), test, switch traffic (swap slots, DNS or load balancer) Instant rollback by switching back, costs double Zero-downtime releases
Canary Send a small percentage of traffic (5%, 25%…) to the new version, watch metrics, then widen Small blast radius Risky changes with good telemetry
Ring-based / progressive exposure Release to rings in order: canary/internal -> early adopters -> broad -> everyone, with validation between Staged, controlled Large user bases, Microsoft’s own model
Feature flags Deploy code dark, turn the feature on per user or percentage at runtime Switch off without redeploying Decouple deploy from release
A/B testing Different users get different variants to compare a business metric Experiment, not a safety net Product experimentation

Key differences: canary is about safety (lower risk), A/B is about learning (compare variants), blue-green is about switching all traffic at once with an instant way back, rings are staged audiences. Deployment decoupled from release is what feature flags give.

In YAML, deployment: jobs support strategy: runOnce, rolling (with maxParallel) and canary (with increments: [10, 20]) lifecycle hooks.

Minimising downtime

  • Load balancer (Azure Load Balancer, Application Gateway, Front Door, Traffic Manager) drains and routes traffic. Take an instance out of rotation, update it, health-check, return it.
  • Rolling deployments on VM scale sets, AKS (RollingUpdate with maxUnavailable and maxSurge) and Azure App Service.
  • Deployment slots (App Service Standard tier and above, Functions Premium/Dedicated): deploy to a staging slot, warm it up, run smoke tests, then swap. The swap redirects traffic without restart and can be swapped back for rollback. Slot-sticky settings (marked “deployment slot setting”) stay with the slot, so connection strings for staging don’t move to production. Swap with preview applies production settings to the staging slot first so you can test. Auto swap swaps after a successful deployment. Testing in production routes a percentage of traffic to a slot (canary).
  • Health probes and readiness/liveness checks avoid sending traffic to cold instances.
  • Database changes must be backward compatible (see below).

Dependency ordering

Deployments often depend on each other (database before app, shared service before consumers). Design the order:

  • Use stages with dependsOn and conditions.
  • Use pipeline resources to trigger a downstream pipeline after an upstream one completes.
  • Add checks (Invoke REST API, Azure Monitor alerts) that confirm a dependency is healthy.
  • Use exclusive lock and deployment ordering so older runs don’t overwrite newer ones.
  • Version interfaces and use backward-compatible changes so consumer and provider can deploy independently. Deploy the provider first (expand), then consumers (migrate), then clean up (contract).
  • For Bicep/ARM, dependsOn and implicit references order resource creation within one deployment.

Hotfix path

A hotfix is an urgent production fix that shouldn’t wait for the normal queue.

  1. Branch from the production tag or release branch (not from main if main has unreleased work).
  2. Make the minimal fix and open a PR with an expedited review (a smaller review quota is allowed by policy, never none).
  3. Run the same pipeline: build, fast tests and smoke tests. Don’t bypass the pipeline.
  4. Deploy through an expedited path (skip non-essential stages or approvals only through an agreed, pre-defined rule).
  5. Merge the fix back to main (and to open release branches) so the bug doesn’t return. Cherry-pick if the branches have diverged.
  6. Tag the hotfix release and update release notes.

Resiliency strategy for deployment

  • Automatic rollback triggered by failed health checks or alerts (a post-deployment gate querying Azure Monitor, then redeploy the last known good artifact or swap back the slot).
  • Immutable artifacts: promote the same package through environments rather than rebuilding.
  • Retries on transient steps (retryCountOnTaskFailure), timeouts, and idempotent scripts that can re-run.
  • Redundancy: deploy to multiple regions or availability zones, ordered one region at a time (Traffic Manager/Front Door for failover), so a bad release doesn’t hit every region.
  • Canary and ring deployments limit blast radius, and feature flags disable a bad feature.
  • Backups and restore points before data changes. Pipeline definitions in source, and a tested disaster-recovery pipeline.

Feature flags with Azure App Configuration

Azure App Configuration stores settings and feature flags. Feature Manager (Microsoft.FeatureManagement libraries) evaluates flags in code.

  • A feature flag has a name, enabled state and optional filters: Percentage (a share of users), Time window (start and end), Targeting (specific users, groups and rollout percentage by group), or custom filters.
  • Code: if (await featureManager.IsEnabledAsync("NewCheckout")) { ... }, or [FeatureGate("NewCheckout")] on controllers, or <feature name="NewCheckout"> in Razor.
  • Dynamic refresh: register a sentinel key so the app reloads configuration when the sentinel changes, without restart.
  • Access with managed identity (App Configuration Data Reader role), and use Key Vault references for secrets.
  • Variants and telemetry in newer releases support A/B experiments.
  • Flags are config, so change them without a deployment. Remove stale flags to limit technical debt.

Application deployment: containers, binaries and scripts

Style What gets deployed Typical tasks
Containers An image built with Docker, pushed to Azure Container Registry (ACR) or ghcr.io, and run on AKS, Container Apps, App Service or Container Instances Docker@2 (buildAndPush), KubernetesManifest@1, HelmDeploy@1, AzureContainerApps@1, azure/aks-set-context, azure/k8s-deploy, azure/webapps-deploy
Binaries/packages A zip, WebDeploy package, JAR, nupkg AzureWebApp@1, AzureFunctionApp@2, azure/webapps-deploy, az webapp deploy
Scripts Bash, PowerShell, az CLI, SSH to VMs, Custom Script Extension AzureCLI@2, AzurePowerShell@5, SSH@0, PowerShell@2, azure/CLI

For container workflows, sign in to the registry with a service connection or OIDC (never the admin account). Build with ACR Tasks (az acr build) if you don’t want a Docker daemon. Scan images before deploy (see the security notes). For Kubernetes: Helm charts or Kustomize parametrise manifests, and GitOps (Flux or Argo CD) pulls from Git instead of the pipeline pushing.

Database deployment tasks

  • Treat the schema as code in source control. Approaches: state-based (SQL project/dacpac, SqlAzureDacpacDeployment@1, SSDT) where a tool diffs desired state against the target, or migration-based (EF Core migrations, Flyway, Liquibase, Redgate) with ordered scripts.
  • Run after infrastructure and before or alongside the app. Use the expand and contract pattern so old and new app versions both work with the schema during rollout.
  • Take a backup or point-in-time restore point before destructive changes. Use SqlAzureDacpacDeployment@1, AzureCLI@2 with sqlcmd, or azure/sql-action in GitHub.
  • Add a manual approval or gate before the production schema change, and test migrations on a copy of production data.
  • Avoid data loss: configure the dacpac BlockOnPossibleDataLoss.
  • The deploy identity should be a managed identity or service principal granted db_owner only as needed.

Infrastructure as code

Configuration management technologies

Tool Style Notes
ARM templates Declarative JSON Native to Azure, verbose
Bicep Declarative DSL that compiles to ARM Azure-only, concise, day-one support for new resource types, no state file, first-class in the exam
Terraform Declarative HCL, multi-cloud Keeps a state file (store in Azure Storage with locking), plan and apply
Azure Resource Manager (deployment stacks) Manage a set of resources as a unit, with deny settings and lifecycle control Deployment stacks protect and clean up resources
Ansible Agentless, YAML playbooks, push Configure OS and apps
Chef, Puppet Agent-based, pull, DSL Configure servers, state enforced
Azure Automation State Configuration (DSC) PowerShell DSC pull Being retired in favour of Azure Machine Configuration (the Automation service’s DSC for Azure/Arc VMs is deprecated, so new designs should use Azure Machine Configuration)
Azure Machine Configuration Audit and configure settings inside guest OS (Windows/Linux), via Azure Policy, works on Arc-enabled servers Replaces Guest Configuration and Automation State Configuration
Cloud-init, Custom Script Extension, VM Applications First-boot or extension-based configuration Simple

Declarative says what the end state is and the engine figures out how. Imperative says how step by step (scripts). IaC wants declarative and idempotent tools: running twice gives the same result.

Immutable infrastructure: replace servers rather than patch in place (images from Packer or Image Builder). Mutable: change existing servers, needing DSC/Ansible/Machine Configuration to stop drift. Configuration drift is reality diverging from the definition.

IaC strategy

  • Source control all templates; protect main with PR review and checks.
  • Automate testing: lint (bicep build, bicep lint, tflint), validate (az deployment group validate), what-if (az deployment group what-if, a PR comment of changes; terraform plan), policy-as-code tests (checkov, PSRule for Azure, Azure Policy), and an ephemeral integration test deployment.
  • Deployment through a pipeline with environments and approvals. Use parameter files (.bicepparam) per environment, modules (published to a Bicep registry in ACR or template specs) and deployment scopes (resource group, subscription, management group, tenant).
  • Secrets from Key Vault references, never in parameter files. Deploy with OIDC federated service principals.
  • Complete vs incremental mode in ARM: incremental (default) leaves resources not in the template; complete deletes them. Use deployment stacks or complete mode carefully.
  • Run what-if before production, and re-run deployment on a schedule to correct drift.
param location string = resourceGroup().location
param storageName string

resource sa 'Microsoft.Storage/storageAccounts@2023-05-01' = {
  name: storageName
  location: location
  sku: { name: 'Standard_LRS' }
  kind: 'StorageV2'
}
- task: AzureCLI@2
  inputs:
    azureSubscription: svc-conn
    scriptType: bash
    scriptLocation: inlineScript
    inlineScript: |
      az deployment group what-if -g rg-app -f main.bicep -p main.bicepparam
      az deployment group create -g rg-app -f main.bicep -p main.bicepparam

Desired state configuration

  • Azure Resource Manager / Bicep define Azure resources and reapply to converge.
  • Azure Policy enforces and audits resource properties (deny, audit, modify, deployIfNotExists).
  • Azure Machine Configuration assesses and (in enforce mode) remediates settings inside machines, such as installed software, registry or file settings, using DSC (Windows) or Chef InSpec (Linux, earlier) packages assigned by policy, covering Azure VMs and Azure Arc servers.
  • Azure Automation State Configuration (DSC pull server) is the legacy service for the same job; know it exists on the exam, but prefer Machine Configuration for new work.
  • Bicep/ARM don’t configure the OS. Combine them with Machine Configuration, Custom Script Extension, VM Applications or Ansible for the guest.

Azure Deployment Environments

Azure Deployment Environments (ADE) lets developers self-service create standardised environments on demand, from catalogs of IaC templates (Bicep, ARM, Terraform, in a GitHub or Azure Repos repo) set up by platform engineers.

  • Pieces: Dev centre (holds catalogs and environment types), Project (developer-facing, maps to team and subscription), Environment types (dev, test, sandbox, mapped to subscriptions with permissions and managed identity), Catalog (templates in Git), and environment definitions (the template plus environment.yaml manifest).
  • Developers create environments from the portal, CLI or developer portal; platform teams control cost and policy centrally through environment types, and can set expiry dates and delete automatically.
  • It works with pipelines: create a deployment environment per PR, run tests, and delete it.
  • It is not a hosting service; it provisions the resources you define.

Maintaining pipelines

Pipeline health

  • Azure Pipelines Analytics > Pipeline reports: pass rate, failure trend, duration, failing tasks, test pass rate and flaky tests. The Tests tab marks a test as flaky when it passes and fails on the same code; you can exclude flaky tests from the pass-rate and the failure status.
  • GitHub Actions: workflow run history, usage metrics, run duration, and status badge. Create alerts for failures with notifications or a workflow that posts to Teams.
  • Track the DORA metrics, queue wait time and failure causes (infrastructure vs code vs tests).
  • Treat flaky tests as defects: quarantine them, fix root causes (timing, shared state, external services), re-run only failed jobs.

Optimise for cost, time, performance and reliability

Goal Techniques
Time Cache dependencies (Cache@2, actions/cache), run jobs and test slices in parallel, shallow clones (fetchDepth), path filters to skip unaffected builds, incremental builds, smaller images, pre-baked agent images, avoid repeating builds (build once, deploy many)
Cost Right-size agents, scale set agents that scale to zero, spot VMs, cancel superseded runs (autoCancel, cancel-in-progress), shorter artifact retention, skip docs-only changes, run expensive tests on schedule or on main only
Performance Faster agent SKUs or larger runners for heavy builds, local SSD, container jobs with cached layers, parallel multi-agent tests
Reliability Retry transient failures, pinned versions and lock files, ephemeral agents, health checks, timeouts, deterministic builds, fix flaky tests

Concurrency

  • Azure DevOps parallel jobs limit how many jobs run at once. Extra jobs queue. Buy more, or move to self-hosted agents (each needs a parallel job licence beyond the free one) and Managed DevOps Pools. Check Project settings > Parallel jobs and Pipelines > Analytics > queue time. Set maxParallel in strategies to avoid hogging capacity, and exclusive locks to serialise deployments. Use lockBehavior.
  • GitHub: job concurrency limits depend on plan (for example the free plan allows 20 concurrent jobs, Team 60, Enterprise 500 for standard runners); macOS jobs have lower limits. Use concurrency: groups to avoid overlaps. Larger runners have their own limits and autoscaling.

Retention strategy

  • Azure Pipelines: retention policies at project settings (days to keep runs, artifacts, pull request runs, and so on), per-pipeline retention leases to keep runs that were released to production (for example a run is retained by a lease while its release is live), and manual retain indefinitely. Pipeline artifacts expire with the run, so retain the ones you deploy or promote.
  • GitHub: artifact and log retention (1 to 90 days, default 90; set per repository, organisation or retention-days: on upload). Packages and release assets stay until deleted. Cache entries are evicted after 7 days unused, with a 10 GB per repository default cap.
  • Dependencies: Azure Artifacts feed retention policies and keeping upstream copies in the feed keep a repeatable build possible. Keep a SBOM for shipped versions.
  • Retain what you need for audits (compliance period) but delete the rest to save storage cost.

Migrate classic to YAML

Classic pipelines are designed in the UI (build definitions and release definitions with stages, tasks, gates). To migrate:

  1. In a classic build pipeline, use Export to YAML (the “View YAML” button) to get a starting file; the Azure Pipelines migration tool and Pipelines > ... > Export to YAML do the same.
  2. Check in azure-pipelines.yml. Convert the release pipeline into stages with deployment jobs mapping to environments.
  3. Replace classic task groups with templates, and variable groups stay (use - group:).
  4. Convert triggers (CI, PR, schedule) into trigger, pr/branch policy, schedules.
  5. Replace release approvals and gates with environment checks.
  6. Move artifact sources to pipeline resources.
  7. Test in parallel with the old pipeline, then disable the classic one.

Why migrate: YAML is versioned with the code, reviewable by PR, reusable through templates, and the only path for new features (classic release pipelines are in maintenance mode and some features like multi-stage and templates exist only in YAML). A gap: classic releases offered manual release creation with per-release variable overrides and deployment groups, which map to runtime parameters and environment VM resources.

Exam checklist

  • You can pick Azure Artifacts (feeds, views, upstream) vs GitHub Packages, and explain feed views promotion and @Local, @Prerelease, @Release.
  • You know SemVer, CalVer and how Azure Pipelines run numbers and GitVersion produce versions.
  • You can design the test stack, publish results and coverage, and place gates, with Azure Load Testing for load.
  • You can compare Azure Pipelines and GitHub Actions, and know the hosted, self-hosted, scale set, Managed DevOps Pool and ARC options and what drives the choice (private network, cost, maintenance).
  • You can write and read YAML: stages, jobs, steps, dependsOn, conditions, deployment jobs, templates, parameters vs variables, output variables, matrix.
  • You know variable groups can link to Key Vault, task groups are classic only, and environments give approvals, branch control, exclusive lock and required template checks.
  • You can contrast blue-green, canary, rolling, ring and A/B, plus feature flags, and name slot swap and sticky settings for zero downtime.
  • You know App Configuration feature filters (percentage, time window, targeting) and the sentinel key.
  • You can describe hotfix flow, database deployment with expand and contract, and rollback designs.
  • You can choose Bicep vs ARM vs Terraform, what-if, Azure Machine Configuration vs Automation State Configuration, and describe Azure Deployment Environments pieces.
  • You know how to monitor pipeline health, optimise caching and concurrency, set retention, and migrate classic pipelines to YAML.

Test what you just read. 133 practice questions on design and implement build and release pipelines.

Practise this domain

All AZ-400 notes