TL;DR

  • Infracost shows cost impact of Terraform changes in every PR — before deployment, not after billing
  • Cost policies can block PRs that exceed thresholds (like security scanners block vulnerabilities)
  • The #1 mistake: treating cost reviews as optional instead of automated gates

Best for: Teams with significant cloud spend or those who’ve been surprised by bills Skip if: You’re on free tier or fixed-price infrastructure Read time: 9 minutes

Your Terraform PR looks good. Tests pass, security scan clean, approved by two reviewers. You merge. Next month’s AWS bill arrives: $47,000 over budget. Someone added a db.r6g.4xlarge RDS instance where db.t3.medium would’ve worked.

This is the FinOps gap — infrastructure decisions made without cost visibility. In 2026, cost estimation belongs in CI alongside security scanning. You shouldn’t deploy what you can’t afford.

The Real Problem

Cloud costs fail to stay predictable because cost feedback comes too late. The billing cycle is monthly. By the time you see the impact, the resources have been running for weeks.

Traditional cost management approaches:

  • Budget alerts that fire after you’ve overspent
  • Monthly cost reviews that happen after resources are deployed
  • Manual estimates that are wrong or skipped entirely

The shift-left approach for costs:

  • Every PR shows estimated monthly cost change
  • Cost policies block changes exceeding thresholds
  • Cost attribution happens at deploy time, not month-end

This isn’t about penny-pinching — it’s about making cost a first-class engineering concern alongside performance and security.

Infracost: The Standard Tool

Infracost has become the de facto standard for Terraform cost estimation. It parses your Terraform files, queries cloud pricing APIs, and outputs cost breakdowns.

# Install
brew install infracost

# Authenticate (free tier available)
infracost auth login

# Basic cost breakdown
infracost breakdown --path ./terraform

# Compare two states (for PR diffs)
infracost diff --path ./terraform --compare-to infracost-base.json

The output shows both existing and new resource costs:

Project: terraform

 Name                                      Monthly Qty  Unit   Monthly Cost

 aws_instance.web
 ├─ Instance usage (Linux/UNIX, on-demand, t3.large)
 │                                               730  hours        $60.74
 └─ root_block_device
    └─ Storage (gp3)                              50  GB            $4.00

 aws_rds_instance.database
 ├─ Database instance (on-demand, db.r6g.4xlarge)
 │                                               730  hours     $1,752.40
 └─ Storage (gp2)                                100  GB           $11.50

 OVERALL TOTAL                                                  $1,828.64

The key insight: you see exact pricing before terraform apply.

CI/CD Integration

Infracost shines when integrated into PR workflows. Here’s a GitHub Actions setup:

name: Cost Estimation

on:
  pull_request:
    paths:
      - 'terraform/**'

jobs:
  infracost:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write

    steps:
      - uses: actions/checkout@v4

      - name: Setup Infracost
        uses: infracost/actions/setup@v3
        with:
          api-key: ${{ secrets.INFRACOST_API_KEY }}

      - name: Generate Infracost baseline
        run: |
          infracost breakdown --path=terraform \
            --format=json \
            --out-file=/tmp/infracost-base.json
        env:
          INFRACOST_API_KEY: ${{ secrets.INFRACOST_API_KEY }}

      - name: Generate Infracost diff
        run: |
          infracost diff --path=terraform \
            --format=json \
            --compare-to=/tmp/infracost-base.json \
            --out-file=/tmp/infracost-diff.json

      - name: Post PR comment
        uses: infracost/actions/comment@v1
        with:
          path: /tmp/infracost-diff.json
          behavior: update

This posts a comment on every PR showing:

  • Current monthly cost
  • Cost after changes
  • Diff (increase or decrease)
  • Per-resource breakdown

Cost Policies

Visibility is step one. Step two is enforcement. Infracost supports policy-as-code for costs:

# infracost-policy.yml
version: 0.1
policies:
  - policy_path: policies/cost-policy.rego
    rego_url: https://raw.githubusercontent.com/infracost/infracost/master/examples/opa/policies/cost-policy.rego

Write cost policies in Rego (same as OPA):

package infracost

# Block PRs that increase monthly cost by more than $500
deny[msg] {
    maxDiff := 500.0
    input.diffTotalMonthlyCost > maxDiff
    msg := sprintf("Monthly cost increase ($%.2f) exceeds maximum allowed ($%.2f)", [
        input.diffTotalMonthlyCost,
        maxDiff
    ])
}

# Require approval for changes over $100
warn[msg] {
    input.diffTotalMonthlyCost > 100
    input.diffTotalMonthlyCost <= 500
    msg := sprintf("Cost increase of $%.2f requires finance team approval", [
        input.diffTotalMonthlyCost
    ])
}

# Block specific expensive instance types
deny[msg] {
    resource := input.projects[_].breakdown.resources[_]
    contains(resource.name, "aws_instance")
    contains(resource.metadata.instance_type, "x2idn")
    msg := sprintf("Instance type x2idn not allowed without exception. Resource: %s", [resource.name])
}

Run policy evaluation in CI:

infracost breakdown --path ./terraform --format json | \
  infracost policy --policy-path ./policies/

Exit code 1 means policy violation — block the PR.

Tagging for Cost Attribution

Cost estimation works best with proper tagging. Enforce tags that enable cost attribution:

# variables.tf
variable "cost_center" {
  description = "Cost center for billing attribution"
  type        = string
  validation {
    condition     = can(regex("^CC-[0-9]{4}$", var.cost_center))
    error_message = "Cost center must match format CC-XXXX"
  }
}

variable "environment" {
  description = "Environment (dev/staging/prod)"
  type        = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod"
  }
}

# main.tf
locals {
  common_tags = {
    CostCenter  = var.cost_center
    Environment = var.environment
    ManagedBy   = "terraform"
    Project     = var.project_name
  }
}

resource "aws_instance" "web" {
  # ...
  tags = merge(local.common_tags, {
    Name = "web-server"
  })
}

Combine with Compliance Testing to enforce tagging policies.

Handling Usage-Based Costs

Infracost estimates based on provisioned resources, but many cloud costs depend on usage. For accurate estimates, provide usage data:

# infracost-usage.yml
version: 0.1
resource_usage:
  aws_lambda_function.api:
    monthly_requests: 10000000
    request_duration_ms: 250

  aws_s3_bucket.data:
    standard:
      storage_gb: 500
      monthly_tier_1_requests: 100000
      monthly_tier_2_requests: 1000000

  aws_dynamodb_table.users:
    monthly_write_request_units: 5000000
    monthly_read_request_units: 25000000

Run with usage file:

infracost breakdown --path ./terraform --usage-file infracost-usage.yml

For production workloads, generate usage from actual CloudWatch metrics:

# Export usage from running infrastructure
infracost breakdown --path ./terraform --sync-usage-file infracost-usage.yml

Multi-Environment Cost Views

Most teams deploy to multiple environments. Structure Infracost runs to show per-environment costs:

# .github/workflows/cost-estimation.yml
jobs:
  infracost:
    strategy:
      matrix:
        environment: [dev, staging, prod]
    steps:
      - name: Infracost for ${{ matrix.environment }}
        run: |
          infracost breakdown \
            --path=terraform/environments/${{ matrix.environment }} \
            --format=json \
            --out-file=/tmp/infracost-${{ matrix.environment }}.json

Aggregate for total cost view:

infracost output --path="/tmp/infracost-*.json" --format=table

AI-Assisted Approaches

Cost optimization involves understanding both pricing models and usage patterns. AI tools accelerate this.

What AI does well:

  • Suggesting cheaper instance types for workloads
  • Identifying Reserved Instance or Savings Plan opportunities
  • Explaining pricing models for unfamiliar services
  • Generating Infracost usage files from requirements

What still needs humans:

  • Deciding acceptable cost vs performance tradeoffs
  • Understanding business context for cost allocation
  • Approving exceptions for expensive resources
  • Validating AI-suggested optimizations actually work

Useful prompt:

Review this Terraform configuration and suggest cost optimizations:
- Current: db.r6g.4xlarge RDS instance
- Workload: 500 QPS read, 50 QPS write, 100GB storage
- Constraints: Must maintain <10ms p99 latency
Provide alternative instance types with estimated monthly savings

When This Breaks Down

Cost estimation has limitations:

Pricing accuracy: Cloud providers change prices. Free tier limits vary by account age. Committed use discounts aren’t reflected in raw estimates.

Usage estimation: Serverless and usage-based services require usage predictions. Lambda, S3 requests, data transfer — all depend on traffic you might not know in advance.

Cross-resource costs: Data transfer between resources (VPC endpoints, cross-region replication) often dominates bills but is hard to estimate from Terraform alone.

Spot/preemptible pricing: If you use spot instances, actual costs vary significantly from on-demand estimates.

Consider complementary approaches:

  • AWS Cost Explorer / Azure Cost Management for historical analysis
  • Cloud provider budgets for real-time alerts
  • Drift Detection to catch manually-created expensive resources

Decision Framework

Enable Infracost on all PRs when:

  • Cloud bill exceeds $10K/month
  • Multiple teams deploy independently
  • You’ve had cost surprises in the past year
  • Engineers don’t know cloud pricing

Add cost policies when:

  • Specific resource types need approval
  • Budget limits are hard constraints
  • Teams request exceptions frequently
  • Cost increases correlate with incidents (over-provisioning as “fix”)

Skip cost estimation when:

  • Fixed-price contracts cover infrastructure
  • Development-only environments with auto-shutdown
  • Greenfield projects where scale is unknown

Measuring Success

MetricBeforeAfterHow to Track
Cost surprises per quarterUnknown0Finance escalations
PR-to-bill correlationManualAutomatedInfracost vs actual
Time to identify cost spike causeDaysMinutesIncident response logs
Over-provisioned resourcesUnknown<10%Right-sizing reports

Warning signs it’s not working:

  • Teams ignoring cost comments
  • Too many policy exceptions requested
  • Estimates consistently wrong vs actual bills
  • Cost reviews happening manually despite automation

What’s Next

Start with visibility, then add enforcement:

  1. Enable Infracost on one repository
  2. Run for 2 sprints without policies (gather baseline)
  3. Identify reasonable thresholds from historical data
  4. Add warning policies first (no blocking)
  5. Graduate to blocking policies for clear violations
  6. Expand to additional repositories

The goal is making cost part of the definition of “done” — a change isn’t complete until you know what it costs.


Related articles:

External resources: