Define custom capabilities for agent (e.g., HasDocker=true, HasSpecialSoftware=true)
Reference in pipeline: demands: HasDocker
GitHub-Hosted Runners
Runner
OS
Free Minutes (private repos)
ubuntu-latest
Ubuntu 24.04
2,000/month
windows-latest
Windows 2022
3,000/month (2x cost)
macos-latest
macOS 14
10,000/month (10x cost)
Self-Hosted GitHub Runners
1
2
3
4
5
6
7
8
9
# Register runner:# Settings → Actions → Runners → New self-hosted runner# In workflow:jobs:build:runs-on:self-hosted# Use any self-hosted runner# ORruns-on:[self-hosted,linux,x64,production]# Specific labels
Runner groups: Organize runners by team/project, control access at org level.
Scale with ARC (Actions Runner Controller):
Kubernetes-based autoscaling for self-hosted runners
Runners spin up on demand, terminate after job completes
Ideal for bursty workloads
GitHub Repositories ↔ Azure Pipelines Integration
Method 1: Connect GitHub as a source in Azure Pipelines
New Pipeline → Select GitHub
Authorize with OAuth or GitHub App
Select repository → Azure Pipelines will create a azure-pipelines.yml
Method 2: GitHub App (recommended)
Install Azure Pipelines GitHub App in GitHub organization
More secure than OAuth (scoped permissions, no user token)
on:push:branches:[main,'release/**']paths-ignore:['docs/**','*.md']tags:['v*']pull_request:branches:[main]types:[opened,synchronize,reopened]schedule:-cron:'02**1-5'# Weekdays at 2 AM UTCworkflow_dispatch:# Manual triggerinputs:environment:description:'Targetenvironment'required:truedefault:'staging'type:choiceoptions:[staging,production]workflow_call:# Called by another workflow (reusable)inputs:version:required:truetype:stringrepository_dispatch:# Triggered via API (webhooks)types:[deploy-prod]
# ─── Pipeline-level settings ───────────────────────────────name:$(Date:yyyyMMdd).$(Rev:r)trigger:[main]# ─── Variables ─────────────────────────────────────────────variables:-name:buildConfigvalue:'Release'-group:'Production-Variables'# Variable group from Library-template:vars/common.yml# Template file# ─── Stages ────────────────────────────────────────────────stages:-stage:BuilddisplayName:'Build&Test'jobs:-job:BuildJobpool:vmImage:'ubuntu-latest'steps:-checkout:selffetchDepth:0# Full history (needed for SonarQube, GitVersion)-template:templates/build-steps.yml# Reusable templateparameters:buildConfig:$(buildConfig)-stage:DeployStagingdependsOn:Buildcondition:and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))jobs:-deployment:DeployStagingenvironment:'staging'strategy:runOnce:deploy:steps:-script:echo "Deploy to staging"-stage:DeployProddependsOn:DeployStagingjobs:-deployment:DeployProdenvironment:'production'# Has approval configuredstrategy:runOnce:deploy:steps:-script:echo "Deploy to prod"
jobs:-job:TestLinuxpool:vmImage:'ubuntu-latest'steps:-script:dotnet test --filter OS=Linux-job:TestWindowspool:vmImage:'windows-latest'steps:-script:dotnet test --filter OS=Windows-job:TestMacpool:vmImage:'macOS-latest'steps:-script:dotnet test --filter OS=Mac-job:PublishResultsdependsOn:-TestLinux-TestWindows-TestMaccondition:always()steps:-script:echo "Publish aggregated results"
Fan-out / Fan-in Pattern
1
2
3
4
┌──► Job A ──►┐
Trigger ──► Gate ├──► Aggregate Job
└──► Job B ──►┘
└──► Job C ──►┘
# GitHub Actionsstrategy:matrix:python-version:['3.8','3.11','3.12']os:[ubuntu-latest,windows-latest]fail-fast:false# Don't cancel other matrix jobs if one failsmax-parallel:4
variables:-group:'Dev-Environment'# Library → Variable Groups# In pipeline, access as:# $(variableName)
Link Variable Group to Key Vault:
Library → Variable Groups → Link secrets from Azure Key Vault
Variables automatically sourced from Key Vault at runtime
Task Groups (Azure Pipelines Classic)
Group steps into a reusable task (like a composite template for Classic pipelines)
Parameterisable, versioned
Converted to YAML templates when migrating
Checks and Approvals (YAML Environments)
Configure in Azure DevOps UI
Pipelines → Environments → [environment name] → Approvals and checks
Available check types:
| Check | Description |
|——-|————-|
| Approvals | Named users/groups must approve |
| Branch control | Only deploy from specific branches (e.g., main) |
| Business hours | Only deploy during defined hours |
| Required template | Pipeline YAML must extend a specific template |
| Invoke Azure Function | Custom validation via Azure Function |
| Query Azure Monitor | Validate no active alerts |
| Invoke REST API | External approval system integration |
Approval in Pipeline
1
2
3
4
5
6
7
8
9
-stage:Productionjobs:-deployment:DeployProdenvironment:'production'# Approvals defined on environmentstrategy:runOnce:deploy:steps:-script:echo "Deploying after approval"
🧠 Key Exam Tips for Pipelines
Scenario
Answer
Run pipeline on PR to main
pr: trigger (Azure Pipelines) or pull_request: event (Actions)