aws-cost-diff
v0.2.0
Published
Estimate AWS cost impact from Terraform plans before merge or deploy
Maintainers
Readme
aws-cost-diff
Estimate AWS cost impact from Terraform plans before merge or deploy.
The Problem
Most teams find out about cost increases after deployment — when the bill arrives. By then the damage is done: an engineer added a NAT Gateway that silently costs $32/mo, an RDS instance got upgraded two sizes too large, or a handful of gp2 volumes went out that could have been gp3 for 20% less.
The tools that exist today are either retroactive dashboards (Cost Explorer, Kubecost) or require you to tag and manually review. There's no easy way to answer the question: "What will this Terraform change cost me per month?" at PR review time.
How aws-cost-diff Solves It
aws-cost-diff plugs into the space between terraform plan and terraform apply. It reads the plan JSON, prices every resource change via the AWS Pricing API, and gives you:
- A dollar-amount diff — per resource and total monthly delta, so reviewers see cost impact the same way they see code changes
- High-risk flags — catches silent cost traps like NAT Gateways, large RDS jumps, and single-AZ databases before they ship
- Idle resource detection — spots unattached EBS volumes, unassociated Elastic IPs, and load balancers with no targets
- Optimization recommendations — actionable suggestions (gp2->gp3, Spot instances, Graviton, old-gen upgrades) with estimated savings
- CI gating — exits with code
2on cost increases so you can block merges that blow the budget - AI analysis — optional Claude-powered deep analysis for rightsizing and architectural cost patterns
One command. No dashboards. No tagging. Cost review becomes part of code review.
$ terraform show -json plan.tfplan | aws-cost-diff
aws-cost-diff v0.1.0
────────────────────────────────────────────────────────────
Cost Impact Summary
Resources added: 5
Resources removed: 1
Resources modified: 1
Estimated Monthly Delta: +$36.05/mo
Before: $0.00/mo → After: $36.05/mo
┌────────────────────────────────────┬──────────┬──────────┬──────────┬──────────┐
│ Resource │ Action │ Before │ After │ Delta │
├────────────────────────────────────┼──────────┼──────────┼──────────┼──────────┤
│ aws_nat_gateway.main │ + create │ - │ $32.40 │ +$32.40 │
│ aws_eip.nat │ + create │ - │ $3.65 │ +$3.65 │
│ aws_instance.web │ + create │ - │ $62.05 │ +$62.05 │
│ aws_db_instance.main │ ~ update │ $25.55 │ $102.20 │ +$76.65 │
└────────────────────────────────────┴──────────┴──────────┴──────────┴──────────┘
High-Risk Changes
[!!!] aws_db_instance.main: Large RDS instance class jump: db.t3.micro → db.r5.large
[!!] aws_nat_gateway.main: New NAT Gateway adds ~$32+/mo (hourly + data processing)
Recommendations
[rule] aws_ebs_volume.data: Switch from gp2 to gp3 — 20% cheaper with better baseline perf
[rule] aws_db_instance.main: Consider Graviton-based db.r6g.large — typically 20% cheaper
[AI] aws_instance.web: t3.large may be oversized — evaluate t3.medium based on expected loadInstall
npm install -g aws-cost-diffOr run directly:
npx aws-cost-diff --plan plan.jsonPrerequisites
- Node.js 18+
- AWS credentials (optional) — for live pricing via the AWS Pricing API. Without credentials, fallback estimates are used for common resources (NAT Gateways, EIPs, VPC Endpoints). EC2, RDS, EBS, and most other services require API access for accurate pricing.
- ANTHROPIC_API_KEY (optional) — required only when using the
--aiflag for Claude-powered recommendations.
Usage
# Pipe from terraform
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan | aws-cost-diff
# From a file
aws-cost-diff --plan plan.json
# With Claude AI recommendations
export ANTHROPIC_API_KEY=sk-ant-...
aws-cost-diff --plan plan.json --ai
# JSON output for CI/CD
aws-cost-diff --plan plan.json --format json
# Markdown report to file
aws-cost-diff --plan plan.json --output cost-report.md
# Specific region
aws-cost-diff --plan plan.json --region eu-west-1
# Compact output (just resource + delta)
aws-cost-diff --plan plan.json --compact
# Disable colors (also respects NO_COLOR env var)
aws-cost-diff --plan plan.json --no-colorOptions
| Flag | Description | Default |
|------|-------------|---------|
| -p, --plan <path> | Path to Terraform plan JSON (or pipe via stdin) | stdin |
| -r, --region <region> | AWS region for pricing lookups | us-east-1 |
| --ai | Enable Claude AI-powered recommendations | false |
| -f, --format <format> | Output format: table or json | table |
| -o, --output <path> | Write markdown report to file | - |
| --no-color | Disable colored output (also respects NO_COLOR env) | auto-detect |
| --compact | Minimal output: resource name and delta only | false |
Exit codes
| Code | Meaning |
|------|---------|
| 0 | No cost increase (or net savings) |
| 1 | Error (invalid input, API failure, missing API key) |
| 2 | Cost increase detected |
Use exit code 2 for CI gating — fail the pipeline when costs go up unexpectedly.
Supported Resource Types
aws-cost-diff supports 30+ AWS resource types across compute, database, storage, networking, and more.
Priced via AWS Pricing API
| Resource | Pricing dimensions |
|----------|-------------------|
| aws_instance | instance_type, region, OS, tenancy |
| aws_db_instance | instance_class, engine, multi_az, storage |
| aws_rds_cluster | Aurora cluster (usage-based estimates) |
| aws_ebs_volume | volume_type, size_gb, iops |
| aws_nat_gateway | hourly + per-GB data processing |
| aws_lb / aws_alb | hourly + LCU |
| aws_elasticache_cluster | node_type, engine, num_nodes |
| aws_eip | hourly ($3.65/mo since Feb 2024) |
| aws_lambda_function | provisioned concurrency |
| aws_dynamodb_table | provisioned capacity (RCU/WCU) |
| aws_ecs_service | Fargate vCPU + memory hours |
| aws_eks_cluster | control plane ($0.10/hr) |
| aws_redshift_cluster | node_type, num_nodes |
| aws_opensearch_domain | instance_type, instance_count |
| aws_kinesis_stream | shard hours (provisioned mode) |
| aws_msk_cluster | broker instance + count |
| aws_sagemaker_endpoint | instance_type, instance_count |
| aws_ec2_transit_gateway | hourly attachment |
| aws_vpn_connection | hourly |
| aws_globalaccelerator_accelerator | hourly |
| aws_wafv2_web_acl | base + per-rule ($5 + $1/rule) |
Fixed-price estimates
| Resource | Monthly estimate |
|----------|-----------------|
| aws_route53_zone | $0.50 |
| aws_kms_key | $1.00 |
| aws_secretsmanager_secret | $0.40 |
Usage-based (recognized but not priced)
These resources are recognized in plans but have usage-dependent pricing that can't be estimated from Terraform config alone:
aws_s3_bucket, aws_api_gateway_rest_api, aws_apigatewayv2_api, aws_cloudfront_distribution, aws_sfn_state_machine, aws_ecr_repository
Fallback estimates
When the AWS Pricing API is unavailable, fallback estimates are used:
| Resource | Fallback |
|----------|----------|
| aws_nat_gateway | $32.40/mo |
| aws_eip | $3.65/mo |
| aws_vpc_endpoint | $7.30/mo |
Usage-based resources (CloudWatch, SQS, SNS) default to $0 when unpriced.
How Pricing Works
- Parse — reads
terraform show -jsonoutput and extracts resource changes - Map — each resource type maps to pricing query parameters via
src/pricing/mappings.ts - Query — calls the AWS Pricing API (
us-east-1endpoint) with resource-specific filters. Results are cached in-memory per run. - Calculate — multiplies unit prices by monthly usage (730 hours/mo for hourly resources). Multi-dimension resources (e.g., RDS instance hours + storage GB) sum multiple queries.
- Fallback — if the API call fails, attempts a static fallback estimate. Returns $0 with an "UNPRICED" warning if no estimate is available.
Supported pricing regions: us-east-1, us-east-2, us-west-1, us-west-2, eu-west-1, eu-west-2, eu-west-3, eu-central-1, eu-north-1, ap-southeast-1, ap-southeast-2, ap-northeast-1, ap-northeast-2, ap-south-1, ca-central-1, sa-east-1. Unknown regions fall back to us-east-1 pricing with a warning.
Risk Detection
- Cost increases >100% (high) or >50% (medium)
- New NAT Gateways (silent $32+/mo)
- Large RDS instance class jumps (e.g., t3.micro -> r5.large)
- RDS Multi-AZ being disabled (removes high availability)
- Standalone EC2 without Auto Scaling Group
- New resources consuming >20% of plan cost or >$100/mo
- Cost decreases >50% (low severity — flags potential mistakes)
Idle Resource Detection
- EBS volumes without instance attachments
- Elastic IPs without instance or ENI associations
- Load balancers without target groups or listeners
Recommendations
Rule-based (always on)
- EBS: gp2 -> gp3 (20% cheaper, better baseline IOPS), io1 -> io2 (better durability)
- EC2 Spot: suggest Spot for stateless workloads (up to 90% savings)
- Instance families: upgrade old generations (m4->m6i, c4->c6i, t2->t3, r3->r6i, i3->i3en, d2->d3)
- NAT Gateway: suggest VPC Gateway Endpoints for S3/DynamoDB traffic (free)
- RDS Multi-AZ: flag when enabled (~2x cost), suggest Single-AZ if HA not required
- RDS Graviton: upgrade old families to Graviton (m5->m6g, r5->r6g, t3->t4g, ~20% savings)
- Reserved Instances: for always-on compute >$50/mo, suggest RI/Savings Plans (22-50% savings)
AI-powered (--ai flag)
When enabled, sends resource context to Claude for deeper analysis:
- Rightsizing based on workload patterns
- Architectural cost anti-patterns
- Cross-resource consolidation opportunities
- Service alternatives (e.g., Aurora Serverless v2)
- Pricing model alignment (Reserved, Spot, Savings Plans)
Requires ANTHROPIC_API_KEY environment variable.
Limitations
- Usage-based services (S3, CloudFront, API Gateway, SQS, SNS, Lambda invocations, Step Functions) cannot be priced from Terraform config alone — they depend on runtime traffic. These show as $0 or "UNPRICED" in the output.
- On-demand DynamoDB tables are not priced (provisioned mode only).
- Aurora Serverless (v1/v2) pricing is not supported — only provisioned Aurora clusters.
- Data transfer costs are not included (varies by traffic patterns).
- Spot pricing is not reflected — estimates use on-demand rates.
- Pricing API availability — the AWS Pricing API is only accessible from
us-east-1andap-south-1. The tool always queriesus-east-1regardless of the--regionflag, but uses the specified region for regional price lookups. - Terraform plan accuracy — estimates are only as good as the values in your plan JSON. Computed or dynamic attributes may not be available at plan time.
CI/CD Integration
GitHub Actions
- name: Cost check
run: |
terraform show -json plan.tfplan | aws-cost-diff --format json > cost-report.json
continue-on-error: true
- name: Comment PR with cost impact
if: github.event_name == 'pull_request'
run: |
DELTA=$(jq .totalDelta cost-report.json)
gh pr comment ${{ github.event.number }} --body "**Cost impact:** \$${DELTA}/mo"GitLab CI
cost-check:
script:
- terraform show -json plan.tfplan | aws-cost-diff --format json
allow_failure: trueGate on cost increases
Use exit code 2 to fail pipelines when costs increase:
- name: Cost gate
run: terraform show -json plan.tfplan | aws-cost-diff
# Exits 0 if no cost increase, 2 if costs go upMarkdown report as artifact
- name: Generate cost report
run: |
terraform show -json plan.tfplan | aws-cost-diff --output cost-report.md
- uses: actions/upload-artifact@v4
with:
name: cost-report
path: cost-report.mdDevelopment
git clone https://github.com/Neha/aws-cost-diff.git
cd aws-cost-diff
npm install
npm run dev -- --plan test/fixtures/sample-plan.jsonScripts
| Command | Purpose |
|---------|---------|
| npm run dev | Run in development mode (tsx) |
| npm test | Run tests (vitest) |
| npm run lint | Lint with ESLint |
| npm run typecheck | Type-check with tsc |
| npm run build | Build for production |
| npm run format | Format with Prettier |
Project structure
src/
├── index.ts # Main analyze() orchestrator
├── types.ts # TypeScript interfaces
├── parser/
│ └── terraform.ts # Parse terraform show -json output
├── pricing/
│ ├── aws.ts # AWS Pricing API client (caching, querying)
│ └── mappings.ts # Resource type -> pricing query mappings
├── analyzer/
│ ├── delta.ts # Compute monthly cost deltas
│ ├── risk.ts # Risk detection rules
│ └── idle.ts # Idle resource detection
├── recommender/
│ ├── rules.ts # Rule-based optimization recommendations
│ └── ai.ts # Claude AI-powered recommendations
└── output/
└── formatter.ts # Table, JSON, markdown, compact output
bin/
├── cli.ts # CLI entry point (commander)
└── analyze-version.ts # Version bump analysis for publish workflow
test/
├── fixtures/ # Sample Terraform plan JSON files
├── parser.test.ts
├── pricing-aws.test.ts
├── pricing-mappings.test.ts
├── risk.test.ts
├── idle.test.ts
├── recommendations.test.ts
├── ai-recommender.test.ts
└── formatter.test.tsLicense
MIT
