DEV Community

Cover image for Terraform Plan: Your Last Line of Defense Before Infrastructure Changes
TerraformMonkey
TerraformMonkey

Posted on • Originally published at controlmonkey.io

Terraform Plan: Your Last Line of Defense Before Infrastructure Changes

Terraform plan is the guardrail between your code and your live infrastructure. Every time you run it, Terraform compares your desired configuration with the current state and shows you exactly what’s going to change β€” before anything actually happens.

If you want to avoid destructive changes, catch drift early, and prevent misconfigured variables from sneaking into production, this guide is for you. πŸš€

This post covers:

  • How the Terraform plan engine works
  • How to read plan output (add/change/destroy)
  • How to automate plan checks in CI/CD
  • Common flags you'll actually use
  • Real copy/paste examples
  • Team-friendly best practices
  • Bonus: risk-aware reviews with ControlMonkey

Let’s get into it.


🧩 Terraform Plan Basics

Terraform plan generates an execution plan without changing any resources. It refreshes state (unless disabled), evaluates providers and data sources, compares current vs. desired state, and prints a diff of proposed actions.

πŸ”§ Common terraform plan flags you'll actually use

-out=plan.tfplan        # Save a binary plan file for apply
-refresh=true|false     # Control state refresh before diff
-var / -var-file        # Pass inputs consistently
-target=addr            # Break-glass-only resource targeting
Enter fullscreen mode Exit fullscreen mode

πŸ“˜ Exit codes with -detailed-exitcode

0 β†’ No changes  
1 β†’ Error  
2 β†’ Changes present  
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Official reference (recommended): terraform plan command reference

πŸ‘‰ Also relevant: How to design a Terraform CI/CD pipeline for AWS


βš™οΈ How Terraform Plan Works Under the Hood

Terraform loads your state (local or remote), optionally refreshes it using provider APIs, and computes the diff.

Key components involved:

  • State β†’ Terraform’s source of truth
  • Providers β†’ Define schemas + CRUD operations
  • Data sources β†’ Read-only lookups executed during the plan
  • Resources β†’ Infrastructure objects that may be created, updated, replaced, or destroyed

A refresh step pulls the actual state of resources from the provider, and Terraform compares it with what your code declares.


🧭 How to Read Terraform Plan Output

Terraform uses clear symbols in the diff:

+   create
-   destroy
~   update in-place
-/+ replace (destroy + create)
Enter fullscreen mode Exit fullscreen mode

A typical summary looks like:

Plan: X to add, Y to change, Z to destroy.
Enter fullscreen mode Exit fullscreen mode

🚨 Production rule of thumb

Treat any destroy or any replace (-/+ ) as a red-flag that requires a second reviewer.

Small input changes (e.g., variable tweak, module version update) can cascade into unintended replacements β€” including databases or network resources.


πŸ“¦ Working With Plan Files + JSON (Automation-Ready)

A recommended workflow is to save the plan, export it to JSON, and run validations.

Canonical snippet:

terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json

# Count destroys & replaces
jq '[.resource_changes[] | select(.change.actions|index("delete"))] | length' plan.json
jq '[.resource_changes[] | select(.change.actions|index("replace"))] | length' plan.json
Enter fullscreen mode Exit fullscreen mode

What you can automate with JSON:

  • Block destroys in production unless approved
  • Enforce mandatory tags/owners
  • Fail if predicted cost exceeds budget
  • Annotate PRs with risk indicators

πŸ”„ Terraform Plan Examples: Local CLI β†’ CI/CD

Local workflow

terraform init
terraform plan -out=plan.tfplan
terraform show plan.tfplan
Enter fullscreen mode Exit fullscreen mode

Review β†’ approve β†’ apply.

Minimal GitHub Actions gate using -detailed-exitcode

name: terraform-plan
on: pull_request

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.6.6
      - run: terraform init

      - id: plan
        run: |
          set +e
          terraform plan -detailed-exitcode -out=plan.tfplan
          echo "code=$?" >> $GITHUB_OUTPUT
          set -e

      - name: Upload Plan Artifact
        uses: actions/upload-artifact@v4
        with:
          name: tfplan
          path: plan.tfplan

      - name: Fail If Changes Present
        if: steps.plan.outputs.code == '2'
        run: exit 1
Enter fullscreen mode Exit fullscreen mode

🧨 Troubleshooting & Best Practices for Stable Terraform Plans

Here are proven ways to reduce surprises:

βœ”οΈ Pin provider versions

Run terraform init -upgrade intentionally, not automatically.

βœ”οΈ Use a consistent remote backend

S3 + DynamoDB locking, GCS, or Terraform Cloud.

Avoid local state in team environments.

βœ”οΈ Stabilize your plan

  • Avoid volatile data sources
  • Use depends_on when needed
  • Keep var-files consistent across environments
  • Align Terraform + provider versions across laptops & CI

A stable plan = fewer loops of β€œwhy is this resource changing again?”


🦍 Where ControlMonkey Fits In (Optional but Powerful)

ControlMonkey adds context around Terraform plans so teams spot risk instantly:

  • Highlights destroys & replacements
  • Surfaces drift before running plan
  • Enforces org-wide guardrails
  • Adds automatic insights during plan review
  • Runs across GitHub, GitLab, Bitbucket, Azure DevOps

If your team reviews plans daily, the noise reduction alone is a productivity unlock.

πŸ”— Related reads:


βœ… Wrap-Up: Review Terraform Plans With Confidence

Terraform plan is the most important checkpoint in IaC. Use it consistently, export JSON for policy checks, and fail PRs when risky changes appear.

If you want faster reviews, automated guardrails, and risk-aware change visibility across teams, ControlMonkey can help β€” request a demo to see how it works.


πŸ’¬ What are your best Terraform plan tips or horror stories? Drop them in the comments β€” DevOps managers learn best from each other.

Top comments (0)