Terraform's state file is the source of truth that maps your configuration to
real OpenStack resources. Most of the time you never touch it directly — but when
you refactor, rename, recover from drift, or split configurations, you'll reach
for the terraform state commands and moved {} blocks. This guide covers them
and the safety habits that keep state surgery from becoming an outage. For
storing state remotely see remote state.
Golden rule: back up state before any mutating operation. Mutating commands (
mv,rm,push) and refactors can't be undone from memory.
terraform state list # all resource addresses in state
terraform state show openstack_compute_instance_v2.instance # one resource
terraform state pull > current.tfstate # dump raw state (read-only)The repo's scripts/tf-state.sh wraps these safely and
prompts before destructive sub-commands:
scripts/tf-state.sh examples/compute/single-instance list
scripts/tf-state.sh examples/compute/single-instance show openstack_compute_instance_v2.instanceterraform state mv changes the address of a resource in state without
recreating the real resource. Use it when you rename a resource block, move it
into a module, or convert to for_each/count.
# Rename a resource block (old_name -> new_name)
terraform state mv \
openstack_compute_instance_v2.web \
openstack_compute_instance_v2.app
# Move a resource into a module
terraform state mv \
openstack_networking_network_v2.net \
module.networking.openstack_networking_network_v2.net
# Move into a for_each instance key
terraform state mv \
'openstack_compute_instance_v2.web' \
'openstack_compute_instance_v2.web["a"]'Always follow with terraform plan and confirm it shows no changes — that
proves the move matched the config.
For anything that ships in code, prefer a moved {} block over a manual
state mv. It is declarative, reviewable in a PR, and applied automatically for
every collaborator and CI run — no one has to remember to run a command:
moved {
from = openstack_compute_instance_v2.web
to = openstack_compute_instance_v2.app
}This is the right tool when renaming resources or restructuring a
module. Keep the block until everyone has applied, then it
can be removed. moved {} requires Terraform >= 1.1 (this repo targets
>= 1.3).
terraform state rm forgets a resource — Terraform stops managing it, but the
real OpenStack resource keeps running. Use it to hand a resource to another
configuration or to drop something you'll re-import.
terraform state rm openstack_compute_instance_v2.appFor code-driven removal (Terraform >= 1.7), a removed {} block forgets a
resource without destroying it on the next apply:
removed {
from = openstack_compute_instance_v2.app
lifecycle {
destroy = false # forget it, do NOT destroy the real instance
}
}To actually delete the resource, don't use state rm — remove the block (or run
terraform destroy) so Terraform tears it down properly.
Drift is when reality diverges from state — someone changed a resource in Horizon, or a resource was deleted out-of-band.
# See what changed without modifying anything:
terraform plan -refresh-only
# Reconcile state with reality (records drift into state):
terraform apply -refresh-only- If a resource was deleted outside Terraform, a refresh marks it gone and
the next
applyrecreates it. - If a resource was changed outside Terraform,
planshows how it will be pushed back to match your config — or update the config to accept the change. - The reference example uses
ignore_changes = [image_name]so an upstream base image rebuild doesn't force-replace a running instance. Uselifecycle { ignore_changes = [...] }for attributes that legitimately drift.
To pull a single resource's current attributes into state without a full apply:
terraform apply -refresh-only -target=openstack_compute_instance_v2.appterraform state pull > state.json # download current state
terraform state push state.json # upload an edited/restored state (DANGER)state push overwrites remote state. Use it only to restore a known-good backup,
and only when you understand the consequences — a bad push can orphan or
duplicate resources.
# Repo helper: pull and timestamp the current state (read-only).
scripts/backup-state.sh examples/compute/single-instance
# -> writes state-backup-YYYYmmdd-HHMMSS.json- Local backends auto-write a
terraform.tfstate.backupon each apply. - Take a manual backup before
mv,rm,push, migrations, and provider upgrades.scripts/migrate-state.shand the import helper do this for you. - Store backups somewhere access-controlled — state can contain secrets.
- Back up first (
scripts/backup-state.sh). - Prefer
moved {}/removed {}blocks over manualstate mv/rm. - After any state change, run
terraform planand expect no changes. - Never hand-edit raw state JSON; use the commands.
- Serialise operations — with the Swift backend there is no locking (remote state).