Terraform's whole job is to make three pictures of the world agree. Almost every command, surprise, and bug is explained by which two pictures it is comparing.
What you want. Your .tf files — the HCL config you write.
What Terraform thinks exists. terraform.tfstate — its memory / bookkeeping.
What actually exists. The live resources in AWS right now.
terraform plan = refresh state from the real world, then diff your desired config against it, and report the actions needed to make the real world match. terraform apply = do those actions, then record the result back into state.
| Step | Command | What it actually does |
|---|---|---|
| Write | edit *.tf | You change the desired state. |
| Init | terraform init | Download providers, configure the backend (where state lives). Run once per new config / backend change. |
| Plan | terraform plan | Refresh → diff → show proposed actions. Changes nothing. Save with -out=tfplan. |
| Apply | terraform apply | Execute the plan in dependency order, then write results to state. |
| Destroy | terraform destroy | Plan + apply the removal of everything in state. |
Source: Core workflow · plan reference
| Symbol | Action | Means |
|---|---|---|
| + | create | In config, not in state → make it. |
| ~ | update in place | In both, but an attribute differs → modify it. |
| - | destroy | In state, not in config → remove it. |
| -/+ | replace | A changed attribute can't be updated live → destroy then recreate (look for "forces replacement"). |
| 0 to add | no-op | All three pictures already agree → nothing to do. |
.tf configuration — the target you declare.terraform.tfstate)plan/apply (skippable with -refresh=false).aws_s3_bucket). Tracked in state.init.