5.1 Generate and Review an Execution Plan

Key Takeaways

  • `terraform plan` compares configuration, state, and a default refresh of real remote objects, then prints create, update, destroy, replace, or no-op actions.
  • Read the legend: `+` create, `-` destroy, `~` update in place, `-/+` or `+/-` replace. A no-op does not appear as a change.
  • `terraform plan -out=tfplan` writes an apply-ready saved plan; without `-out` the plan is speculative and is not intended to be applied.
  • Plan does not change infrastructure, but the default refresh may persist an updated state file. `-refresh=false` skips refresh; `-refresh-only` plans only to reconcile state.
  • `-target` is a last-resort recovery option. Official docs: use it only in exceptional circumstances, not as a weekly workflow.
Last updated: August 2026

5.1 Generate and Review an Execution Plan

Quick Answer: terraform plan compares your configuration, the current state, and a default refresh of real remote objects, then prints the create, update, destroy, replace, or no-op actions Terraform would take. It does not change infrastructure. By default it may write an updated state file because it refreshes. Save an apply-ready plan with -out=tfplan. Without -out, the plan is speculative.

Objective 3d on Terraform Associate (004) asks you to generate and review an execution plan. The product version on the exam is Terraform 1.12. The same plan language appears in Terraform Community Edition and in HCP Terraform (the current name of HashiCorp's hosted product, formerly marketed as Terraform Cloud).

Why this objective exists

HashiCorp's write–plan–apply workflow separates preview from change. Official CLI docs say the terraform plan command creates an execution plan so you can preview the changes Terraform plans to make to your infrastructure. The plan command alone does not carry out the proposed changes. You use it to check that the proposed changes match what you expected, or to share the preview with a team, before anyone applies.

If you are sitting at an interactive terminal and you already expect to apply, you can run terraform apply directly. By default apply generates a new plan and prompts you to approve it. Objective 3d still wants you to read that plan, whether it came from terraform plan or from the first half of apply.

What Terraform does when it creates a plan

By default Terraform performs three operations:

  1. Refresh. It reads the current state of already-existing remote objects so the Terraform state is up-to-date.
  2. Compare. It compares the current configuration to that prior (now refreshed) state and notes differences.
  3. Propose. It proposes a set of change actions that should, if applied, make the remote objects match the configuration.

If Terraform detects that no changes are needed to resource instances or to root-module output values, terraform plan reports that no actions need to be taken.

That three-way compare is the exam definition. A plan is not a pretty-print of the .tf files, and it is not a dump of the state file. It is a calculated delta among desired HCL, recorded state, and live objects.

Plan does not change infrastructure — except it may refresh state

Memorize the official nuance. Plan does not create, update, or destroy infrastructure objects. HashiCorp is explicit: the plan command alone does not carry out the proposed changes.

The trap is the word "nothing." By default plan does refresh. Refresh queries provider APIs and may persist an updated state snapshot so recorded attributes match reality. HashiCorp's published 004-style sample item treats terraform plan and terraform apply as the commands that automatically refresh state unless you pass extra flags. terraform validate and terraform fmt do not.

Use -refresh=false when you need a faster plan and you are willing to treat cached state as truth. Official docs: this reduces remote API requests, but Terraform ignores external changes, which can produce an incomplete or incorrect plan. You cannot combine -refresh=false with refresh-only mode, because that would disable the entire planning operation.

How to read plan output

Terraform prints a legend ("Resource actions are indicated with the following symbols"), then a per-resource diff, then a summary line such as Plan: 2 to add, 1 to change, 1 to destroy.

SymbolActionWhat it means on 004
+createObject is not in state; Terraform will create it
-destroyObject is in state but is no longer desired
~update in placeSame remote object; arguments change without replacement
-/+replace (destroy, then create)A force-new change; default replacement order
+/-replace (create, then destroy)Same replacement when create_before_destroy applies

A no-op instance does not appear as a change. The summary counts add / change / destroy. A replace increments both destroy and add. Values printed as (known after apply) are computed by the provider only after the API call—ids, ARNs, random pets, generated names.

Worked read

# aws_instance.web will be updated in-place
~ resource "aws_instance" "web" {
    id            = "i-0abc"
  ~ instance_type = "t3.micro" -> "t3.small"
  }

# aws_instance.db must be replaced
-/+ resource "aws_instance" "db" {
      ~ ami = "ami-old" -> "ami-new" # forces replacement
    }

web is a ~ in-place update: the instance id stays. db is -/+ replace because AMI is a force-new argument on typical AWS instance types. Approving that plan destroys and recreates the database instance. Root-volume data is gone unless you designed around it. That is why 3d is a reading skill, not a "run plan and shrug" skill.

Speculative plans versus apply-ready saved plans

Official CLI language:

  • terraform plan without -out=FILE creates a speculative plan: a description of the effect without any intent to actually apply it. Developers use speculative plans to verify a change before code review.
  • terraform plan -out=tfplan writes the generated plan to an opaque file. You later execute it with terraform apply tfplan. HashiCorp's primary audience for this two-step workflow is automation.

Other changes to the target system can make an earlier speculative plan stale. Official docs: always re-check the final non-speculative plan before applying.

Saved-plan rules that show up on exams and in production:

  • Terraform allows any filename, but the convention is tfplan. Do not name the file with a suffix Terraform treats as configuration (especially .tf). Subsequent commands would try to parse the binary as HCL and fail.
  • The file is not a public interchange format. It contains the full configuration, planned values, and plan options including input variables. Sensitive data is stored in cleartext even if the terminal obscured it. Treat saved plans as secrets. Never commit them.
  • Inspect a saved plan with terraform show tfplan (human) or terraform show -json (automation).
  • -detailed-exitcode is the CI helper: 0 succeeded with empty diff, 1 error, 2 succeeded with a non-empty diff.

On HCP Terraform, a VCS-backed workspace can run speculative plans automatically on pull requests so reviewers see the same create/update/destroy legend without applying. Workspace settings can disable those automatic PR plans on public or untrusted repositories; that setting does not block a manual speculative plan from the CLI or the runs API. When the CLI is pointed at HCP Terraform, terraform plan -out=tfplan often stores a reference to the remote run rather than a full local binary—but the exam idea is the same: -out means apply-ready, no -out means speculative.

Planning modes and the flags 004 actually tests

Default is normal mode: change the remote system to match configuration. Two alternative modes exist on both terraform plan and terraform apply. They are mutually exclusive.

ModeFlagIntended outcome
Normal(default)Make infrastructure match configuration
Destroy-destroyDestroy all remote objects currently in state and leave an empty state. Same idea as terraform destroy
Refresh-only-refresh-onlyUpdate Terraform state and root-module outputs to match objects already changed outside Terraform. No config-driven creates, updates, or destroys

-refresh-only is the exam answer when an on-call engineer resized a disk in the console during an incident and you need Terraform's records to catch up without immediately enforcing the .tf files. You review that refresh-only plan, then apply it (terraform apply -refresh-only) if you want the state write. -refresh-only is not the same as -refresh=false. The first is "only refresh." The second is "skip refresh."

-replace=ADDRESS is a planning option, available since Terraform v0.15.2 and current on 1.12. It forces a replace even if the plan would have been an update or a no-op. Use it when a remote object is degraded and you want a new object from the same configuration. You cannot combine -replace with -destroy.

-target is last resort

-target=ADDRESS focuses planning on matching resource instances and on objects those instances depend on. You can target one instance (aws_instance.example[0]), a whole resource, or a module instance. Official docs: this capability is for exceptional circumstances such as recovering from mistakes or working around Terraform limitations. It is not recommended for routine operations, because targeted plans hide drift in everything you did not select. Prefer breaking a huge root module into smaller configurations over weekly -target applies.

Exam traps for objective 3d

  • Plan is not apply. Reading a plan does not create the VPC.
  • Plan may refresh and write state by default. "Plan never touches disk" is false.
  • + means create, not "optional."
  • -/+ and +/- are replaces, not in-place updates.
  • No -out means speculative, not apply-ready.
  • -refresh-only does not skip refresh. -refresh=false skips refresh.
  • -target is not how you "apply just networking" every Tuesday.

Official reference: terraform plan command.

Loading diagram...
How terraform plan decides what to print
Test Your Knowledge

When Terraform Associate (004) asks what terraform plan compares, which three inputs produce the execution plan?

A
B
C
D
Test Your Knowledge

In terraform plan output, what does the ~ symbol mean?

A
B
C
D
Test Your Knowledge

A teammate runs terraform plan with no extra flags and no -out file. What did they just create?

A
B
C
D