What is the Terraform state file, and why is it necessary?
Quick Answer
`terraform.tfstate` is a JSON file that maps every resource block in your configuration to the real-world object it created (e.g., an AWS instance ID) and caches that object's attributes. Terraform needs it because most cloud APIs have no way to ask "which resources belong to configuration X" — state is Terraform's source of truth for what it manages, letting `plan` diff desired vs. actual without re-querying every attribute from every provider on every run, and letting Terraform know what to destroy when a resource block is removed.
Detailed Answer
terraform.tfstate is arguably the most important — and most misunderstood — file in a Terraform project. It's a JSON document that Terraform generates and maintains automatically; you almost never hand-edit it.
What's in it
{
"resources": [
{
"type": "aws_instance",
"name": "web",
"instances": [
{
"attributes": {
"id": "i-0abcdef1234567890",
"ami": "ami-0abcdef1234567890",
"instance_type": "t3.micro",
"private_ip": "10.0.1.15"
}
}
]
}
]
}
For every resource block in your configuration, state records the real-world object it maps to (here, an actual EC2 instance ID) along with a cached copy of every attribute the provider returned when it was last read.
Why Terraform needs it
Most cloud APIs have no concept of "which resources belong to Terraform configuration X" — an AWS account just has instances, buckets, and security groups with no inherent grouping by IaC tool. State is what lets Terraform:
- Know what it owns. Without state, Terraform couldn't tell "this VPC is mine to manage" from "this VPC belongs to something else."
- Compute diffs without re-deriving everything from scratch.
plancompares configuration against the cached attributes in state (refreshing from the real API as needed) rather than needing to reverse-engineer intent from the live infrastructure alone. - Know what to destroy. If you delete a
resourceblock from your.tffiles, Terraform only knows to destroy the corresponding real object because state still has a record of it — the configuration itself no longer mentions it at all. - Map configuration addresses to real IDs, which is also what makes commands like
terraform state mv,terraform import, and-targetpossible.
The takeaway
State is Terraform's memory of "what I created and what it currently looks like." Losing it (or letting it drift out of sync via manual changes) is one of the most common sources of Terraform pain — which is exactly why remote state, locking, and drift detection (covered in later questions) matter so much in real teams.