Managing State, Backends, and State Operations
What state is for
State maps configuration addresses to real objects and stores attribute values, including secrets in plaintext. Candidates must know the CLI surface: terraform state list, show, mv, rm, pull, push, replace-provider, plus -refresh=false, -target, and -replace on plan and apply.
Backends
A backend block must be static: it cannot reference variables, locals, or resources. Use partial configuration and supply the rest with -backend-config=prod.hcl. terraform init -migrate-state copies existing state into the new backend; -reconfigure ignores it. S3 locking historically uses a DynamoDB table with a LockID attribute, while gcs, azurerm, and HCP Terraform lock natively. -lock-timeout waits for a lock; terraform force-unlock <ID> releases a stale one and should only be used when you are certain no run is active.
Safe refactors
Prefer declarative blocks in shared code: moved for renames, import for adoption, removed with lifecycle { destroy = false } for disowning. terraform state rm only forgets an object; it does not destroy the real resource, and if the address still exists in configuration Terraform plans a create. Multiple workspaces share one backend configuration but each holds separate state; the local backend stores them under terraform.tfstate.d/.
Trap
terraform import can add a single resource to state per invocation, requires the address to already exist in configuration, and refreshes nothing else - the modern import block with -generate-config-out is preferable for bulk adoption.
Rule of thumb
If a state operation changes addresses, express it in configuration so reviewers and CI see it; reserve terraform state push and force-unlock for emergencies.