Azure Pipelines architecture: stages, agents and protected resources
Understand how Azure Pipelines expands YAML, schedules jobs and protects deployment resources. Diagnose queueing, variable timing and approval failures without guessing.
TL;DR: Azure Pipelines evaluates a run, authorizes required resources and schedules jobs onto suitable agents. Agents execute steps. Resource owners configure approvals and checks separately from pipeline YAML, so editing a deployment stage does not automatically grant access to production.
Distinguish the levels of execution
A pipeline run is one execution of the configured workflow. Stages group work and dependencies, jobs define units scheduled for execution, and steps run within jobs. A typical delivery design separates validation, artifact creation and deployment while preserving the identity of the artifact between them.
The service evaluates templates and expressions at defined times, identifies dependencies and checks authorization. For an eligible agent job, it needs both a parallel-job slot and a compatible available agent. A self-hosted pool adds capacity and capabilities you manage; it also adds responsibility for patching, isolation and cleanup. Microsoft's run sequence documents these boundaries.
Two kinds of dependency shape the run
The graph is conceptual; resource authorization and checks occur for the resources each stage consumes. An environment check does not mean every earlier build stage waits for the production approver.
A small YAML example, with its limits
This example shows stage dependency and artifact transfer. It assumes Azure DevOps Services and a repository containing package.json, a lockfile, and build/test scripts. The build must produce dist/. production is an existing environment with checks configured by its owner. The deployment step only verifies the downloaded file; replace it with the application's reviewed deployment command before using this as a delivery workflow.
trigger:
- main
pool:
vmImage: ubuntu-latest
stages:
- stage: Validate
jobs:
- job: Build
steps:
- task: NodeTool@0
inputs:
versionSpec: '22.x'
- script: npm ci && npm test && npm run build
- publish: dist
artifact: web
- stage: Release
dependsOn: Validate
condition: succeeded()
jobs:
- deployment: DeployWeb
environment: production
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: web
- script: test -d "$(Pipeline.Workspace)/web"
displayName: Verify downloaded artifact directory
A production design should record the artifact's identity, pin supported tool versions under an update policy and verify the actual rollout outcome. This sample's green Release stage establishes only that an artifact directory was downloaded. It intentionally makes no claim that a service has been deployed.
Approval belongs to the protected resource
An environment owner can require approval or other checks before a consuming stage begins. Checks can also apply to resources such as service connections and agent pools. They are administered outside the YAML definition. That separation helps prevent an author from deleting a YAML approval step to authorize their own production change. The approvals and checks documentation defines the supported resources and evaluation order.
Review every credential path into production. An environment approval provides little protection if a broadly accessible service connection lets another job run the same deployment commands without consuming that environment. Restrict the service connection and bind its use to the intended pipeline and trust conditions.
Use timing to explain variable surprises
| Value or construct | When it matters | Failure to look for |
|---|---|---|
| Template parameters and compile-time expressions | Pipeline expansion | A runtime result is unavailable when the graph is built |
| Runtime job outputs | After the producing job executes | Consumer lacks the correct dependency or output reference |
| Macro variables in task inputs | Task execution context | A value exists in one scope but not the consumer's scope |
| Shell environment variables | Within the process that receives them | An export in one step is assumed to persist into another |
The exact expression syntax and scope are documented in Azure variables. Keep non-secret configuration distinct from credentials and avoid placing secret values in output variables or diagnostic echoes.
Diagnose a queued deployment
A pipeline has passed validation and waits for forty minutes. Increasing the agent VM size is proposed. First read the waiting reason. If it is awaiting an environment approval, compute capacity is irrelevant. If no parallel slot is available, adding a VM alone does not add the licensed or granted slot. If the job requires a capability absent from every self-hosted agent, inspect demands and pool selection.
The templates and resource checks follow-up tests whether a repository author can bypass the intended release controls.
Self-check: a deployment job exports APP_VERSION in one script, but the next script cannot read it. Why?
Each step runs in a separate process. Use the pipeline's supported variable/output mechanism for non-secret cross-step values, or write a clearly scoped artifact that the next step reads. A shell export only affects that process and its children.
Practice Azure DevOps pipeline design, then compare the same trust boundaries in the general pipeline architecture.