Skip to content
| Marketplace
Sign in
Azure DevOps>Azure Pipelines>Cost Guardrails
Cost Guardrails

Cost Guardrails

Cost Guardrails - SKYXOPS

| (0) | Free
Shift-left cost governance for Terraform and CloudFormation. Reviews infrastructure costs on every PR.
Get it free

Cost Guardrails Azure DevOps Extension

Cost Guardrails Azure DevOps Version

Know your infrastructure costs before you merge.

Cost Guardrails reviews every PR for cost impact. It works with both Terraform (plan.json) and CloudFormation (changeset.json) — auto-detected, no extra config.

On every PR, Cost Guardrails:

  • Posts a cost breakdown as a PR comment (per-resource, with regions)
  • Returns a decision — ALLOW, WARN, or BLOCK — that gates your merge
  • Validates against your budget and guardrails
  • Provides AI-powered recommendations to optimize costs
  • Saves an HTML report as a build artifact

Quick Start

1. Install the Extension

An org admin installs Cost Guardrails from the Visual Studio Marketplace:

Organization Settings → Extensions → Browse Marketplace → search "Cost Guardrails" → Install

This is a one-time step. Once installed, all pipelines in the org can use task: Cost Guardrails@1.

2. Set Pipeline Variables

Go to Pipelines → Edit → Variables and add:

Variable Secret Description
COST_GUARDRAILS_API_KEY Yes Your Cost Guardrails API key
COST_GUARDRAILS_BUDGET_CODE No Your budget code from Cost Guardrails dashboard (optional)

3. Add to your pipeline

Terraform — azure-pipelines.yml:

trigger:
  branches:
    include: [main]

pool:
  vmImage: 'ubuntu-latest'

stages:
  - stage: Plan
    jobs:
      - job: TerraformPlan
        steps:
          - task: TerraformInstaller@1
            inputs:
              terraformVersion: '1.7.5'
          # Add your AWS credentials step here
          - script: |
              terraform init -input=false
              terraform plan -out=tfplan -input=false
              terraform show -json tfplan > plan.json
            displayName: 'Terraform Plan'
          - publish: plan.json
            artifact: plan

  - stage: Cost Guardrails
    dependsOn: Plan
    jobs:
      - job: Cost Guardrails
        steps:
          - download: current
            artifact: plan
          - task: Cost Guardrails@1
            inputs:
              planPath: $(Pipeline.Workspace)/plan/plan.json
              budgetCode: $(COST_GUARDRAILS_BUDGET_CODE)
            env:
              COST_GUARDRAILS_API_KEY: $(COST_GUARDRAILS_API_KEY)
              SYSTEM_ACCESSTOKEN: $(System.AccessToken)

CloudFormation — If your pipeline already generates changeset.json, just add the Cost Guardrails stage:

  - stage: Cost Guardrails
    dependsOn: CreateChangeset
    jobs:
      - job: Cost Guardrails
        steps:
          - download: current
            artifact: changeset
          - task: Cost Guardrails@1
            inputs:
              planPath: $(Pipeline.Workspace)/changeset/changeset.json
              budgetCode: $(COST_GUARDRAILS_BUDGET_CODE)
            env:
              COST_GUARDRAILS_API_KEY: $(COST_GUARDRAILS_API_KEY)
              SYSTEM_ACCESSTOKEN: $(System.AccessToken)

Generate changeset.json with: aws cloudformation describe-change-set --change-set-name <arn> > changeset.json

4. Enable branch policy

Repos → Branches → main → Branch policies:

Setting Value
Build validation Select your pipeline, Automatic
Require reviewers 1+

Without a build validation policy, the pipeline only runs after merge and Cost Guardrails can't block.

That's it. Create a PR and Cost Guardrails will post a cost review comment.


How It Works

Developer creates PR
  │
  ├─ Build validation triggers pipeline
  │
  ├─ Your plan job produces:
  │     Terraform:      terraform show -json tfplan > plan.json
  │     CloudFormation: aws cloudformation describe-change-set > changeset.json
  │
  └─ Cost Guardrails task
       │
       ├─ Installs cost-guardrails (pip install)
       ├─ Runs cost-guardrails with your inputs
       ├─ Posts PR comment with cost breakdown
       ├─ Saves result JSON + HTML report as artifacts
       │
       └─ Task result controls your pipeline:
            Succeeded            → ALLOW  → PR can merge
            Failed               → BLOCK  → PR blocked
            SucceededWithIssues  → WARN   → PR can merge (review recommended)

Cost Guardrails posts one comment per PR and updates it on every push. No duplicate comments.


What You'll See

On every PR, Cost Guardrails posts a comment with:

  • Decision badge — ALLOW (green), WARN (yellow), or BLOCK (red)
  • Total monthly cost — estimated infrastructure spend
  • Per-resource breakdown — cost per resource with region and pricing details
  • Budget status — within budget, near limit, or over budget
  • AI recommendations — optimization suggestions to reduce costs

The comment updates automatically on each push — no duplicates.


Supported IaC Types

Type Input File How to generate
Terraform plan.json terraform show -json tfplan > plan.json
CloudFormation Changeset changeset.json aws cloudformation describe-change-set > changeset.json

Cost Guardrails auto-detects the type from file content.


Inputs

Input Required Default Description
planPath No plan.json Path to plan or changeset file
budgetCode No "" Budget code for cost validation
skipBudget No false Skip budget check (pricing-only mode)
skipNarrative No false Skip AI analysis (faster runs)
skipGuardrails No false Skip guardrail evaluation
autoApprove No false Pass when no budget found
postComment No true Post cost breakdown as PR comment
format No markdown Comment format: markdown or terminal
tag No "" Tag to identify this comment (e.g. production, staging). Each tag gets its own PR comment
extraArgs No "" Extra CLI flags (escape hatch)

Required environment variables

These must be mapped via env: in your pipeline:

- task: Cost Guardrails@1
  inputs:
    planPath: plan.json
  env:
    COST_GUARDRAILS_API_KEY: $(COST_GUARDRAILS_API_KEY)         # secret variable
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)        # required for PR comments

Why env: mapping? Azure DevOps doesn't auto-expose secret variables or System.AccessToken to tasks. You must explicitly pass them via the env: block.

Output Variables

Variable Description
decision ALLOW, WARN, or BLOCK
monthlyCost Estimated monthly cost
resultFile Path to the cached result JSON
reportFile Path to the HTML report

Use outputs in subsequent steps:

- task: Cost Guardrails@1
  name: cost-guardrails
  inputs:
    planPath: plan.json
  env:
    COST_GUARDRAILS_API_KEY: $(COST_GUARDRAILS_API_KEY)
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)

- script: |
    echo "Decision: $(cost-guardrails.decision)"
    echo "Monthly cost: $(cost-guardrails.monthlyCost)"
  displayName: 'Check Cost Guardrails result'

Examples

Pricing only (skip budget + AI)

- task: Cost Guardrails@1
  inputs:
    planPath: plan.json
    skipBudget: true
    skipNarrative: true
  env:
    COST_GUARDRAILS_API_KEY: $(COST_GUARDRAILS_API_KEY)
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)

Auto-approve when no budget found

- task: Cost Guardrails@1
  inputs:
    planPath: plan.json
    budgetCode: $(COST_GUARDRAILS_BUDGET_CODE)
    autoApprove: true
  env:
    COST_GUARDRAILS_API_KEY: $(COST_GUARDRAILS_API_KEY)
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)

Multiple environments with tags

  - stage: Cost Guardrails_Production
    dependsOn: PlanProduction
    jobs:
      - job: Cost Guardrails
        steps:
          - download: current
            artifact: plan-production
          - task: Cost Guardrails@1
            inputs:
              planPath: $(Pipeline.Workspace)/plan-production/plan.json
              budgetCode: $(COST_GUARDRAILS_BUDGET_CODE_PROD)
              tag: production
            env:
              COST_GUARDRAILS_API_KEY: $(COST_GUARDRAILS_API_KEY)
              SYSTEM_ACCESSTOKEN: $(System.AccessToken)

  - stage: Cost Guardrails_Staging
    dependsOn: PlanStaging
    jobs:
      - job: Cost Guardrails
        steps:
          - download: current
            artifact: plan-staging
          - task: Cost Guardrails@1
            inputs:
              planPath: $(Pipeline.Workspace)/plan-staging/plan.json
              budgetCode: $(COST_GUARDRAILS_BUDGET_CODE_STG)
              tag: staging
            env:
              COST_GUARDRAILS_API_KEY: $(COST_GUARDRAILS_API_KEY)
              SYSTEM_ACCESSTOKEN: $(System.AccessToken)

Each tag posts a separate PR comment — production and staging results won't overwrite each other.

Save artifacts

- task: Cost Guardrails@1
  name: cost-guardrails
  inputs:
    planPath: plan.json
  env:
    COST_GUARDRAILS_API_KEY: $(COST_GUARDRAILS_API_KEY)
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)

- publish: $(cost-guardrails.resultFile)
  artifact: cost-guardrails-result
  condition: always()

- publish: $(cost-guardrails.reportFile)
  artifact: cost-guardrails-report
  condition: always()

Build Service Permissions

For PR comments to work, the pipeline's build service identity needs permission:

Project Settings → Repos → Security → {Project} Build Service ({Org}):

Permission Value
Contribute to pull requests Allow

Without this, Cost Guardrails runs but can't post PR comments.


Troubleshooting

Problem Cause Fix
PR comment not posted Missing SYSTEM_ACCESSTOKEN Add env: SYSTEM_ACCESSTOKEN: $(System.AccessToken)
PR comment 403 error Build service lacks permission Project Settings → Repos → Security → "Contribute to pull requests" = Allow
BLOCK doesn't prevent merge No build validation policy Repos → Branches → main → Build validation → add your pipeline
COST_GUARDRAILS_API_KEY not found Variable not set or not secret Pipelines → Edit → Variables → add as secret
Task not found Extension not installed Org admin: Organization Settings → Extensions → install Cost Guardrails
API timeout on first run Cold start Re-run pipeline — subsequent runs complete in ~5 seconds

Shift-left cost governance for cloud infrastructure

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft