State Commands
Terraform tracks every managed resource in a state file, and most of the time you never touch it directly — plan and apply keep it in sync for you. But occasionally you need to look inside, rename a resource without destroying it, or detach something Terraform should no longer manage. The terraform state family of subcommands gives you precise, read and write access to that data. They are powerful and, in the case of the write operations, genuinely dangerous, so this page covers both how to use them and when not to.
Inspecting state
The two read-only commands — list and show — are completely safe and the ones you will reach for daily. terraform state list prints the resource addresses currently tracked in state, one per line.
terraform state list
Output:
aws_instance.web
aws_security_group.web
aws_s3_bucket.assets
module.network.aws_vpc.main
module.network.aws_subnet.public[0]
module.network.aws_subnet.public[1]
You can filter by passing a resource address or module path, which is invaluable in large configurations:
terraform state list module.network
terraform state list 'aws_subnet.public[0]'
To see the full attribute set Terraform has recorded for a single resource, use terraform state show. This reads from state, not the provider, so it reflects what Terraform believes — useful when debugging drift.
terraform state show aws_instance.web
Output:
# aws_instance.web:
resource "aws_instance" "web" {
ami = "ami-0c7217cdde317cfec"
arn = "arn:aws:ec2:us-east-1:123456789012:instance/i-0abc123def4567890"
associate_public_ip_address = true
availability_zone = "us-east-1a"
id = "i-0abc123def4567890"
instance_type = "t3.micro"
private_ip = "10.0.1.42"
public_ip = "54.210.18.7"
tags = {
"Name" = "web"
}
}
Tip:
terraform state listandshoware read-only and never modify the backend, so they are safe to run against production. Both behave identically in OpenTofu viatofu state list/tofu state show.
Modifying state
The write commands change what Terraform tracks without changing real infrastructure. terraform state mv moves an item to a new address — use it after refactoring, such as renaming a resource or pulling resources into a module. Without it, a rename looks like a destroy-and-create to Terraform.
terraform state mv aws_instance.web aws_instance.frontend
terraform state mv 'aws_instance.web' 'module.compute.aws_instance.web'
Output:
Move "aws_instance.web" to "module.compute.aws_instance.web"
Successfully moved 1 object(s).
terraform state rm removes a resource from state so Terraform stops managing it — the real infrastructure is left untouched. This is how you hand a resource off to another configuration or stop tracking something you imported by mistake.
terraform state rm aws_s3_bucket.assets
Output:
Removed aws_s3_bucket.assets
Successfully removed 1 resource instance(s).
Since Terraform 1.7, prefer declarative
movedandremovedblocks in your.tffiles overstate mv/state rmwhere possible. They are reviewable in pull requests, version-controlled, and run as part of normalapply— far safer than ad-hoc CLI surgery.
Reading and writing the raw state
terraform state pull downloads the current state from the configured backend and prints it as JSON to stdout. It is handy for inspection, backups, or piping into jq.
terraform state pull > backup.tfstate
terraform state pull | jq '.resources[].type'
terraform state push does the reverse — it uploads a local state file to the backend, overwriting what is there. This is the most dangerous command in the suite and should be considered a last resort, typically only for disaster recovery.
terraform state push backup.tfstate
Command reference
| Command | Modifies state | Touches real infra | Typical use |
|---|---|---|---|
state list | No | No | List tracked resource addresses |
state show | No | No | Inspect one resource’s attributes |
state mv | Yes | No | Rename / re-parent without recreating |
state rm | Yes | No | Stop managing a resource |
state pull | No | No | Export raw state (backup, inspection) |
state push | Yes | No | Overwrite backend state (recovery only) |
The danger of manual state surgery
State is the source of truth that maps your configuration to real-world resources. If it gets out of sync — an address Terraform thinks exists but doesn’t, or vice versa — the next apply can delete or duplicate live infrastructure. The write commands bypass the normal plan/review cycle, so a typo in an address is applied immediately with no preview.
Every write operation increments the state serial; with a remote backend and state locking, a lock is acquired for the duration. Always take a backup with state pull first, and never run write commands while a teammate might be applying.
Best Practices
- Use
state listandstate showfreely; they are read-only and safe in any environment. - Prefer declarative
movedandremovedblocks overstate mv/state rmso changes are reviewed and versioned. - Always run
terraform state pull > backup.tfstatebefore any write command. - Never edit pulled state JSON by hand and push it back unless you are recovering from corruption and understand the schema.
- Ensure state locking is enabled so concurrent writes can’t corrupt the backend.
- Quote resource addresses that contain index brackets (e.g.
'aws_subnet.public[0]') to protect them from your shell. - Reserve
state pushfor genuine disaster recovery — treat it as the break-glass option.