Lesson894 words

Develop pipelines by using YAML

Develop pipelines by using YAML

A YAML pipeline is stored with its repository, so its history can be versioned and governed alongside the code it builds. Azure DevOps lets an author commit directly to a branch or create another branch and optionally open a pull request; whether a PR is mandatory depends on repository policy.

The four levels

Every YAML pipeline uses the same nesting, whether or not every level is written:

Loading Diagram...
Figure 1 — Mermaid diagram
LevelExecution targetPurpose
StageA major division such as build, test, or deploy
Agent-pool jobOne assigned agentThe scheduling unit whose steps run sequentially on that agent
StepIts job's execution targetOne script, task, powershell, bash, or checkout
Deployment jobAn environmentA job with a deployment strategy; each lifecycle hook resolves to an agent or server job, and VM hooks run on their VM targets

You can omit levels. A pipeline containing only steps: is legal because Azure Pipelines implies one job in one stage. Server jobs are agentless, so “one job, one agent” is specifically an agent-pool-job rule rather than a universal rule.

Job and stage defaults

Jobs in a stage run in parallel by default. Ordering happens when you set dependsOn. Actual concurrency still depends on agents and parallel-job capacity. Stages, by contrast, run sequentially by default.

yaml
jobs: - job: A steps: - script: echo "A and B may start together" - job: B steps: - script: echo "no dependsOn, so B does not wait for A" - job: C dependsOn: [A, B] condition: succeeded() steps: - script: echo "C waits for both"

At least one job must have no dependencies so the graph can start. Classic release pipelines differ: job dependencies are unsupported and multiple jobs run in sequence.

Conditions have a parent boundary

dependsOn defines the dependency graph and order. condition decides whether a scheduled stage, job, or step runs, and a custom condition replaces the implicit succeeded().

ConditionEvaluates true when
succeeded()Dependencies succeeded; this is the default
failed()A dependency failed; useful for rollback or notification
always()Success, failure, or cancellation
canceled()The run was cancelled
succeededOrFailed()Success or failure, but not cancellation

always() does not escape hierarchy: if a parent is skipped, its child cannot run regardless of the child's condition. Cleanup after cancellation also needs enough cancellation timeout to finish.

yaml
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))

Deployment jobs and strategies

A deployment job targets an environment. Environments record deployment history and provide a resource boundary for checks and approvals.

yaml
jobs: - deployment: DeployWeb environment: production strategy: runOnce: preDeploy: steps: - script: echo "initialise" deploy: steps: - script: echo "deploy the app" routeTraffic: steps: - script: echo "shift traffic" postRouteTraffic: steps: - script: echo "watch health" on: failure: steps: - script: echo "roll back" success: steps: - script: echo "clean up"
StrategyBehaviourConstraint
runOnceEach lifecycle hook executes onceSimplest strategy
rollingReplaces targets in maxParallel batchesVM resources only
canaryDeploys in increasing incrementsUses increments

For rolling deployments, Azure Pipelines enters each lifecycle phase once per batch. Within that phase, lifecycle-hook jobs and steps run on each VM target in the batch. Six VMs with maxParallel: 2 therefore means three batches but six executions of a per-VM install step.

Reuse and enforcement are separate

yaml
# Include: insert reusable content steps: - template: templates/npm-steps.yml # Extend: let the template define allowed pipeline structure extends: template: templates/secure-pipeline.yml parameters: buildSteps: - script: npm ci

template: composes content. extends: lets a platform-owned template define the consumer's allowed structure, and typed parameters constrain what the consumer can inject. But a team that controls its YAML can omit bare extends:.

To enforce adoption, configure a Required template check on a protected resource every governed pipeline must consume. A pipeline fails that check if it does not extend the required template. Enforcement therefore comes from the protected-resource check; extends: supplies the constrained structure.

yaml
parameters: - name: buildSteps type: stepList default: []

Template-usable parameter types are string, number, boolean, object, step, stepList, job, jobList, deployment, deploymentList, stage, and stageList. stringList is not available inside templates. Choose the narrowest type that supports the intended extension point.

What to carry into the exam

  • Jobs default to parallel; stages default to sequential.
  • Agent-pool job steps share one assigned agent; server jobs are agentless.
  • A custom condition replaces succeeded(), but a skipped parent still wins.
  • Rolling is VM-only: lifecycle phases iterate by batch and steps execute per VM.
  • extends: constrains structure; a Required template check enforces adoption.
  • Typed template parameters restrict the structures consumers can inject.

Primary sources

Ready to study Designing and Implementing Microsoft DevOps Solutions (AZ-400)?

Practice tests, flashcards, and all study notes — free, no sign-up needed.

Start Studying — Free