The $2,300 Terraform Apply
It happens every month in teams running infrastructure as code. Someone runs terraform apply for what looks like a routine change — switching an RDS instance class, adding a second NAT gateway, or upgrading a load balancer — and three days later the billing dashboard shows an unexpected jump. The team lead asks "who approved this?" Nobody did. Terraform doesn't ask about cost. It just applies.
The pattern is predictable:
- A developer changes
instance_classfromdb.t3.mediumtodb.r5.largein a Terraform module terraform planshows the change cleanly: "db.t3.medium → db.r5.large"- Nobody on the team knows the monthly cost difference off the top of their head
- The apply goes through. The bill increases by $2,300/month. Nobody notices until the monthly invoice arrives.
The problem: terraform plan tells you what will change, but never how much it will cost. CloudFormation change sets are the same. You get resource diffs, not price diffs.
This is a governance gap. Most teams solve it with manual review, tagging policies, or FinOps tools that report costs after the money is already spent. What's missing is a cost check before the apply — in the same CI pipeline that runs your tests and lints your code.
DeployDiff Cost Estimation: How It Works
DeployDiff reads your Terraform plan JSON or CloudFormation change set and produces a per-resource cost impact estimate before you apply any changes. Here's the simplest usage:
# After generating a Terraform plan
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
# Estimate cost impact
deploydiff cost --tf plan.json
For CloudFormation:
# After creating a change set
aws cloudformation create-change-set --stack-name prod --change-set-name preview ...
# Estimate cost impact
deploydiff cost --cfn changeset.json
What the Output Looks Like
Resource Before/mo After/mo Delta
──────────────────────────────────────────────────────────────────
aws_rds_instance.main $52.00 $387.00 +$335.00
aws_nat_gateway.secondary — $32.00 +$32.00
aws_lb.target (upgrade) $16.20 $43.70 +$27.50
aws_ebs_volume.data (expand) $5.00 $10.00 +$5.00
aws_instance.bastion (t3→t3a) $30.40 $27.60 -$2.80
──────────────────────────────────────────────────────────────────
TOTAL $103.60 $500.30 +$396.70
Every resource that changes has a before/after cost and a delta. Creates show no "before" cost. Deletes show no "after" cost. The summary row gives you the total monthly impact at a glance.
The key insight: You don't need to know every AWS price by heart. DeployDiff reads provider-native pricing metadata from your plan file and calculates the delta automatically.
Setting Up CI/CD Cost Gates
Manual review doesn't scale. The real value of cost estimation is automating it into your deployment pipeline so that expensive changes get blocked unless someone explicitly approves them.
GitHub Actions: Block Deploys Over $500/month
name: Terraform Deploy
on:
pull_request:
paths: ['infra/**']
jobs:
cost-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Generate plan
run: |
cd infra
terraform init
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
- name: Check cost impact
run: |
pip install deploydiff
deploydiff cost --tf infra/plan.json \
--max-monthly-increase 500 \
--format json > cost_report.json
# Exit 1 if any resource exceeds threshold
deploydiff cost --tf infra/plan.json \
--max-monthly-increase 500 \
--fail-on-threshold
When a pull request would increase monthly infrastructure costs by more than $500, the CI pipeline fails. The developer sees the cost breakdown in the CI logs. They can either redesign the change to be cheaper, or a team lead can approve the PR with a cost-aware review.
GitLab CI: Cost Thresholds Per Environment
stages:
- plan
- cost-gate
- apply
terraform:plan:prod:
stage: plan
script:
- terraform plan -out=tfplan
- terraform show -json tfplan > plan.json
artifacts:
paths: [plan.json]
cost-gate:prod:
stage: cost-gate
script:
- pip install deploydiff
- deploydiff cost --tf plan.json
--max-monthly-increase 200
--fail-on-threshold
needs: [terraform:plan:prod]
cost-gate:staging:
stage: cost-gate
script:
- pip install deploydiff
- deploydiff cost --tf plan.json
--max-monthly-increase 50
--fail-on-threshold
needs: [terraform:plan:staging]
Production gets a $200/month threshold. Staging gets $50/month. Different budgets for different environments — enforced automatically, not by hoping someone checks the billing dashboard next week.
Real-World Scenarios
Scenario 1: The Accidental RDS Upsize
A junior engineer changes instance_class from db.t3.medium to db.r5.xlarge to "make the dev database faster for testing." The cost delta is +$1,840/month. DeployDiff catches it in CI. The PR is updated to use db.t3.large instead — same performance gain, +$44/month.
Scenario 2: The Dual NAT Gateway
A Terraform module update adds a second NAT gateway for high availability. Each gateway costs ~$32/month plus data processing. DeployDiff shows +$64/month plus variable data costs. The team decides the HA is worth it and approves — but they made the decision with full cost visibility, not after the fact.
Scenario 3: The Spot Instance Migration
An infrastructure team migrates 20 EC2 instances from on-demand to spot. DeployDiff shows -$2,100/month in savings. The cost report is attached to the PR as evidence. Approval is fast because the financial impact is documented up front.
Scenario 4: CloudFormation Reserved Capacity
A CloudFormation change set proposes switching from provisioned DynamoDB capacity to on-demand. DeployDiff reads the change set, compares the per-request pricing to the current provisioned throughput, and shows +$180/month at current traffic levels. The team defers the switch until traffic justifies it.
Custom Pricing Data
Cloud pricing isn't always straightforward. Enterprise discount programs, reserved instances, and savings plans change the effective price. DeployDiff supports custom pricing files so your cost estimates reflect your actual rates:
# Use your negotiated pricing
deploydiff cost --tf plan.json --pricing custom-pricing.json
The custom pricing file maps resource types and configurations to your actual costs — including reserved instance rates, private pricing, and savings plan discounts. If your team has a 30% enterprise discount on compute, the cost estimate reflects that, not list price.
How This Differs From Other FinOps Tools
| Capability | DeployDiff | Infracost | AWS Cost Explorer | Kubecost |
|---|---|---|---|---|
| Cost estimate before apply | Yes — from plan/changeset | Yes — from plan | No — retrospective only | No — retrospective only |
| CI/CD cost gate (fail on threshold) | Yes — built-in | Yes — via GitHub Action | No | No |
| Per-resource before/after delta | Yes | Yes | Aggregate only | Per-namespace only |
| Infrastructure diff + cost in one tool | Yes — preview + cost + rollback | No — cost only | No | No |
| CloudFormation support | Yes | No | N/A | N/A |
| Pulumi support | Yes | No | N/A | N/A |
| Custom pricing file | Yes | Yes (usage file) | No | No |
Tools like Infracost focus on cost estimation alone. DeployDiff combines cost estimation with infrastructure diff preview and rollback command generation — so you see what changes, how much it costs, and how to undo it in a single CLI. And DeployDiff covers CloudFormation and Pulumi alongside Terraform, while most FinOps tools are Terraform-only.
Getting Started in 5 Minutes
# Install DeployDiff
pip install git+https://github.com/Coding-Dev-Tools/deploydiff.git
# Or via Homebrew (macOS/Linux)
brew tap Coding-Dev-Tools/tap
brew install deploydiff
# Generate a plan and check costs
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
deploydiff cost --tf plan.json
That's it. No cloud credentials required for the cost estimate — DeployDiff reads pricing metadata from the plan file itself. Add --max-monthly-increase and --fail-on-threshold to turn it into a CI gate.
Combine with rollback: Run deploydiff preview --tf plan.json to see the full infrastructure diff, deploydiff cost --tf plan.json for the financial impact, and deploydiff rollback --tf plan.json to generate undo commands. Three commands, three safety nets, one CLI.
Key Takeaways
- Cost estimation belongs in CI/CD, not in after-the-fact billing reports. By the time you see a cost spike in your dashboard, the money is already spent.
- Per-resource deltas are more useful than total estimates. Knowing the total increase is $400/month is helpful. Knowing that $335 of it comes from a single RDS instance upsize is actionable.
- Cost gates should be environment-specific. A $200/month increase is fine for production but wasteful for a staging environment that gets torn down weekly.
- Custom pricing matters. List prices don't reflect your actual costs. Use a custom pricing file to account for reserved instances, savings plans, and enterprise discounts.
- Cost awareness should be a first-class part of the deployment review — right alongside the code diff and the test results.