Advanced AZ-400

AZ-400: Designing and Implementing Microsoft DevOps Solutions

The full AZ-400 blueprint — source control, pipelines, artifacts, IaC, security and monitoring.

Course: AZ-400 – Designing and Implementing Microsoft DevOps Solutions (A Cloud Guru)
Level: Advanced (AZ-104 or AZ-204 prerequisite recommended)
Total course duration: ~18 hours

AZ-400 Certification Overview

The AZ-400: Azure DevOps Engineer Expert certification is one of the most advanced in the Microsoft Azure ecosystem. It validates the skills required to design and implement comprehensive DevOps strategies on Azure, covering CI/CD, source control, security, compliance, and instrumentation.

mindmap
  root((AZ-400))
    Process and Communications
      Azure Boards
      Scrum / Agile
      Dashboards
      Teams & Slack
    Source Control
      Azure Repos
      GitHub Enterprise
      Git Workflow
      Branching Strategies
    Build and Release Pipelines
      Azure Pipelines YAML
      Agents
      Artifacts
      Deployment
      Tests
    Security and Compliance
      PIM
      Conditional Access
      Key Vault
      Dependency Scanning
    Instrumentation
      Azure Monitor
      Application Insights
      Log Analytics
      Distributed Tracing


Exam Information

Skill Domains

DomainPercentage
Configure processes and communications10–15%
Design and implement source control15–20%
Design and implement build and release pipelines40–45%
Develop a security and compliance plan10–15%
Implement an instrumentation strategy10–15%

Practical Details

  • Duration: 150 min (without lab) / 170 min (with lab).
  • Questions: approximately 40–60 (typically ~50).
  • Passing score: 700/1000 (weighted by domain/difficulty, ≠ 70%).
  • Validity: 1 year (renewal possible after 6 months).

Running Scenario: Ctrl Alt Sweets

  • Bakery chain: $100M revenue, 500+ stores, 16 countries.
  • Critical applications: POS (point of sale), inventory management, e-commerce website.
  • Situation: recent cloud migration (lift-and-shift), siloed teams, transitioning to DevOps.

Module 2 – Analyzing Metrics (SRE and Monitoring)

Site Reliability Engineering (SRE)

  • Definition of reliability: the system is available and performant when and as expected.
  • SLA (Service Level Agreement): contractual commitment on reliability.
  • SLO (Service Level Objective): internal reliability objectives.
  • SLI (Service Level Indicator): actual metrics measuring reliability.
  • DevOps vs SRE: DevOps = culture + processes to deliver fast. SRE = discipline that applies DevOps principles to ops (measurement, automation, feedback).

SRE Principles

  • Constant feedback loop (always improve).
  • Measure everything to understand reliability.
  • Actionable alerts based on metrics.
  • Automate as much as possible to reduce errors and “toil”.
  • Small, frequent changes to limit the blast radius.
  • Embrace risk as an integral part of the process.

Azure Monitor

  • Metrics: time-series numerical data (CPU, memory, requests/s).
  • Logs: event messages analyzed via KQL in Log Analytics.
  • Metrics Explorer: visualize, compare, analyze. Save to a dashboard or workbook.

Application Insights

  • Monitors application health (not just infrastructure).
  • Availability Tests:
    • URL Ping Test: verifies that a URL responds with HTTP 200.
    • Triggers alerts if the test fails.

Failure Mode Analysis

  • Identify fault points (potential failure points in the architecture).
  • Analyze fault modes (ways each point can fail).
  • Assess probability, impact, data loss, and expected behavior.

Baseline Metrics

  • Define a “normal” state before running performance tests.
  • Allows detection of deviations (e.g., configuration drift between environments).
  • Tools: Azure Monitor Insights (VM, Storage, App Service…), Log Analytics.

Dynamic Thresholds and Smart Detection

  • Static thresholds: fixed manual thresholds.
  • Dynamic Thresholds: Machine Learning that adjusts thresholds based on history. Adaptive to seasonal trends.
  • Application Insights Smart Detection: automatically detects performance and behavior anomalies (e.g., sudden degradation in response rate).

Dependencies and Alerts

  • Strong dependency: component failure makes the application unusable.
  • Weak dependency: degraded functionality but app still usable.
  • Application Map in App Insights: visualizes dependencies and the health of each component.

Module 3 – Source Control Strategy (Azure Repos)

Fundamental Concepts

  • Decentralized (Git): each developer has a full copy of the history.
  • Centralized (TFVC): only one developer can check out a file at a time.
  • Recommendation: use Git by default. TFVC only for specific centralization needs.

Azure Repos

  • Managed Git/TFVC repositories, at the project level in an Azure DevOps organization.
  • Optional for Azure Pipelines (can also use GitHub, GitLab, Bitbucket).
  • Import methods: external clone, local push, or copy from another Azure Repo.

Submodules

  • Embed an external repository in your own Git repository.
  • Key commands:
    • git submodule add <url>: adds a submodule.
    • git submodule update --init: initializes submodules after cloning.
    • git clone --recurse-submodules: clones the repo and its submodules.
  • Azure authentication: requires special configuration for cross-project submodules.

Scalar

  • Git client tool to optimize very large repositories.
  • Problem: Git loads the entire history by default → slow for very large projects.
  • Scalar solution: partial clone (doesn’t download unnecessary objects) + sparse checkout (only checks out needed files).
  • Commands:
    • scalar clone <url>: optimized clone for a new repo.
    • scalar register: configures an existing repo.

GitHub Enterprise + Azure Active Directory (SSO)

  • Integrate GitHub Enterprise with Entra ID for Single Sign-On.
  • Single identity management point.
  • GitHub prerequisites: Enterprise plan. Entra ID prerequisites: SAML SSO configuration rights.

Changelogs (git log)

  • Commit history: git log (detailed format).
  • Compact format: git log --oneline.
  • Custom format: git log --pretty=format:"%h - %an, %ar : %s".
  • Commit range: git log <tag1>..<tag2>.

Module 4 – Branching Strategies

What Is a Branch?

  • An isolated copy of the source code to work without affecting the main branch.
  • Commit: save changes locally.
  • Push: send local changes to the remote repository.
  • Pull: retrieve remote changes.
  • Fetch: see differences without applying them.
  • Merge: merge two branches.

Branching Strategies

StrategyRecommended UseCI/CD
Trunk-basedVery short branches (hours), mature teams. Frequent merges to main.✅ Ideal for fast CI/CD
Feature branching1 branch per user story/feature. Merged via PR.✅ Good for CI/CD
Feature flag branchingFeature flags to enable/disable features without branches.✅ Trunk-based + flags
Release branchingMaintenance of multiple versions or per-customer customizations.For multiple versions

Pull Requests

  • PR: formalized process of code review before merging.
  • Contents of a PR: what, why, how, references to tests, remaining work.
  • Author: responsible for the merge, documentation, and changes.
  • Reviewer: responsible for comments, suggestions, and approval.

Code Reviews

  • Automatic assignment: Round Robin or Load Balancing algorithm.
  • Scheduled reminders: notifications for pending PRs.
  • Pull analytics: metrics on PRs (review time, approval rate).

Static Code Analysis

  • Code analysis without executing it (static analysis).
  • Rules: < 400 lines per PR. < 60 minutes of continuous review.
  • GitHub Marketplace tools: SonarQube, CodeClimate, etc.

Work Items + Pull Requests

  • Link a PR to a work item via #<number> in the title/description.
  • Keywords to automatically close: fix, fixes, fixed, close, closes, closed, resolve, resolves, resolved.

Module 5 – Configuring Repositories

Git Tags

  • Markers on specific commits (e.g., release versions).
  • Lightweight tag: simple pointer to a commit. No annotation.
  • Annotated tag: includes a message, author, date. Only type creatable in Azure Repos (web portal).
  • Commands: git tag v1.0, git tag -a v1.0 -m "Release 1.0", git push origin v1.0.

Large File Storage (Git LFS)

  • Problem: Git is not designed for large binary files.
  • Solution: Git LFS stores large files on a separate server. The Git repo only contains a pointer.
  • What should NOT be committed:
    • Frequently modified binaries (builds, packages).
    • Build outputs (compiled artifacts).
    • Dependencies (node_modules, NuGet packages).

git gc (Garbage Collection)

  • Cleans unreferenced objects and compresses large objects.
  • Options: --aggressive (more thorough), --prune (removes unused objects).

Branch Permissions (Azure Repos)

  • Granular management per branch.
  • Inheritance from project permissions → repo → branch.
  • Branch Lock: prevents new modifications to a branch (useful for releases).

Removing Data

  • Git retains all history. Deleting a file from a commit doesn’t erase it from history.
  • To permanently delete (e.g., accidentally committed credentials):
    • git filter-branch --tree-filter 'rm -f secrets.txt' (old method).
    • BFG Repo Cleaner: third-party tool, faster.
    • git filter-repo (recommended).
  • IMPORTANT: These operations rewrite history → require a force push and synchronization from all developers.

Recovering Data

  • Revert to a previous commit: git revert <hash> (creates a new commit reversing the changes). Or git reset (more destructive).
  • Deleted Azure Repos branch: recoverable from the portal (Branches tab → Deleted).
  • Deleted repo: recoverable from organization settings for 30 days.

Module 6 – Designing and Implementing Pipelines

Azure Pipelines

  • Central CI/CD engine of Azure DevOps.
  • Structure: PipelineStagesJobsSteps (tasks).
  • Triggers: automatic triggers (commit, schedule, pull request, pipeline completion).

Classic vs YAML Pipelines

AspectClassicYAML
InterfaceGUI editorCode in a .yaml file
Versioning❌ Not versioned with code✅ Versioned in the repo
SharingScreenshotsCode snippets
Code Reviews❌ Not applicable✅ Via PRs
TemplatesTask GroupsYAML Templates
FutureMaintenance mode✅ Recommended

Build Agents

  • Microsoft-hosted: agents managed by Microsoft (clean slate per run). Fixed specs (DS2_v2: 2 vCPU, 7 GB RAM).
  • Self-hosted: agents on your infrastructure. More control but must be maintained.
  • When to use self-hosted:
    • Custom hardware (GPU, more RAM).
    • Access to on-premises or non-Azure resources.
    • Reuse artifacts between builds.

Triggers

  • CI Triggers: on push/commit to a branch.
  • PR Triggers: on opening/updating a PR.
  • Scheduled Triggers: based on a cron (e.g., every night at 2am).
  • Pipeline Completion Triggers: triggered when another pipeline finishes.

Multi-build (Matrix)

strategy:
  matrix:
    python38:
      pythonVersion: '3.8'
    python39:
      pythonVersion: '3.9'
steps:
- task: UsePythonVersion@0
  inputs:
    versionSpec: $(pythonVersion)

Containerized Agents

  • Microsoft-hosted with Docker: Docker image specified in YAML.
  • Self-hosted non-orchestrated: Docker image managed manually.
  • Self-hosted orchestrated: AKS as Docker agent pool → Kubernetes orchestration.

Module 7 – Package Management (Azure Artifacts)

Azure Artifacts

  • Package management service integrated into Azure DevOps.
  • Supports: NuGet, npm, Maven, Python (pip), Cargo, Universal Packages.
  • Feeds: package management unit. Scope: organization or project.
  • Views: views restricting access based on maturity level (Prerelease, Release, custom).
  • Upstreams: caches public packages (NuGet.org, npmjs.com) in the private feed.

Versioning Strategy (Semantic Versioning)

  • Format: MAJOR.MINOR.PATCH[-qualifier]
    • MAJOR: incompatible changes.
    • MINOR: new compatible features.
    • PATCH: compatible bug fixes.
  • Packages are immutable: impossible to modify or reuse a version number.

Modules 11–12 – Infrastructure as Code (IaC)

Configuration Management

DimensionOption 1Option 2
InfrastructureMutable (modify existing)Immutable (recreate entirely)
Code styleImperative (detailed logic)Declarative (desired state)
OrganizationCentralized (pull server)Decentralized
AgentAgent-basedAgentless

PowerShell DSC

  • Mutable, declarative, centralized (optional), agent-based (Windows Management Framework).
  • Useful for: CI/CD pipelines, Azure Automation.
  • Tool: DSCBaseline to quickly generate configuration files.
  • Structure: ConfigurationNodeResources.

Azure Policy

  • Define, enforce, and monitor the compliance of Azure resources.
  • Examples: naming conventions, allowed SKUs, mandatory tags, data retention.
  • DevOps integration: gate pre/post-deployment in Azure Pipelines.

ARM vs Terraform vs Bicep

ToolMulti-cloudState fileCleanupMaturity
ARM❌ Azure only❌ No❌ No✅ Stable
Terraform✅ Multi-cloud✅ Yes✅ Yes✅ Stable
Bicep❌ Azure only❌ (managed by ARM)❌ No✅ Recommended

DACPAC vs BACPAC

  • DACPAC (Data Application Package): contains the schema (tables, views, objects). No data.
  • BACPAC (Backup Package): contains schema + data. For backups and migrations.
  • Deployment via SqlPackage.exe in a pipeline.

Modules 13–14 – Orchestration and Deployment

Release Strategies

StrategyDescriptionAdvantage
CanaryProgressive deployment to a subset of usersLow impact if issues arise
RollingProgressive replacement of instancesTemporary old/new coexistence
Blue/GreenTwo identical environments, switch trafficZero downtime, fast rollback

Azure App Configuration

  • Centralizes application settings and feature flags.
  • Point-in-time replay of configurations.
  • Compatible with: microservices, Azure Service Fabric, serverless, CD pipelines.
  • To be combined with Azure Key Vault for secrets (App Config does not store secrets).

Release Gates (Validation Gates)

  • Automatic checks before/after deployment:
    • Active incident management (no priority-0 bugs).
    • External approvals (Teams, Slack, ServiceNow).
    • Code quality (code coverage, pass rate).
    • Security scans.
    • Performance baseline (telemetry).

Deployment Slots

  • Equivalent of deployment groups for Web Apps (App Service).
  • Each slot = live app with its own hostname.
  • Swap: switch between staging and production in one click.

Module 15 – Sensitive Information Management

PIM (Privileged Identity Management)

  • Just-in-time access (temporary elevated access).
  • Time-bound access (start + end time).
  • Required approvals + MFA + justifications.
  • Complete audit trail.

Conditional Access

  • “If-then” policy: if signal → decision → enforcement.
  • Signals: membership, location, device, real-time risk.
  • Decisions: block, grant, grant with MFA.

Service Principals vs Managed Identities

  • Service Principal: service account for apps. Requires Tenant ID + Client ID + Secret.
  • Managed Identity: Azure-managed identity. No credentials to manage.
    • System-assigned: tied to the service, deleted if the service is deleted.
    • User-assigned: standalone, shareable between services.

Azure Key Vault in Pipelines

  1. Create a service principal (az ad sp create-for-rbac).
  2. Create the Key Vault and store the secret.
  3. Grant permissions to the service principal on the Key Vault.
  4. Configure a service connection in Azure DevOps.
  5. In the pipeline: use the AzureKeyVault task to load secrets as pipeline variables.

Module 16 – Security and Compliance Scanning

Dependency Scanning

  • Direct dependencies: packages used by the code.
  • Transitive/nested dependencies: packages used by your dependencies.
  • Security scanning: compare against known CVEs.
  • Compliance scanning: verify licenses (MIT = permissive; GPL = copyleft).

Licenses

  • MIT: full permission for reuse (even in proprietary software).
  • GPL: derivatives must use the same license.

Container Dependency Scanning

  • Docker Enterprise: Docker Trusted Registry.
  • Docker Hub: Snyk + Repo scanning.
  • Azure Container Registry: Qualys + Azure Security Center.

Compliance Tools

  • WhiteSource Bolt: Azure Marketplace extension for security and compliance.
  • SonarQube: 3 tasks in the pipeline (Prepare Analysis, Run Analysis, Publish Quality Gate).

Modules 17–20 – Monitoring, Communication, Documentation

Monitoring Stack

  • Azure Monitor: centralized hub (metrics + logs).
  • Log Analytics: KQL queries on logs.
  • Application Insights: application monitoring with distributed tracing.
  • App Center: for mobile apps (crash analytics, user analytics).
  • Crash Analytics: Visual Studio App Center Diagnostics, Google Firebase Crashlytics.

Distributed Tracing

  • Monolithic: single artifact, everything coupled.
  • Microservices: independent services, communication via APIs/messages.
  • Distributed tracing follows a request across multiple services.
  • Visible in Application Insights: Transaction search + Application Map.

Communication: Teams / Slack / GitHub Mobile

  • Service hooks: subscriptions to Azure DevOps events → actions on external services.
  • Teams Apps: Azure Boards, Azure Pipelines, Azure Repos.
  • Slash commands: @azure boards, /az boards, etc.
  • GitHub mobile: approve PRs, respond to issues from mobile.

Azure DevOps Analytics

  • Burndown: remaining work vs remaining time.
  • Burnup: completed work vs total.
  • Cumulative Flow Diagram: visualization of bottlenecks.
  • Velocity: average team throughput per sprint.
  • Cycle Time / Lead Time: time to complete an item.

Key Points for the Exam

  • Build & Release Pipelines = 40-45% of exam content.
  • YAML pipelines recommended over Classic Pipelines (version control, PR reviews).
  • YAML Templates = modify once, update everywhere for jobs/tasks.
  • Variable Groups = modify once, update everywhere for variables.
  • Dynamic Thresholds = ML for auto-adjusted alerts.
  • Semantic Versioning: MAJOR.MINOR.PATCH. Packages are immutable.
  • Canary = subset of users. Blue/Green = full traffic switch.
  • SLI = actual metrics. SLO = objectives. SLA = contractual commitment.

Module 6 – Azure Pipelines YAML: Complete Guide

Complete Pipeline Structure

flowchart TD
    TRIGGER[Trigger\nCI / PR / Schedule / Pipeline] --> PIPELINE[YAML Pipeline]
    PIPELINE --> STAGE1[Stage: Build]
    PIPELINE --> STAGE2[Stage: Test]
    PIPELINE --> STAGE3[Stage: Deploy Dev]
    PIPELINE --> STAGE4[Stage: Deploy Prod]
    STAGE1 --> JOB1[Job: BuildApp\nAgent: ubuntu-latest]
    JOB1 --> STEP1[Step: Checkout]
    JOB1 --> STEP2[Step: Build]
    JOB1 --> STEP3[Step: Publish Artifact]
    STAGE2 --> JOB2[Job: RunTests\nAgent: windows-latest]
    STAGE3 --> JOB3[Job: DeployDev\ncondition: succeeded]
    STAGE4 --> JOB4[Job: DeployProd\ncondition: succeeded\nManual approval gate]
# Complete pipeline - AZ-400 reference example
trigger:
  branches:
    include:
      - main
      - release/*
  paths:
    exclude:
      - docs/**
      - '*.md'

pr:
  branches:
    include:
      - main
  drafts: false

schedules:
  - cron: "0 2 * * *"        # Every night at 2am UTC
    displayName: Nightly Build
    branches:
      include:
        - main
    always: true

variables:
  - group: Production-Variables     # Shared Variable Group
  - name: buildConfiguration
    value: 'Release'
  - name: dotnetVersion
    value: '8.0.x'

stages:
  # ─────────────────────────────────────────────
  - stage: Build
    displayName: 'Build Application'
    jobs:
      - job: BuildDotNet
        displayName: 'Build .NET Application'
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - checkout: self
            fetchDepth: 0              # Full history for GitVersion

          - task: UseDotNet@2
            displayName: 'Install .NET $(dotnetVersion)'
            inputs:
              version: $(dotnetVersion)

          - task: DotNetCoreCLI@2
            displayName: 'Restore dependencies'
            inputs:
              command: 'restore'
              projects: '**/*.csproj'

          - task: DotNetCoreCLI@2
            displayName: 'Build solution'
            inputs:
              command: 'build'
              arguments: '--configuration $(buildConfiguration) --no-restore'

          - task: DotNetCoreCLI@2
            displayName: 'Run unit tests'
            inputs:
              command: 'test'
              projects: '**/*Tests.csproj'
              arguments: >
                --configuration $(buildConfiguration)
                --no-build
                --collect "Code coverage"
                --logger trx
              publishTestResults: true

          - task: DotNetCoreCLI@2
            displayName: 'Publish application'
            inputs:
              command: 'publish'
              publishWebProjects: true
              arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
              zipAfterPublish: true

          - task: PublishBuildArtifacts@1
            displayName: 'Publish artifact'
            inputs:
              pathToPublish: '$(Build.ArtifactStagingDirectory)'
              artifactName: 'drop'

  # ─────────────────────────────────────────────
  - stage: DeployDev
    displayName: 'Deploy to Development'
    dependsOn: Build
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: DeployWebApp
        displayName: 'Deploy to Dev App Service'
        environment: 'development'
        pool:
          vmImage: 'ubuntu-latest'
        strategy:
          runOnce:
            deploy:
              steps:
                - task: AzureWebApp@1
                  displayName: 'Deploy to Azure Web App'
                  inputs:
                    azureSubscription: 'AzureServiceConnection'
                    appType: 'webApp'
                    appName: 'my-app-dev'
                    package: '$(Pipeline.Workspace)/drop/*.zip'

  # ─────────────────────────────────────────────
  - stage: DeployProd
    displayName: 'Deploy to Production'
    dependsOn: DeployDev
    condition: succeeded()
    jobs:
      - deployment: DeployProd
        displayName: 'Deploy to Prod App Service'
        environment:
          name: 'production'
          resourceType: VirtualMachine   # Or 'Kubernetes'
        pool:
          vmImage: 'ubuntu-latest'
        strategy:
          rolling:
            maxParallel: 2
            preDeploy:
              steps:
                - script: echo "Pre-deploy health check"
            deploy:
              steps:
                - task: AzureWebApp@1
                  inputs:
                    azureSubscription: 'AzureServiceConnection'
                    appType: 'webApp'
                    appName: 'my-app-prod'
                    package: '$(Pipeline.Workspace)/drop/*.zip'
            postRouteTraffic:
              steps:
                - script: echo "Post-deploy smoke test"
            on:
              failure:
                steps:
                  - script: echo "Rolling back deployment"

YAML Templates — Reusability

# File: templates/build-steps.yaml
parameters:
  - name: buildConfiguration
    type: string
    default: 'Release'
  - name: testProject
    type: string
    default: '**/*Tests.csproj'

steps:
  - task: DotNetCoreCLI@2
    displayName: 'Build'
    inputs:
      command: 'build'
      arguments: '--configuration ${{ parameters.buildConfiguration }}'

  - task: DotNetCoreCLI@2
    displayName: 'Test'
    inputs:
      command: 'test'
      projects: '${{ parameters.testProject }}'
# File: azure-pipelines.yaml (main pipeline)
stages:
  - stage: Build
    jobs:
      - job: BuildApp
        steps:
          - template: templates/build-steps.yaml
            parameters:
              buildConfiguration: 'Release'
              testProject: '**/UnitTests.csproj'

Variable Groups and Azure Key Vault

# Link a Variable Group to a Key Vault
variables:
  - group: MyVariableGroup    # Contains Key Vault references

# In a step: secrets are automatically masked in logs
steps:
  - script: |
      echo "Connecting to database..."
      # $(DbConnectionString) is loaded from Key Vault
    env:
      DB_CONNECTION: $(DbConnectionString)

Create a Variable Group linked to Key Vault (CLI):

# Create the service principal
az ad sp create-for-rbac --name "pipeline-kv-access" --query "{appId:appId,password:password,tenant:tenant}"

# Create the Key Vault and store a secret
az keyvault create --name "myPipelineKV" --resource-group "DevOps-RG" --location "eastus"
az keyvault secret set --vault-name "myPipelineKV" --name "DbConnectionString" --value "Server=...;Database=...;"

# Grant access to the service principal
az keyvault set-policy --name "myPipelineKV" \
  --spn "<appId>" \
  --secret-permissions get list

Pipeline Caching — Performance

variables:
  NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages

steps:
  - task: Cache@2
    displayName: 'Cache NuGet packages'
    inputs:
      key: 'nuget | "$(Agent.OS)" | **/packages.lock.json'
      restoreKeys: |
        nuget | "$(Agent.OS)"
        nuget
      path: $(NUGET_PACKAGES)
      cacheHitVar: CACHE_RESTORED

  - task: DotNetCoreCLI@2
    displayName: 'Restore (if cache missing)'
    condition: ne(variables.CACHE_RESTORED, 'true')
    inputs:
      command: 'restore'
      projects: '**/*.csproj'

Code Coverage and Tests

steps:
  - task: DotNetCoreCLI@2
    displayName: 'Tests with code coverage'
    inputs:
      command: 'test'
      projects: '**/*Tests.csproj'
      arguments: >
        --collect:"XPlat Code Coverage"
        --results-directory $(Agent.TempDirectory)
      publishTestResults: true

  - task: PublishCodeCoverageResults@1
    displayName: 'Publish code coverage'
    inputs:
      codeCoverageTool: 'Cobertura'
      summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'
      reportDirectory: '$(Agent.TempDirectory)/CoverageReport'

Code coverage frameworks by language:

LanguageFramework
.NETCoverlet (Cobertura), OpenCover
JavaJaCoCo
JavaScriptIstanbul / NYC
Pythonpytest-cov
Gogo test -cover

Module 7 – Azure Artifacts: Complete Guide

Feed Architecture

flowchart LR
    NuGetOrg[NuGet.org\nnpmjs.com] -->|upstream proxy| FEED[Azure Artifacts Feed\norg-level or project-level]
    DEV[Developer] -->|publish| FEED
    PIPELINE[Azure Pipeline] -->|publish artifact| FEED
    FEED -->|restore / install| DEV
    FEED -->|restore / install| PIPELINE

    subgraph Views
        FEED --> PRE[Prerelease View\n@prerelease]
        FEED --> REL[Release View\n@release]
        FEED --> LOCAL[Local View\nlocal packages only]
    end

In-Depth Semantic Versioning

MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]

Examples:
  1.0.0               → Initial stable version
  1.1.0               → New compatible feature
  1.1.1               → Bug fix
  2.0.0               → Breaking change
  2.0.0-alpha.1       → Alpha pre-release
  2.0.0-beta.2        → Beta pre-release
  2.0.0-rc.1          → Release candidate
  1.0.0+20240315.sha1 → Build metadata (does not change precedence)
# Auto-generate version with GitVersion
steps:
  - task: gitversion/setup@0
    displayName: 'Install GitVersion'
    inputs:
      versionSpec: '5.x'

  - task: gitversion/execute@0
    displayName: 'Run GitVersion'

  # $(GitVersion.SemVer) is now available
  - script: |
      echo "Version: $(GitVersion.SemVer)"
      dotnet pack --version-suffix $(GitVersion.PreReleaseSuffix)

Publish and Consume a NuGet Package

# Publish to Azure Artifacts
- task: NuGetCommand@2
  displayName: 'Publish NuGet package'
  inputs:
    command: 'push'
    packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
    nuGetFeedType: 'internal'
    publishVstsFeed: 'MyFeedName'
    allowPackageConflicts: true  # Ignore if version already exists
<!-- NuGet.config to consume the Azure Artifacts feed -->
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="AzureArtifacts" 
         value="https://pkgs.dev.azure.com/{org}/{project}/_packaging/{feed}/nuget/v3/index.json" />
    <add key="NuGet.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
  <activePackageSource>
    <add key="All" value="(Aggregate source)" />
  </activePackageSource>
</configuration>

Module 8 – IaC: ARM, Bicep, and Terraform

Azure Bicep vs ARM Template: Syntax Comparison

Create a Storage Account with ARM:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountName": {
      "type": "string",
      "metadata": { "description": "Unique name for the storage account" }
    },
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2023-01-01",
      "name": "[parameters('storageAccountName')]",
      "location": "[parameters('location')]",
      "sku": { "name": "Standard_LRS" },
      "kind": "StorageV2",
      "properties": {
        "minimumTlsVersion": "TLS1_2",
        "allowBlobPublicAccess": false,
        "supportsHttpsTrafficOnly": true
      }
    }
  ]
}

Same resource in Bicep (much more readable):

@description('Unique name for the storage account')
param storageAccountName string

@description('Azure region')
param location string = resourceGroup().location

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
  properties: {
    minimumTlsVersion: 'TLS1_2'
    allowBlobPublicAccess: false
    supportsHttpsTrafficOnly: true
  }
}

output storageAccountId string = storageAccount.id

Deploy Bicep via Azure Pipeline:

- task: AzureCLI@2
  displayName: 'Deploy Bicep template'
  inputs:
    azureSubscription: 'AzureServiceConnection'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
      az deployment group create \
        --resource-group "MyRG" \
        --template-file "infra/main.bicep" \
        --parameters "infra/parameters.prod.json" \
        --name "deploy-$(Build.BuildNumber)"

Terraform in Azure Pipelines

# main.tf - Azure infrastructure with Terraform
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~>3.0"
    }
  }
  backend "azurerm" {
    resource_group_name  = "terraform-state-rg"
    storage_account_name = "tfstatestorage"
    container_name       = "tfstate"
    key                  = "prod.terraform.tfstate"
  }
}

provider "azurerm" {
  features {}
}

variable "environment" {
  description = "Environment (dev, staging, prod)"
  type        = string
  default     = "dev"
}

resource "azurerm_resource_group" "main" {
  name     = "myapp-${var.environment}-rg"
  location = "East US"
  
  tags = {
    Environment = var.environment
    ManagedBy   = "Terraform"
  }
}

resource "azurerm_app_service_plan" "main" {
  name                = "myapp-${var.environment}-asp"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  
  sku {
    tier = var.environment == "prod" ? "Standard" : "Free"
    size = var.environment == "prod" ? "S1" : "F1"
  }
}

resource "azurerm_app_service" "main" {
  name                = "myapp-${var.environment}"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  app_service_plan_id = azurerm_app_service_plan.main.id

  app_settings = {
    "ASPNETCORE_ENVIRONMENT" = var.environment == "prod" ? "Production" : "Development"
    "WEBSITE_RUN_FROM_PACKAGE" = "1"
  }

  connection_string {
    name  = "DefaultConnection"
    type  = "SQLAzure"
    value = var.db_connection_string
  }
}

output "app_url" {
  value = "https://${azurerm_app_service.main.default_site_hostname}"
}
# Terraform Pipeline
stages:
  - stage: TerraformPlan
    jobs:
      - job: Plan
        steps:
          - task: TerraformInstaller@0
            inputs:
              terraformVersion: '1.6.x'

          - task: TerraformTaskV3@3
            displayName: 'Terraform Init'
            inputs:
              command: 'init'
              workingDirectory: '$(System.DefaultWorkingDirectory)/infra'
              backendServiceArm: 'AzureServiceConnection'
              backendAzureRmResourceGroupName: 'terraform-state-rg'
              backendAzureRmStorageAccountName: 'tfstatestorage'
              backendAzureRmContainerName: 'tfstate'
              backendAzureRmKey: 'prod.terraform.tfstate'

          - task: TerraformTaskV3@3
            displayName: 'Terraform Plan'
            inputs:
              command: 'plan'
              workingDirectory: '$(System.DefaultWorkingDirectory)/infra'
              environmentServiceNameAzureRM: 'AzureServiceConnection'
              commandOptions: '-out=tfplan -var="environment=prod"'

          - task: PublishPipelineArtifact@1
            inputs:
              targetPath: '$(System.DefaultWorkingDirectory)/infra/tfplan'
              artifactName: 'terraform-plan'

  - stage: TerraformApply
    dependsOn: TerraformPlan
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: Apply
        environment: 'production'
        steps:
          - task: TerraformTaskV3@3
            displayName: 'Terraform Apply'
            inputs:
              command: 'apply'
              workingDirectory: '$(System.DefaultWorkingDirectory)/infra'
              environmentServiceNameAzureRM: 'AzureServiceConnection'
              commandOptions: 'tfplan'

Linting and Template Validation

# Lint ARM/Bicep with Azure CLI
az bicep build --file main.bicep             # Validates and compiles to ARM JSON
az deployment group validate \
  --resource-group myRG \
  --template-file main.bicep \
  --parameters @parameters.json

# What-if: preview changes without deploying
az deployment group what-if \
  --resource-group myRG \
  --template-file main.bicep \
  --parameters @parameters.json
# ARM validation pipeline with JSONLint + ARM TTK
steps:
  - task: Npm@1
    displayName: 'Install jsonlint'
    inputs:
      command: 'custom'
      customCommand: 'install -g jsonlint'

  - script: jsonlint arm-template.json
    displayName: 'Validate JSON syntax'

  - task: RunARMTTKTests@1
    displayName: 'ARM Template Toolkit Tests'
    inputs:
      templatesLocation: '$(System.DefaultWorkingDirectory)/arm'
      resultLocation: '$(System.DefaultWorkingDirectory)/results'

Module 9 – Release Strategies: Detailed Implementation

Blue/Green Architecture with Azure App Service

sequenceDiagram
    participant LB as Azure Traffic Manager\n(global routing)
    participant BLUE as Production Slot\n(Blue - Live)
    participant GREEN as Staging Slot\n(Green - New version)
    participant USERS as Users

    USERS->>LB: Requests
    LB->>BLUE: 100% of traffic

    Note over GREEN: Deploying new version
    LB->>BLUE: 100% during tests
    BLUE-->>USERS: Stable responses

    Note over GREEN: Smoke tests passed ✅
    LB->>BLUE: 0% of traffic
    LB->>GREEN: 100% of traffic
    GREEN-->>USERS: New version active

    Note over BLUE: Blue becomes the new staging
# Blue/Green deployment with deployment slots
- stage: DeployStaging
  jobs:
    - deployment: BlueGreenDeploy
      environment: 'production'
      strategy:
        runOnce:
          deploy:
            steps:
              # Deploy to staging slot (Green)
              - task: AzureWebApp@1
                displayName: 'Deploy to staging slot'
                inputs:
                  azureSubscription: 'AzureServiceConnection'
                  appType: 'webApp'
                  appName: 'my-app-prod'
                  deployToSlotOrASE: true
                  resourceGroupName: 'MyRG'
                  slotName: 'staging'
                  package: '$(Pipeline.Workspace)/drop/*.zip'

              # Smoke test on staging slot
              - script: |
                  $url = "https://my-app-prod-staging.azurewebsites.net/health"
                  $response = Invoke-RestMethod -Uri $url
                  if ($response.status -ne "Healthy") { exit 1 }
                  Write-Host "Smoke test passed ✅"
                displayName: 'Smoke test on staging slot'
                condition: succeeded()

              # Swap staging → production
              - task: AzureAppServiceManage@0
                displayName: 'Swap staging → production'
                inputs:
                  azureSubscription: 'AzureServiceConnection'
                  Action: 'Swap Slots'
                  WebAppName: 'my-app-prod'
                  ResourceGroupName: 'MyRG'
                  SourceSlot: 'staging'

Canary Deployment with Azure Traffic Manager

flowchart LR
    TM[Azure Traffic Manager\nWeighted routing] -->|90%| STABLE[App v1.0\nStable]
    TM -->|10%| CANARY[App v2.0-beta\nCanary]
    STABLE --> MON1[Application Insights\nMonitoring v1]
    CANARY --> MON2[Application Insights\nMonitoring v2]
    MON1 --> COMPARE[Metrics\nComparison]
    MON2 --> COMPARE
    COMPARE -->|✅ No errors| PROMOTE[Increase traffic\n→ 50% → 100%]
    COMPARE -->|❌ Errors detected| ROLLBACK[Rollback → 0%]
# Configure Azure Traffic Manager for Canary
az network traffic-manager profile create \
  --name "myapp-tm" \
  --resource-group "MyRG" \
  --routing-method "Weighted" \
  --unique-dns-name "myapp-global"

# Stable endpoint (v1) - 90% of traffic
az network traffic-manager endpoint create \
  --name "stable-endpoint" \
  --profile-name "myapp-tm" \
  --resource-group "MyRG" \
  --type "azureEndpoints" \
  --target-resource-id "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-v1" \
  --weight 90

# Canary endpoint (v2) - 10% of traffic
az network traffic-manager endpoint create \
  --name "canary-endpoint" \
  --profile-name "myapp-tm" \
  --resource-group "MyRG" \
  --type "azureEndpoints" \
  --target-resource-id "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/myapp-v2" \
  --weight 10

Release Gates (Validation Gates)

flowchart TD
    DEPLOY[Start deployment] --> GATE1{Gate #1\nPre-deployment}
    GATE1 -->|Checks| CHECK1[✅ 0 priority-0 bugs]
    GATE1 --> CHECK2[✅ Security scan OK]
    GATE1 --> CHECK3[✅ Manager approval]
    CHECK1 & CHECK2 & CHECK3 --> DEPLOY_ACTION[Deployment in progress]
    DEPLOY_ACTION --> GATE2{Gate #2\nPost-deployment}
    GATE2 --> METRIC1[✅ Error rate < 1%]
    GATE2 --> METRIC2[✅ Response time < 500ms]
    GATE2 --> METRIC3[✅ Availability > 99.9%]
    METRIC1 & METRIC2 & METRIC3 --> DONE[✅ Deployment validated]
    GATE1 -->|Check failed| ABORT[❌ Pipeline stopped]
    GATE2 -->|Degraded metric| ROLLBACK[🔄 Automatic rollback]

Module 10 – Security and Compliance Advanced

Complete DevSecOps Pipeline

flowchart LR
    CODE[Code Push] --> PR[Pull Request]
    PR --> SA[Static Analysis\nSonarQube / Roslyn]
    SA --> SAST[SAST\nSecurity scan]
    SAST --> BUILD[Build]
    BUILD --> UNIT[Unit Tests]
    UNIT --> DAST[DAST\nPen test]
    DAST --> SCAN[Dependency Scan\nSnyk / WhiteSource]
    SCAN --> CONTAINER[Container Scan\nQualys / Trivy]
    CONTAINER --> DEPLOY_DEV[Deploy Dev]
    DEPLOY_DEV --> AUDIT[Compliance Audit\nAzure Policy]
    AUDIT --> DEPLOY_PROD[Deploy Prod]
    DEPLOY_PROD --> MONITOR[Monitor\nSecurity Center]

SonarQube Integration in Azure Pipelines

steps:
  # 1. Prepare analysis
  - task: SonarQubePrepare@5
    displayName: 'Prepare SonarQube'
    inputs:
      SonarQube: 'SonarQubeServiceConnection'
      scannerMode: 'MSBuild'
      projectKey: 'myapp-key'
      projectName: 'MyApplication'
      extraProperties: |
        sonar.exclusions=**/obj/**,**/*.dll
        sonar.coverage.exclusions=**/*Tests*/**
        sonar.cs.opencover.reportsPaths=$(Agent.TempDirectory)/**/coverage.opencover.xml

  # 2. Normal build
  - task: DotNetCoreCLI@2
    inputs:
      command: 'build'

  # 3. Tests with coverage
  - task: DotNetCoreCLI@2
    inputs:
      command: 'test'
      arguments: '--collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover'

  # 4. Run analysis
  - task: SonarQubeAnalyze@5
    displayName: 'Run SonarQube analysis'

  # 5. Publish results (Quality Gate)
  - task: SonarQubePublish@5
    displayName: 'Publish Quality Gate'
    inputs:
      pollingTimeoutSec: '300'

Snyk — Vulnerability Analysis

steps:
  - task: SnykSecurityScan@1
    displayName: 'Snyk Security Scan'
    inputs:
      serviceConnectionEndpoint: 'SnykConnection'
      testType: 'app'
      targetFile: 'MyApp/MyApp.csproj'
      failOnIssues: true
      monitorWhen: 'always'
      additionalArguments: '--severity-threshold=high'

Azure Policy in CI/CD Pipelines

# Gate: verify Azure Policy compliance before deployment
- task: AzurePowerShell@5
  displayName: 'Check Azure Policy compliance'
  inputs:
    azureSubscription: 'AzureServiceConnection'
    ScriptType: 'InlineScript'
    Inline: |
      $nonCompliant = Get-AzPolicyState `
        -ResourceGroupName "MyRG" `
        -Filter "ComplianceState eq 'NonCompliant'" |
        Where-Object { $_.PolicyDefinitionAction -eq "deny" }
      
      if ($nonCompliant.Count -gt 0) {
        Write-Error "❌ $($nonCompliant.Count) non-compliant resource(s) found!"
        $nonCompliant | Format-Table PolicyDefinitionName, ResourceId
        exit 1
      }
      Write-Host "✅ All resources are compliant"
    azurePowerShellVersion: 'LatestVersion'

PowerShell DSC — Complete Configuration

# WebServerConfiguration.ps1 - DSC configuration for a web server
Configuration WebServerConfig {
    param(
        [string]$NodeName = "localhost",
        [string]$WebsitePath = "C:\inetpub\wwwroot\myapp"
    )

    Import-DscResource -ModuleName PSDesiredStateConfiguration
    Import-DscResource -ModuleName xWebAdministration

    Node $NodeName {
        # Ensure IIS is installed
        WindowsFeature IIS {
            Ensure = "Present"
            Name   = "Web-Server"
        }

        WindowsFeature IIS_Management_Console {
            Ensure    = "Present"
            Name      = "Web-Mgmt-Console"
            DependsOn = "[WindowsFeature]IIS"
        }

        # Create website directory
        File WebsiteDirectory {
            Ensure          = "Present"
            DestinationPath = $WebsitePath
            Type            = "Directory"
        }

        # Configure IIS site
        xWebsite DefaultWebSite {
            Ensure       = "Present"
            Name         = "MyApp"
            State        = "Started"
            PhysicalPath = $WebsitePath
            BindingInfo  = @(
                MSFT_xWebBindingInformation {
                    Protocol = "http"
                    Port     = 80
                }
                MSFT_xWebBindingInformation {
                    Protocol = "https"
                    Port     = 443
                }
            )
            DependsOn    = "[WindowsFeature]IIS", "[File]WebsiteDirectory"
        }
    }
}

# Generate the MOF file
WebServerConfig -OutputPath "C:\DSC\WebServer"

# Apply the configuration
Start-DscConfiguration -Path "C:\DSC\WebServer" -Wait -Verbose -Force

# Test compliance
Test-DscConfiguration -Detailed

Module 11 – Monitoring and Instrumentation

Complete Monitoring Architecture

flowchart TD
    subgraph Sources
        APP[Application\nASP.NET Core]
        VM[Virtual Machines\nLinux / Windows]
        K8S[AKS Kubernetes]
        SQL[Azure SQL]
        FUNC[Azure Functions]
    end

    subgraph AzureMonitor[Azure Monitor]
        METRICS[Metrics\nTime series]
        LOGS[Log Analytics\nWorkspace KQL]
        ALERTS[Alerts\nAction Groups]
    end

    subgraph Analysis
        APPINS[Application Insights\nAPM + Distributed Tracing]
        WORKBOOKS[Azure Workbooks\nInteractive dashboards]
        DASHBOARDS[Azure Dashboards\nOperational view]
    end

    subgraph Notifications
        EMAIL[Email]
        SMS[SMS]
        TEAMS[Microsoft Teams\nWebhook]
        SLACK[Slack\nWebhook]
        ITSM[ServiceNow\nITSM]
        RUNBOOK[Azure Automation\nRunbook auto-remediation]
    end

    APP --> APPINS
    VM & K8S & SQL & FUNC --> METRICS
    VM & K8S & SQL & FUNC --> LOGS
    APPINS --> METRICS
    APPINS --> LOGS
    METRICS & LOGS --> ALERTS
    METRICS & LOGS --> WORKBOOKS
    APPINS --> DASHBOARDS
    ALERTS --> EMAIL & SMS & TEAMS & SLACK & ITSM & RUNBOOK

KQL (Kusto Query Language) — Essential Queries for the Exam

// Analyze errors from the last 24 hours
requests
| where timestamp > ago(24h)
| where resultCode startswith "5"
| summarize ErrorCount = count(), ErrorRate = round(count() * 100.0 / toscalar(
    requests | where timestamp > ago(24h) | count()), 2)
  by bin(timestamp, 1h), resultCode
| order by timestamp desc

// Find the slowest dependencies
dependencies
| where timestamp > ago(1h)
| where duration > 1000  // > 1 second
| summarize AvgDuration = avg(duration), Count = count()
  by name, type, target
| order by AvgDuration desc
| take 20

// Trace a distributed request end-to-end
union requests, dependencies, traces, exceptions
| where operation_Id == "abc123"
| project timestamp, itemType, name, duration, success, message
| order by timestamp asc

// Alert if error rate exceeds 5%
let timeRange = 5m;
let errorThreshold = 0.05;
requests
| where timestamp > ago(timeRange)
| summarize
    TotalRequests = count(),
    FailedRequests = countif(success == false)
| extend ErrorRate = FailedRequests * 1.0 / TotalRequests
| where ErrorRate > errorThreshold
| project ErrorRate, TotalRequests, FailedRequests

// Performance metrics by region
customMetrics
| where name == "ResponseTime"
| summarize
    P50 = percentile(value, 50),
    P95 = percentile(value, 95),
    P99 = percentile(value, 99)
  by bin(timestamp, 1h), tostring(customDimensions["region"])

Application Insights — .NET Configuration

// Program.cs — Configure Application Insights
var builder = WebApplication.CreateBuilder(args);

// Add Application Insights
builder.Services.AddApplicationInsightsTelemetry(options =>
{
    options.ConnectionString = builder.Configuration["ApplicationInsights:ConnectionString"];
    options.EnableAdaptiveSampling = true;
    options.EnablePerformanceCounterCollectionModule = true;
    options.EnableDependencyTrackingTelemetryModule = true;
});

// Configure adaptive sampling
builder.Services.ConfigureTelemetryModule<AdaptiveSamplingTelemetryModule>(
    (module, o) =>
    {
        module.MaxTelemetryItemsPerSecond = 20;
        module.ExcludedTypes = "Exception;Request"; // Do not sample errors
    });

// Add a custom TelemetryInitializer
builder.Services.AddSingleton<ITelemetryInitializer, CustomTelemetryInitializer>();

// In a Controller: custom telemetry
public class OrderController : ControllerBase
{
    private readonly TelemetryClient _telemetry;

    public OrderController(TelemetryClient telemetry)
    {
        _telemetry = telemetry;
    }

    [HttpPost]
    public async Task<IActionResult> CreateOrder(OrderRequest request)
    {
        // Track a business event
        _telemetry.TrackEvent("OrderCreated", new Dictionary<string, string>
        {
            ["OrderId"] = request.OrderId,
            ["ProductCount"] = request.Items.Count.ToString(),
            ["TotalAmount"] = request.Total.ToString("C")
        });

        // Track a custom dependency
        using var operation = _telemetry.StartOperation<DependencyTelemetry>("ProcessPayment");
        try
        {
            var result = await _paymentService.ProcessAsync(request);
            operation.Telemetry.Success = true;
            return Ok(result);
        }
        catch (Exception ex)
        {
            operation.Telemetry.Success = false;
            _telemetry.TrackException(ex, new Dictionary<string, string>
            {
                ["OrderId"] = request.OrderId
            });
            throw;
        }
    }
}

Alerts — Action Groups and Logic Apps

# Create an Action Group with Teams notification
az monitor action-group create \
  --name "CriticalAlerts" \
  --resource-group "Monitoring-RG" \
  --short-name "Critical" \
  --action webhook "TeamsWebhook" \
    "https://my-org.webhook.office.com/webhookb2/..." \
  --action email "OpsEmail" \
    "ops-team@company.com" \
  --action sms "OpsPhone" \
    "+12025551234"

# Create an alert on Application Insights error rate
az monitor scheduled-query create \
  --name "HighErrorRate" \
  --resource-group "Monitoring-RG" \
  --scopes "/subscriptions/{sub}/resourceGroups/{rg}/providers/microsoft.insights/components/myapp" \
  --condition "count 'errors' > 50" \
  --description "High error rate detected" \
  --evaluation-frequency "5m" \
  --window-size "15m" \
  --severity 2 \
  --action-groups "/subscriptions/{sub}/resourceGroups/{rg}/providers/microsoft.insights/actionGroups/CriticalAlerts"

Module 12 – Communication and DevOps Documentation

Azure Boards — Agile and Scrum

flowchart LR
    subgraph SprintPlanning[Sprint Planning]
        BACKLOG[Product Backlog] --> SPRINT_BACKLOG[Sprint Backlog]
        SPRINT_BACKLOG --> EPIC[Epic]
        EPIC --> FEATURE[Feature]
        FEATURE --> US[User Story]
        US --> TASK[Task]
    end

    subgraph Workflow
        TODO[To Do] --> ACTIVE[Active]
        ACTIVE --> RESOLVED[Resolved]
        RESOLVED --> CLOSED[Closed]
    end

    subgraph Ceremonies
        PLANNING[Sprint Planning\n1-2h] --> DAILY[Daily Standup\n15 min]
        DAILY --> REVIEW[Sprint Review]
        REVIEW --> RETRO[Retrospective]
    end

Scrum process in Azure DevOps:

ArtifactContentCycle
Product BacklogAll unplanned User StoriesContinuous
Sprint BacklogStories selected for the sprintPer sprint (2 weeks)
IncrementPotentially shippable versionEnd of sprint
Definition of DoneStory completion criteriaDefined once
# Create a commit linked to an Azure Boards work item
git commit -m "Fix login bug AB#1234"
# AB#1234: automatically links the commit to work item #1234

# Resolve a work item via commit
git commit -m "Fixes AB#1234: Implement OAuth2 login"
# Automatically closes work item #1234
# Close a work item from a GitHub PR → Azure Boards
## Description
Fixes AB#1234

This PR fixes the login bug described in Azure Boards work item #1234.

Delivery Plans — Dependency Visualization

gantt
    title Delivery Plan — Ctrl Alt Sweets Q1
    dateFormat  YYYY-MM-DD
    section Frontend Team
    Feature A       :active, f1, 2024-01-08, 2024-01-19
    Feature B       :f2, 2024-01-22, 2024-02-02
    Feature C       :f3, 2024-02-05, 2024-02-16

    section Backend Team
    API v2          :active, b1, 2024-01-08, 2024-01-26
    Database Migration :b2, 2024-01-29, 2024-02-09
    Integration Tests  :b3, 2024-02-12, 2024-02-23

    section DevOps Team
    Pipeline Setup  :d1, 2024-01-08, 2024-01-12
    Monitoring Setup :d2, 2024-01-15, 2024-01-19
    Security Scan Integration :d3, 2024-01-22, 2024-01-26

Azure DevOps Dashboards — Essential Widgets

WidgetDataPurpose
Build HistoryStatus of recent buildsCI visibility
Release Pipeline OverviewDeployment statusCD visibility
Burndown ChartRemaining work in the sprintScrum tracking
VelocityStory points per sprintTeam capacity
Cumulative Flow DiagramDistribution of work item statesIdentify bottlenecks
Test Results TrendTest evolution (pass/fail)Code quality
Code Coverage% code coverageTechnical quality

Module 13 – Advanced Source Control Strategy

Branching Strategies Compared

gitGraph
   commit id: "Initial commit"
   branch feature/login
   checkout feature/login
   commit id: "Add login form"
   commit id: "Add OAuth2"
   checkout main
   branch hotfix/critical-bug
   checkout hotfix/critical-bug
   commit id: "Fix critical bug"
   checkout main
   merge hotfix/critical-bug id: "Merge hotfix"
   checkout feature/login
   commit id: "Tests"
   checkout main
   merge feature/login id: "Merge feature via PR"
   branch release/v1.1
   checkout release/v1.1
   commit id: "Version bump 1.1.0"

Branch Policies in Azure Repos

// Branch Policy configuration (via REST API or CLI)
{
  "isEnabled": true,
  "isBlocking": true,
  "type": {
    "id": "fa4e907d-c16b-452d-8106-7efa0cb84489"
  },
  "settings": {
    "minimumApproverCount": 2,
    "creatorVoteCounts": false,
    "allowDownvotes": false,
    "resetOnSourcePush": true,
    "requireVoteOnLastIteration": true,
    "scope": [
      {
        "repositoryId": null,
        "refName": "refs/heads/main",
        "matchKind": "exact"
      }
    ]
  }
}

Key policies for the main branch:

PolicyDescriptionRecommended
Minimum reviewersAt least N approvers✅ 2 reviewers
Check for linked work itemsPR must reference a work item✅ Required
Check for comment resolutionAll comments must be resolved✅ Required
Limit merge typesAllow only Squash merge✅ Clean history
Build validationBuild must succeed before merge✅ Required
Status checksExternal checks required (SonarQube, Snyk)✅ Recommended

git filter-repo — Removing Sensitive Data

# Install git-filter-repo (replaces git filter-branch)
pip install git-filter-repo

# Remove a file from all history
git filter-repo --path credentials.json --invert-paths

# Remove all occurrences of a pattern in code
git filter-repo --replace-text <(echo "password123==>***REMOVED***")

# After modification, force push
git push origin --force --all
git push origin --force --tags

# All developers must re-clone
# (old clones still contain the removed data)

⚠️ IMPORTANT: After git filter-repo, history is rewritten. All commit hashes change. Coordinate with the entire team before proceeding.


Module 14 – Feature Flags and Azure App Configuration

Feature Flags with Azure App Configuration

// Program.cs — Configure Feature Management
builder.Configuration.AddAzureAppConfiguration(options =>
{
    options.Connect(builder.Configuration["AzureAppConfiguration:ConnectionString"])
           .UseFeatureFlags(flagOptions =>
           {
               flagOptions.CacheExpirationInterval = TimeSpan.FromSeconds(30);
           });
});

builder.Services.AddFeatureManagement()
    .AddFeatureFilter<PercentageFilter>()    // Canary
    .AddFeatureFilter<TargetingFilter>()     // User targeting
    .AddFeatureFilter<TimeWindowFilter>();   // Time window
// Use feature flags in a Controller
[ApiController]
[Route("[controller]")]
public class CheckoutController : ControllerBase
{
    private readonly IFeatureManager _featureManager;

    public CheckoutController(IFeatureManager featureManager)
    {
        _featureManager = featureManager;
    }

    [HttpPost]
    public async Task<IActionResult> Checkout([FromBody] CartModel cart)
    {
        // Feature flag for new checkout
        if (await _featureManager.IsEnabledAsync("NewCheckoutFlow"))
        {
            return await ProcessNewCheckout(cart);
        }
        else
        {
            return await ProcessLegacyCheckout(cart);
        }
    }
}

Configuration in Azure App Configuration (JSON):

{
  "feature_management": {
    "feature_flags": [
      {
        "id": "NewCheckoutFlow",
        "enabled": true,
        "conditions": {
          "client_filters": [
            {
              "name": "Microsoft.Targeting",
              "parameters": {
                "Audience": {
                  "Users": ["user1@company.com", "user2@company.com"],
                  "Groups": [
                    {
                      "Name": "BetaTesters",
                      "RolloutPercentage": 20
                    }
                  ],
                  "DefaultRolloutPercentage": 5
                }
              }
            }
          ]
        }
      },
      {
        "id": "DarkMode",
        "enabled": true,
        "conditions": {
          "client_filters": [
            {
              "name": "Microsoft.TimeWindow",
              "parameters": {
                "Start": "2024-01-01T00:00:00Z",
                "End": "2024-03-31T23:59:59Z"
              }
            }
          ]
        }
      }
    ]
  }
}

AZ-400 Review Questions

Q1 — YAML Pipelines

Question: You have 3 Azure Pipelines that share the exact same .NET build steps. How do you avoid duplication while allowing centralized updates?

  • A. Copy-paste the steps into each YAML pipeline
  • B. Create a Classic Pipeline for the common steps
  • C. Use YAML Templates with template: include
  • D. Use Variable Groups to store the steps

Explanation: YAML Templates allow you to define reusable steps, jobs, or stages in a separate file. Each pipeline can include the template with - template: templates/build-steps.yaml. A modification to the template automatically propagates to all pipelines that include it.


Q2 — Agents

Question: Your pipeline needs to build an application that requires a GPU for machine learning tests. Which agent configuration do you choose?

  • A. Microsoft-hosted agent with vmImage: ‘ubuntu-latest’
  • B. Microsoft-hosted agent with vmImage: ‘windows-latest’
  • C. Self-hosted agent configured with GPU
  • D. Container agent on Azure Container Instances

Explanation: Microsoft-hosted agents have fixed specs (DS2_v2: 2 vCPU, 7 GB RAM) with no GPU. For a GPU, you need a self-hosted agent installed on an Azure VM with GPU (e.g., NC-series) or on a physical machine with GPU.


Q3 — Release Strategy

Question: Your application handles critical financial transactions. You need to deploy a new version with zero downtime and the ability to roll back immediately if problems arise. Which strategy do you choose?

  • A. Rolling deployment
  • B. Canary deployment
  • C. Blue/Green deployment
  • D. Recreate deployment

Explanation: Blue/Green deployment maintains two identical environments. Traffic is instantly switched from Green (new version) to Blue (old version) if a problem is detected, allowing immediate rollback without affecting ongoing transactions.


Q4 — Feature Flags

Question: What is the main difference between feature flags and branching strategies for managing progressive feature rollouts?

  • A. Feature flags are more secure
  • B. Branching strategies require less code
  • C. Feature flags separate code deployment from feature deployment
  • D. Feature flags are only for A/B testing

Explanation: With feature flags, code is deployed to production but the feature remains disabled. You can progressively enable it for user groups (canary) without redeploying. Branches require a new deployment for each activation change.


Q5 — Dependency Scanning

Question: Your team uses an open-source library under GPL v3 license. You want to incorporate this library into your proprietary commercial software. Is this legally possible?

  • A. Yes, all open-source licenses allow commercial use
  • B. No, the GPL license requires derivatives to use the same GPL license
  • C. Yes, as long as you mention the library in documentation
  • D. No, you need an MIT license for commercial use

Explanation: GPL (General Public License) is a copyleft license: if you distribute software that includes GPL code, your entire software must also be distributed under GPL. For proprietary software, you need libraries with permissive licenses such as MIT, Apache 2.0, or BSD.


Q6 — DACPAC vs BACPAC

Question: You want to create a test environment that exactly mirrors production, with representative data. Which SQL package type do you use?

  • A. DACPAC (Data Application Package)
  • B. BACPAC (Backup Package)
  • C. SQL Script
  • D. Database Snapshot

Explanation: A BACPAC contains schema AND data. It is ideal for creating a production-ready test environment with real (or anonymized) data. A DACPAC only contains the schema (tables, views, stored procedures) without data.


Q7 — Branch Policies

Question: You want to prevent a developer from merging code into main if review comments are not resolved. Which branch policy do you enable?

  • A. Require a minimum number of reviewers
  • B. Check for linked work items
  • C. Build validation
  • D. Check for comment resolution

Explanation: The “Check for comment resolution” policy blocks the merge while all PR comments are not marked as resolved. This ensures every review feedback has been addressed before merging.


Q8 — PIM (Privileged Identity Management)

Question: A junior developer accidentally compromised an account with Azure administrator rights. To prevent this type of incident, which Azure AD feature do you implement?

  • A. Azure AD Conditional Access with geographic blocking
  • B. Mandatory Multi-Factor Authentication
  • C. Azure AD Privileged Identity Management (PIM) with Just-in-Time access
  • D. Azure RBAC with limited roles

Explanation: PIM (Privileged Identity Management) implements Just-in-Time access: users don’t have elevated rights permanently. They must explicitly activate their privileged role for a limited duration, with approval and MFA. Even if an account is compromised, the attacker doesn’t have elevated rights without activation.


Q9 — Application Insights

Question: Your microservices application shows intermittent high latencies. How do you identify which service in the call chain is responsible?

  • A. Enable Azure Monitor diagnostics on each service
  • B. Check CPU/memory metrics of each VM
  • C. Configure latency alerts on each API
  • D. Use Application Insights Distributed Tracing (Transaction Search + Application Map)

Explanation: Application Insights Distributed Tracing follows a request through all services in a microservices architecture, using operation_Id to correlate telemetry. The Application Map graphically visualizes each dependency with its latency and error rate.


Q10 — Semantic Versioning

Question: A NuGet library is currently at version 2.3.4. An update introduces new features compatible with existing APIs. What version should be published?

  • A. 3.0.0 (MAJOR bump)
  • B. 2.4.0 (MINOR bump)
  • C. 2.3.5 (PATCH bump)
  • D. 2.3.4-beta.1 (pre-release)

Explanation: In Semantic Versioning (MAJOR.MINOR.PATCH):

  • MAJOR = breaking changes (incompatible APIs)
  • MINOR = new backward-compatible features
  • PATCH = backward-compatible bug fixes

New compatible features → increment MINOR → 2.4.0


AZ-400 Quick Reference Table

Azure Services by Domain

DomainServicePrimary Use Case
CI/CDAzure PipelinesBuild & deploy automation
Source ControlAzure ReposManaged Git/TFVC repositories
Work ItemsAzure BoardsScrum/Agile tracking
PackagesAzure ArtifactsNuGet, npm, Maven, pip
TestsAzure Test PlansManual & automated testing
InfrastructureBicep / TerraformDeclarative IaC
ConfigAzure App ConfigurationFeature flags, centralized settings
SecretsAzure Key VaultPasswords, certificates, keys
MonitoringApplication InsightsAPM, distributed tracing
LogsLog AnalyticsCentralized KQL queries
AlertsAzure Monitor AlertsProactive notifications
IdentityAzure AD / PIMIAM, Just-in-Time access
ComplianceAzure PolicyGovernance as code
SecuritySonarQube / SnykSAST, dependency scanning
CommunicationTeams / SlackService hooks, notifications

Essential Azure CLI Commands

# Azure DevOps — Create an organization and project
az devops configure --defaults organization=https://dev.azure.com/MyOrg
az devops project create --name "MyProject" --description "My DevOps Project"

# Create a pipeline
az pipelines create \
  --name "MyPipeline" \
  --description "CI/CD Pipeline" \
  --repository "MyRepo" \
  --repository-type "tfsgit" \
  --branch "main" \
  --yml-path "azure-pipelines.yaml"

# Trigger a pipeline
az pipelines run --name "MyPipeline" --branch "feature/my-feature"

# Manage Variable Groups
az pipelines variable-group create \
  --name "ProductionVars" \
  --variables DB_SERVER="prod-sql.database.windows.net" APP_ENV="Production"

# Manage Azure Key Vault
az keyvault create --name "MyKV" --resource-group "MyRG" --location "eastus"
az keyvault secret set --vault-name "MyKV" --name "ApiKey" --value "secret-value"

# Link Key Vault to a Variable Group
az pipelines variable-group create \
  --name "KeyVaultVars" \
  --authorize true \
  --variables PLACEHOLDER="placeholder" \
  --provider "KeyVault" \
  --vault-name "MyKV" \
  --service-endpoint "AzureServiceConnection"

Useful PowerShell Scripts

# Deploy with ARM Template
$deploymentParams = @{
    ResourceGroupName = "MyRG"
    TemplateFile      = ".\arm\template.json"
    TemplateParameterFile = ".\arm\parameters.prod.json"
    Name              = "deploy-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
    Verbose           = $true
}
New-AzResourceGroupDeployment @deploymentParams

# Check Azure Policy compliance
$nonCompliant = Get-AzPolicyState -ResourceGroupName "MyRG" |
    Where-Object { $_.ComplianceState -eq "NonCompliant" }

if ($nonCompliant) {
    Write-Warning "⚠️ $($nonCompliant.Count) non-compliant resources!"
    $nonCompliant | Select-Object PolicyDefinitionName, ResourceId |
        Format-Table -AutoSize
}

# Rotate Key Vault secrets
$vault = "MyKV"
$secretName = "DbPassword"
$newPassword = [System.Web.Security.Membership]::GeneratePassword(32, 8)

az keyvault secret set `
    --vault-name $vault `
    --name $secretName `
    --value $newPassword

Write-Host "✅ Secret '$secretName' updated in '$vault'"

AZ-400 Glossary

TermDefinition
Agent (Pipeline)Computer that executes tasks in an Azure Pipelines pipeline
ArtifactPipeline output (compiled binary, NuGet package, Docker image)
BACPACSQL package containing schema + data (for migrations and backups)
Branch PolicyRule protecting a branch (approvals, builds, comments)
BurndownChart showing remaining work vs time in a sprint
Canary DeploymentProgressive deployment to a subset of users
CI/CDContinuous Integration / Continuous Deployment
DACPACSQL package containing only the schema (no data)
Deployment SlotParallel environment of an App Service (staging, prod)
Dynamic ThresholdAlert threshold automatically adjusted by Machine Learning
Feature FlagSoftware switch to enable/disable a feature
FeedPackage registry in Azure Artifacts
GateAutomatic validation (pre/post) in a deployment pipeline
IaCInfrastructure as Code (ARM, Bicep, Terraform)
KQLKusto Query Language — query language for Log Analytics
Managed IdentityAzure-managed identity, no credentials to maintain
PIMPrivileged Identity Management — Just-in-Time role access
Rolling DeploymentProgressive replacement of application instances
ScalarGit tool to optimize very large repositories
Service HookSubscription to an Azure DevOps event → external action
SLA/SLO/SLIService Level Agreement / Objective / Indicator
SRESite Reliability Engineering — ops with DevOps practices
SubmoduleGit repository embedded in another repository
YAML TemplateReusable YAML file across multiple Azure Pipelines

Search Terms

az-400 · designing · microsoft · devops · azure · iac · pipelines · configuration · git · management · monitoring · release · strategies · app · branch · dependency · repos · scanning · strategy · yaml · agents · analysis · application · architecture

Interested in this course?

Contact us to book it or get a custom training plan for your team.