Azure Repos: unlimited private Git. Branch policies on main: require pull requests, a minimum number of reviewers, linked work items and build validation (the pipeline must pass before merging). With Azure Repos, PR builds come from build validation, not from the pr: key.
Pipeline as code: azure-pipelines.yml lives in the repo and is reviewed through PRs. The classic (drag-and-drop) editor is the old way.
trigger:
branches:
include: [main]
pool:
vmImage: ubuntu-latest
variables:
nodeVersion: '22.x'
steps:
- task: UseNode@1
inputs:
version: $(nodeVersion)
- script: npm ci && npm test
displayName: Install and test
env:
DB_PASSWORD: $(DB_PASSWORD) # secrets must be mapped explicitly
flowchart TB PIPE["azure-pipelines.yml"] --> ST["stage: Build"] ST --> J["job: build (pool ubuntu-latest)"] J --> S1["task: UseNode@1"] J --> S2["script: npm ci && npm test"] J --> S3["publish: dist (artifact web)"]
| Block | Meaning |
|---|---|
trigger |
Which branch pushes run CI (trigger: none = CI off) |
pool |
The agent running the job (vmImage: ubuntu-latest) |
variables |
YAML variables, used as $(name) |
stages → jobs → steps |
A single-job pipeline may have only steps |
task: Name@major |
Prebuilt, versioned steps: UseNode@1, Docker@2, AzureCLI@2 |
template: |
Reuse steps/jobs from another file |
- Secret variables (set in the UI/CLI): masked in logs and not added to a script's environment automatically — map them with
env:. - Variable groups: shared by many pipelines, can be linked to Azure Key Vault.
Traps: tab indentation or wrong nesting; forgetting env: for secrets; ubuntu-latest changes over time.
| Command | Meaning |
|---|---|
az repos create --name my-app |
Create a repo |
az repos pr create --repository my-app --source-branch feature/login --target-branch main --title "Add login" |
Create a PR |
az pipelines create --name my-app-ci --repository my-app --repository-type tfsgit --branch main --yml-path azure-pipelines.yml |
Create a pipeline |
az pipelines variable create --pipeline-name my-app-ci --name DB_PASSWORD --value changeme-example --secret true |
Secret variable |
az pipelines variable-group create --name app-settings --variables LOG_LEVEL=info |
Variable group |
Triggers in more detail
trigger:
branches: { include: [main, release/*] }
paths: { include: [src/*], exclude: [docs/*] }
pr:
branches: { include: [main] }
schedules:
- cron: "0 2 * * *"
displayName: Nightly build
branches: { include: [main] }
always: false
Path filters avoid running the whole pipeline when only documentation changes. The pr: key applies to GitHub and Bitbucket repositories; for Azure Repos, PR validation comes from branch policies. Scheduled runs with always: false skip when nothing changed.
Variables and expressions
| Syntax | Evaluated | Typical use |
|---|---|---|
$(name) |
At runtime, just before a task runs | Most task inputs and scripts |
${{ variables.name }} |
At compile time, when YAML is expanded | Conditions, template parameters, inserting whole blocks |
$[ variables.name ] |
At runtime, for conditions and variable values | condition: and output variables |
Predefined variables such as Build.BuildId, Build.SourceBranch, and Build.SourceVersion (the commit SHA) are available in every run.
Templates
# templates/build-node.yml
parameters:
- name: nodeVersion
default: '22.x'
steps:
- task: UseNode@1
inputs: { version: ${{ parameters.nodeVersion }} }
- script: npm ci && npm test
A pipeline uses it with - template: templates/build-node.yml and parameters: { nodeVersion: '20.x' }. Templates can live in another repository through resources: repositories:, letting a platform team publish standard building blocks. extends: templates go further and force pipelines into an approved structure.
Speeding up runs
The Cache@2 task restores dependency folders such as ~/.npm keyed by a lock file, cutting install time on Microsoft-hosted agents. Splitting work into parallel jobs and publishing test results with PublishTestResults@2 makes failures visible in the run's Tests tab.
Other ways to work with Git
Azure Pipelines builds code from Azure Repos, GitHub, GitHub Enterprise, Bitbucket Cloud, and generic Git. GitHub repositories integrate through the Azure Pipelines GitHub App, which also reports status checks on pull requests.