This chapter follows Terraform modules. State is why declarative IaC works in practice: without a durable record of what Terraform already created, the next plan would not know whether to create, update, or leave resources alone.
What state contains
State is JSON (often pretty-printed locally) listing resources Terraform manages: type, name, provider-specific id, and attributes last read from the API. It also stores outputs, module tree structure, and metadata such as serial and lineage for concurrent safety.
Terraform compares configuration plus state to provider-reported reality during plan (refresh). State is not a general-purpose inventory of your cloud account—only addresses declared in configuration (or imported into state) appear.
Local state versus GCS backend
By default, state sits in terraform.tfstate in the working directory. Solo experiments can use local state; teams should use a remote backend.
Google Cloud Storage is a common choice:
terraform { backend "gcs" { bucket = "my-org-terraform-state" prefix = "envs/production/network" }}Create the bucket once (often in a bootstrap stack with versioning enabled). GCS backends support state locking through object metadata so two applies do not corrupt state simultaneously.
After configuring the backend, run terraform init and migrate when prompted. From then on, CLI commands read and write state in the bucket; local copies may exist as cache but the bucket is authoritative.
Inspecting and editing state
Read-only inspection:
terraform state listterraform state show google_storage_bucket.tutorialterraform showterraform outputMutating state addresses is for exceptional maintenance, not daily workflow:
terraform state mv google_storage_bucket.old google_storage_bucket.newterraform state rm google_compute_instance.orphanPrefer moved blocks in configuration for renames when possible so the change is reviewable in git. Use state rm when Terraform should stop managing a resource without destroying it in the cloud (the resource becomes untracked).
Import and lifecycle blocks
Import brings existing cloud resources under Terraform management. Modern workflows use import blocks in configuration (Terraform 1.5+):
import { to = google_storage_bucket.existing id = "my-existing-bucket-name"}
resource "google_storage_bucket" "existing" { name = "my-existing-bucket-name" location = "EU"}Run terraform plan to preview the import, then apply. Legacy terraform import CLI still works for ad hoc operations.
removed blocks (Terraform 1.7+) describe resources leaving configuration—optionally destroying them or leaving them in the cloud while dropping them from state.
moved blocks retarget state when refactoring, as described in the modules chapter.
Drift and refresh
Drift is when real infrastructure differs from state and configuration—someone changed a firewall in the console, or an autoscaler resized a disk. Plan refresh reads current API attributes into state before proposing changes.
terraform plan -refresh-onlyApply refresh-only when you want state to match reality without changing resources. To force replacement of a resource that cannot update in place:
terraform plan -replace=google_compute_instance.vmReview replace plans carefully; replacement often means destroy-then-create.
Sensitive values and secrets
Mark outputs and variables sensitive = true to redact CLI output. Sensitive values may still be stored in state in plaintext unless you use external secret stores and references. Restrict IAM on the state bucket to Terraform roles and break-glass admins.
Some teams encrypt state at rest with bucket encryption and CMEK; OpenTofu adds optional state encryption features—see Terraform vs OpenTofu and alternatives.
Workspaces and separate directories
Workspaces (terraform workspace select) maintain multiple state instances inside one backend prefix layout. They suit short-lived environment splits when configuration is identical.
Many production teams prefer separate directories (envs/dev, envs/prod) with distinct backend prefixes or buckets instead of workspaces—clearer code review boundaries and IAM separation. Workspaces are not a substitute for different variable files when configuration itself diverges.
Remote state versus data sources
terraform_remote_state data source reads outputs from another Terraform stack’s state—useful when a network stack exports subnet ids consumed by an application stack.
Provider data sources (data "google_compute_network") query APIs directly and do not require the other stack to use Terraform.
Choose remote state when the upstream stack is Terraform-managed and publishes stable outputs; choose data sources when ownership is outside Terraform or you only need live API data.
Next: Terraform vs OpenTofu and alternatives compares Terraform with other IaC and configuration tools.
Check your understanding
Question 1 of 8
0 of 8 checked