TerraformX · State Operations: Read, Move, Remove, ImportProduction Terraform
Read-Only Operations: list, show, pull
What you'll learn
- Use terraform state list, state show, and state pull for inspection
- Use terraform output and -raw to read declared outputs
- Choose the right read-only command for each inspection task
- Apply the read-before-write discipline before any mutating operation
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
The first habit of state discipline: every mutating operation is preceded by a read-only inspection. The inspection takes seconds. The mistake it prevents takes hours. This lesson is the catalogue of read commands; the production habit is the lesson’s point.
The three read commands and one related one
| Command | Output | Use |
|---|---|---|
terraform state list | One address per line | Enumerate resources |
terraform state show <address> | A single resource block | Inspect one resource |
terraform state pull | Full JSON document | Feed tooling, audit |
terraform output [-raw] | Declared output values | Read configuration outputs |
All four are read-only. None of them write to state. None of them
call the provider API for attribute refresh (with the exception of
terraform output reading from the same state cache).
terraform state list in detail
terraform state list
Default output is one address per line, sorted alphabetically:
aws_instance.web
aws_lb.main
aws_security_group.web
aws_subnet.public[0]
aws_subnet.public[1]
aws_subnet.public[2]
data.aws_ami.ubuntu
module.network.aws_route_table.rt["us-east-1a"]
module.network.aws_route_table.rt["us-east-1b"]
module.network.aws_route_table.rt["us-east-1c"]
module.network.aws_vpc.main
Patterns narrow the list:
terraform state list module.network
terraform state list 'aws_subnet.public[*]'
terraform state list -json | jq '.[].name'
The -json flag (available since 0.15) produces machine-readable
output for tooling.
Common pre-flight uses:
# How many resources does this state manage?
terraform state list | wc -l
# Are there any orphan resources (in state, not in configuration)?
terraform state list > /tmp/state-list.txt
grep -Fxf <(terraform state list) - <(grep -hE '^resource ' *.tf) | sort
# Confirm a specific resource exists
terraform state list | grep -F 'aws_instance.web'
terraform state show in detail
terraform state show 'aws_instance.web'
Output is a formatted HCL block (Terraform syntax, not JSON):
# aws_instance.web:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
arn = "arn:aws:ec2:us-east-1:123456789012:instance/i-0a1b2c3d4e5f6a7b8"
id = "i-0a1b2c3d4e5f6a7b8"
instance_type = "t3.medium"
tags = {
"Environment" = "production"
"Name" = "web"
}
...
}
state show reads from state as-is. The id is the real-world
identifier. Other attributes are last-known values. If you want
current provider values, run terraform plan -refresh-only first.
Use cases:
# What is the real-world ID of this resource?
terraform state show 'aws_instance.web' | grep '^ id '
# Are the tags in state what we expect?
terraform state show 'aws_instance.web' | sed -n '/tags = {/,/^ }/p'
# Confirm a sensitive attribute value is what we expect
terraform state show 'aws_db_instance.primary' | grep -A1 password
The last case will print (sensitive value) if the attribute is
sensitive in the provider schema. Use the next lesson’s pattern
for safe inspection of sensitive values.
terraform state pull in detail
terraform state pull
The full state JSON. Pipe through jq for navigation:
# Serial and lineage
terraform state pull | jq '{serial, lineage}'
# Resources by type
terraform state pull | jq '.resources | group_by(.type) | map({type: .[0].type, count: length})'
# Outputs
terraform state pull | jq '.outputs | to_entries | map({name: .key, type: .value.type})'
# All resource IDs in state
terraform state pull | jq -r '.resources[].instances[]?.attributes.id'
state pull is the canonical way to feed state into other
production tooling:
- Drift detection (compare
state pulloutput against provider API). - CMDB sync (push resource IDs and tags into the inventory system).
- Audit pipelines (track serial advances over time).
- Backup verification (confirm the pulled state matches the backup).
For a backup, save the output directly:
terraform state pull > /tmp/state-$(date +%Y%m%d-%H%M%S).json
This is read-only and produces a single artifact. The remote backend’s own versioning is the production control; local snapshots like this are for incident response only.
terraform output in detail
terraform output
All declared outputs:
vpc_id = "vpc-0123456789abcdef0"
subnet_ids = [
"subnet-0aaa",
"subnet-0bbb",
]
database_endpoint = "db.example.internal"
For one output:
terraform output vpc_id
# vpc_id = "vpc-0123456789abcdef0"
Without quotes (for shell use):
terraform output -raw vpc_id
# vpc-0123456789abcdef0
The -json flag for tooling:
terraform output -json | jq 'with_entries(if .value.sensitive then .value.value = "REDACTED" else . end)'
Sensitive outputs print as (sensitive value) in the CLI and as
null in JSON (without -raw). With -raw, the value is
returned but a warning is emitted to stderr.
The pre-flight pattern
A production pre-flight before any mutating operation:
# 1. Confirm the state is reachable and intact
terraform state list | wc -l # resource count
terraform state pull | jq '.serial' # current serial
# 2. Confirm the resource exists and looks right
terraform state show 'aws_instance.web' | grep '^ id '
# 3. Confirm outputs are correct
terraform output vpc_id
# 4. Confirm no drift
terraform plan -refresh-only -out=/tmp/refresh.tfplan
terraform show -json /tmp/refresh.tfplan | jq '.resource_changes | length'
# Expect 0 (or a documented, classified drift)
If any of these returns something unexpected, investigate before applying. If all return expected, proceed.
Validation
READ-ONLY
terraform state list > /tmp/list.txt
terraform state pull > /tmp/full.json
terraform output -json > /tmp/outputs.json
Three artifacts, all read-only. Inspect with jq or open them in
a viewer. The artifacts are disposable; the live state in the
backend is the source of truth.
Production failure modes
Symptom: state list returns an empty list. Cause: state is
reachable but contains no resources. Confirm with terraform plan
— if plan is empty, there is no work to do. If the configuration
declares resources, investigate why state is empty.
Symptom: state show 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: state pull returns malformed JSON. Cause: the state
has been corrupted. Stop. Restore from the versioned backup. Do
not attempt to fix the JSON.
Symptom: terraform output prints (sensitive value) in a
shell pipeline. Cause: the output is marked sensitive = true.
Use -raw and pipe the value directly to the next command; never
echo the result.
Symptom: serial advanced unexpectedly between pre-flights. Cause: a refresh-only apply ran, or another operator applied. Re-pull and confirm the state is consistent.
Recovery
Read-only commands do not need recovery. When read-only output looks wrong, the recovery pattern is:
terraform state pull > current.json— confirm valid JSON.terraform state list— confirm resource count.terraform plan— confirm planned diff matches expectation.- If any are inconsistent, restore state from the versioned backup.
What comes next
The next lessons cover the mutating state commands. The first is
terraform state mv — renaming an address in state without
touching real infrastructure.
Verification
- You can choose between
state list,state show, andstate pullfor each inspection task. - You can pipe
state pullthroughjqto extract specific fields. - You can read declared outputs with
terraform outputand handle sensitive values correctly. - You can run a pre-flight before any mutating operation.
Knowledge check · 7 questions
Q1. Which command returns the full state JSON document for piping into jq or a backup script?
Q2. Which flag on terraform output strips quotes and prints a single value for shell variable assignment?
Q3. terraform state show refreshes the state from the provider API before showing the resource.
Q4. Which command is read-only?
Q5. Which of these commands produce read-only output? (Select all that apply.)
Q6. Before running a `state mv`, the right pre-flight is:
Q7. An operator wants to verify the real-world AWS resource ID for an aws_instance.web before any change. Which read-only command produces that?
Passing score: 75%. Answers are checked in this browser.