terraform plan

By Vishvesh Patel · DevOps EngineerUpdated July 12, 2026

Master terraform plan. Reading the output symbols, saving plan files, targeting resources, detecting drift, and using plan in CI/CD.

What Is terraform plan?

terraform plan is the safety net of Terraform. Before making any infrastructure changes, it calculates exactly what would happen and shows you a precise diff, without actually doing anything.

terraform plan

Think of it as asking Terraform: "If I run apply right now, what changes will happen?" The answer is printed as an execution plan: a line-by-line breakdown of every resource that will be created, updated, replaced, or deleted.

Running plan is always safe. It has zero side effects. No API calls are made to create or destroy resources. You can run it as many times as you like.

Reading the Execution Plan

The plan output uses symbols to communicate each action:

SymbolMeaningRisk
+Resource will be createdNew infrastructure
-Resource will be destroyedPermanent deletion
~Resource will be updated in-placeChange without recreation
-/+Resource will be destroyed then recreatedDowntime risk!
<=Data source will be readNo infrastructure change

Example Plan Output

Terraform will perform the following actions:

  # aws_instance.web will be created
  + resource "aws_instance" "web" {
      + ami                          = "ami-0c55b159cbfafe1f0"
      + instance_type                = "t3.micro"
      + id                           = (known after apply)
      + public_ip                    = (known after apply)
      + vpc_security_group_ids       = (known after apply)
    }

  # aws_s3_bucket.assets will be updated in-place
  ~ resource "aws_s3_bucket" "assets" {
      ~ tags = {
          + "Environment" = "production"
            "Name"        = "my-assets"
        }
    }

  # aws_instance.old will be destroyed
  - resource "aws_instance" "old" {
      - ami           = "ami-0abcdef1234567890" -> null
      - instance_type = "t2.micro" -> null
      - id            = "i-0abc12345def67890" -> null
    }

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

The final line is the summary. Read it every time before approving.

The -/+ Symbol, The Most Dangerous One

The -/+ symbol (destroy then create) is the one to pay careful attention to:

  # aws_instance.web must be replaced
-/+ resource "aws_instance" "web" {
      ~ id            = "i-0abc123" -> (known after apply) # forces replacement
    ~ ami             = "ami-0old" -> "ami-0new"           # forces replacement
    }

Some resource attributes, like ami on an EC2 instance, cannot be changed in-place. Changing them forces Terraform to delete the old resource and create a new one. This means downtime.

Rule: Whenever you see -/+ in your plan, pause and think about the impact. Can your application handle a restart? Is there a safer way to do this migration?

Saving a Plan File with -out

You can save the execution plan to a file:

terraform plan -out=tfplan

This is the recommended practice for production deployments. When you later run terraform apply tfplan, Terraform applies exactly what was planned, nothing more, nothing less. No interactive approval prompt is shown.

This is the standard CI/CD pattern:

  1. CI pipeline runs terraform plan -out=tfplan and posts the output for review
  2. A human reviews and approves the plan
  3. CI pipeline runs terraform apply tfplan, guaranteed to match what was reviewed
# In CI/CD
terraform plan -input=false -out=tfplan
terraform apply -input=false tfplan

The plan file is binary. Do not commit it to Git, it may contain secrets. In CI/CD, pass it between pipeline stages as an artifact.

Targeting Specific Resources with -target

The -target flag limits the plan to only the specified resource (and its dependencies):

# Plan only the S3 bucket
terraform plan -target=aws_s3_bucket.assets

# Plan only a specific module
terraform plan -target=module.vpc

-target is useful for debugging or making emergency changes to a single resource without touching the rest of the infrastructure.

Warning: Avoid -target in routine operations. It can leave your state out of sync with reality. Use it only for emergencies or debugging.

Detecting Drift

Drift happens when someone manually changes infrastructure (e.g., via the AWS console) without updating the Terraform config. When you run plan, Terraform compares your configuration against the real infrastructure (via the state file) and shows the drift:

# aws_security_group.web has been modified manually
~ resource "aws_security_group" "web" {
    ~ ingress = [
        + {
            + cidr_blocks = ["203.0.113.5/32"]
            + from_port   = 22
            + to_port     = 22
            + protocol    = "tcp"
          },
      ]
  }

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

This plan shows that someone opened port 22 manually. Running apply will remove that manual change and bring infrastructure back to the declared state. This is exactly the value of IaC, configuration is the source of truth.

terraform plan in CI/CD Pipelines

In automated pipelines, use these flags:

terraform plan \
  -input=false \       # Prevent interactive prompts
  -no-color \          # Disable ANSI colors for cleaner logs
  -out=tfplan          # Save plan for later apply

A minimal GitLab CI example:

plan:
  stage: plan
  script:
    - terraform init -input=false
    - terraform plan -input=false -no-color -out=tfplan
  artifacts:
    paths:
      - tfplan

Common plan Errors

Error: No configuration files

Error: No configuration files
│ Plan requires configuration to be present.

You are running plan in the wrong directory. cd to the directory containing your .tf files.

Error: Invalid reference

Error: Reference to undeclared resource
│   on main.tf line 14
│ A managed resource "aws_vpc" "main" has not been declared.

You referenced a resource (aws_vpc.main) that does not exist in your config. Check your resource names.


Knowledge Check

Exercise

Question 1: Plan Symbols

You run terraform plan and see this in the output: "-/+ resource aws_instance.web (forces replacement)". What does this mean?

Exercise

Question 2: terraform plan -out

Your team uses terraform plan -out=tfplan in CI, then terraform apply tfplan in a separate stage. Why is this the recommended production pattern?

Exercise

Question 3: Drift Detection

A colleague manually added an ingress rule to a security group in the AWS console. You run terraform plan. What will happen?


Frequently Asked Questions

What does terraform plan do?

It compares your configuration against the recorded state and the real infrastructure, then prints exactly what it would change without changing anything. Resources are marked + to create, ~ to update in place, - to destroy, and -/+ to replace. It is the review step that separates Terraform from clicking in a console, and it should be read before every apply. Particularly for the -/+ symbol, which means a resource will be destroyed and recreated.

What does the -/+ symbol mean and why is it dangerous?

It means replace: Terraform will destroy the existing resource and create a new one, because the property you changed cannot be modified in place. On a stateless resource that is harmless. On a database, an EBS volume or anything holding data, it is data loss delivered by an apparently routine change. Terraform states the reason with a "forces replacement" annotation next to the offending attribute. Read every plan for this before applying to anything stateful.

Why does terraform plan show changes when I have not changed anything?

Usually drift. Something was changed outside Terraform, in a console or by another tool, and Terraform intends to bring it back in line. It can also be a provider upgrade that computes a default differently, a value that genuinely changes on every run such as a timestamp, or a resource whose API returns attributes in a different order. Read which attribute is changing: drift from a console edit is obvious, whereas a perpetual diff on the same attribute every run usually means a provider quirk that needs ignore_changes.

What is a saved plan file and should I use one?

terraform plan -out=tfplan writes the plan to a file, and terraform apply tfplan applies exactly that plan without recalculating. This matters in CI and for anything reviewed by a human, because it guarantees what was approved is what gets applied, without it, the state or the real infrastructure could change between the plan and the apply. The caveat is that a saved plan contains sensitive values in plain text, so treat it as a secret and do not archive it carelessly.

Should I use -target?

Sparingly, and as a recovery tool rather than a habit. -target limits the plan to specific resources, which is genuinely useful when untangling a partially failed apply or working around a dependency problem. It is not a normal workflow: Terraform warns about it because operating on a subset means the rest of your configuration is not validated against reality, and it is easy to leave infrastructure in a state that does not match the code. If you reach for it routinely, the configuration probably wants splitting up.

How do I use terraform plan in a CI pipeline?

Run terraform plan -input=false -out=tfplan on pull requests and post the output as a comment so the change is reviewable, then apply the saved plan file on merge. Use -detailed-exitcode, which returns 0 for no changes, 2 for changes present and 1 for an error, that lets a pipeline distinguish "nothing to do" from "changes await approval" and act accordingly. Never let apply run automatically from a feature branch against shared infrastructure.

Continue Learning

Explore Related Topics

Try the Tool

Practise this in the browser

Terraform Playground

Run init, plan, apply and destroy against simulated infrastructure.

When it breaks

Related Resources