Terraform State File
Terraform’s state file (terraform.tfstate) is the source of truth that maps your configuration to real-world infrastructure. Without it, Terraform cannot determine what exists, what needs to change, or what to destroy. Understanding how it works is fundamental to operating Terraform safely.
What the State File Does
Terraform uses the state to:
- Track resource identity — which cloud resource corresponds to which block in your config
- Cache resource attributes — avoid fetching data from the provider API on every plan
- Detect drift — compare desired (config) vs actual (state) vs real (provider) state
- Manage dependencies — know the correct destroy order when resources depend on each other
State File Structure
{ "version": 4, "terraform_version": "1.9.5", "serial": 12, "lineage": "3b2b3b2b-...", "outputs": { "vpc_id": { "value": "vpc-0abc123def456789", "type": "string" } }, "resources": [ { "mode": "managed", "type": "aws_vpc", "name": "main", "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", "instances": [ { "schema_version": 1, "attributes": { "id": "vpc-0abc123def456789", "cidr_block": "10.0.0.0/16", "tags": { "Environment": "production", "Name": "main-vpc" } } } ] } ]}Key fields:
serial— incremented on every change; used to detect concurrent modificationslineage— unique ID for this state file; prevents mixing up states from different environmentsresources— the full list of all managed resources and their current attributes
State Commands
# List all resources in stateterraform state list
# Show details for a specific resourceterraform state show aws_vpc.mainterraform state show 'aws_instance.workers[0]'terraform state show 'module.app.aws_ecs_service.main'
# Move a resource to a new address (refactoring)terraform state mv aws_instance.app aws_instance.web
# Move a resource into a moduleterraform state mv aws_s3_bucket.logs module.storage.aws_s3_bucket.logs
# Remove a resource from state (stops managing it, doesn't destroy it)terraform state rm aws_instance.legacy
# Pull current remote state and print it locallyterraform state pull
# Push a local state to the remote backend (use with caution)terraform state push terraform.tfstateLocal State (Default)
By default, Terraform creates terraform.tfstate in the working directory:
my-infra/├── main.tf├── variables.tf├── terraform.tfstate ← Created after first apply├── terraform.tfstate.backup ← Previous state (auto-created)└── .terraform/Local state problems:
- Not shared between team members
- No locking — concurrent applies corrupt state
- Accidentally committed to git (contains secrets)
- Lost if the file is deleted
Why State Must Be Protected
State files often contain sensitive values:
"attributes": { "password": "actualDatabasePassword123!", "access_key": "AKIAIOSFODNN7EXAMPLE", "secret": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"}sensitive = true in your config only hides values from CLI output — the plaintext value still lives in state.
Protect state by:
- Using remote state with encryption (S3 + KMS, Terraform Cloud)
- Restricting IAM access to the state bucket
- Never committing
.tfstatefiles to git — add to.gitignore
*.tfstate*.tfstate.*.terraform/Remote State: The Production Standard
terraform { backend "s3" { bucket = "mycompany-terraform-state" key = "production/terraform.tfstate" region = "us-east-1" encrypt = true kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/mrk-..." dynamodb_table = "terraform-state-lock" }}Remote state provides:
- Shared access — all team members plan/apply against the same state
- State locking — prevents two people from running apply simultaneously
- Versioning — S3 bucket versioning lets you roll back a bad state
- Encryption — state at rest is encrypted with your KMS key
State File Versioning and Backup
Enable S3 versioning to protect against accidental state corruption:
resource "aws_s3_bucket_versioning" "state" { bucket = aws_s3_bucket.terraform_state.id
versioning_configuration { status = "Enabled" }}To restore a previous state version:
# List state versionsaws s3api list-object-versions \ --bucket mycompany-terraform-state \ --prefix "production/terraform.tfstate"
# Download a previous versionaws s3api get-object \ --bucket mycompany-terraform-state \ --key "production/terraform.tfstate" \ --version-id "abc123..." \ terraform.tfstate.restore
# Push the restored state back (with care)terraform state push terraform.tfstate.restore