Skip to content
Infrastructure as Code iac state 4 min read

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 list and show are read-only and never modify the backend, so they are safe to run against production. Both behave identically in OpenTofu via tofu 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 moved and removed blocks in your .tf files over state mv / state rm where possible. They are reviewable in pull requests, version-controlled, and run as part of normal apply — 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

CommandModifies stateTouches real infraTypical use
state listNoNoList tracked resource addresses
state showNoNoInspect one resource’s attributes
state mvYesNoRename / re-parent without recreating
state rmYesNoStop managing a resource
state pullNoNoExport raw state (backup, inspection)
state pushYesNoOverwrite 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 list and state show freely; they are read-only and safe in any environment.
  • Prefer declarative moved and removed blocks over state mv / state rm so changes are reviewed and versioned.
  • Always run terraform state pull > backup.tfstate before 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 push for genuine disaster recovery — treat it as the break-glass option.
Last updated June 14, 2026
Was this helpful?