GitHub Actions Terraform cost estimation: cost on every pull request
Add a job that runs after terraform plan, prices the plan JSON, and comments the monthly total and diff on the PR — with a budget gate that fails the check. Here's the workflow, the input that keeps it accurate, and how to keep it fast.
Quick answer
Run cost estimation as a GitHub Actions job on pull_request, right after terraform plan. Price the plan JSON, comment the monthly total and the diff versus the base branch, and exit non-zero when a budget is exceeded so the required check fails. It runs in seconds, needs no cloud credentials, and puts the cost in the PR where the change is still cheap to fix.
Every other quality gate lives in the pull request, tests, linting, security scans. Cost usually doesn't, which is why an expensive change sails through review and shows up weeks later on the bill. GitHub Actions is where you close that gap: estimate before deploy on the same PR that adds the resource.
Estimate from the plan, not the HCL
Produce a plan and convert it to JSON, then price that. The plan has already resolved count, for_each, conditionals and module inputs, so it's the most accurate input, see why plan JSON is the most accurate input.
terraform init -input=false
terraform plan -out=tf.plan -input=false
terraform show -json tf.plan > plan.json
c3x estimate --path plan.jsonA minimal workflow
The job runs on pull requests, generates the plan, estimates, and comments the result. It needs pull-requests: write to post the comment and no cloud credentials to estimate.
name: cost
on: pull_request
permissions:
contents: read
pull-requests: write
jobs:
estimate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: |
terraform init -input=false
terraform plan -out=tf.plan -input=false
terraform show -json tf.plan > plan.json
- run: c3x estimate --path plan.json --format markdown > cost.md
- run: gh pr comment "$PR" --body-file cost.md
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ github.event.pull_request.number }}Turn visibility into a guardrail
A comment informs; a budget gate enforces. Add a threshold so the job fails when the monthly total, or the delta versus the base branch, crosses a line, and make it a required status check. That's the budget-guardrail pattern: the expensive PR can't merge until someone signs off.
- run: c3x estimate --path plan.json --budget 500Keep it fast and cheap
Reuse the plan you already generate for review, don't run a second one just for cost. Cache the provider plugins and the Terraform install, run estimation in parallel with your validate job, and only post one comment per PR (update it in place rather than stacking new ones). The whole step adds seconds, not minutes.
Compared to other CI systems
The shape is identical everywhere: plan, convert to JSON, estimate, comment, gate. If you run GitLab CI, Atlantis, Azure DevOps, or a Terraform Cloud run task, the estimator and the budget logic are the same, only the comment API and the pipeline syntax change.
FAQ
Where in the workflow should cost estimation run?
Right after terraform plan, on pull_request events. The plan has already resolved variables and data sources, so pricing its JSON output is the most accurate input. Run it as its own job (or step) that depends on the plan artifact, so it never blocks the plan itself.
Should I estimate from HCL or from the plan?
From the plan when you can. terraform plan -out=tf.plan then terraform show -json tf.plan gives resolved resources, count, for_each, conditionals and module inputs all computed. Estimating raw HCL works for a quick local check but can miss anything resolved at plan time.
How do I comment the cost on the pull request?
Give the job pull-requests: write permission and post the estimate with the GitHub CLI (gh pr comment) or actions/github-script. Comment the monthly total and the delta versus the base branch so the reviewer sees 'this PR adds $340/month' next to the diff.
How do I fail the build when a PR is too expensive?
Add a budget gate: have the estimator exit non-zero when the monthly total (or the per-PR delta) exceeds a threshold, and let that fail the job. Because the job is a required check, the expensive change can't merge until it's reviewed.
Will this slow down my pipeline?
No. A static estimate runs in seconds and can run in parallel with plan/validate. If you already produce a plan artifact, the estimation job just consumes it, no second terraform init or plan needed.
Does it need cloud credentials?
No. Estimation reads the Terraform plan or HCL and prices it against a catalog. It doesn't call your cloud account, so the job needs no AWS/Azure/GCP credentials, only the plan file and (optionally) network access to a pricing catalog, which can be self-hosted or bundled for air-gapped runs.
What to do next
Add the job to one repository and watch the first PR comment land. C3X reads your Terraform plan, prices the resources against a live catalog, and comments the monthly cost with optional budget gating, free and self-hostable. The quickstart gets you a first estimate in minutes, and the CI/CD guide has the full workflow.
Share this post
Try C3X on your own Terraform
Free and open source. No API key required. One command to install, one command to estimate.