TerraformX · State Operations: Read, Move, Remove, ImportProduction Terraform
state rm: Removing From State, Not From Reality
What you'll learn
- Use terraform state rm to detach a resource from state without destroying it
- Distinguish state rm from terraform destroy (state vs reality)
- Choose between state rm and import when an address needs to be re-adopted
- Apply the backup-before-rm discipline and verify the plan afterwards
Prerequisites
None — start here.
Verified against Terraform CLI 1.9.x · OpenTofu 1.7.x · HCL 2.0 · bpg/proxmox provider 0.66+ · hashicorp/local provider 2.5+ · hashicorp/null provider 3.2+ · hashicorp/random provider 3.6+ · hashicorp/http provider 3.4+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-13
There are two operations that remove a resource from production:
terraform destroy (the real-world resource is deleted) and
terraform state rm (only the state entry is deleted; the
real-world resource keeps running). They are not interchangeable.
state rm is the surgical tool for handing a resource over to a
different management system, deleting a configuration block that
no longer matches reality, or recovering from a wrong import. It
is also one of the easiest commands to misuse; one mistyped
address and a database is unmanaged.
What state rm does, and does not
terraform state rm <address> removes the state entry for one
address. The real-world object is unaffected. The next plan sees
the configuration but no state, and proposes to create a new
real-world object — not destroy the existing one.
Before state rm:
Configuration: aws_instance.web
State: aws_instance.web → i-0abc...
Real world: i-0abc... (running)
After state rm:
Configuration: aws_instance.web
State: (empty)
Real world: i-0abc... (still running, now unmanaged)
Next plan:
Configuration: aws_instance.web
State: (empty)
Real world: i-0abc... (still running)
Plan: create aws_instance.web (a NEW instance)
This is the critical behaviour: state rm does not destroy.
The real-world resource keeps running until you destroy it
explicitly or import it back under a different address.
When state rm is the right tool
- Hand-off to a different system. A resource is being moved
from Terraform management to a different system (Ansible, a
manual runbook, another IaC tool).
state rmdetaches it cleanly. - Configuration no longer describes the resource. A refactor
removed the resource block from configuration, but the
real-world object should remain. Without
state rm, the next apply would destroy it. - Wrong import cleanup. A resource was imported under the
wrong address.
state rmremoves the wrong address; the correct address is then imported. - Recover from a half-failed apply. A resource was created in
the real world but the state entry was lost.
state rmcleans up the partial state and the real-world resource is re-imported under the correct address.
When state rm is the wrong tool
- The resource should not exist anymore. Use
terraform destroy(or remove the resource block and apply). - The address is wrong but the resource should still be
managed. Use
state mvor amovedblock. - You want to “clean up” state because it is messy.
state rmhides the mess; it does not fix it. Investigate first.
The procedure
# 1. Pull state to a backup file
terraform state pull > /tmp/state-before-rm.json
# 2. Confirm the address exists in state
terraform state list | grep 'aws_instance.web'
# aws_instance.web
# 3. Confirm the address exists in configuration (this is the safety check)
grep -A 1 '^resource "aws_instance" "web"' main.tf
# 4. If the resource block should also be removed, do that FIRST in a separate commit
# 5. Run the rm
terraform state rm -backup-file=/tmp/rm-backup.tfstate 'aws_instance.web'
# 6. Confirm the rm with state list
terraform state list | grep 'aws_instance.web'
# (empty)
# 7. Plan; expect the right next step
terraform plan
# If the configuration block was removed: no changes.
# If the configuration block remains: a new resource will be created.
The -backup-file flag is the recovery artifact. The pre-rm state
is written to the file before the mutation, so a wrong rm can be
undone.
Bulk operations
For an entire module:
terraform state rm -backup-file=/tmp/rm-backup.tfstate 'module.network'
This removes every resource in module.network from state. The
real-world resources keep running. The next plan will propose to
create them all again.
This is dangerous. The discipline:
- Confirm every resource in the module with
state list 'module.network'. - Confirm the team agrees with the hand-off.
- Pull state to a backup before running.
- Run the rm.
- Plan; expect the configuration to be either gone (no changes) or re-imported (a batch of creates).
Worked example: hand-off to Ansible
The team is migrating database management from Terraform to
Ansible. The database is aws_db_instance.primary.
# Before migration:
terraform state show 'aws_db_instance.primary' | grep '^ id '
# primary-db.example.us-east-1.rds.amazonaws.com
# Step 1: remove from Terraform state (the database stays running)
terraform state rm -backup-file=/tmp/hand-off.tfstate 'aws_db_instance.primary'
# Step 2: remove the resource block from configuration
# (in a separate commit, after the rm)
# Step 3: import into Ansible inventory; verify Ansible can manage the database
# Step 4: terraform plan should now be empty for that resource
terraform plan
# No changes.
If the team changes its mind, restoring from the backup file puts the resource back under Terraform management.
Worked example: wrong import cleanup
An operator imported aws_s3_bucket.logs under the wrong address
(module.logging.aws_s3_bucket.log_archive). The real-world bucket
is the same one that should be managed at aws_s3_bucket.logs.
# Confirm the wrong address
terraform state list | grep 'aws_s3_bucket'
# module.logging.aws_s3_bucket.log_archive (wrong)
# aws_s3_bucket.logs (not yet)
# Remove the wrong entry
terraform state rm -backup-file=/tmp/import-fix.tfstate 'module.logging.aws_s3_bucket.log_archive'
# Re-import under the correct address
terraform import 'aws_s3_bucket.logs' 'my-log-bucket-name'
# Verify
terraform plan
# No changes.
Validation
READ-ONLY
# Confirm the address is gone from state
terraform state list | grep 'aws_instance.web'
# (empty)
# Confirm the real-world resource is still running (this depends on the provider)
aws ec2 describe-instances --filters "Name=tag:Name,Values=web" \
--query 'Reservations[].Instances[].State.Name'
# running
# Confirm the plan
terraform plan
# Either no changes (if config was also removed) or "create aws_instance.web" (if config remains)
The provider-side check is essential. The point of state rm is
that the real-world resource keeps running. Verify it.
Production failure modes
Symptom: state rm succeeds but the next plan proposes to
create the resource again. Cause: the resource block was not
removed from configuration. Either remove the block (and re-plan)
or re-import the real-world resource under the same address.
Symptom: state rm errors with “Resource not found in state”.
Cause: the address is misspelled or in a different module path.
Use state list to find the correct address.
Symptom: the wrong address was rm’d. Cause: address typo or wrong module path. Restore from the backup file; verify the correct addresses; retry.
Symptom: lock acquire failed during rm. Cause: another apply is in progress. Wait or break the lock with team agreement.
Symptom: serial advanced but the resource block in configuration was not removed, and the team now has duplicates. Cause: the rm was performed but the config was not updated. The duplicates exist now in the cloud. Decide: destroy the duplicate, or import it under a different address.
Recovery
- Stop. Do not apply.
- Pull the pre-rm state from the backup file:
cat /tmp/rm-backup.tfstateto inspect. - For S3 with versioning: restore the previous version of the state object. For Terraform Cloud: use the state versions UI.
- Verify with
terraform plan— expect the original state. - Investigate what went wrong with the rm before retrying.
What comes next
The next lesson covers terraform import — the inverse of
state rm. Where state rm removes a resource from state,
import adds it. The companion commands cover the lifecycle of
adopting and handing off real-world resources.
Verification
- You can describe the difference between
state rmandterraform destroy. - You can name the production procedure for
state rm: backup, confirm address, run, plan, verify. - You can choose between
state rm,state mv, andimportfor each refactor scenario. - You can recover from a wrong
state rmusing the backup file.
Knowledge check · 7 questions
Q1. What does `terraform state rm <address>` do?
Q2. After `terraform state rm aws_instance.web`, the next `terraform apply` will destroy the real-world EC2 instance.
Q3. Which flag writes a backup of the state before a state rm?
Q4. When handing a real-world resource over to a different management system (e.g. Ansible), the right sequence is:
Q5. Which of the following are valid use cases for `terraform state rm`? (Select all that apply.)
Q6. After running `terraform state rm aws_instance.web` while the configuration block remains, the next plan will:
Q7. An operator runs `terraform state rm aws_instance.wseb` (typo) instead of `aws_instance.web`. The real aws_instance.web is now unmanaged. What is the right recovery?
Passing score: 75%. Answers are checked in this browser.