Master Terraform State Management for the 004 Exam

State management is Domain 6 on the HashiCorp Certified Terraform Associate 004 exam, which tests Terraform version 1.12. The official exam objectives list four sub-objectives under this domain: describe the local backend, describe state locking, configure remote state using the backend block, and manage resource drift and Terraform state. State questions on the 004 exam are scenario-driven, so memorizing commands without understanding how state connects to real infrastructure will cost you points. This guide breaks down every state objective so that each scenario becomes predictable rather than a guessing game. For broader coverage, pair this deep dive with our Terraform Associate 004 complete study guide, then test your recall with the 254-question Terraform Associate 004 practice test on the site.

Why Terraform State Exists

State is a necessary requirement for Terraform to function, not an optional layer you can work around. The state file is Terraform’s database for mapping configuration to real-world resources. When you declare a resource such as aws_instance "web", the state records the binding between that configuration address and the physical object so that every subsequent run knows exactly what to manage. Early prototypes of Terraform had no state files at all and relied on cloud-provider tags for this mapping, but that approach failed because not all resources support tags and not all cloud providers expose tagging capabilities. Terraform therefore adopted its own state structure to guarantee a consistent mapping regardless of provider limitations.

Beyond mapping, state stores critical metadata. When you delete a resource from your configuration, Terraform can no longer read the dependency order from the configuration alone, because the configuration no longer contains the resource. Instead, it falls back on the copy of dependencies retained in the state file to determine the correct destruction sequence. State also improves performance for large infrastructures by caching resource attributes locally, letting Terraform compare the previous state against a new plan without querying every provider API on every run. The official documentation notes that Terraform expects that each remote object is bound to only one resource instance, and importing objects created outside Terraform requires care to maintain that mapping.

State Locking Prevents Corruption

Locking is the mechanism that keeps concurrent writers from corrupting your state file. If supported by your backend, Terraform locks the state for all operations that could write state, preventing others from acquiring the lock and potentially corrupting your state. This locking happens automatically on all operations that could write state. You do not see any message that it happens, and if state locking fails, Terraform does not continue. You can disable state locking for most commands with the -lock=false flag, but HashiCorp explicitly recommends against it because it removes the only safeguard against simultaneous writes.

Not all backends support locking, so the documentation for each backend includes details on whether the feature is available. When an automatic unlock fails, Terraform provides the terraform force-unlock command. To protect you, the force-unlock command requires a unique lock ID that Terraform outputs if unlocking fails. This lock ID acts as a nonce, ensuring that locks and unlocks target the correct lock rather than someone else’s. Force unlock should only be used to unlock your own lock in the situation where automatic unlocking failed, because unlocking when another user holds the lock could cause multiple writers.

Remote Backends for Team Collaboration

Backends are responsible for storing state and providing an API for state locking. The default local backend writes state to a JSON file on disk in the working directory, which is fine for learning but dangerous for teams because a single disk failure or divergent copy can corrupt state and lose infrastructure bindings. When you switch to a non-local backend, Terraform will not persist the state anywhere on disk except in the case of a non-recoverable error where writing the state to the backend failed. This behavior is a major benefit for backends: if sensitive values are stored in your state, a remote backend lets you use Terraform without that state ever being persisted to a local disk.

The S3 backend stores state as an object at a key path you define within a specified bucket and supports state locking through the use_lockfile argument set to true. DynamoDB-based locking is deprecated and will be removed in a future minor version, though you can configure the S3 and DynamoDB arguments simultaneously to support migration from older Terraform versions. HashiCorp recommends enabling Bucket Versioning on the S3 bucket to allow for state recovery in the case of accidental deletions and human error. If a backend write fails, Terraform writes the state locally to prevent data loss, and you must manually push it back with terraform state push once the error is resolved. You can pull the current remote state to stdout with terraform state pull for inspection or backup.

State Subcommands You Must Know

The terraform state command exposes a family of subcommands for advanced state management, and every subcommand works with remote state just as if it were local. Reads and writes may take longer because each operation does a full network roundtrip. All terraform state subcommands that modify the state write backup files, and this behavior cannot be disabled due to the sensitivity of the state file. Read-only subcommands like list do not write backups since they are not modifying the state.

terraform state list prints every resource in the state, sorted according to module depth order followed alphabetically, and accepts address patterns or an -id flag to filter results. terraform state show displays the attributes of a single resource at a given address. Its output is intended for human consumption, not programmatic consumption. For programmatic extraction, you should use terraform show -json instead.

terraform state mv changes bindings in Terraform state so that existing remote objects bind to new resource instance addresses. The most common use cases are renaming a resource block in your configuration or moving a resource into a child module, in both cases with the intention of retaining the existing object. terraform state rm removes the binding to an existing remote object without first destroying it. The remote object continues to exist but is no longer managed by Terraform. You can achieve the same result declaratively with removed blocks, which let you review removals as part of your normal plan and apply workflow rather than running an imperative command.

SubcommandPurposeModifies state
state listList resources in state, filterable by address or IDNo
state showDisplay attributes of a single resourceNo
state mvRebind an object to a new address without destroy or createYes
state rmForget a resource without destroying the remote objectYes
state pullOutput remote state to stdoutNo
state pushOverwrite remote state from a local fileYes

Detecting and Fixing Drift

The Terraform state file is a record of all resources Terraform manages, and you should not make manual changes to resources controlled by Terraform because the state file will drift from the real infrastructure. If your state and configuration do not match your infrastructure, Terraform will attempt to reconcile the difference, which may unintentionally destroy or recreate resources. HashiCorp recommends running terraform plan -refresh-only to determine the drift between your current state file and actual infrastructure. If you ran terraform plan or terraform apply without the -refresh-only flag, Terraform would attempt to revert your manual changes rather than simply reporting them.

Drift detection is available in HCP Terraform Standard Edition and surfaces out-of-band changes across workspaces. When you discover drift on the exam, you have two reconciliation paths: update your configuration to match reality and re-apply, or import the drifted resource into state so Terraform adopts its current attributes. The -refresh-only planning mode updates state to reflect reality without proposing changes to your configuration, whereas a standard plan proposes changes to bring infrastructure back in line with configuration. Distinguishing which mode a scenario describes is the key to answering drift questions correctly.

Workspaces and State Isolation

Each Terraform configuration has an associated backend that defines how Terraform executes operations and where it stores persistent data like state. The persistent data belongs to a workspace, and the backend initially has only one workspace called default that you cannot delete. Some backends support multiple named workspaces, including AzureRM, Consul, GCS, Kubernetes, Local, Postgres, and S3, allowing multiple states to be associated with a single configuration without configuring a new backend or changing credentials. When you run terraform plan in a new workspace, Terraform does not access resources in other workspaces. Those resources still physically exist, but you must switch workspaces to manage them.

HashiCorp warns that workspaces are not appropriate for system decomposition or deployments requiring separate credentials and access controls. For environments that need isolation at the access-control level, you should use separate configurations or separate HCP Terraform workspaces and projects rather than CLI workspaces. Within configuration, you can reference the current workspace name using the ${terraform.workspace} interpolation sequence to vary behavior, such as provisioning smaller instance counts in non-default workspaces.

Importing Resources With Blocks

The import block lets you bring existing infrastructure under Terraform management declaratively rather than imperatively. Importing unmanaged resources to your workspace requires an import block that specifies the unique infrastructure resource ID to import and declares an address for the imported resource in state. You must also create a destination resource block that matches the address declared in the import block. Alternatively, you can write only the import block and run terraform plan with the -generate-config-out flag to have Terraform generate the resource blocks for you.

For large-scale adoption, you can define queries as HCL to discover and import sets of unmanaged resources in bulk, then apply the import configuration. Terraform uniquely identifies resources according to either the ID assigned by the cloud provider or a collection of specific attributes defined by the provider. To reference a resource identity in Terraform configuration, you can use either the id or identity attribute. The exam expects you to distinguish the older imperative terraform import CLI command, which writes to state immediately without generating configuration, from the newer import block workflow, which is plan-driven and auditable.

Sources

Scroll to Top