Lab: Azure Pipelines controlled delivery
Outcome
Create an Azure Pipelines YAML pipeline that validates a change, publishes an artifact, and pauses at a protected training environment. You will deliberately fail validation and confirm that a failed build cannot reach deployment.
Before you begin
Use a personal Azure DevOps project and a disposable repository. You need permission to create a pipeline and a training environment. Do not connect this lab to a production subscription or service connection.
Learn
- A YAML pipeline is version-controlled delivery logic; the Azure DevOps environment is the deployment governance boundary.
- An agent executes a job. A stage organizes jobs;
dependsOndefines ordering; an artifact moves output between jobs/stages. - A successful build does not waive environment approvals/checks. A service connection, if added later, is an independent authorization boundary.
Do
- Add
validate.shcontaining a safe validation command such asset -euo pipefailandecho validation-passed. Make it executable. - Add
azure-pipelines.yml:
trigger:
branches:
include: [main]
pr:
branches:
include: [main]
stages:
- stage: Validate
jobs:
- job: test
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
- bash: ./validate.sh
displayName: Run validation
- publish: validate.sh
artifact: validation-evidence
- stage: DeployTraining
dependsOn: Validate
condition: succeeded()
jobs:
- deployment: training
environment: training-production
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: validation-evidence
- bash: echo 'Safe training deployment'- Create the
training-productionenvironment. Add an approval/check appropriate to your training project. - Run the pipeline from a pull request and from
main. Confirm the deployment stage only becomes eligible afterValidatesucceeds, then approve the training deployment.
Verify
Capture the PR validation result, published validation-evidence artifact, stage dependency view, pending environment approval, and final successful deployment job. Explain which control would stop: a failing test, an unapproved environment, and a missing service connection.
Add test results and coverage
Use a small JavaScript project in the training repository. Add the same src/math.js, test/math.test.js, and package.json from the GitHub Actions lab, including "type": "module". The test command must create test-results.xml and coverage/cobertura-coverage.xml.
Replace the validation job steps after checkout with this source-checked Azure Pipelines sequence:
- task: NodeTool@0
inputs:
versionSpec: '22.x'
- script: npm ci
displayName: Install test dependencies
- script: npm run test:ci
displayName: Run tests and create coverage
- task: PublishTestResults@2
condition: succeededOrFailed()
inputs:
testResultsFormat: JUnit
testResultsFiles: '**/test-results.xml'
failTaskOnFailedTests: true
- task: PublishCodeCoverageResults@2
condition: succeededOrFailed()
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/cobertura-coverage.xml'
failIfCoverageEmpty: true
- script: |
node -e "const c=require('fs').readFileSync('coverage/cobertura-coverage.xml','utf8'); const m=c.match(/line-rate=\"([\d.]+)\"/); if (!m || Number(m[1]) < .8) process.exit(1)"
displayName: Require at least 80% line coverageRun this on the training branch. Record the Tests tab, the Code Coverage summary, and the 80% threshold result. Deliberately add an uncovered branch or set the threshold to 1; verify that the deployment stage is skipped while the results still publish because their tasks use succeededOrFailed(). Restore the 80% threshold and the intended test.
Break and fix
- Change
validate.shtoexit 1. ConfirmDeployTrainingis skipped. Restore the successful command and rerun. - Temporarily remove
condition: succeeded()on a training branch. Explain why stage dependency alone may still not express the intended safety rule; restore it. - Remove the environment declaration on a training branch. Observe that the approval no longer applies, then restore it.
Scenario practice
- Configure: Choose the job dependency, artifact, environment check, and coverage threshold for a change that may deploy only after tests pass.
- Diagnose: Tests publish results but deployment is skipped. Which job, condition, artifact, coverage, and environment evidence identifies the gate that stopped it?
- Distinguish: Contrast a failed test, a failed coverage threshold, a missing service connection, and an unapproved environment.
Clean up
Delete the training environment, pipeline, artifacts, and repository when the exercise is complete. Do not carry a training approval or service connection into a real project without a separate access review.
Sources and re-audit
- Publish test results in Azure Pipelines, checked 2026-08-22.
- Review code coverage in Azure Pipelines, checked 2026-08-22.
Re-audit when Azure Pipelines test/coverage tasks, report formats, or task versions change.
Related
azure-pipelines · deployment-environment · pipeline-retention · All labs