In Terraform, what do `terraform state list` and `terraform state show` tell you, and how do they differ from the other `terraform state` subcommands?
answer
- addresses, not attributes
- the read-only half of terraform state
- show prints the recorded object ID
- no provider call, values as of last refresh
- mv and rm rewrite; list and show never do
basics
~10 sterraform state list prints the resource addresses Terraform is tracking; terraform state show <address> prints that one resource's recorded attributes. Both only read state. The other subcommands, such as mv and rm, rewrite it.
solid answer
~50 s`terraform state list` prints one resource address per line — things like `aws_s3_bucket.assets` or `module.network.aws_subnet.private[0]` — and you can pass an address prefix to filter, which is how you find the exact address of something buried in a module. `terraform state show <address>` prints the attributes Terraform has recorded for that single resource, including the real object ID, in a readable HCL-like form. Neither call contacts the provider: they render what state holds as of the last apply or refresh, so the values can be stale. That read-only half is the safe half. The mutating half — `terraform state mv` and `terraform state rm` — rewrites the mapping and takes the state lock, and in a team I would reach for committed `moved`, `removed` or `import` blocks instead. In practice `list` then `show` is step one of every state repair: you cannot move, remove or import anything until you know the exact address.
code
bash · 2 linesterraform state list module.network
terraform state show 'module.network.aws_subnet.private[0]'go deeper
Know that terraform state list prints the addresses Terraform tracks and terraform state show <address> prints that one resource's recorded attributes, and say plainly that both are read-only.
Explain that these read stored state without calling the provider, so values are as of the last apply or refresh, and that mv, rm and push are the mutating half of the same command family.
Show that you use them as step one of any repair: locate the exact address, confirm the recorded object ID against the real console object, and only then choose between moved, removed, import or a state subcommand.
Own the guardrail: read-only inspection is fine for anyone, mutating state subcommands should be rare and reviewed, and the default path for the team is committed moved, removed and import blocks that run in the pipeline.
## Addresses are the vocabulary Everything in this command family is keyed by a **resource address** — the identifier Terraform uses for one object it manages. An address is the resource type and local name (`aws_s3_bucket.assets`), optionally prefixed by module path (`module.network.aws_vpc.main`) and suffixed by an index key when the resource was declared with multiple instances (`module.network.aws_subnet.private[0]`, `aws_instance.web["blue"]`). Nothing else in state surgery works until you have the address exactly right, and that is what `terraform state list` is for. ## terraform state list Run with no arguments, it prints every address in the current workspace's state, one per line: ```bash $ terraform state list aws_s3_bucket.assets aws_s3_bucket_versioning.assets module.network.aws_vpc.main module.network.aws_subnet.private[0] module.network.aws_subnet.private[1] ``` Pass one or more addresses to filter — `terraform state list module.network` prints only the addresses under that module. On a large estate this is the fastest way to answer "is that thing actually managed by this configuration, or did someone build it by hand?", which is the question that precedes every import. Note that it lists what Terraform **tracks**, not what exists in the cloud. An object created in the console does not appear here, and an object that was deleted out of band still appears here until a refresh notices it is gone. ## terraform state show `terraform state show <address>` dumps the recorded attributes of exactly one resource: ```bash $ terraform state show module.network.aws_vpc.main # module.network.aws_vpc.main: resource "aws_vpc" "main" { id = "vpc-0abc123" cidr_block = "10.0.0.0/16" } ``` The output looks like configuration but is not configuration — it includes computed attributes that you never write, and pasting it into a `.tf` file will not work as-is. Its practical value is the recorded `id`: that is the string Terraform believes maps to a real object, and comparing it against the console is how you confirm an import landed on the right thing or that a resource is pointing where you expect. `terraform state show` does not call the provider. It renders stored values from the last apply or refresh, so if someone changed the object an hour ago you will not see it here — you would see it in a plan, which refreshes first. ## The read/write split The `terraform state` family splits cleanly in two: - **Read-only**: `list`, `show`, and `pull` (which streams the raw state JSON to stdout, useful for piping into `jq`). - **Mutating**: `mv` (change an address), `rm` (stop tracking an object), `push` (overwrite state wholesale), `replace-provider`. The mutating ones acquire the state lock and write new state; with local state they leave a backup file behind. They are the ones that can ruin an afternoon: `rm` can orphan a live production database, `push` can clobber a colleague's work. Modern Terraform gives you declarative equivalents for the common cases — `moved` blocks instead of `state mv`, `removed` blocks instead of `state rm`, `import` blocks instead of `terraform import` — and those go through code review, run in CI, and are idempotent. The CLI subcommands remain for the situations no block covers. There is also plain `terraform show`, which renders the whole state, or a saved plan file, rather than a single resource. Do not confuse it with `terraform state show`. ## Why not just open the state file Because state is a JSON document with a `serial` counter and internal structure that Terraform maintains, and hand-editing it is the one thing every piece of Terraform documentation tells you not to do. `state list` and `state show` exist precisely so you never need to. On a remote backend, opening the file is also not free — you would be pulling and re-uploading an object other people are locking. ## Typical session A real state repair almost always starts the same way: `terraform state list` filtered down to find the address, `terraform state show <address>` to confirm the recorded ID matches the object you mean, and only then a `moved`, `removed`, or `import` block — written into the configuration, opened as a pull request, and applied by the pipeline like any other change.
- Does `terraform state show` contact the cloud provider to fetch current values?No. It renders what is stored in state from the last apply or refresh, so the values can be stale if someone changed the object out of band. To see reality you need something that refreshes — a plan, or `terraform plan -refresh-only` — not `state show`.
- How would you extract the recorded object ID for scripting rather than reading it off the screen?`terraform show -json` renders the whole state as a machine-readable document you can pipe into `jq` and query by address; `terraform state pull` gives you the raw state JSON directly. Both are read-only, which makes them safe to run in a pipeline step.
- Why is opening the state JSON in an editor a bad habit even when you only intend to read it?Reading is harmless, but the habit is what is dangerous: the same file is where people then "just fix" an ID by hand, which desynchronises the serial, can corrupt the document, and races with anyone holding the lock. `state list` and `state show` give you the same information without the temptation.
saying these in an interview costs you the question
- Thinks terraform state list enumerates real cloud resources rather than tracked addresses
- Believes terraform state show queries the provider live for current values
- Confuses terraform show with terraform state show
- Opens and hand-edits the state JSON to find or fix an address
- Assumes every terraform state subcommand is read-only and therefore safe