Plan Changes with Terraform Plan

Plan changes with terraform plan — Terraform foundations tutorial, lesson 6. Learn to preview infrastructure changes before applying, with hands-on steps and troubleshooting.

Focus: plan changes with terraform plan

Sponsored

You are about to change your infrastructure — but what if you hit apply and discover you are about to delete a production database? That is exactly the kind of surprise terraform plan is designed to prevent. In this lesson, you will learn how to plan changes with terraform plan, interpret the output, and use it as a safety net before any mutation happens. It is the sixth step in your Terraform foundations track, and it will turn you from someone who blindly applies into an engineer who reviews and approves changes with confidence.

The Problem: Blindly Applying Infrastructure Changes

Imagine you have been working on a Terraform configuration for weeks. You run terraform apply without thinking, and it works — until one day, a teammate removes an resource block, and your apply deletes a critical load balancer in production. No warning, no undo. This is the pain terraform plan solves: it gives you a preview of every change Terraform will make before you commit to it.

Without a plan step, you are navigating infrastructure changes blind. You might think you are updating an AMI, but Terraform might be planning to replace the entire EC2 instance. Or you might think you are adding a security group rule, but you are actually about to destroy the whole security group. The cost of a mistake here is downtime, data loss, or a security incident — all avoidable.

terraform plan is not an optional safeguard; it is a required habit for anyone serious about infrastructure-as-code. It is the difference between shipping changes with confidence and shipping changes with hope.

Core Concept / Mental Model

Think of terraform plan as a dry run or a simulation of your infrastructure changes. It reads your configuration, compares it against the current state (stored in the state file), and computes a detailed list of actions that would bring your infrastructure in line with your code. It does not actually change anything — it only reports.

Here is the mental model:

  • Configuration = the desired state (your .tf files)
  • State = the current reality (your terraform.tfstate file)
  • Plan = the diff between the two

A plan output shows three types of actions:

  • Create: resource exists in configuration but not in state — Terraform will add it
  • Update in-place: resource exists in both, but attributes differ — Terraform will modify it (no resource replacement)
  • Replace: resource cannot be updated in-place (e.g., AMI change) — Terraform will destroy the old and create a new one

Additionally, a plan can show Destroy actions when a resource is present in state but absent from configuration.

The plan is not just a list; it is an audit trail. You can save it to a file and review it, share it in a pull request, or feed it into terraform apply to guarantee that what you planned is exactly what gets applied.

How It Works Step by Step

Here is the cause-and-effect sequence of how terraform plan works under the hood:

  1. Refresh state — Terraform reads the current state file and, by default, refreshes the actual infrastructure to detect any drift (resources changed outside of Terraform). This updates the state to reflect reality.
  2. Read configuration — Terraform parses all .tf files in your working directory, including variables, modules, and providers.
  3. Compute the diff — Terraform evaluates the desired state from the configuration and compares it to the refreshed state. It calculates the minimal set of actions needed to reconcile the two.
  4. Generate the plan — Terraform creates an execution plan that lists every action with details, including resource addresses and attribute changes.
  5. Persist the plan (optional) — You can save the plan to a file with -out=plan.tfplan for later use.
  6. Present the output — The plan is displayed in a human-readable format, with additions in green (+), changes in amber (~), destructions in red (-), and replacements as both - and +.

Pro tip: Always use terraform plan -out=plan.tfplan to save your plan. That way, when you apply, you are applying exactly the reviewed plan — no surprises from a configuration edit between plan and apply.

Now, let’s see this in action in the hands-on walkthrough.

Hands-On Walkthrough

Let’s create a simple setup to experience terraform plan firsthand. We’ll assume you already have Terraform installed and configured with AWS credentials.

Step 1: Create a basic configuration

Create a directory and add a file named main.tf with the following content. We’ll use the AWS provider and create an S3 bucket.

# main.tf
terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

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

resource "aws_s3_bucket" "example" {
  bucket = "my-demo-bucket-2025"
  tags = {
    Name = "my-demo-bucket"
  }
}

Step 2: Initialize and apply the initial state

Run these commands in your terminal:

terraform init
terraform apply -auto-approve

You will see output showing the bucket being created. This establishes a baseline state for our plan demonstration.

Step 3: Run terraform plan after making a change

Now, modify the main.tf to add a second bucket and change the tags of the first bucket. Let’s update the file:

# main.tf (updated)
resource "aws_s3_bucket" "example" {
  bucket = "my-demo-bucket-2025"
  tags = {
    Name = "my-demo-bucket"
    Environment = "dev"
  }
}

resource "aws_s3_bucket" "second" {
  bucket = "my-demo-bucket-2025-second"
}

Now run:

terraform plan

You’ll see output like this (abridged):

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create
  ~ update in-place

Terraform will perform the following actions:

  # aws_s3_bucket.example will be updated in-place
  ~ resource "aws_s3_bucket" "example" {
        id                          = "my-demo-bucket-2025"
      ~ tags                        = {
          + "Environment" = "dev"
        }
      + versioning                  = []
    }

  # aws_s3_bucket.second will be created
  + resource "aws_s3_bucket" "second" {
      + bucket                     = "my-demo-bucket-2025-second"
      + force_destroy              = false
      + tags                       = {}
    }

Plan: 1 to add, 1 to change, 0 to destroy.

Take note of the summary line: Plan: 1 to add, 1 to change, 0 to destroy. This is your at-a-glance risk assessment.

Step 4: Save and apply the plan

Save the plan to a file and then apply it exactly as reviewed:

terraform plan -out=plan.tfplan
terraform apply plan.tfplan

The apply will show the same actions and ask for confirmation (unless you use -auto-approve). This ensures you are applying precisely what you planned.

Pro tip: In a CI/CD pipeline, always save the plan artifact and use it during apply. This prevents configuration drift between planning and applying.

Step 5: Practice with a destructive change

To see what destruction looks like, remove the second bucket from main.tf, then run terraform plan. You will see a - symbol and the summary will include 1 to destroy.

Plan: 0 to add, 0 to change, 1 to destroy.

This is exactly the kind of change you want to catch early!

Compare Options / When to Choose What

terraform plan is the default, but there are variations and flags that suit different contexts. Here is a comparison of common commands and their best use cases:

Command / Flag Description Best Use Case
terraform plan Standard plan; refreshes state and shows changes. Daily workflow; pre-apply review.
terraform plan -out=plan.tfplan Saves the plan to a binary file for later apply. CI/CD pipelines; ensuring apply matches plan.
terraform plan -refresh=false Skips refreshing state; uses cached state. When you are certain no drift exists; saves time.
terraform plan -target=resource Limits plan to a specific resource. Testing a single resource changes; focused debugging.
terraform plan -destroy Plans a full destruction of all resources. Deprovisioning an environment.
terraform plan -var-file=prod.tfvars Uses a specific variable file. Different environments (dev/staging/prod).

When to choose what:

  • For day-to-day development, the plain terraform plan is enough.
  • For production changes, always use -out and store the plan as an artifact.
  • For a large codebase, use -target sparingly — it can lead to partial state changes if misused.
  • For decommissioning, -destroy is your friend, but you should review the plan very carefully.

Troubleshooting & Edge Cases

Even with terraform plan, things can go wrong. Here are common issues and how to fix them.

Issue 1: "Error: Failed to load state: terraform state file is locked"

  • Cause: Another process (like an apply or plan) is holding the state lock.
  • Fix: Release the lock with terraform force-unlock <lock-id> after verifying no other operation is running. Never force-unlock during a real operation — it can corrupt state.

Issue 2: Plan shows unexpected "replace" instead of "update"

  • Cause: Some attributes are immutable, like AWS AMI for EC2 instances. Any change to those forces replacement.
  • Fix: Read the plan carefully to understand why. If you need to avoid replacement, consider using lifecycle meta-argument with create_before_destroy = true to minimize downtime.

Issue 3: Troubleshooting "Resource already exists" error during apply

  • Cause: The resource exists in the cloud but not in the state file (e.g., created manually). Terraform wants to create it again and fails.
  • Fix: Use terraform import to bring the resource into state, then plan to see only the differences. Do not blindly delete the resource.

Issue 4: Secret values exposed in plan output

  • Cause: Sensitive values (like database passwords) are shown in plain text in the plan.
  • Fix: Mark variables and outputs as sensitive = true in the configuration, and use -no-color or -json if you need to parse output programmatically.

Edge Case: Plan succeeds but apply fails

This can happen if the cloud API changes between plan and apply, or if a dependency is missing. Always re-run terraform plan right before apply, and use -out so you know exactly what you applied. Also, enable precondition and postcondition checks to validate assumptions.

What You Learned & What's Next

You now know how to plan changes with terraform plan effectively. Core takeaways from this lesson:

  • terraform plan is a dry run that shows creates, updates, and destroys without applying.
  • Always review the plan summary (e.g., “Plan: 1 to add, 1 to change, 0 to destroy”) before applying.
  • Use -out to save plans for audit and consistent apply.
  • Use troubleshooting techniques to handle state locks, replacements, and sensitive data.

These skills set you up for the next lesson, where you will dive into state management — how Terraform stores and handles state, which is the backbone of plan accuracy. Mastering state will make your planning even more reliable.

Continue to the next lesson to explore terraform state commands.

Practice recap

Create a Terraform config with two resources (e.g., an S3 bucket and an IAM user). Apply it, then make a change that forces a replacement (e.g., change a region) and run terraform plan. Note how the plan indicates a replacement. Then save the plan to a file and run terraform show to inspect it. This will solidify your understanding of plan output.

Common mistakes

  • Skipping terraform plan and going straight to apply — this defeats the entire purpose of a safe IaC workflow.
  • Ignoring -out and letting a plan drift: between plan and apply, someone edits the config, and apply uses a different set of changes.
  • Using -target too broadly, which can lead to partial state updates and unplanned resource modifications.
  • Forgetting to mark sensitive variables as sensitive, causing credentials to appear in plan logs.
  • Assuming a plan with 0 destroys is safe — always read the diff, not just the summary.

Variations

  1. Use terraform plan -json to get machine-readable output for custom tooling or CI integrations.
  2. Utilize terraform show plan.tfplan to inspect a saved plan in detail later.
  3. Leverage terraform plan -destroy to plan a full teardown of your environment.

Real-world use cases

  • Reviewing a plan in a pull request to ensure infrastructure changes are safe before merging.
  • Using terraform plan -out in CI pipelines to produce an artifact for audited production applies.
  • Catching unintended resource deletions before a scheduled maintenance window.

Key takeaways

  • Terraform plan is a dry-run simulation that shows exactly what will change before apply.
  • Always review the plan summary and diff to catch destructive changes early.
  • Save plans with -out to ensure apply matches the reviewed plan.
  • Handle state locks, replacements, and sensitive data with proper troubleshooting techniques.
  • Use variations like -json and -destroy to adapt planning to your workflow.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.