🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Build an AI Terraform Cost Estimator Using Claude (2026)

Before you run terraform apply, wouldn't you want to know how much it'll cost? Build an AI cost estimator that reads your Terraform plan output and gives you a detailed cost breakdown using Claude as the reasoning engine.

DevOpsBoysMay 20, 20267 min read
Share:Tweet

You're about to run terraform apply on a new EKS cluster setup. Do you know what it'll cost?

Infracost exists but requires setup and API registration. What if you could just pipe your terraform plan output to an AI and get a cost breakdown in seconds?

In this post we'll build exactly that — an AI Terraform cost estimator that:

  1. Reads terraform plan -json output
  2. Extracts all resources being created/modified
  3. Uses Claude to identify cost-bearing resources and estimate costs
  4. Outputs a formatted cost report

Architecture

terraform plan -json → cost-estimator.py → Claude API → Cost Report

Simple, no external APIs needed except Claude/OpenAI.


Prerequisites

  • Python 3.11+
  • Anthropic API key
  • Terraform project to test with

Step 1: The Core Estimator

python
#!/usr/bin/env python3
# cost_estimator.py
 
import anthropic
import json
import sys
import subprocess
from pathlib import Path
 
def get_terraform_plan(terraform_dir: str = ".") -> dict:
    """Run terraform plan and capture JSON output."""
    print("Running terraform plan...")
    
    # Generate plan file
    result = subprocess.run(
        ["terraform", "plan", "-out=tfplan.binary"],
        cwd=terraform_dir,
        capture_output=True,
        text=True
    )
    
    if result.returncode != 0:
        print(f"terraform plan failed:\n{result.stderr}")
        sys.exit(1)
    
    # Convert plan to JSON
    result = subprocess.run(
        ["terraform", "show", "-json", "tfplan.binary"],
        cwd=terraform_dir,
        capture_output=True,
        text=True
    )
    
    return json.loads(result.stdout)
 
 
def extract_resource_changes(plan: dict) -> list[dict]:
    """Extract resources being created or modified from the plan."""
    changes = []
    
    resource_changes = plan.get("resource_changes", [])
    
    for change in resource_changes:
        actions = change.get("change", {}).get("actions", [])
        
        # Only care about create, update, replace
        if not any(a in actions for a in ["create", "update", "replace"]):
            continue
        
        resource_type = change.get("type", "")
        resource_name = change.get("name", "")
        after_config = change.get("change", {}).get("after", {})
        
        changes.append({
            "type": resource_type,
            "name": resource_name,
            "action": actions[0] if actions else "unknown",
            "config": after_config
        })
    
    return changes
 
 
def estimate_costs_with_ai(resources: list[dict], region: str = "us-east-1") -> str:
    """Use Claude to estimate costs for the resources."""
    
    client = anthropic.Anthropic()
    
    # Build the resource summary for the prompt
    resource_summary = json.dumps(resources, indent=2)
    
    prompt = f"""You are an AWS cost estimation expert. I'm about to create/modify the following Terraform resources in region {region}.
 
RESOURCES:
{resource_summary}
 
Please provide:
 
1. **COST SUMMARY TABLE** — For each resource that has a cost, provide:
   | Resource | Type | Config | Monthly Cost (USD) |
   
2. **TOTAL MONTHLY ESTIMATE** — Sum of all costs
 
3. **TOTAL ANNUAL ESTIMATE** — Monthly × 12
 
4. **COST BREAKDOWN BY CATEGORY:**
   - Compute
   - Storage
   - Network
   - Database
   - Other
 
5. **COST OPTIMIZATION TIPS** — 2-3 specific suggestions to reduce cost based on the resources being created
 
6. **ASSUMPTIONS** — Any assumptions you made (e.g., "assumed 730 hours/month for EC2")
 
Important notes:
- Use current 2026 AWS pricing (on-demand)
- If you're unsure of exact pricing, provide a range
- Mark free-tier-eligible resources with [FREE TIER]
- For resources with no cost (IAM roles, security groups, etc.), you can skip them or note "No direct cost"
- Format numbers clearly (e.g., $12.50/month, not $12.5)
 
Be specific and practical — this will be used to evaluate whether to proceed with this infrastructure change."""
 
    message = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=2000,
        messages=[
            {"role": "user", "content": prompt}
        ]
    )
    
    return message.content[0].text
 
 
def load_plan_from_file(filepath: str) -> dict:
    """Load a pre-generated terraform plan JSON file."""
    with open(filepath) as f:
        return json.load(f)
 
 
def main():
    import argparse
    
    parser = argparse.ArgumentParser(description="AI-powered Terraform cost estimator")
    parser.add_argument("--plan-file", help="Path to terraform plan JSON file")
    parser.add_argument("--dir", default=".", help="Terraform directory (runs terraform plan)")
    parser.add_argument("--region", default="us-east-1", help="AWS region for pricing")
    parser.add_argument("--output", help="Save report to file")
    args = parser.parse_args()
    
    # Get plan
    if args.plan_file:
        print(f"Loading plan from {args.plan_file}...")
        plan = load_plan_from_file(args.plan_file)
    else:
        plan = get_terraform_plan(args.dir)
    
    # Extract resources
    resources = extract_resource_changes(plan)
    
    if not resources:
        print("No resource changes found in plan.")
        sys.exit(0)
    
    print(f"\nFound {len(resources)} resource change(s):")
    for r in resources:
        action_symbol = {"create": "+", "update": "~", "replace": "±", "delete": "-"}.get(r["action"], "?")
        print(f"  {action_symbol} {r['type']}.{r['name']}")
    
    print("\nEstimating costs with AI...")
    
    # Get cost estimate
    report = estimate_costs_with_ai(resources, region=args.region)
    
    # Print report
    print("\n" + "="*60)
    print("TERRAFORM COST ESTIMATE REPORT")
    print("="*60)
    print(report)
    print("="*60)
    
    # Save to file if requested
    if args.output:
        output_path = Path(args.output)
        output_path.write_text(report)
        print(f"\nReport saved to {args.output}")
 
 
if __name__ == "__main__":
    main()

Step 2: Try It

bash
pip install anthropic
 
# Option 1: Point at a Terraform directory (runs plan automatically)
python cost_estimator.py --dir ./my-terraform-project --region us-east-1
 
# Option 2: Use a pre-generated plan file
terraform plan -out=plan.binary
terraform show -json plan.binary > plan.json
python cost_estimator.py --plan-file plan.json --region eu-west-1
 
# Save report to file
python cost_estimator.py --dir . --output cost-report.md

Example Output

Given a plan that creates an EKS cluster, RDS instance, and EC2 bastion:

============================================================
TERRAFORM COST ESTIMATE REPORT
============================================================

## COST SUMMARY TABLE

| Resource | Type | Config | Monthly Cost (USD) |
|---|---|---|---|
| eks_cluster | aws_eks_cluster | v1.31, 3 nodes | $73.00 |
| eks_workers | aws_eks_node_group | 3x t3.medium | $100.80 |
| rds_postgres | aws_db_instance | db.t3.medium, 100GB | $85.20 |
| bastion | aws_instance | t3.micro | $8.32 |
| nat_gateway | aws_nat_gateway | 1 AZ | $32.40 |
| ebs_volumes | aws_ebs_volume | 3x 20GB gp3 | $7.20 |

## TOTAL MONTHLY ESTIMATE: $307.32/month

## TOTAL ANNUAL ESTIMATE: $3,687.84/year

## COST BREAKDOWN BY CATEGORY:
- Compute: $182.12 (59%)
- Database: $85.20 (28%)
- Network: $32.40 (11%)
- Storage: $7.20 (2%)

## COST OPTIMIZATION TIPS:

1. **Use Spot instances for worker nodes:** Switching from on-demand t3.medium 
   to Spot could save 60–70% on EC2 costs (~$60-70/month savings). 
   Use Karpenter with spot instance support.

2. **Single NAT Gateway:** You're creating 1 NAT Gateway (good). 
   If deploying in multiple AZs later, consider sharing one NAT Gateway 
   across AZs in non-prod to save $32/month per additional gateway.

3. **RDS Reserved Instance:** If this is production, a 1-year Reserved Instance 
   for db.t3.medium saves ~40% vs on-demand ($85 → $51/month = $408/year savings).

## ASSUMPTIONS:
- 730 hours/month for all compute
- EKS cluster pricing at $0.10/hour
- NAT Gateway at $0.045/hour + $0.045/GB data processed (estimated 10GB)
- RDS Multi-AZ not enabled (single AZ pricing)

Step 3: Add It to Your CI Pipeline

yaml
# .github/workflows/cost-estimate.yml
name: Terraform Cost Estimate
 
on:
  pull_request:
    paths:
      - '**.tf'
      - '**.tfvars'
 
jobs:
  cost-estimate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "1.9.0"
 
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
 
      - name: Install dependencies
        run: pip install anthropic
 
      - name: Terraform Init
        run: terraform init
        working-directory: ./terraform
 
      - name: Generate Terraform Plan JSON
        run: |
          terraform plan -out=tfplan.binary
          terraform show -json tfplan.binary > plan.json
        working-directory: ./terraform
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
 
      - name: Run Cost Estimator
        id: cost
        run: |
          python cost_estimator.py --plan-file terraform/plan.json --output cost-report.md
          echo "report<<EOF" >> $GITHUB_OUTPUT
          cat cost-report.md >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
 
      - name: Comment on PR
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## 💰 Terraform Cost Estimate\n\n\`\`\`\n${{ steps.cost.outputs.report }}\n\`\`\``
            })

Now every Terraform PR automatically gets a cost estimate comment. Engineers see the cost impact before merging infrastructure changes.


Enhancements to Try

1. Compare costs between revisions:

python
# Compare plan costs vs current state
def compare_costs(old_resources, new_resources):
    # Find added/removed resources
    added = [r for r in new_resources if r['name'] not in [o['name'] for o in old_resources]]
    removed = [r for r in old_resources if r['name'] not in [n['name'] for n in new_resources]]
    # Ask Claude for delta cost

2. Add actual AWS pricing API: Use boto3 to fetch real-time pricing data and give Claude accurate numbers instead of relying on its training data.

3. Multi-cloud support: The same pattern works for GCP and Azure Terraform resources — just adjust the prompt to reference GCP/Azure pricing.

4. Slack notification: Post the cost estimate to a #terraform-costs Slack channel for team visibility.


Affiliate note: Anthropic Claude API — the intelligence behind this estimator. Start free with $5 of credit. For cost optimization at scale, Infracost provides a dedicated solution with 1,000+ resource types supported — worth considering if you need production-grade accuracy.

Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments