4.01 Terraform Commands
Overview
Beyond init, plan, and apply, Terraform ships a set of utility commands for validating syntax, formatting code, inspecting state, listing providers, reading outputs, syncing state with real infrastructure, and visualizing dependencies.
Abstract
These commands don't provision infrastructure — they help you write correct configuration, keep code readable, inspect current state, and understand resource dependencies before and after an apply.
Why It Matters in Production
Catching syntax errors with validate before a plan saves time in CI pipelines. Consistent formatting via fmt keeps configuration reviewable across a team. show, output, and graph give visibility into what Terraform actually did and why, which matters when debugging drift or onboarding new engineers to a codebase. refresh keeps the state file honest when infrastructure changes outside of Terraform's control.
Key Concepts
| Command | Purpose |
|---|---|
terraform validate |
Checks configuration syntax without planning or applying |
terraform fmt |
Rewrites files into Terraform's canonical formatting style |
terraform show |
Prints the current state (or a saved plan) in human-readable or JSON form |
terraform providers |
Lists providers required by the configuration and state |
terraform output |
Prints output variable values |
terraform refresh |
Syncs the state file with real-world infrastructure |
terraform graph |
Generates a DOT-format dependency graph |
Common Use Cases
- Running
terraform validatein a CI pipeline as a fast pre-commit/pre-merge check. - Running
terraform fmtas a pre-commit hook to enforce consistent style across a team. - Using
terraform show -jsonto feed state data into external tooling or scripts. - Using
terraform providers mirrorto pre-download provider plugins for air-gapped or offline environments. - Using
terraform output pet-namein scripts that need a single value rather than the full output block. - Using
terraform graphpiped into Graphviz to visually audit resource dependencies before a large apply.
Example Configuration or Commands
terraform validate
resource "local_file" "pet" {
filename = "/root/pets.txt"
content = "We love pets!"
file_permissions = "0777"
}
Error: Unsupported argument
on main.tf line 4, in resource "local_file" "pet":
4: file_permissions = "0777"
An argument named "file_permissions" is not expected here. Did you mean "file_permission"?
Fixing the argument name to file_permission and re-running validate returns Success! The configuration is valid.
terraform fmt
The command rewrites main.tf in place, aligning the = signs and indentation, and prints the name of every file it changed.
terraform show
# local_file.pet:
resource "local_file" "pet" {
content = "We love pets!"
directory_permission = "0777"
file_permission = "0777"
filename = "/root/pets.txt"
id = "cba595b7d9f94ba1107a46f3f731912d95fb3d2c"
}
{"format_version":"0.1","terraform_version":"0.13.0","values":{"root_module":{"resources":[{"address":"local_file.pet","mode":"managed","type":"local_file","name":"pet","provider_name":"registry.terraform.io/hashicorp/local","schema_version":0,"values":{"content":"We love pets!","content_base64":null,"directory_permission":"0777","file_permission":"0777","filename":"/root/pets.txt","id":"cba595b7d9f94ba1107a46f3f731912d95fb3d2c","sensitive_content":null}}]}}}
terraform providers
Providers required by configuration:
.
└── provider[registry.terraform.io/hashicorp/local]
Providers required by state:
provider[registry.terraform.io/hashicorp/local]
To copy provider plugins to another directory for offline use:
- Mirroring hashicorp/local...
- Selected v1.4.0 with no constraints
- Downloading package for windows_amd64...
- Package authenticated: signed by HashiCorp
terraform output
output "content" {
value = local_file.pet.content
sensitive = false
description = "Print the content of the file"
}
output "pet-name" {
value = random_pet.cat.id
sensitive = false
description = "Print the name of the pet"
}
terraform refresh
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
random_pet.cat: Refreshing state... [id=huge-owl]
local_file.pet: Refreshing state... [id=cba595b7d9f94ba1107a46f3f731912d95fb3d2c]
------------------------------------------------------------
No changes. Infrastructure is up-to-date.
refresh runs automatically before plan and apply. Skip it with -refresh=false, or run it explicitly without changing infrastructure:
terraform graph
resource "local_file" "pet" {
filename = "/root/pets.txt"
content = "My favorite pet is ${random_pet.my-pet.id}"
}
resource "random_pet" "my-pet" {
prefix = "Mr"
separator = "."
length = "1"
}
The output is DOT-format text — hard to read directly, so pipe it through Graphviz:
Opening graph.svg in a browser shows local_file.pet depending on random_pet.my-pet, since the file's content references the pet's ID.
Note
terraform graph can run as soon as the configuration file exists — it does not require the directory to be initialized first.
Best Practices
Best Practices
- Run
terraform validateandterraform fmtas part of CI/CD beforeplanorapply. - Use
terraform output -jsonwhen piping output values into scripts or other tools. - Use
terraform graphto visually review dependencies before applying large or unfamiliar configurations. - Prefer
terraform apply -refresh-onlyover a barerefreshwhen you want an explicit, reviewable state sync.
Security Best Practices
Security
terraform showandterraform outputcan print sensitive values in plain text unlesssensitive = trueis set on the output or attribute.- Restrict access to state files and CI logs that capture
show -jsonoroutputresults, since they may contain secrets or infrastructure details. - Verify provider checksums and signatures (visible in
providers mirroroutput) to avoid supply-chain tampering when mirroring plugins.
Do and Don't
| ✅ Do | ❌ Don't |
|---|---|
Run terraform validate before every commit or PR |
Skip validation and rely only on apply to catch syntax errors |
Mark sensitive outputs with sensitive = true |
Print secrets via terraform output in shared logs |
Use -refresh=false deliberately when you know state is current |
Disable refresh blindly to "speed up" every plan |
Review terraform graph output before large applies |
Apply complex multi-resource configs without checking dependencies |
Common Mistakes
Common Mistakes
- Misspelling resource arguments (e.g.
file_permissionsinstead offile_permission) and only catching it atapplytime instead ofvalidatetime. - Assuming
terraform refreshupdates real infrastructure — it only updates the state file to match what already exists. - Forgetting that
terraform graphoutput is raw DOT text and needs Graphviz (or similar) to render into something readable.
Troubleshooting
# Check why a plan shows unexpected drift
terraform refresh
terraform show
# Confirm which provider versions are actually in use
terraform providers
# Re-check syntax after editing configuration
terraform validate
Real-World Examples
Platform Team — CI Pipeline Catching Bad Config Early
Scenario: A platform engineering team running Terraform across dozens of microservice repos.
Problem: Misconfigured arguments were only discovered during apply, sometimes mid-deployment, causing partial infrastructure changes.
Solution: Added terraform validate and terraform fmt -check as mandatory CI steps before any plan could run.
Outcome: Configuration errors were caught in PR review instead of during deployment, cutting failed pipeline runs by a significant margin.
Infra Team — Drift Detection After Manual Hotfix
Scenario: An on-call engineer manually edited a server's firewall rule outside of Terraform during an incident.
Problem: The next terraform plan showed unexpected changes because state no longer matched reality.
Solution: Ran terraform refresh to reconcile state with the manual change, then reviewed the diff before deciding whether to codify or revert the hotfix.
Outcome: The team avoided accidentally reverting an emergency fix, and updated the configuration to match the new desired state.
Startup — Dependency Graph Reveals Hidden Coupling
Scenario: A small team inherited a Terraform codebase with implicit resource references scattered across files.
Problem: No one was confident which resources depended on which before making changes.
Solution: Generated a visual dependency graph with terraform graph | dot -Tsvg > graph.svg and reviewed it as a team.
Outcome: The team identified and documented previously hidden dependencies, reducing risky changes during refactors.
Quick Recap
terraform validatechecks syntax without planning or applying.terraform fmtstandardizes formatting across configuration files.terraform showprints current state (human-readable or-json).terraform providerslists required providers;providers mirrorcopies plugins locally.terraform outputprints output variable values, optionally by name.terraform refreshsyncs the state file with real infrastructure (does not modify infrastructure itself).terraform graphoutputs a DOT-format dependency graph, best viewed through Graphviz.
Interview / Revision Notes
- Q: Does
terraform refreshmodify infrastructure? No — it only updates the state file to reflect real-world resource attributes. - Q: How do you bypass automatic refresh during plan/apply? Use the
-refresh=falseflag. - Q: What format does
terraform graphoutput? DOT format, typically rendered with Graphviz. - Q: Can
terraform graphrun beforeterraform init? Yes, as long as the configuration file exists. - Q: How do you get a single output value instead of all outputs?
terraform output <variable_name>. - Q: What's the difference between
terraform showandterraform show -json? Same state data, but-jsonis machine-readable for scripting/tooling.