Skip to main content

Azure Pipeline Validator

License: MIT Python Version Tests Lint CI Ready

azure-pipeline-validator is a batteries‑included Azure DevOps YAML inspector that runs the same validations you rely on in the service, but locally. It combines three feedback loops:

  1. yamllint – fast structural linting using a tuned configuration for Azure Pipelines quirks.
  2. JSON Schema – offline validation against Microsoft’s published schema (/distributedtask/yamlschema).
  3. Preview REST API – invokes POST .../_apis/pipelines/{id}/preview with yamlOverride, returning the real finalYaml and any validationResults that Azure DevOps would produce.

The CLI understands both single files and whole repositories, wraps templates automatically (steps/jobs/stages), and mirrors the live API response schema (including continuation_token).

Table of contents

Features

  • Template auto-wrapping – detects steps/jobs/stages templates, wrapping them into runnable pipelines before previewing.
  • Schema caching – fetches the official schema once per run and reuses it for every file, keeping validation snappy.
  • Rich reporting – console output shows pass/fail per file with the first offending message per stage.
  • Toggleable stages – disable lint/schema/preview individually for quick iteration.
  • Failure ergonomics – --fail-fast stops on first failure; exit codes are 0 (success) or 1 (any failures/API issues).
  • Selective scanning – --exclude / -x lets you skip generated folders or files with glob patterns.
  • UV-native – built with uv, so you can run it via uv run, uvx, or install it as a global tool.

Installation & invocation

Getting started (recommended)

  1. Install uv (pick the command for your OS):

    • macOS / Linux

      curl -LsSf https://astral.sh/uv/install.sh | sh
      
    • Windows (PowerShell)

      powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
      
  2. Pick the validations you need – nothing runs until you opt in:

    • Lint-only (no Azure creds needed):

      uvx azure-pipeline-validator --lint .
      
    • Schema check (downloads the public schema anonymously):

      uvx azure-pipeline-validator --schema workflows/ci.yml
      
    • Preview (requires AZDO_* + AZDO_PAT because it calls your org):

      uvx azure-pipeline-validator --preview --schema workflows/
      

Short flags -l, -s, and -p map to --lint, --schema, and --preview if you prefer brevity, and you can combine them as needed.

Global install with uv (install once, use anywhere):

uv tool install git+https://github.com/andrewmaspero/azure-pipeline-validator.git
azure-pipeline-validator --help

Once published to PyPI, you can also use:

uv tool install azure-pipeline-validator
azure-pipeline-validator --help

Pip install will also work once published (pip install azure-pipeline-validator).

Required environment

Environment variables (or their CLI equivalents) are only required when --preview/-p is enabled. Lint (--lint/-l) and schema (--schema/-s) checks run entirely offline using the public schema.

Export the same variables you would in an Azure Pipelines job, or pass them via the --azdo-* options:

Variable Description
AZDO_ORG / --azdo-org Organization URL, e.g. https://dev.azure.com/contoso. (Falls back to az devops configure --defaults organization=....)
AZDO_PROJECT / --azdo-project Project that owns the pipeline. (Falls back to az devops configure --defaults project=....)
AZDO_PIPELINE_ID / --azdo-pipeline-id ID of an existing YAML pipeline (any pipeline is fine).
AZDO_PAT / --azdo-pat PAT with Build (Read & Execute); use SYSTEM_ACCESSTOKEN inside CI.
AZDO_REFNAME Optional ref used when expanding templates (default refs/heads/main).
AZDO_TIMEOUT_SECONDS Optional HTTP timeout override (default 30).

Tips:

  • You can set variables inline without shell-specific syntax: uvx azure-pipeline-validator AZDO_ORG=https://dev.azure.com/contoso AZDO_PROJECT=demo ...
  • Every Azure option also has a CLI flag, e.g. uvx azure-pipeline-validator --azdo-org https://dev.azure.com/contoso --azdo-pat token workflows/.
  • Already signed in via az devops login? The cached PAT from the Azure CLI DevOps extension (or AZURE_DEVOPS_EXT_PAT) is picked up automatically.
  • Defaults configured via az devops configure --defaults organization=... project=... are used whenever AZDO_ORG/AZDO_PROJECT are missing.
  • Inside Azure Pipelines you can skip AZDO_PAT by enabling “Allow scripts to access the OAuth token” and mapping it to SYSTEM_ACCESSTOKEN.

Discovering available projects

Need to remember project names? Run:

uvx azure-pipeline-validator projects --top 20

The command reuses the same credential discovery (env vars, az devops login, defaults configured via az devops configure).

Usage examples

Validate the entire repo with every check (requires AZDO_* + PAT):

uv run azure-pipeline-validator --lint --schema --preview . --repo-root $(pwd)

Validate a single template file using only the schema:

uv run azure-pipeline-validator --schema common/templates/steps/build.yml

Lint a directory quickly:

uv run azure-pipeline-validator --lint workflows/

Skip generated templates while linting:

uv run azure-pipeline-validator --lint workflows/ --exclude "workflows/generated"

CLI reference

Usage: azure-pipeline-validator [OPTIONS] [PATH]

Run yamllint, schema validation, and Azure preview against YAML files.

Arguments:
  PATH  File or directory to validate. Directories are scanned recursively for *.yml and *.yaml files.  [default: .]

Options:
  --repo-root PATH                     Base path used when resolving template references (defaults to CWD).
  --azdo-org URL                       Organization URL (overrides AZDO_ORG).
  --azdo-project NAME                  Project name (overrides AZDO_PROJECT).
  --azdo-pipeline-id ID                Pipeline ID used for preview (overrides AZDO_PIPELINE_ID).
  --azdo-pat TOKEN                     PAT or OAuth token (overrides AZDO_PAT / SYSTEM_ACCESSTOKEN).
  --azdo-ref-name REF                  Ref name for template expansion (overrides AZDO_REFNAME).
  --azdo-timeout-seconds SECONDS       HTTP timeout override (overrides AZDO_TIMEOUT_SECONDS).
  --lint / --no-lint, -l / --no-l      Run yamllint (aliases: --lint, -l). All checks are opt-in; lint is disabled by default.
  --schema / --no-schema, -s / --no-s  Validate against Microsoft's published YAML schema (aliases: --schema, -s).
  --preview / --no-preview, -p / --no-p
                                       Call the Azure DevOps preview endpoint (aliases: --preview, -p).
  --fail-fast / --no-fail-fast         Stop immediately after the first file that fails validation.
  --exclude PATTERN                    Glob pattern or relative path to skip during scanning. Repeat the option to exclude multiple files/directories.
  --help                               Show this message and exit.

Output format

Every file gets one row with three columns (yamllint / schema / preview). A passing stage prints pass; the first error message is shown otherwise (plus a “(+N more)” suffix when applicable). Example:

┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ File                 ┃ yamllint ┃ schema ┃ preview                      ┃
┣━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃ workflows/ci.yml     ┃ pass     ┃ pass   ┃ pass                         ┃
┃ workflows/deploy.yml ┃ L3 C5: … ┃ pass   ┃ path not found (+2 more)     ┃
┗━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
Validated 2 file(s). Failures: 1.

Exit code is non-zero whenever any file fails or when the preview/schema endpoints error.

CI integration

Add a job that installs uv, exports AZDO_*, and runs the command. When running inside Azure Pipelines you can reuse $(System.AccessToken) and the current pipeline id:

- job: Validate
  pool:
    vmImage: ubuntu-latest
  steps:
    - task: UsePythonVersion@0
      inputs:
        versionSpec: '3.12'

    - script: |
        uv tool install azure-pipeline-validator
        azure-pipeline-validator --lint --schema --preview workflows/
      env:
        AZDO_ORG: $(System.TeamFoundationCollectionUri)
        AZDO_PROJECT: $(System.TeamProject)
        AZDO_PIPELINE_ID: $(System.DefinitionId)
        AZDO_PAT: $(System.AccessToken)
        AZDO_REFNAME: $(Build.SourceBranch)

The preview call runs with yamlOverride, so no build is queued.

Development workflow

Local development (inside this repo):

cd /path/to/azure-pipeline-validator
uv run azure-pipeline-validator --help
# Format and lint
uv run ruff format
uv run ruff check

# Run the test suite
uv run python -m pytest

pyproject.toml configures Ruff (line length 100, py313) and pytest/coverage. The tests include CLI help verification plus mock preview responses that mirror the real API payload captured from Azure DevOps.

Publishing the package

The package is published to PyPI automatically via GitHub Actions when a new tag is pushed using Trusted Publishing (OIDC) - no API tokens needed!

First-time Setup (Required Before First Publish)

Important: Trusted Publishing cannot create new projects on PyPI. You must create the project manually first:

  1. Create the project on PyPI:

  2. Set up Trusted Publishing:

    • Go to https://pypi.org/manage/account/publishing/
    • Click "Add a new trusted publisher"
    • Fill in:
      • PyPI project name: azure-pipeline-validator
      • Owner: andrewmaspero (your GitHub username)
      • Repository name: azure-pipeline-validator
      • Workflow filename: pipeline.yml
      • Environment name: pypi (optional but recommended)
    • Click "Add trusted publisher"

Publishing a New Version

  1. Update version in pyproject.toml.
  2. Commit and push the changes.
  3. Create and push a tag: git tag v0.x.y && git push --tags.

The CI pipeline will automatically:

  • Run all tests
  • Build the package
  • Publish to PyPI using Trusted Publishing (OIDC)

Once published, consumers can install and use it via:

# Using uvx (no installation needed)
uvx azure-pipeline-validator --help

# Or install globally
uv tool install azure-pipeline-validator
azure-pipeline-validator --help

# Or with pip
pip install azure-pipeline-validator
azure-pipeline-validator --help

For manual publishing, use uv directly:

uv build
uv publish

For manual publishing, you'll need to set UV_PUBLISH_USERNAME / UV_PUBLISH_PASSWORD environment variables, or use a PyPI API token.

Troubleshooting

Symptom Fix
Set AZDO_PAT ... before running validation. Export AZDO_PAT or SYSTEM_ACCESSTOKEN so the preview call can authenticate.
Preview API returns 401/403 Confirm AZDO_PIPELINE_ID is correct and the PAT has Build Read & Execute permissions.
Templates reference other repos/branches Set AZDO_REFNAME appropriately; cross-repo templates may require additional repository resources in the payload.
yamllint errors but schema/preview pass Temporarily omit --lint if you need to focus on schema/preview issues, but try to fix lint problems quickly.

Feel free to fork, contribute improvements, or publish your own build. This README should give you everything you need to adopt the validator in local workflows, UV-based tooling, and CI/CD.

License

azure-pipeline-validator is open source software released under the MIT License. Contributions are welcome—just open an issue or pull request so we can review changes together.

Metadata

Release files for azure-pipeline-validator 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for azure-pipeline-validator 0.2.1
File Size Uploaded
azure_pipeline_validator-0.2.1.tar.gz 63.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for azure-pipeline-validator 0.2.1
File Interpreter ABI Platform
azure_pipeline_validator-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 90.9 kB

Release files / azure_pipeline_validator-0.2.1.tar.gz

Download URL azure_pipeline_validator-0.2.1.tar.gz
Size 63.9 kB
Tags Source
SHA-256 checksum
How to use checksums
96a17c4e3535b1d4dee48a000e65f303cda3f1dbe3851b580db192e3c1e071d9
BLAKE2b-256 checksum
How to use checksums
69e6919f3c283ae824ade06bb3d3743d0dd695e3d0422c9e7313909c3bb79a13
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.9 {"installer":{"name":"uv","version":"0.9.9"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / azure_pipeline_validator-0.2.1-py3-none-any.whl

Download URL azure_pipeline_validator-0.2.1-py3-none-any.whl
Size 27.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1c8c31a81767f30d6be27ac10be84ee21ff81610eb7023c269001e3613875b0c
BLAKE2b-256 checksum
How to use checksums
fc05800c44f7e90537815343fffa6faecc6eac75345427bc1a8525c10fd804e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.9 {"installer":{"name":"uv","version":"0.9.9"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page