Stage 4 · Provision
Terraform State Operations
State File Structure
Resource addresses, instances, lineage, serials, sensitive values, and backend metadata — understanding the state file internals.
State File JSON Structure
The state file is JSON with a predictable structure. Understanding this structure helps with debugging, state manipulation, and building tooling around Terraform.
{
"version": 4,
"terraform_version": "1.7.0",
"serial": 12,
"lineage": "abc-123-def-456",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "aws_instance",
"name": "web",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"id": "i-0123456789abcdef0",
"ami": "ami-0c55b159cbfafe1f0",
"instance_type": "t3.micro"
}
}
]
}
]
}The version field is the state format version. terraform_version is the version that last wrote to the state. serial is incremented on each write. lineage uniquely identifies the state file. resources contains all managed resources.
Resource Addresses
// Simple resource
"aws_instance.web"
// Resource with for_each
"aws_instance.web[\"web-1\"]"
// Module resource
"module.vpc.aws_subnet.private[\"us-east-1a\"]"
// Data source
"data.aws_ami.amazon_linux"
// Provider
"provider[\"registry.terraform.io/hashicorp/aws\"]"Resource addresses uniquely identify every resource in the state file. The address format is module.name[instance_key]. Addresses are used in terraform state commands, output references, and lifecycle rules.
Instance Structure
{
"mode": "managed",
"type": "aws_subnet",
"name": "private",
"each": "map",
"instances": [
{
"index_key": "us-east-1a",
"schema_version": 0,
"attributes": {
"id": "subnet-abc123",
"availability_zone": "us-east-1a",
"cidr_block": "10.0.1.0/24"
}
},
{
"index_key": "us-east-1b",
"schema_version": 0,
"attributes": {
"id": "subnet-def456",
"availability_zone": "us-east-1b",
"cidr_block": "10.0.2.0/24"
}
}
]
}Resources using for_each have multiple instances. Each instance has an index_key that matches the for_each key. The attributes contain all resource state — IDs, computed values, and configuration.
Lineage and Serial
{
"version": 4,
"terraform_version": "1.7.0",
"serial": 47,
"lineage": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}lineage is a UUID that uniquely identifies a state file. It is generated once and never changes. serial is incremented every time the state is written. If serial does not match, Terraform refuses to update the state.
Sensitive Values
{
"attributes": {
"id": "sg-0123456789abcdef0",
"name": "web-sg",
"ingress": [
{
"from_port": 80,
"to_port": 80,
"cidr_blocks": ["0.0.0.0/0"]
}
]
},
"sensitive_values": {
"ingress": [
{
"cidr_blocks": [true]
}
]
}
}sensitive_values tracks which attributes contain sensitive data. The values are still stored in plaintext in the state file, but Terraform masks them in output. The sensitive_values field is separate from the actual values.
Backend Metadata
{
"backend": {
"type": "s3",
"config": {
"bucket": "my-terraform-state",
"key": "prod/vpc/terraform.tfstate",
"region": "us-east-1"
}
}
}The backend section records which backend stores the state. This metadata helps Terraform reconnect to the correct backend. It is also used for state migration when changing backends.
The state file is JSON and technically editable, but manual edits can corrupt it. Use terraform state commands for state manipulation. Manual edits break lineage tracking and can cause unexpected changes.
The state file stores all resource attributes, including computed values. This means database passwords, API keys, and certificates are in plaintext. Treat state files as sensitive data.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.