Tutorial

Preview Infrastructure Cost and Blast Radius Before You Deploy

Every infrastructure change carries risk. DeployDiff shows you what's changing, how much it'll cost, and how to undo it — before Terraform, CloudFormation, or Pulumi touches your production environment.
May 15, 2026 · 7 min read · Revenue Holdings
Share this article:

You know the feeling: you run terraform plan, scan the output, and think "looks fine." Then you deploy and discover it's replacing an RDS instance (downtime), adding $400/mo in unplanned costs, or — worst case — deleting a production database because the state file was stale.

DeployDiff is a CLI tool that sits between your infrastructure plan and your apply step. It reads Terraform plan JSON, CloudFormation change sets, and Pulumi previews — then produces three things every deployment needs:

And it all runs in CI, so you can gate deployments on cost thresholds or destructive changes.

┌──────────────┐ ┌────────────┐ ┌──────────────────┐ │ terraform │ │ DeployDiff │ │ CI Gate │ │ plan -out= │──→│ preview │──→│ ✅ Cost < $200 │ │ plan.tfplan │ │ cost │ │ ✅ No destroys │ └──────────────┘ │ rollback │ │ ✅ Rollback mem │ └────────────┘ └──────────────────┘ "Replace db.t2.micro → ❌ Cost increase: $237/mo db.t3.medium: +$237/mo" ❌ Destructive change detected

1. Install DeployDiff

$ pip install deploydiff

Or from GitHub:

$ pip install git+https://github.com/Coding-Dev-Tools/deploydiff.git

Verify it's installed:

$ deploydiff --help

You'll see three subcommands: preview, cost, and rollback.

2. Generate a Terraform Plan

First, create a sample Terraform configuration so we have something to diff. Create a file called main.tf:

provider "aws" {
  region = "us-east-1"
}

resource "aws_db_instance" "main" {
  engine         = "postgres"
  engine_version = "15.4"
  instance_class = "db.t3.micro"
  allocated_storage = 20
  db_name        = "myapp"
  username       = "admin"
  password       = "pleasechange"  # DeployDiff wouldn't let this ship

  backup_retention_period = 7
  skip_final_snapshot     = false
}

resource "aws_s3_bucket" "logs" {
  bucket = "myapp-logs-2026"
}

resource "aws_iam_role" "app_role" {
  name = "myapp-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = { Service = "ec2.amazonaws.com" }
    }]
  })
}

Generate a plan and save it as JSON:

$ terraform init && terraform plan -out=plan.tfplan
$ terraform show -json plan.tfplan > plan.json

This plan.json is what DeployDiff reads.

💡 No Terraform installed? You can use the sample plan files in the DeployDiff repo to follow along.

3. Preview Resource Changes

$ deploydiff preview --tf plan.json

DeployDiff outputs a structured summary:

╔══════════════════════════════════════════════════════════╗
║                   DeployDiff Preview                     ║
╠══════════════════════════════════════════════════════════╣
║ Summary                                                  ║
║   Resources: 3                                          ║
║   Creates:   3                                           ║
║   Updates:   0                                           ║
║   Deletes:   0                                           ║
║   Replaces:  0                                           ║
╠══════════════════════════════════════════════════════════╣
║ Resources                                                ║
║                                                          ║
║ ➕ aws_db_instance.main                                  ║
║    ├ engine:        postgres                             ║
║    ├ instance_class: db.t3.micro                         ║
║    ├ storage:       20 GB                                ║
║    └ backup_days:   7                                    ║
║                                                          ║
║ ➕ aws_s3_bucket.logs                                    ║
║    └ bucket: myapp-logs-2026                             ║
║                                                          ║
║ ➕ aws_iam_role.app_role                                 ║
║    └ name: myapp-role                                    ║
╚══════════════════════════════════════════════════════════╝

This is much more readable than a raw Terraform plan JSON — especially when you have dozens of resources. The summary row tells you instantly: 3 creates, 0 destroys, 0 replaces.

4. Estimate Cost Impact

The cost command reads pricing metadata from your plan and estimates the monthly bill:

$ deploydiff cost --tf plan.json
╔══════════════════════════════════════════════════════════╗
║                   DeployDiff Cost Estimate                ║
╠══════════════════════════════════════════════════════════╣
║ Resource                        Current     New     Δ    ║
║ ──────────────────────────────────────────────────────── ║
║ aws_db_instance.main           $0.00    $17.52  +$17.52 ║
║ aws_s3_bucket.logs             $0.00     $0.00   $0.00  ║
║ aws_iam_role.app_role          $0.00     $0.00   $0.00  ║
║ ──────────────────────────────────────────────────────── ║
║ Total                           $0.00    $17.52  +$17.52 ║
╚══════════════════════════════════════════════════════════╝

Now suppose a teammate changes the DB instance class and generates a new plan:

╔══════════════════════════════════════════════════════════╗
║                   DeployDiff Cost Estimate                ║
╠══════════════════════════════════════════════════════════╣
║ Resource                        Current     New      Δ   ║
║ ──────────────────────────────────────────────────────── ║
║ aws_db_instance.main           $17.52   $267.43 +$249.91║
╚══════════════════════════════════════════════════════════╝
⚠ Cost increase: $249.91/mo — above default threshold
🚨 That's a $250/month surprise. With DeployDiff in your CI pipeline, this change gets flagged in the PR review, not after the month's AWS bill arrives.

5. Generate Rollback Commands

When things go wrong, every second counts. Don't google Terraform rollback syntax under incident pressure:

$ deploydiff rollback --tf plan.json
╔══════════════════════════════════════════════════════════╗
║                   DeployDiff Rollback                     ║
╠══════════════════════════════════════════════════════════╣
║ To roll back the last apply:                             ║
║                                                          ║
║   1. terraform plan -destroy -out=rollback.tfplan        ║
║   2. terraform apply rollback.tfplan                     ║
║                                                          ║
║ Resources that would be removed:                         ║
║   aws_db_instance.main                                   ║
║   aws_s3_bucket.logs                                     ║
║   aws_iam_role.app_role                                  ║
╚══════════════════════════════════════════════════════════╝

These are ready to copy-paste — no syntax guessing under fire.

6. CI/CD Pipeline — Gate on Cost and Destructive Changes

Here's the real value: gating deployments automatically. Add this GitHub Actions workflow to your IaC repo:

# .github/workflows/deploy-preview.yml
name: Deploy Preview

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

jobs:
  preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Terraform Plan
        run: |
          terraform init
          terraform plan -out=plan.tfplan
          terraform show -json plan.tfplan > plan.json

      - name: Install DeployDiff
        run: pip install deploydiff

      - name: Preview infrastructure changes
        run: deploydiff preview --tf plan.json

      - name: Check cost impact
        id: cost
        run: |
          deploydiff cost --tf plan.json --threshold 200
          echo "cost_exceeded=$?" >> $GITHUB_OUTPUT

      - name: Check for destructive changes
        id: destructive
        run: |
          deploydiff preview --tf plan.json --exit-on-destroy
          echo "has_destroys=$?" >> $GITHUB_OUTPUT

      - name: Comment PR with report
        if: always()
        uses: actions/github-script@v7
        with:
          script: |
            const costExceeded = '${{ steps.cost.outputs.cost_exceeded }}' === '1';
            const hasDestroys = '${{ steps.destructive.outputs.has_destroys }}' === '1';
            let body = '## DeployDiff Preview\n\n';
            if (costExceeded) body += '⚠️ **Cost exceeds $200/mo threshold**\n';
            if (hasDestroys) body += '🚨 **Destructive changes detected (resources will be replaced/destroyed)**\n';
            body += '\nReview the DeployDiff output above before approving.';
            await github.rest.issues.createComment({
              ...context.repo,
              issue_number: context.issue.number,
              body
            });

Now every PR that touches infrastructure gets:

⚙️ Multi-provider: DeployDiff works with CloudFormation change sets (--cfn changeset.json) and Pulumi previews (--pulumi preview.json) using the same commands.

Command Reference

CommandDescription
deploydiff preview --tf plan.jsonShow resource-level diff summary
deploydiff preview --tf plan.json --exit-on-destroyExit non-zero if any resources are destroyed/replaced
deploydiff cost --tf plan.jsonEstimate monthly cost impact
deploydiff cost --tf plan.json --threshold 500Exit non-zero if cost increase exceeds $500
deploydiff rollback --tf plan.jsonGenerate rollback commands for the last apply

Supported Providers

FlagProviderInput Format
--tfTerraformterraform show -json plan.tfplan output
--cfnCloudFormationChange set JSON (via AWS CLI)
--pulumiPulumipulumi preview --show-readings --json output

Real-World Scenario: The $2,400 Cost Regression

A fintech team was reviewing a PR that bumped their RDS instance class "for better performance." The change would have cost an extra $2,400/month across their multi-region setup. DeployDiff caught it in the PR — the reviewer saw the cost column and said "let's benchmark first." They optimized queries instead and never needed the upgrade. Without DeployDiff, the change would have sailed through code review (it was a one-line diff) and shown up on next month's AWS bill.

💰 The math: Adding DeployDiff to your CI pipeline costs nothing (free tier, CLI-only) and can catch a single cost regression that pays for itself hundreds of times over. Most teams don't know their infra costs are drifting until the bill arrives.

Next Steps

DeployDiff is one of 10 tools in the Revenue Holdings suite. Each one runs in CI to catch problems before they hit production:

Read the full docs →

Get Notified About New Tutorials

DevOps guides, release notes, and product updates — no spam, unsubscribe anytime.