Playbook - AZ-400 Microsoft Azure DevOps Engineer Expert
Last reviewed: May 2026
A scannable reference of architectural patterns the AZ-400 exam tests. Read top-to-bottom, or jump to a section.
Design and Implement Source Control
Enforce code quality (build, test, coverage) before merging a pull request.
On the target branch, configure a build validation policy that triggers a pipeline on PR creation. The pipeline must publish code coverage results. Set a branch policy for minimum coverage.
Why: This enforces quality pre-merge. A standard CI trigger runs post-merge. Release gates are for deployments, not PRs.
Select a Git branching strategy that minimizes merge conflicts and supports rapid, continuous deployment to production.
Implement trunk-based development with short-lived feature branches that are merged to `main` frequently (daily or more).
Why: Keeps branches from diverging significantly, reducing merge conflicts and ensuring the `main` branch is always close to a releasable state.
Protect a critical branch (e.g., `main`) by enforcing code reviews, successful builds, and work item linking before merges.
Configure Branch Policies on the `main` branch in Azure Repos. Enable policies for minimum reviewers, build validation, and work item linking.
Why: Branch policies provide server-side enforcement that cannot be bypassed by developers, ensuring consistent quality and process compliance.
Select a Git branching strategy for a team with scheduled releases, parallel feature development, and a need for dedicated hotfix branches.
Implement the GitFlow branching model, which uses `main`, `develop`, `feature/*`, `release/*`, and `hotfix/*` branches.
Why: GitFlow provides a robust framework for managing complex release cycles, isolating new development from release stabilization and emergency fixes.
A secret was accidentally committed and pushed. It must be completely removed from all of Git history.
First, rotate the exposed secret. Then, use a tool like `git-filter-repo` or BFG Repo-Cleaner to rewrite history, removing the file. Force-push the changes and notify all developers to re-clone.
Why: A simple `git rm` or revert does not remove the secret from the history. History rewriting is required for a permanent purge.
Design and Implement Build and Release Pipelines
Model a complex workflow with parallel stages and dependencies between stages.
Use YAML multi-stage pipelines. Use the `dependsOn` keyword for stage dependencies and configure parallel jobs within stages.
Why: YAML provides the most flexible, code-based approach for complex orchestration, superior to classic pipelines or chaining separate pipelines.
Implement zero-downtime, low-risk deployment for a web app with instant rollback capability.
Use Azure App Service deployment slots. Deploy to a staging (green) slot, validate, then perform a slot swap with production (blue).
Why: A slot swap is an atomic, near-instantaneous operation that redirects traffic. Rollback is as simple as swapping back.
Minimize pipeline duplication for numerous microservices that share common build/deploy steps but require specific customizations.
Create YAML templates in a central repository. In each service-specific pipeline, use the `extends` keyword and pass parameters for customization.
Why: `extends` promotes DRY principles and enforces standards while allowing flexibility through parameters. More powerful than task groups for entire pipeline structures.
Restrict a pipeline stage (e.g., production deployment) to only run on merges to a specific branch (e.g., main).
Use a `condition` on the stage or job. E.g., `condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))`.
Why: PR validation builds use a different source branch reference (e.g., `refs/pull/...`), so this condition correctly prevents deployment during the PR lifecycle.
Deploy applications from Azure DevOps to on-premises servers behind a corporate firewall.
Install self-hosted agents on the on-premises servers. Register them to an agent pool in Azure DevOps.
Why: Self-hosted agents initiate outbound communication to Azure DevOps, so no inbound firewall rules are needed. They can access local network resources for deployment.
Require multi-person approval for production deployments and restrict them to specific maintenance windows.
Define an Azure DevOps Environment for production. Configure approvals with required approvers. Add a "Business Hours" check as a gate to enforce the time window.
Why: Environments centralize deployment controls. Approvals and gates provide robust, automated policy enforcement before a stage runs.
Control feature exposure to users without redeploying the application, with near-real-time updates.
Use Azure App Configuration for feature management. Instrument the application to read flags and enable its dynamic refresh capabilities.
Why: Decouples feature releases from deployments. App Configuration provides a centralized UI and SDKs for dynamic updates, avoiding application restarts.
Manage Kubernetes cluster state declaratively, where Git is the single source of truth and changes are automatically applied.
Deploy a GitOps agent like Flux or ArgoCD to the AKS cluster. Configure the agent to monitor a Git repository containing Kubernetes manifests and automatically synchronize the cluster state.
Why: This pull-based model enables continuous reconciliation and drift detection, which is core to GitOps. It is more robust than push-based `kubectl` pipelines.
Manage Terraform state for team collaboration, ensuring security and preventing concurrent modifications.
Configure the Terraform backend to use an Azure Storage Account. This provides remote state storage, with state locking handled via Azure Blob lease.
Why: Prevents state file corruption from simultaneous `apply` operations and keeps sensitive state data out of source control.
In a monorepo, trigger an application's CI pipeline only when files in its specific directory (or a shared directory) are changed.
In the pipeline's YAML, use the `trigger.paths.include` filter to specify the relevant directories, e.g., `include: ['/apps/frontend/**', '/apps/shared/**']`.
Why: This avoids unnecessary builds for unrelated code changes, saving CI time and compute resources.
Optimize a test stage with both fast (unit) and slow (integration) tests for quicker feedback.
Run unit tests and integration tests in parallel jobs within the same stage.
Why: Parallel execution provides unit test results much faster while slower tests run concurrently. Total stage duration is determined by the longest job, not the sum.
Automatically version a library package based on commit history to clearly communicate the impact of changes (breaking, feature, fix).
Integrate a tool like GitVersion into the CI pipeline. It analyzes commit messages, branches, and tags to automatically calculate a SemVer (Major.Minor.Patch) version.
Why: SemVer provides meaningful versioning that consumers can rely on for dependency management, unlike build numbers or commit hashes.
Deploy an application to multiple geographic regions one by one, with validation after each regional deployment.
Use a multi-stage YAML pipeline with sequential stages, one for each region, using `dependsOn` to enforce order. Use environment gates between stages for validation.
Why: This ring-based deployment model contains the blast radius of a bad deployment to a single region, allowing for rollback before impacting all users.
Configure a pipeline to support a trunk-based development model, ensuring the main branch is always deployable.
Configure a CI trigger on the `main` branch. Enforce PRs with a build validation policy that runs fast, comprehensive tests. Integrate rapid notifications (e.g., to Teams/Slack) for build breaks.
Why: Immediate feedback is critical in trunk-based development. This combination prevents broken code from merging and ensures fast remediation when issues occur.
Pass large artifacts (e.g., ML models, >5GB) between pipeline stages efficiently.
Upload the large artifact to Azure Blob Storage in the producer stage. Pass the blob URI to the consumer stage as an output variable.
Why: Azure Blob Storage is more cost-effective and performant than built-in pipeline artifacts for multi-gigabyte files.
Reduce build times by avoiding re-downloading dependencies (e.g., NuGet, npm) on every run.
Use the `Cache@2` task. Define a key based on the package lock file (e.g., `packages.lock.json`). The task will store and restore the dependency folder.
Why: Can save several minutes per build by restoring from a fast, local cache instead of fetching from external repositories.
Build or deploy the same code against multiple targets (e.g., different OSs, regions) in parallel.
Use a `strategy: matrix` in the YAML pipeline job. Define variables for each combination, which will generate a job for each matrix entry.
Why: A matrix strategy keeps the pipeline definition DRY, creating multiple job variations from a single definition and running them in parallel.
Implement a canary deployment on AKS that automatically shifts traffic and promotes or rolls back based on real-time metrics.
Use a progressive delivery controller like Flagger, integrated with a service mesh (e.g., Istio) and a metrics provider (e.g., Prometheus).
Why: Flagger automates the entire canary analysis process, providing safer and more reliable progressive delivery than manual scripts.
An application pipeline needs to trigger when code changes in its own repository OR in a separate, shared library repository.
In the application's YAML, define the shared library under `resources.repositories` and configure a `trigger` block on that resource.
Why: Creates a declarative dependency between repositories, ensuring the application is always rebuilt with the latest shared components.
A pipeline needs to create temporary infrastructure for testing and ensure it's destroyed afterward, even if tests fail.
Use a multi-stage pipeline with separate apply and destroy stages for IaC (Terraform/Bicep). Configure the destroy stage with `condition: always()`.
Why: The `always()` condition guarantees the cleanup stage runs regardless of the success or failure of previous stages, preventing orphaned resources.
Prevent a production deployment from proceeding unless there is an approved change request in an ITSM tool like ServiceNow.
Configure an Environment gate that invokes the "Query ServiceNow" gate to check the status of the change request.
Why: Automates integration with enterprise change management processes, ensuring compliance without manual hand-offs.
Provide a pool of self-hosted build agents that scales dynamically with demand to reduce queue times and control costs.
Configure an Azure DevOps agent pool using an Azure Virtual Machine Scale Set (VMSS), set to automatically scale based on the number of pending jobs.
Why: VMSS agents combine the customization of self-hosted agents with the elasticity of cloud-hosted agents, optimizing performance and cost.
Deploy database schema changes in a way that prevents data loss and supports rollbacks.
Use a migration tool (e.g., Flyway, DbUp). Implement the expand/contract pattern for schema changes to maintain backward compatibility.
Why: Migration tools provide versioning and control. The expand/contract pattern decouples application and database rollbacks, enabling safer deployments.
Self-hosted agents are running out of disk space from accumulated build artifacts.
In the pipeline YAML, at the job level, configure `workspace: clean: all`.
Why: This preventative pipeline configuration solves the root cause without requiring manual intervention or continuous infrastructure changes.
Integration tests require an isolated database instance for each pipeline run.
Define a container resource (e.g., SQL Server, Postgres) as a service in the pipeline YAML. The test job can then connect to this ephemeral service.
Why: Provides fast, isolated, and automatically cleaned-up dependencies for tests, preventing test interference and simplifying setup.
Improve reliability and performance of package restoration from public repositories (e.g., npmjs, nuget.org).
In Azure Artifacts, create a feed and configure upstream sources pointing to the public repositories. Have clients consume packages from the Azure Artifacts feed.
Why: The feed caches packages from upstream sources, protecting against public repository outages and speeding up restores for frequently used packages.
Deploy a Helm chart to multiple environments (dev, prod) with different configuration values.
Use separate `values-<env>.yaml` files for each environment. In the `HelmDeploy` task, use the `valueFile` input to specify the appropriate file and `overrideValues` to inject dynamic values like image tags.
Why: This pattern separates static environment configuration from dynamic pipeline variables, keeping deployments clean and maintainable.
Develop a Security and Compliance Plan
Securely manage and consume secrets (e.g., connection strings) in pipelines without hardcoding them.
Store secrets in Azure Key Vault. In Azure DevOps, create a Variable Group linked to the Key Vault. Reference secrets from the variable group in the pipeline.
Why: Centralizes secret management, enables rotation without pipeline changes, and provides robust access control and auditing through Key Vault.
Implement security scanning in the CI pipeline to detect vulnerabilities in application code (SAST) and third-party dependencies (SCA).
Integrate the Microsoft Security DevOps extension, which includes multiple scanners. Also consider GitHub Advanced Security for Azure DevOps for a native, comprehensive suite.
Why: This "shift-left" approach identifies vulnerabilities early in the development lifecycle, reducing cost and risk.
Restrict developer access to production environments to prevent direct changes, while still allowing for audited emergency access.
Remove standing Contributor/Owner roles. Use pipeline Service Connections for deployments. For emergencies, use Azure AD Privileged Identity Management (PIM) for Just-In-Time (JIT) elevated access.
Why: PIM provides time-bound, approval-gated, and fully audited elevated access, adhering to the principle of least privilege.
Provide secrets to microservices in AKS securely, with automatic rotation and workload-specific access.
Use Azure Key Vault integrated with the Secrets Store CSI Driver for AKS. Use workload identity for pods to authenticate to Key Vault.
Why: Mounts secrets directly into pods from Key Vault, avoiding Kubernetes Secrets. Enables pod-level identity and seamless secret rotation.
Enforce that only scanned and signed container images can be deployed to a production Kubernetes cluster.
Use Azure Container Registry (ACR) content trust for image signing. Use Microsoft Defender for Containers for scanning. Use Azure Policy for Kubernetes to enforce policies on AKS.
Why: Provides a comprehensive, policy-driven, defense-in-depth strategy for container image security, from build to runtime.
Connect Azure Pipelines to Azure resources without using client secrets or certificates.
Create an Azure Resource Manager service connection using "Workload Identity Federation".
Why: Eliminates the need to manage and rotate secrets, improving the security posture of the CI/CD system.
Deploy to a customer's Azure subscription from your Azure DevOps organization without exchanging secrets.
Deploy a self-hosted agent within the customer's Azure environment. Assign a Managed Identity to the agent's VM/VMSS and grant it the necessary RBAC roles.
Why: Keeps all authentication principals within the customer's tenant, adhering to a zero-trust model. No secrets cross tenant boundaries.
Configure Processes and Communications
Manage work for multiple teams on a large product, allowing team autonomy while providing cross-team visibility for leadership.
Use a single Project with Area Paths for each team to give them filtered backlogs. Use Delivery Plans to visualize progress and dependencies across teams.
Why: Enables rollup reporting and dependency tracking while allowing each team to manage its own sprints and work items independently.
Automatically link commits/PRs to work items and transition the work item's state (e.g., to "Resolved") upon PR merge.
In Project Settings, enable "Automatically complete work items with pull requests". Developers must use `#<ID>` in commit messages or link PRs.
Why: Reduces manual overhead for developers, keeps work item status up-to-date, and improves traceability between code and requirements.
Implement an Instrumentation Strategy
Measure key process metrics like Cycle Time, Lead Time, and DORA metrics for value stream analysis.
Use the Azure DevOps Analytics service and its OData feed. Connect Power BI or use built-in dashboard widgets to visualize these metrics.
Why: The Analytics service provides the underlying data for key flow and SRE metrics, enabling data-driven process improvement.
Investigate intermittent slow response times that are hidden by average performance metrics.
In Application Insights, use the Performance blade with percentile analysis (P95, P99) and Transaction Search to drill into specific slow request samples.
Why: Averages can be misleading. Percentiles reveal the "long tail" of performance issues, which often represent the most frustrated users.
Trace a single user request as it travels across multiple microservices to identify bottlenecks or failure points.
Instrument all services with the Application Insights SDK. It automatically propagates a W3C Trace Context correlation ID across service calls.
Why: Provides a unified view of a distributed transaction in the Application Map, making it possible to debug complex interactions.
Proactively monitor an application's Service Level Objective (SLO) and get alerted *before* the SLO is breached.
Define SLIs using KQL queries in Azure Monitor. Create an alert rule that triggers based on the error budget burn rate (how fast the budget is being consumed).
Why: Burn rate alerts are predictive, providing time to react before an SLO is breached and users are significantly impacted.
Validate that a deployment was functionally successful from a user's perspective, not just that the pipeline tasks completed.
As a post-deployment step or release gate, run Application Insights availability tests (synthetic monitoring) that simulate key user flows.
Why: Pipeline success only indicates bits were moved. Synthetic tests confirm the application is actually working, catching misconfigurations or dependency failures.
Visually correlate deployment events with changes in application performance and error rate metrics.
Use the Azure Pipelines task to create "Release Annotations" in Application Insights, which places a marker on metric charts.
Why: Provides immediate visual feedback to quickly identify if a recent deployment introduced a performance regression or bug.
Proactively test application and infrastructure resilience to failures in a controlled manner.
Integrate Azure Chaos Studio into release pipelines. Create experiments that inject faults (e.g., VM shutdown, network latency) and validate system behavior.
Why: Moves beyond testing for expected behavior to testing for unexpected failures, building confidence in system resilience.