YAML pipelines basics: Azure and GitHub

Updated October 9, 2026

A YAML pipeline is a text file in your repository that tells a CI/CD service when to run, where to run, and which steps to execute. Azure Pipelines and GitHub Actions use different keywords but the same shape: a trigger starts a run, the run is split into jobs, and each job is an ordered list of steps on one machine. The exam rule: jobs are independent machines that run in parallel unless you order them, steps share one machine and run in sequence.

Side-by-side structure

ConceptAzure PipelinesGitHub Actions
File locationAny path you register; azure-pipelines.yml by convention.github/workflows/, as .yml or .yaml (required location)
What starts a runtrigger (push), pr, schedules, pipeline resourceson: events such as push, pull_request, schedule, workflow_dispatch
Top-level groupingstages (optional)none
Unit of workjob, or deployment for deploysjobs.<id>
Machinepool (vmImage or a named agent pool)runs-on
Run somethingscript, bash, pwsh, taskrun (shell command) or uses (an action)
Order jobsdependsOnneeds
Skip logicconditionif
Reusetemplates (template:, extends)reusable workflows and composite actions
Deploy gateenvironment with approvals and checksenvironment with required reviewers

Azure Pipelines nests four levels: pipeline, stages, jobs, steps. Stages are optional; with no stages: keyword the file has one implicit stage. GitHub Actions has three: workflow, jobs, steps.

Reading a file top to bottom

A small Azure Pipelines file:

trigger:
  branches:
    include: [ main ]

pool:
  vmImage: ubuntu-latest

variables:
  buildConfig: Release

stages:
- stage: Build
  jobs:
  - job: Compile
    steps:
    - script: dotnet build -c $(buildConfig)
      displayName: Build
- stage: Deploy
  dependsOn: Build
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
  jobs:
  - deployment: Web
    environment: production
    strategy:
      runOnce:
        deploy:
          steps:
          - script: echo deploying

What each part does:

  • trigger fires on pushes to main. pr would do the same for pull requests. Setting trigger: none turns off CI runs.
  • pool is inherited by every job unless a stage or job sets its own.
  • stages run one after another in file order by default. Add dependsOn: [] to run a stage in parallel, or a list to wait for several stages.
  • A stage’s default condition is “all stages I depend on succeeded”. Writing your own condition removes that, which is why it usually starts with and(succeeded(), ...).
  • The deployment job targets an environment, and that is where approvals live.

The same idea in GitHub Actions:

name: CI
on:
  push:
    branches: [ main ]
permissions:
  contents: read
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - run: dotnet build -c Release
  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    steps:
      - run: echo deploying

Jobs run in parallel by default; needs creates the order. permissions limits the token the workflow gets, which is the safe default to set explicitly. Third-party actions are referenced with uses: and should be pinned to a version (a full commit SHA is the strictest form).

Three kinds of variable syntax in Azure Pipelines

Azure Pipelines has three syntaxes, and they differ by when they are evaluated (Microsoft Learn, expressions):

SyntaxExampleEvaluatedNotes
Macro$(buildConfig)Just before a task runsReplaced as text; unknown values stay as written
Template expression${{ parameters.env }}When the YAML is compiledHas access to parameters and static variables
Runtime expression$[ eq(variables['Build.SourceBranch'], 'refs/heads/main') ]At run timeUsed in variables and conditions; no parameters

Variables are always strings. If you need a typed value (a number, a boolean, a list), use a parameter. In GitHub Actions the equivalent is ${{ github.ref }}, ${{ secrets.NAME }} and the env map.

Worked example: fan-out and fan-in

A team wants a build, then unit tests and a security scan in parallel, then one deployment that waits for both. In Azure Pipelines that is three stages with explicit dependsOn:

  • Build has no dependency.
  • Test and Scan both say dependsOn: Build, so they start together after it.
  • Deploy says dependsOn: [Test, Scan] and does not start until both succeeded.

Without any dependsOn, the stages would run in file order, one at a time, and the scan would wait for the tests. In GitHub Actions the same shape is three jobs where test and scan have needs: build and deploy has needs: [test, scan]. If the four jobs have no needs at all, they all start at once, and deploy could run before build finishes. Related pipeline design is covered in CI/CD explained, and trunk-based flows in Git branching strategies.

Approvals belong to the environment, not the YAML

Manual approval before production is configured as a check on the environment, not as a step in the YAML. That matters for security: a developer who can edit the pipeline file cannot delete a check that lives on the environment. Azure Pipelines also supports trigger: manual on a stage to hold it until someone starts it, and isSkippable: false to stop users skipping a mandatory stage such as a malware scan. In GitHub, required reviewers are set on the environment and the job refers to it with environment:. When a question says “approval that pipeline authors cannot bypass”, the answer is the environment check.

Which exams test this

ExamHow the topic appearsWhere on this site
AZ-400Design and implement build and release pipelines (50–55%): develop pipelines in YAML, trigger rules, job execution order with parallelism and multi-stage pipelines, YAML templates and variable groups, checks and approvals on YAML environments, and migrating classic pipelines to YAMLAZ-400 build and release pipelines and the AZ-400 cheat sheet
AI-300Minor topic. Design and implement an MLOps infrastructure (15–20%): automate resource provisioning with GitHub Actions workflowsMLOps infrastructure

Other exams, such as Cisco’s CCNA Automation exam (200-901), ask you to describe the components of a CI/CD pipeline (see its automation guide) rather than write pipeline syntax. Infrastructure deployment steps usually call Bicep or Terraform.

How exam questions are built

  • Parallel by default. Jobs run in parallel, stages in sequence (Azure Pipelines), and GitHub jobs are parallel. Questions describe an unwanted order and ask what is missing: dependsOn or needs.
  • Which level. “Share one machine and a workspace” means steps in one job. “Different operating systems” means different jobs.
  • Condition wording. “Run only on main” is a branch condition. “Run even if the previous stage failed” needs a status function such as always() or failed(), because the default is success only.
  • Compile time versus runtime. A value that comes from a parameter must use ${{ }}; a value computed during the run uses $[ ] or a macro.
  • Secrets. Secrets are never written in the YAML; they come from variable groups, Key Vault or the platform’s secret store.

Question 1. A GitHub Actions workflow has three jobs: build, test and deploy. No job sets any dependency. The deploy job sometimes starts before the build job has finished. What is the correct fix?

  • A. Move the deploy job to the bottom of the file
  • B. Change runs-on of the deploy job to a different runner
  • C. Add needs: [build, test] to the deploy job
  • D. Move the deploy job into a separate workflow file
Show answer

Answer: C

Jobs in a workflow run in parallel unless ordered. Adding needs to the deploy job makes it wait for the jobs it lists, and it is skipped if they fail. A separate workflow file starts its own run on the same event, so it adds no ordering at all. Reordering the jobs in the file has no effect on execution order. The runs-on setting selects the machine, not the order.

Question 2. Production deployments in an Azure Pipelines YAML pipeline must wait for a manual approval. Developers can edit the pipeline file, but the security team requires that they cannot remove the approval. Where should you configure it?

  • A. Add a manual intervention step to the YAML deploy stage
  • B. Add an approval check to the production environment
  • C. Add a branch policy requiring a build validation
  • D. Add a condition to the Deploy stage that checks a variable
Show answer

Answer: B

Approvals are checks on an environment, and they are managed by the owners of that environment, not by the people who edit the YAML. A pipeline referencing the environment must pass the check. A manual step written in the YAML can be deleted by anyone with edit rights. Branch policies control merging, not deployment gates. A condition on the stage is also part of the editable file.

Go deeper