TutorialsTerraformCommands › terraform plan

Terraform Commands · Command 4 of 8

terraform plan

Compares the configuration against the real state of Azure, and shows exactly what it would create, change, or destroy — without touching anything yet. This is Terraform's preview step, and the single most important safety net in the whole workflow.

Codey the Sr Developer, standing with a pointer, ready to walk through the guide
Touches real Azure resources → reads only Needs Azure login → yes Can be saved → -out <file>

What this command does in detail

Three steps, in orderbreakdown
  1. Refresh. Reads the current state, and checks the real Azure resources it describes, to see if anything has drifted (for example, someone editing a resource by hand in the Portal).
  2. Diff. Compares the refreshed picture of reality against what the .tf files say should exist.
  3. Report. Prints a human-readable list of every resource that would be created (+), changed in place (~), destroyed (-), or replaced (destroy then re-create, shown as -/+).

Nothing is created, changed, or destroyed by plan itself — it only computes and displays what would happen. Nothing is real until terraform apply runs.

Where to run it from working directory

terraform -chdir=terraform plan

Same working-directory pattern as every other command: run it against the folder holding the configuration you want to preview, after that folder has already been initialized.

Precondition what must already be true

  • terraform init has completed, with a real backend connection (not -backend=false) — plan needs to read the current state.
  • You're authenticated to Azure (az login locally, or the OIDC identity in CI) with at least read access to the resources involved, so the refresh step can check their current status.
  • Every required variable without a default — subscription_id, db_password, client_ip — has a value supplied, whether from terraform.tfvars, TF_VAR_* environment variables, or -var flags. See variables.tf for the full list.

Postcondition what becomes true after it succeeds

  • No Azure resources are created, changed, or destroyed — plan only reads.
  • State is not permanently updated by a plain plan (the refresh happens in memory for the comparison, but nothing is written back to the stored state file).
  • If -out <file> was used, a binary plan file is written to disk. Treat it as sensitive — it can contain values like the database password in plain form — and this project's workflow deletes it at the end of every run.
  • With -detailed-exitcode, the exit code itself communicates the result: 0 = no changes, 1 = error, 2 = changes are pending.

How this project uses it real examples

Building the plan the workflow will apply.github/workflows/terraform.yml
args=()
if [[ "$OPERATION" == destroy ]]; then args+=(-destroy); fi
terraform -chdir=terraform plan -input=false -lock-timeout=5m "${args[@]}" -out workflow.tfplan

The workflow always runs plan first, saving its result to workflow.tfplan. The -destroy flag turns this into a destroy plan instead of a normal one — this project doesn't call a separate terraform destroy command at all; it reuses this exact same plan-then-apply pattern for both directions, so both operations get the same review-before-apply treatment. See terraform destroy.

Locally, for a quick lookterraform/README.md
terraform -chdir=terraform plan

Without -out, this is a read-only preview with nothing saved — useful for a quick sanity check, but not something a later apply can replay exactly.

Common errors and how to fix them

What you seeLikely causeHow to fix it
“No value for required variable”A required variable (like db_password or client_ip) wasn't supplied anywhere.Set it via TF_VAR_<name>, terraform.tfvars, or -var. See variables.tf.
Authentication / authorization error during refreshNot logged in to Azure, logged in as the wrong account, or missing read permission on an existing resource.Run az login (or check the OIDC secrets in CI); confirm the identity has at least read access to the resource group.
“Error acquiring the state lock”Another Terraform run (a teammate, or an overlapping CI job) is already using this state.Wait for the other run to finish. This project's workflow also serializes runs with a concurrency group so overlapping jobs queue instead of colliding.
Unexpected changes to resources you didn't touchSomeone edited a resource manually in the Azure Portal, and Terraform's refresh detected the drift.Decide whether to accept Terraform's plan to fix the drift, or update the .tf files to match the manual change — but avoid manual Portal edits on Terraform-managed resources going forward.
Codey giving a thumbs up

The preview looks right — next, making it real.