The Terraform State File
Terraform state is a JSON document, typically named terraform.tfstate, that records the mapping between the resources declared in your configuration and the real objects that exist in your cloud provider. Without state, Terraform would have no way of knowing that the aws_instance.web block in your .tf files corresponds to a specific EC2 instance with a specific ID somewhere in AWS. Every time you run terraform plan or terraform apply, Terraform reads this file, compares it against both your configuration and the live infrastructure (via provider API calls), and computes the difference needed to reconcile the three. State also stores resource metadata, dependency ordering, and sometimes sensitive attribute values, which is why how and where you store it matters enormously in production.
Cricket analogy: The state file is like a scorer's master ledger mapping each named player in the team sheet to their actual live stats on the field — without that ledger, the scoreboard operator wouldn't know that 'Kohli' in the lineup corresponds to the specific batter currently facing ball three.
What Lives Inside State
A state file contains a top-level schema version, a Terraform version marker, a unique lineage identifier, a serial number that increments on every write, and a resources array. Each resource entry records its type, name, provider configuration, and one or more instances (multiple instances occur with count or for_each) holding the full set of attributes returned by the provider after creation — not just the ones you set in your configuration. This is why a plan can detect drift: it re-reads live attributes and compares them to what state recorded last time.
Cricket analogy: A state file's serial number incrementing on every write is like a scorebook edition number that ticks up after every innings update, and just as the scorebook records every actual run scored (not just predicted ones), Terraform's resource entries store every real attribute a provider returns, enabling drift detection.
{
"version": 4,
"terraform_version": "1.8.2",
"serial": 14,
"lineage": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"resources": [
{
"mode": "managed",
"type": "aws_instance",
"name": "web",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"id": "i-0abcd1234efgh5678",
"instance_type": "t3.micro",
"ami": "ami-0c55b159cbfafe1f0",
"private_ip": "10.0.1.14"
}
}
]
}
]
}Why Local State Fails in Teams
By default Terraform writes state to a local file in the working directory. This works fine solo, but breaks down the moment more than one person or one CI job needs to run Terraform against the same infrastructure: two engineers with two divergent local copies of terraform.tfstate can each apply changes that overwrite the other's understanding of reality, leading to duplicated resources, orphaned resources Terraform no longer tracks, or an apply that destroys something a teammate just created. This is the core motivation for remote state backends, which centralize the file and (when the backend supports it) add locking so only one apply can proceed at a time.
Cricket analogy: Two engineers with divergent local state files is like two team analysts each keeping their own private scorebook and both submitting conflicting final scores to the ICC — a shared, centrally locked scorebook (remote backend with locking) prevents that conflict entirely.
Never hand-edit terraform.tfstate directly. A single malformed attribute or a stale serial number can desynchronize Terraform's understanding of your infrastructure, and provider-specific IDs are easy to get subtly wrong. Use terraform state commands (state show, state mv, state rm) or terraform import instead — they validate and update state safely.
Think of state as Terraform's memory, not its plan. Your .tf files describe desired intent; state describes what Terraform believes actually exists right now. terraform plan is essentially a three-way diff between configuration (desired), state (remembered), and the live API (actual).
Because state stores every attribute a provider returns, it frequently contains secrets in plaintext — database passwords generated by a resource, private keys, connection strings. Terraform marks some attributes as sensitive so they're redacted from CLI output, but the underlying state file itself is not encrypted by Terraform. Anyone with read access to the state file can potentially read those values. This is a strong argument for backends that support encryption at rest (such as an S3 bucket with SSE-KMS) and for tightly scoped IAM access to the state storage location.
Cricket analogy: A state file storing a generated database password in plaintext is like a team's private medical file sitting unencrypted in a shared cabinet — the public injury report may redact details, but anyone with cabinet access can still read the raw file, arguing for a locked, access-logged cabinet.
- State maps your Terraform configuration to real resource IDs and their full live attribute sets.
- The file has a lineage (unique per state) and a serial (incrementing version counter) used to detect conflicting writes.
- Local state is unsafe for team use; it lacks locking and creates conflicting sources of truth.
- State frequently contains sensitive values in plaintext and should be stored on an encrypted, access-controlled backend.
- Never hand-edit the state file; use terraform state subcommands or terraform import to modify it safely.
- terraform plan performs a three-way comparison: configuration, state, and live infrastructure.
Practice what you learned
1. What is the primary purpose of the Terraform state file?
2. Why is local state considered risky for team-based Terraform usage?
3. Which field in the state file increments every time state is written, helping detect conflicting updates?
4. What is the recommended way to fix a mistake in the state file, such as a resource tracked under the wrong address?
5. Why might a Terraform state file be considered a security concern?
Was this page helpful?
You May Also Like
Remote State Backends
Remote backends store Terraform's state file outside the local disk, enabling team collaboration, durability, and integration with locking mechanisms.
State Locking
State locking prevents two Terraform operations from writing to the same state file simultaneously, protecting against corruption and lost updates.
Importing Existing Infrastructure
Importing lets Terraform take ownership of resources that already exist in a cloud account, bringing them under state management without recreating them.
Refactoring State with moved Blocks
The moved block lets Terraform track renamed or relocated resources declaratively, avoiding destructive replace plans when refactoring configuration.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics