linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Knowledge base
  • Cheat sheet
  • Capstone
  • Interview prep
home/terraform/kb/Terraform basics/tf-plan

kb/core ── Terraform basics ── beginner

terraform plan: see what Terraform is about to do

plan is a dry run: Terraform reads your HCL, reads the state, and shows the diff between them. It changes nothing in the cloud. This is your main tool for not breaking prod by mistake.

view as markdown

Why you need plan

terraform plan is a fitting. You write HCL: "I want one bucket with tags." Terraform looks at what already exists (recorded in the state file) and shows the difference: "one new bucket needs to be created, nothing to change, nothing to destroy."

The key point: plan changes nothing. It does not call AWS with a "create" command. It only looks and compares. This is a safe command, and you can run it as often as you like.

What plan shows

Say you wrote this:

hcl
resource "aws_s3_bucket" "demo" {
  bucket = "my-bucket-12345"
  tags = {
    Owner = "student"
  }
}

And you ran terraform plan. The output looks roughly like this:

Terraform will perform the following actions:
  # aws_s3_bucket.demo will be created
  + resource "aws_s3_bucket" "demo" {
      + bucket = "my-bucket-12345"
      + id     = (known after apply)
      + tags   = {
          + "Owner" = "student"
        }
    }
Plan: 1 to add, 0 to change, 0 to destroy.

Line by line:

  • + resource ... means this resource will be created. The + sign means add.
  • + bucket = "my-bucket-12345" is the value Terraform will send to the API.
  • + id = (known after apply) is an attribute that appears only after creation; you do not know it yet.
  • Plan: 1 to add, 0 to change, 0 to destroy is the summary. This is the most important line.

Signs in the diff (memorize these)

SignWhat it means
+Create a new resource or attribute
-Destroy a resource or attribute
~Change a value in place (no recreation needed)
-/+Destroy and recreate (force replacement)

The most dangerous one is -/+. It means "this resource has to be recreated from scratch." If it is a database, the data is gone. If it is an EC2 instance, you lose the IP address. Always read the -/+ lines carefully.

Exit code: 0, 1, 2

By default terraform plan always returns exit code 0, even when there are changes. This is convenient for CI: the command does not fail.

But run it with the -detailed-exitcode flag and the logic changes:

  • 0 means no changes, state == HCL. "No changes".
  • 1 means an error (a syntax problem, the provider is not responding, the token is invalid).
  • 2 means there is a plan and changes are required.
bash
terraform plan -detailed-exitcode
echo $?   # 0, 1, or 2

This is essential in scripts: "if there is drift, send me an alert."

Saving the plan to a file

You can save a plan and later apply exactly that plan, with no recomputation:

bash
terraform plan -out=tfplan.bin    # saved
terraform apply tfplan.bin        # apply this exact plan

Why people do this:

  • In CI: a reviewer sees the plan in a PR, merges it, and apply runs exactly what was shown. Between the review and the apply no code can sneak in.
  • tfplan.bin may contain secrets (if they are in the state). Do not commit it to git.

Pitfalls

  • "No changes" is the invariant you want to reach after apply. If a second plan right after apply still shows changes, you have drift (something changed outside Terraform), or your HCL does not match reality. That is a bad sign.

  • plan reads the state, not real AWS. If someone deleted the bucket by hand in the console, plan will not notice until you run terraform refresh or terraform plan -refresh=true (the default, though some people turn it off). See tf-state.

  • plan can take a while. On large projects (thousands of resources) plan can run for minutes. Terraform queries the cloud about each one. That is normal. If it really hurts, use -target=resource.name for a narrow plan.

  • plan does not predict everything. Some attributes are computed on the cloud side. plan shows (known after apply), and that is fine. It is not a "secret"; the value simply appears after creation.

  • Use -target rarely. It is a "crutch for unexpected situations." Reaching for -target regularly is a sign that your HCL is structured wrong.

§ команды

bash
terraform plan

Basic run: show the diff between HCL and state.

bash
terraform plan -detailed-exitcode

Exit 0: no changes; 1: error; 2: there are changes. For CI scripts.

bash
terraform plan -out=tfplan.bin

Save the plan to a file so apply runs exactly that plan.

bash
terraform plan -no-color

No ANSI colors: for CI logs or comparing diffs.

bash
terraform plan -target=aws_s3_bucket.demo

Narrow plan for a single resource. Use rarely.

§ см. также

  • tf-initterraform init: the first command in any projectterraform init downloads the provider plugins (AWS, GCP, and so on), creates a lockfile that pins their versions, and prepares the working directory. Without it, neither plan nor apply will run.
  • tf-applyterraform apply: apply a plan to a real cloudapply takes the result of plan and actually calls the cloud API: it creates, changes, and deletes resources. After apply, the state is updated. This is the command that turns money into infrastructure.
  • tf-destroyterraform destroy: tear down everything you createddestroy removes every resource described in HCL and recorded in state. It is apply with a minus sign in front of everything. It is essential for learning tasks and ephemeral environments. In production it is a last resort.
  • tf-stateState: Terraform's memory of what it createdState is the JSON file `terraform.tfstate` where Terraform records what it created in the cloud. Without it, Terraform would have no way to tell which bucket is "its own" and which belongs to something else. The file holds resource IDs, all attributes, and often secrets. It is the most sensitive part of any project.
  • tf-plan-diffHow to read a terraform plan: symbols, formatting, trapsA plan shows a diff: `+` create, `~` update in place, `-/+` replace, `-` destroy, `<=` data read. The summary line at the bottom reads `Plan: X to add, Y to change, Z to destroy`. The rule that matters: a second plan after apply should be clean ("No changes").
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies