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; dependsOn defines 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

  1. Add validate.sh containing a safe validation command such as set -euo pipefail and echo validation-passed. Make it executable.
  2. 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'
  1. Create the training-production environment. Add an approval/check appropriate to your training project.
  2. Run the pipeline from a pull request and from main. Confirm the deployment stage only becomes eligible after Validate succeeds, 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 coverage

Run 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

  1. Change validate.sh to exit 1. Confirm DeployTraining is skipped. Restore the successful command and rerun.
  2. Temporarily remove condition: succeeded() on a training branch. Explain why stage dependency alone may still not express the intended safety rule; restore it.
  3. Remove the environment declaration on a training branch. Observe that the approval no longer applies, then restore it.

Scenario practice

  1. Configure: Choose the job dependency, artifact, environment check, and coverage threshold for a change that may deploy only after tests pass.
  2. Diagnose: Tests publish results but deployment is skipped. Which job, condition, artifact, coverage, and environment evidence identifies the gate that stopped it?
  3. 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

Re-audit when Azure Pipelines test/coverage tasks, report formats, or task versions change.

azure-pipelines · deployment-environment · pipeline-retention · All labs