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
| Domain | Percentage |
|---|---|
| Configure processes and communications | 10–15% |
| Design and implement source control | 15–20% |
| Design and implement build and release pipelines | 40–45% |
| Develop a security and compliance plan | 10–15% |
| Implement an instrumentation strategy | 10–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
| Strategy | Recommended Use | CI/CD |
|---|---|---|
| Trunk-based | Very short branches (hours), mature teams. Frequent merges to main. | ✅ Ideal for fast CI/CD |
| Feature branching | 1 branch per user story/feature. Merged via PR. | ✅ Good for CI/CD |
| Feature flag branching | Feature flags to enable/disable features without branches. | ✅ Trunk-based + flags |
| Release branching | Maintenance 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 pushand synchronization from all developers.
Recovering Data
- Revert to a previous commit:
git revert <hash>(creates a new commit reversing the changes). Orgit 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: Pipeline → Stages → Jobs → Steps (tasks).
- Triggers: automatic triggers (commit, schedule, pull request, pipeline completion).
Classic vs YAML Pipelines
| Aspect | Classic | YAML |
|---|---|---|
| Interface | GUI editor | Code in a .yaml file |
| Versioning | ❌ Not versioned with code | ✅ Versioned in the repo |
| Sharing | Screenshots | Code snippets |
| Code Reviews | ❌ Not applicable | ✅ Via PRs |
| Templates | Task Groups | YAML Templates |
| Future | Maintenance 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
| Dimension | Option 1 | Option 2 |
|---|---|---|
| Infrastructure | Mutable (modify existing) | Immutable (recreate entirely) |
| Code style | Imperative (detailed logic) | Declarative (desired state) |
| Organization | Centralized (pull server) | Decentralized |
| Agent | Agent-based | Agentless |
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:
Configuration→Node→Resources.
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
| Tool | Multi-cloud | State file | Cleanup | Maturity |
|---|---|---|---|---|
| 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.exein a pipeline.
Modules 13–14 – Orchestration and Deployment
Release Strategies
| Strategy | Description | Advantage |
|---|---|---|
| Canary | Progressive deployment to a subset of users | Low impact if issues arise |
| Rolling | Progressive replacement of instances | Temporary old/new coexistence |
| Blue/Green | Two identical environments, switch traffic | Zero 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
- Create a service principal (
az ad sp create-for-rbac). - Create the Key Vault and store the secret.
- Grant permissions to the service principal on the Key Vault.
- Configure a service connection in Azure DevOps.
- In the pipeline: use the
AzureKeyVaulttask 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:
| Language | Framework |
|---|---|
| .NET | Coverlet (Cobertura), OpenCover |
| Java | JaCoCo |
| JavaScript | Istanbul / NYC |
| Python | pytest-cov |
| Go | go 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:
| Artifact | Content | Cycle |
|---|---|---|
| Product Backlog | All unplanned User Stories | Continuous |
| Sprint Backlog | Stories selected for the sprint | Per sprint (2 weeks) |
| Increment | Potentially shippable version | End of sprint |
| Definition of Done | Story completion criteria | Defined once |
Link Work Items to PRs and Commits
# 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
| Widget | Data | Purpose |
|---|---|---|
| Build History | Status of recent builds | CI visibility |
| Release Pipeline Overview | Deployment status | CD visibility |
| Burndown Chart | Remaining work in the sprint | Scrum tracking |
| Velocity | Story points per sprint | Team capacity |
| Cumulative Flow Diagram | Distribution of work item states | Identify bottlenecks |
| Test Results Trend | Test evolution (pass/fail) | Code quality |
| Code Coverage | % code coverage | Technical 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:
| Policy | Description | Recommended |
|---|---|---|
| Minimum reviewers | At least N approvers | ✅ 2 reviewers |
| Check for linked work items | PR must reference a work item | ✅ Required |
| Check for comment resolution | All comments must be resolved | ✅ Required |
| Limit merge types | Allow only Squash merge | ✅ Clean history |
| Build validation | Build must succeed before merge | ✅ Required |
| Status checks | External 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_Idto 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
| Domain | Service | Primary Use Case |
|---|---|---|
| CI/CD | Azure Pipelines | Build & deploy automation |
| Source Control | Azure Repos | Managed Git/TFVC repositories |
| Work Items | Azure Boards | Scrum/Agile tracking |
| Packages | Azure Artifacts | NuGet, npm, Maven, pip |
| Tests | Azure Test Plans | Manual & automated testing |
| Infrastructure | Bicep / Terraform | Declarative IaC |
| Config | Azure App Configuration | Feature flags, centralized settings |
| Secrets | Azure Key Vault | Passwords, certificates, keys |
| Monitoring | Application Insights | APM, distributed tracing |
| Logs | Log Analytics | Centralized KQL queries |
| Alerts | Azure Monitor Alerts | Proactive notifications |
| Identity | Azure AD / PIM | IAM, Just-in-Time access |
| Compliance | Azure Policy | Governance as code |
| Security | SonarQube / Snyk | SAST, dependency scanning |
| Communication | Teams / Slack | Service 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
| Term | Definition |
|---|---|
| Agent (Pipeline) | Computer that executes tasks in an Azure Pipelines pipeline |
| Artifact | Pipeline output (compiled binary, NuGet package, Docker image) |
| BACPAC | SQL package containing schema + data (for migrations and backups) |
| Branch Policy | Rule protecting a branch (approvals, builds, comments) |
| Burndown | Chart showing remaining work vs time in a sprint |
| Canary Deployment | Progressive deployment to a subset of users |
| CI/CD | Continuous Integration / Continuous Deployment |
| DACPAC | SQL package containing only the schema (no data) |
| Deployment Slot | Parallel environment of an App Service (staging, prod) |
| Dynamic Threshold | Alert threshold automatically adjusted by Machine Learning |
| Feature Flag | Software switch to enable/disable a feature |
| Feed | Package registry in Azure Artifacts |
| Gate | Automatic validation (pre/post) in a deployment pipeline |
| IaC | Infrastructure as Code (ARM, Bicep, Terraform) |
| KQL | Kusto Query Language — query language for Log Analytics |
| Managed Identity | Azure-managed identity, no credentials to maintain |
| PIM | Privileged Identity Management — Just-in-Time role access |
| Rolling Deployment | Progressive replacement of application instances |
| Scalar | Git tool to optimize very large repositories |
| Service Hook | Subscription to an Azure DevOps event → external action |
| SLA/SLO/SLI | Service Level Agreement / Objective / Indicator |
| SRE | Site Reliability Engineering — ops with DevOps practices |
| Submodule | Git repository embedded in another repository |
| YAML Template | Reusable 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