.agents/skills/ci-analysis/references/azure-cli.md
The AzDO MCP tools handle most pipeline queries directly. This reference covers the Azure CLI fallback for cases where MCP tools are unavailable or the endpoint isn't exposed (e.g., downloading artifacts, inspecting pipeline definitions).
When the CI script and GitHub APIs aren't enough (e.g., investigating internal pipeline definitions or downloading build artifacts), use the Azure CLI with the azure-devops extension.
💡 Prefer
az pipelines/az devopscommands over raw REST API calls. The CLI handles authentication, pagination, and JSON output formatting. Only fall back to manualInvoke-RestMethodcalls when the CLI doesn't expose the endpoint you need (e.g., build timelines). The CLI's--query(JMESPath) and-o tableflags are powerful for filtering without extra scripting.
Before making AzDO API calls, verify the CLI is installed and authenticated:
# Ensure az is on PATH (Windows may need a refresh after install)
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
# Check if az CLI is available
az --version 2>$null | Select-Object -First 1
# Check if logged in and get current account
az account show --query "{name:name, user:user.name}" -o table 2>$null
# If not logged in, prompt the user to authenticate:
# az login # Interactive browser login
# az login --use-device-code # Device code flow (for remote/headless)
# Get an AAD access token for AzDO REST API calls (only needed for raw REST)
$accessToken = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv)
$headers = @{ "Authorization" = "Bearer $accessToken" }
⚠️ If
azis not installed, usewinget install -e --id Microsoft.AzureCLI(Windows). Theazure-devopsextension is also required — install or verify it withaz extension add --name azure-devops(safe to run if already installed). Ask the user to authenticate if needed.
⚠️ Do NOT use
az devops configure --defaults— it sets user-wide defaults that may not match the organization/project needed for dotnet repositories. Always pass--organd--project(or-p) explicitly on each command.
$org = "https://dev.azure.com/dnceng"
$project = "internal"
# Find a pipeline definition by name
az pipelines list --name "dotnet-unified-build" --org $org -p $project --query "[].{id:id, name:name, path:path}" -o table
# Get pipeline definition details (shows YAML path, triggers, etc.)
az pipelines show --id 1330 --org $org -p $project --query "{id:id, name:name, yamlPath:process.yamlFilename, repo:repository.name}" -o table
# List recent builds for a pipeline (replace {TARGET_BRANCH} with the PR's base branch, e.g., main or release/9.0)
az pipelines runs list --pipeline-ids 1330 --branch "refs/heads/{TARGET_BRANCH}" --top 5 --org $org -p $project --query "[].{id:id, result:result, finish:finishTime}" -o table
# Get a specific build's details
az pipelines runs show --id $buildId --org $org -p $project --query "{id:id, result:result, sourceBranch:sourceBranch}" -o table
# List build artifacts
az pipelines runs artifact list --run-id $buildId --org $org -p $project --query "[].{name:name, type:resource.type}" -o table
# Download a build artifact
az pipelines runs artifact download --run-id $buildId --artifact-name "TestBuild_linux_x64" --path "$env:TEMP\artifact" --org $org -p $project
Fall back to REST API only when the CLI doesn't expose what you need:
# Get build timeline (stages, jobs, tasks with results and durations) — no CLI equivalent
$accessToken = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv)
$headers = @{ "Authorization" = "Bearer $accessToken" }
$timelineUrl = "https://dev.azure.com/dnceng/internal/_apis/build/builds/$buildId/timeline?api-version=7.1"
$timeline = (Invoke-RestMethod -Uri $timelineUrl -Headers $headers)
$timeline.records | Where-Object { $_.result -eq "failed" -and $_.type -eq "Job" }
All dotnet repos that use arcade put their pipeline definitions under eng/pipelines/ or in the root. Use az pipelines show to find the YAML file path, then fetch it:
# Find the YAML path for a pipeline
az pipelines show --id 1330 --org $org -p $project --query "{yamlPath:process.yamlFilename, repo:repository.name}" -o table
# Fetch the YAML from the repo (example: dotnet/efcore's CI pipeline)
# Read the pipeline YAML from the repo to understand build stages and conditions
# e.g., azure-pipelines-public.yml in dotnet/efcore
# For VMR unified builds, the YAML is in dotnet/dotnet:
# eng/pipelines/unified-build.yml
# Templates are usually in eng/pipelines/common/ or eng/pipelines/templates/
This is especially useful when: