Azure Pipelines
Run Infracost in Azure Pipelines to see cloud cost estimates and FinOps best practices in pull requests. Works with both Azure Repos (Git only) and GitHub-backed pipelines.
We recommend using the Azure Repos App (or, for GitHub-backed pipelines, the GitHub App) where possible — they're simpler to set up and faster to run.
Quick start
-
Install the Infracost tasks from the Azure DevOps Marketplace into your organization. If you don't have permission, you can submit a request to your org admin.
-
Install the Infracost CLI and run
infracost auth loginto get a free API key. Retrieve it withinfracost configure get api_key. -
Follow the appropriate quick start below for your repo type:
- Azure Repos
- GitHub repos
Azure Repos
- Create a new pipeline:
- Choose Azure Repos Git in the Connect stage
- Pick the repo you want to integrate
- Choose Starter Pipeline in the Configure stage
- Replace the starter YAML with the following, and save
pr:
- '*'
trigger:
branches:
include:
- main
- master
variables:
- name: SSH_AUTH_SOCK
value: /tmp/ssh_agent.sock
jobs:
- job: infracost_pull_request_checks
condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest'))
displayName: Run Infracost on pull requests
pool:
vmImage: ubuntu-latest
steps:
- task: InfracostSetup@2
displayName: Setup Infracost
inputs:
apiKey: $(infracostApiKey)
- bash: |
git -c http.extraheader="Authorization: Bearer $(System.AccessToken)" clone $(Build.Repository.Uri) --branch=$(System.PullRequest.TargetBranchName) --single-branch /tmp/base
displayName: Checkout base branch
- bash: |
cd /tmp/base
infracost breakdown --path=. \
--format=json \
--out-file=/tmp/infracost-base.json
displayName: Generate Infracost cost estimate baseline
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- bash: |
cd -
infracost diff --path=. \
--format=json \
--compare-to=/tmp/infracost-base.json \
--out-file=/tmp/infracost.json
displayName: Generate Infracost diff
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- bash: |
infracost comment azure-repos \
--path=/tmp/infracost.json \
--azure-access-token=$(System.AccessToken) \
--pull-request=$(System.PullRequest.PullRequestId) \
--repo-url=$(Build.Repository.Uri) \
--behavior=update
displayName: Post PR comment
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
# Required when using Infracost Cloud
- job: infracost_cloud_update
displayName: Update Infracost Cloud
condition: and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))
pool:
vmImage: ubuntu-latest
steps:
- task: InfracostSetup@2
displayName: Setup Infracost
inputs:
apiKey: $(infracostApiKey)
- bash: |
PATTERN="Merged PR ([0-9]+):"
if [[ "$(Build.SourceVersionMessage)" =~ $PATTERN ]]; then
PR_ID=${BASH_REMATCH[1]}
curl \
--request POST \
--header "Content-Type: application/json" \
--header "X-API-Key: $(infracostApiKey)" \
--data "{ \"query\": \"mutation {updatePullRequestStatus( url: \\\"$(Build.Repository.Uri)/pullrequest/${PR_ID}\\\", status: MERGED )}\" }" \
"https://dashboard.api.infracost.io/graphql";
fi
displayName: Update PR status in Infracost Cloud
- bash: |
infracost breakdown \
--path=. \
--format=json \
--out-file=/tmp/infracost.json
infracost upload --path=/tmp/infracost.json || echo "Always pass main branch runs"
displayName: Run Infracost on default branch and update Infracost Cloud
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
-
Enable PR build triggers. Without this, Azure Pipelines won't trigger builds with the pull request ID, so comments can't be posted.
- From your Azure DevOps organization: your project → Project Settings → Repositories → your repo → Policies → your default branch
- Under Build Validation, click + and add the pipeline you created. Leave Path filter blank, set Trigger to Automatic, and Policy requirement to Optional.
-
Allow the pipeline to post PR comments. From the same Repository settings, open the Security tab, find the
[project name] Build Service ([org name])user, and set Contribute to pull requests to Allow. -
Add secret variables. From your project: Pipelines → your pipeline → Edit → Variables → click + to add
infracostApiKey. Tick Keep this value secret. -
Open a test PR — see these steps for what to expect.
GitHub repos
For GitHub-backed pipelines, the GitHub App is much simpler. Use this only if your team specifically needs Azure Pipelines.
- Create a new pipeline:
- Choose GitHub in the Connect stage
- Pick the repo
- Choose Starter Pipeline in the Configure stage
- Replace the starter YAML with the following, and save
pr:
- '*'
trigger:
branches:
include:
- main
- master
variables:
- name: SSH_AUTH_SOCK
value: /tmp/ssh_agent.sock
jobs:
- job: infracost_pull_request_checks
condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest'))
displayName: Run Infracost on pull requests
pool:
vmImage: ubuntu-latest
steps:
- task: InfracostSetup@2
displayName: Setup Infracost
inputs:
apiKey: $(infracostApiKey)
- bash: |
REPO_URL=$(Build.Repository.Uri)
REPO_URL_WITH_TOKEN=${REPO_URL/https:\/\//https:\/\/x-access-token:$(githubToken)@}
git clone $REPO_URL_WITH_TOKEN --branch=$(System.PullRequest.TargetBranchName) --single-branch /tmp/base
displayName: Checkout base branch
- bash: |
cd /tmp/base
infracost breakdown --path=. \
--format=json \
--out-file=/tmp/infracost-base.json
displayName: Generate Infracost cost estimate baseline
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- bash: |
cd -
infracost diff --path=. \
--format=json \
--compare-to=/tmp/infracost-base.json \
--out-file=/tmp/infracost.json
displayName: Generate Infracost diff
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- bash: |
infracost comment github \
--path=/tmp/infracost.json \
--github-token=$(githubToken) \
--pull-request=$(System.PullRequest.PullRequestNumber) \
--repo=$(Build.Repository.Name) \
--behavior=update
displayName: Post PR comment
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
-
Create a GitHub Personal Access Token with
reposcope. If you use SAML SSO, authorize the token. -
Add secret variables: Pipelines → your pipeline → Edit → Variables. Add
infracostApiKeyandgithubToken, both with Keep this value secret ticked. -
Open a test PR — see these steps for what to expect.
The InfracostSetup task
The setup task installs and configures the Infracost CLI:
steps:
- task: InfracostSetup@v2
inputs:
apiKey: $(infracostApiKey)
Inputs:
apiKey(required) — your Infracost API key.version(optional, default0.10.x) — SemVer ranges are supported. Stick with0.10.xto automatically pick up bug fixes and new resources.currency(optional) — convert output from USD to your preferred ISO 4217 currency, e.g.EUR,BRL,INR.
Troubleshooting
If something goes wrong, tick Enable system diagnostics when running the pipeline manually, or see Microsoft's pipeline troubleshooting docs.
403 error when posting to Azure Repos
This usually means the build agent can't post to the repo. Try:
-
Re-check the security setting described in step 3 of the Azure Repos quick start above.
-
Pass the token via env var instead of inline. Update the comment task to use
$SYSTEM_ACCESSTOKENand add theSYSTEM_ACCESSTOKENenv to thegit clone,breakdown, anddiffsteps too:- script: |infracost comment azure-repos \--path=/tmp/infracost.json \--azure-access-token=$SYSTEM_ACCESSTOKEN \--pull-request=$(System.PullRequest.PullRequestId) \--repo-url=$(Build.Repository.Uri) \--behavior=updatedisplayName: Post Infracost commentenv:SYSTEM_ACCESSTOKEN: $(System.AccessToken) -
Limited job auth scope. If your Azure Repo uses "Limit job authorization scope to current project for non-release pipelines", see this article — the build service identity changes and may need different permissions.
-
Test with a Personal Access Token. Create an Azure DevOps PAT with full permissions, add it as a secret variable
personalAccessToken, and swap it in temporarily:- script: |infracost comment azure-repos \--path=/tmp/infracost.json \--azure-access-token=$PERSONAL_ACCESS_TOKEN \--pull-request=$(System.PullRequest.PullRequestId) \--repo-url=$(Build.Repository.Uri) \--behavior=updatedisplayName: Post Infracost commentenv:PERSONAL_ACCESS_TOKEN: $(personalAccessToken)If comments work with the PAT but not
System.AccessToken, contact Microsoft Azure Support to investigate.