azure-pipelines-ci-doctor

Audit any azure-pipelines.yml for waste, cost leaks, and security gaps. Eight rules. One command. MIT. Sister project to ci-doctor (GitHub Actions), gitlab-ci-doctor (GitLab CI), and bitbucket-ci-doctor (Bitbucket Pipelines).

npm npmjs.com/package/azure-pipelines-ci-doctor  |  source github.com/depmedicdev-byte/azure-pipelines-ci-doctor  |  try it in-browser scanner

Try it in 5 seconds

$ npx azure-pipelines-ci-doctor                  # audit current repo
$ npx azure-pipelines-ci-doctor --markdown       # PR-comment friendly output
$ npx azure-pipelines-ci-doctor --json           # machine-readable
$ npx azure-pipelines-ci-doctor --severity=warn  # warn + error only
$ npx azure-pipelines-ci-doctor --rules          # list checks
$ npx azure-pipelines-ci-doctor --demo           # smoke-test against bundled bad pipeline

Exit code is 1 when there are error-level findings, so it drops into an Azure DevOps pipeline (or a pre-commit hook) without ceremony.

The 8 rules

RuleSeverityWhat it catches
expensive-vm-imagewarn / costpool.vmImage = macOS-latest (~10x) or windows-latest (~2x) without commands that need it. Move to ubuntu-latest.
container-no-pinwarn / securityJob container.image is not pinned to image@sha256:<digest> — floating tags break reproducibility and are a supply-chain risk.
missing-timeout-in-minuteswarn / costJob has no timeoutInMinutes. Default is 60 min on Microsoft-hosted, 360 min on self-hosted — a hang can burn the whole window.
missing-cachewarn / costJob runs npm/pip/maven/gradle/cargo/go/bundler install but no Cache@2 task — redownloads everything every run.
wide-triggerwarn / costtrigger: or pr: is unscoped (no branch / path filter). Pipeline runs on every push including draft PRs and doc-only changes.
inline-secret-leakwarn / securityStep uses $(SECRET_NAME) macro that expands inline in build logs. Pass via env: mapping and reference $env:NAME / $NAME instead.
legacy-task-versionwarn / reliabilityBuilt-in task pinned to outdated major version (e.g. UseNode@1). Loses Node 20 runtime + current bug fixes.
unbounded-parallelismwarn / coststrategy.parallel >= 5 or matrix >= 5 legs without maxParallel. Will starve other pipelines on shared agent pools.

Drop into a pipeline

- stage: AuditPipeline
  jobs:
    - job: cidoctor
      timeoutInMinutes: 5
      pool:
        vmImage: 'ubuntu-latest'
      steps:
        - task: UseNode@2
          inputs:
            version: '20.x'
        - script: npx --yes azure-pipelines-ci-doctor --markdown > $(Build.ArtifactStagingDirectory)/ci-doctor.md
        - publish: $(Build.ArtifactStagingDirectory)/ci-doctor.md
          artifact: ci-doctor-report

Why a fourth one?

Azure DevOps Pipelines have their own quirks: free Microsoft-hosted parallelism is limited (1 free job for new orgs) and the macOS-latest image bills at 10x the ubuntu-latest rate. Every minute spent on the wrong image, every hour spent on a missing timeout, eats your budget directly.

And: the $(SECRET_NAME) macro syntax is famously easy to leak into logs — even Microsoft's own docs flag this as the #1 secret-handling mistake. The same hygiene the other three CIs have, applied here.

Audit one repo right now

The in-browser scanner takes a paste, runs all 8 rules, and shows the report inline. Nothing leaves your browser.

Open the scanner View source