| permalink | /labs/lab-07-ado |
|---|---|
| title | Lab 07-ADO - ADO YAML Pipelines and Cost Gates |
| description | Build automated scanning pipelines, cost gates, and deployment workflows with Azure DevOps YAML Pipelines. |
| Duration | 50 minutes |
| Level | Advanced |
| Prerequisites | Lab 02, Lab 03, Lab 04, Lab 05, Lab 06-ADO |
By the end of this lab, you will be able to:
- Build an ADO YAML pipeline with matrix strategy for multi-app scanning
- Configure workload identity federation (WIF) service connections
- Implement variable groups for centralized configuration
- Set up schedule triggers and environment approvals
- Create pipeline templates for reuse
- Link work items using
AB#syntax
You will walk through the centralized scanning pipeline that runs all 4 tools across all 5 demo apps.
-
Open
.azuredevops/pipelines/finops-scan.ymland review the overall architecture:finops-scan.yml ├── Stage: Scan │ ├── Job: PSRule (strategy: matrix × 5 apps) │ ├── Job: Checkov (strategy: matrix × 5 apps) │ └── Job: Custodian (strategy: matrix × 5 apps) └── Stage: Upload └── Job: SARIF Upload (strategy: matrix × 5 apps) -
Review the trigger configuration:
trigger: none schedules: - cron: '0 6 * * 1' displayName: 'Weekly Monday 06:00 UTC' branches: include: - main
The pipeline runs on a weekly schedule and can be triggered manually. Unlike GitHub Actions
workflow_dispatch, ADO pipelines withtrigger: noneare always available for manual runs from the UI or CLI. -
Review the pool configuration:
pool: vmImage: 'ubuntu-latest'
-
Review the variables section. The pipeline references shared variable groups:
variables: - group: finops-oidc-config - group: finops-secrets
Variable groups centralize configuration across multiple pipelines. The
finops-oidc-configgroup stores WIF-related values (Client ID, Tenant ID, Subscription ID), andfinops-secretsstores sensitive values. -
Review the matrix strategy used by each scan job:
strategy: matrix: app-001: appId: '001' app-002: appId: '002' app-003: appId: '003' app-004: appId: '004' app-005: appId: '005'
This creates 5 parallel jobs per scanner — one for each demo app. ADO expands the matrix into individual jobs named
PSRule app-001,PSRule app-002, and so on. -
Compare the ADO matrix syntax with GitHub Actions:
Aspect GitHub Actions Azure DevOps Syntax matrix: { app: ['001','002'] }matrix: { app-001: { appId: '001' } }Access ${{ matrix.app }}$(appId)Naming Auto-generated (e.g., app=001)Key name (e.g., app-001)
Tip
ADO matrix jobs run in parallel by default. With 3 scanners × 5 apps = 15 scan jobs + 5 upload jobs, the entire scan completes in the time of the slowest individual job — the same parallelism model as GitHub Actions.
You will review the workload identity federation (WIF) service connections that authenticate ADO pipelines to Azure.
-
Navigate to Project Settings → Service connections in the FinOps project.
-
Review the 6 WIF service connections:
Connection Name Purpose finops-scanner-adoMain scanner pipeline authentication finops-app-001Demo app 001 deployment finops-app-002Demo app 002 deployment finops-app-003Demo app 003 deployment finops-app-004Demo app 004 deployment finops-app-005Demo app 005 deployment -
Click on
finops-scanner-adoto inspect the configuration. A WIF service connection uses:- Subscription ID — the target Azure subscription
- Service Principal (App Registration) — the Azure AD app with federated credentials
- Tenant ID — the Azure AD tenant
- Workload Identity Federation — instead of client secrets, ADO exchanges its pipeline token for an Azure token
-
Compare WIF with the GitHub OIDC approach from Lab 07 Exercise 7.2:
Aspect GitHub OIDC ADO WIF Permission id-token: writein workflowService connection assigned to pipeline Configuration Federated credential with repo subject WIF service connection in Project Settings Secrets AZURE_CLIENT_ID,AZURE_TENANT_ID,AZURE_SUBSCRIPTION_IDas repo secretsValues stored in the service connection Token lifetime Short-lived (job duration) Short-lived (job duration) -
List service connections from the command line:
az devops service-endpoint list \ --organization https://dev.azure.com/MngEnvMCAP675646 \ --project FinOps \ --output table
Important
Workload identity federation eliminates the need for client secrets in ADO pipelines. The pipeline runtime exchanges its ADO-issued token for an Azure AD token using the federated trust. No long-lived credentials are stored in ADO variable groups or service connections.
You will trigger the finops-scan pipeline manually and monitor its execution.
-
Trigger the pipeline from the command line:
az pipelines run \ --name finops-scan \ --organization https://dev.azure.com/MngEnvMCAP675646 \ --project FinOps
-
Alternatively, trigger from the ADO web UI:
- Navigate to Pipelines → Pipelines
- Find the finops-scan pipeline
- Click Run pipeline
- Select the
mainbranch - Click Run
-
Monitor the pipeline execution. Click on the running pipeline to see the stages and jobs.
-
Expand the Scan stage to see the matrix jobs. You should see 15 jobs:
- 5 PSRule scan jobs
- 5 Checkov scan jobs
- 5 Cloud Custodian scan jobs
-
Expand an individual job to view the step-level logs. Each scan job:
- Checks out the scanner configuration
- Checks out the target demo app repository
- Runs the scanner tool
- Publishes the SARIF file as a build artifact
-
Wait for the scan stage to complete. PSRule and Checkov jobs typically finish in 1–2 minutes. Cloud Custodian jobs may take longer because they query live Azure resources.
Note
If Cloud Custodian jobs fail with authentication errors, verify that the finops-scanner-ado service connection is configured correctly (Exercise 7.2) and that the service principal has Reader role on the target subscription.
You will inspect the SARIF artifacts and variable group configuration after the pipeline completes.
-
After the pipeline completes, click on the run summary.
-
Navigate to the Artifacts section of the pipeline run. You should see SARIF artifacts for each app and scanner combination (for example,
sarif-psrule-001,sarif-checkov-002). -
Download a SARIF artifact and verify it contains findings:
- Click on an artifact name
- Download and open the
.sariffile - Verify it has
runs[].results[]with at least one finding
-
Review the variable groups used by the pipeline:
- Navigate to Pipelines → Library
- Review the 3 variable groups:
Variable Group Purpose finops-oidc-configWIF Client ID, Tenant ID, Subscription ID finops-secretsSensitive values (PATs, connection strings) wiki-accessWiki publishing credentials -
Click on
finops-oidc-configto see the stored variables. These are referenced in the pipeline YAML as$(variableName).
Tip
Variable groups in ADO are equivalent to GitHub repository secrets and variables combined. You can mark individual values as secret (encrypted, masked in logs) or leave them as plain text. Variable groups can be shared across multiple pipelines.
You will review the cost gate pipeline and configure it as a branch policy for pull requests.
-
Open
.azuredevops/pipelines/finops-cost-gate.yml. This pipeline:- Triggers on pull requests targeting the
mainbranch - Runs
infracost diffto compare the PR branch cost against themainbranch baseline - Posts a cost summary comment on the PR
- Triggers on pull requests targeting the
-
Review the pipeline trigger:
trigger: none pr: branches: include: - main
Unlike the scan pipeline, this pipeline triggers automatically on PRs. In ADO, you can also configure it as a build validation branch policy for additional enforcement.
-
Configure the cost gate as a branch policy:
- Navigate to Repos → Branches
- Click the
...menu on themainbranch - Select Branch policies
- Under Build Validation, click Add build policy
- Select the finops-cost-gate pipeline
- Set Trigger to Automatic
- Set Policy requirement to Required
- Click Save
-
Now every pull request targeting
mainmust pass the cost gate before it can be completed. If a PR increases monthly costs beyond the threshold, the policy blocks the merge. -
Compare with the GitHub Actions approach from Lab 07 Exercise 7.5:
Aspect GitHub Actions Azure DevOps Trigger on: pull_requestpr: branches: include+ branch policyEnforcement Status check (required) Build validation policy (required) Comment Infracost --behavior updateInfracost --behavior updateOverride Bypass branch protection (admin) Override branch policy (with permissions)
Tip
Branch policies in ADO are the equivalent of GitHub branch protection rules. Both enforce quality gates before code reaches the main branch. The cost gate ensures no PR lands without a cost impact review.
You will review the deploy and teardown pipelines that manage the demo app lifecycle.
-
Open
.azuredevops/pipelines/deploy-all.yml. The deploy pipeline has 2 stages:deploy-all.yml ├── Stage 1: Deploy Storage │ └── Job: Create shared storage account └── Stage 2: Deploy Apps ├── Job: Deploy app-001 (template) ├── Job: Deploy app-002 (template) ├── Job: Deploy app-003 (template) ├── Job: Deploy app-004 (template) └── Job: Deploy app-005 (template)Stage 2 uses pipeline templates (
templates/deploy-app.yml) to avoid repeating the same deployment steps 5 times. -
Open
.azuredevops/pipelines/teardown-all.yml. The teardown pipeline runs sequentially with environment approvals:teardown-all.yml ├── Job: Teardown app-005 (environment: production) ├── Job: Teardown app-004 (environment: production) ├── Job: Teardown app-003 (environment: production) ├── Job: Teardown app-002 (environment: production) ├── Job: Teardown app-001 (environment: production) └── Job: Teardown storage (environment: production) -
Review the environment approval gate:
- Navigate to Pipelines → Environments
- Click on the production environment
- Review the Approvals and checks tab
- The environment requires at least 1 approval before any job targeting it can proceed
-
Trigger the deploy pipeline:
az pipelines run \ --name deploy-all \ --organization https://dev.azure.com/MngEnvMCAP675646 \ --project FinOps
-
After deployment completes, verify the resources:
az group list --query "[?starts_with(name, 'rg-finops-demo')].[name, location]" -o table -
When you are ready to tear down, trigger the teardown pipeline:
az pipelines run \ --name teardown-all \ --organization https://dev.azure.com/MngEnvMCAP675646 \ --project FinOps
-
Navigate to the pipeline run in the ADO web UI and approve each environment deployment when prompted.
Important
The teardown pipeline uses ADO environment approvals as a safety gate. This is the ADO equivalent of GitHub Actions environment protection rules. Always require approvals for destructive operations in production FinOps workflows.
You will learn how to link Git commits to Azure DevOps work items using the AB# syntax.
-
ADO uses the
AB#prefix to link commits to work items. When you includeAB#{work-item-id}in a commit message, ADO automatically creates a link between the commit and the work item. -
Create a test commit with a work item reference:
git commit -m "feat: add cost gate pipeline AB#1234"Replace
1234with an actual work item ID from theMngEnvMCAP675646/FinOpsproject. -
Push the commit:
git push
-
Navigate to the work item in ADO Boards. Under the Development section, you should see the linked commit.
-
To auto-close a work item when a PR merges, use
Fixes AB#{id}in the commit message or PR description:git commit -m "fix: update SARIF upload path Fixes AB#1235" -
Review the full branching and commit workflow defined in the project:
Step Convention Branch naming feature/{work-item-id}-short-descriptionCommit message Include AB#{id}to link work itemsAuto-close Use Fixes AB#{id}to close on mergePR description Reference AB#{id}for traceability -
Compare with GitHub issue linking:
Aspect GitHub Azure DevOps Link syntax #123ororg/repo#123AB#1234Auto-close Fixes #123,Closes #123Fixes AB#1234PR linking Automatic from branch name or description Automatic from AB#in commit or PRWork items Issues and Projects Boards (Epics → Features → User Stories/Bugs)
Note
The AB# linking syntax works through the GitHub and Azure DevOps integration. Commits pushed to GitHub repositories that are connected to ADO automatically resolve AB# references and create bidirectional links.
Before completing the ADO track, verify:
-
finops-scanpipeline ran successfully with matrix jobs across all 5 apps - SARIF artifacts uploaded to ADO Advanced Security for at least one app
- Cost gate pipeline configured as a branch policy on
main - Can explain WIF service connections and how they replace stored credentials
- Can explain
AB#work item linking syntax and auto-close behaviour
You have completed the ADO track of the FinOps Cost Governance Scanner Workshop. Here is a summary of the full workshop:
| Lab | What You Learned |
|---|---|
| Lab 00 | Set up the development environment with all 4 scanner tools |
| Lab 01 | Identified the 5 demo app FinOps violations and the 7 required governance tags |
| Lab 02 | Ran PSRule against Bicep templates for Azure best practice analysis |
| Lab 03 | Ran Checkov for security and CIS benchmark scanning |
| Lab 04 | Ran Cloud Custodian against live Azure resources for runtime violation detection |
| Lab 05 | Used Infracost to estimate costs and compare infrastructure changes |
| Lab 06 | Understood the SARIF format and uploaded results to GitHub Security Tab |
| Lab 06-ADO | Uploaded SARIF results to ADO Advanced Security and compared platforms |
| Lab 07 | Built automated pipelines with GitHub Actions, OIDC, and PR cost gates |
| Lab 07-ADO | Built ADO YAML pipelines with WIF, variable groups, and branch policies |
You now have the skills to implement a complete FinOps scanning platform on both GitHub and Azure DevOps:
- Scan IaC templates before deployment (PSRule, Checkov, Infracost)
- Scan live resources after deployment (Cloud Custodian)
- Produce unified SARIF output for all tools
- Integrate with GitHub Security Tab or ADO Advanced Security for centralised alert management
- Block expensive changes with PR cost gates and branch policies
- Run automatically on a schedule via GitHub Actions or ADO YAML Pipelines
- Link work items using
AB#syntax for full traceability
Return to the workshop home page.
Note
For the GitHub variant of this lab, see Lab 07 — GitHub Actions Pipelines and Cost Gates.







