Terraform Cheatsheet - Infrastructure as Code Command Reference

The Terraform rhythm is fixed: plan to see the diff, apply to commit, destroy with real care. Everything else follows from the state file, which records how declared resources map to real ones and acts as the source of truth for the tool. Lose the state and the next plan proposes recreating everything; drift between state and reality can trigger unexpected replacement. That is why shared workspaces belong on a remote backend with locking enabled. Sections cover the core workflow, state management, variables and outputs, modules, remote backends, advanced features, and debugging. A few rules hold up in practice: pass secrets as variables or mark them sensitive rather than inlining them, keep state out of Git because it carries credentials, and run terraform fmt so reviews stay readable. When something looks wrong, state list and state show come first, and refresh-only is the smallest tool for correcting drift. Import turns an existing hand-built resource into a managed one without destroying anything, which is the safe first step when adopting Terraform on a live stack.

SysOps·53 commands·Last updated 2026-07-21
terraformiacinfrastructuredevopsCloud Native

Typical Use Case

For engineers responsible for infrastructure as code in the cloud. The familiar rhythm is init, plan, apply, but the time actually goes into four other things this sheet covers: state management, where list, show, mv, rm and import are the tools for investigating drift and mismatches between config and reality, plus taint to force replacement; remote backends, with S3 for state and DynamoDB for locking, which is what makes team and concurrent apply safe; choosing between count and for_each for bulk creation; and debugging through the console, TF_LOG and output logs. Workspace covers keeping staging and production state apart.

Core Workflow 7

terraform init
Initialize the working dir; download providers and modules
terraform plan
Preview changes: resources to create/modify/destroy
terraform apply
Apply changes to infrastructure
terraform destroy
Destroy all managed infrastructure
terraform fmt
Format .tf files to the standard style
terraform validate
Validate config syntax and internal consistency
terraform apply -auto-approve
Auto-approve (for CI/CD)

State Management 9

terraform state list
List all resources in the state file
terraform state show <resource>
Show detailed state of a resource
terraform state mv <src> <dst>
Move a resource's address in state
terraform state rm <resource>
Remove a resource from state (keeps the real one)
terraform state pull
Pull remote state to local stdout
terraform state push
Push local state to the remote backend
terraform import <address> <id>
Import an existing resource into Terraform
terraform taint <resource>
Mark a resource as 'tainted'; rebuilt on next apply
terraform untaint <resource>
Clear the 'tainted' mark

Variables & Outputs 8

variable "region" {\n type = string\n default = "us-east-1"\n}
Define a variable (type, default)
var.region
Reference a defined variable
TF_VAR_region=us-west-2
Set a variable via environment variable
terraform plan -var="region=us-west-2"
Set a variable via CLI flag
output "ip" {\n value = aws_instance.web.public_ip\n}
Define an output value
terraform output
Show all output values
terraform output -json
Show outputs as JSON
locals {\n name = "${var.prefix}-web"\n}
Local values to simplify expressions

Modules 6

module "vpc" {\n source = "terraform-aws-modules/vpc/aws"\n version = "5.0.0"\n}
Reference a Registry module, pin version
module "vpc" {\n source = "./modules/vpc"\n}
Reference a local module (relative path)
module "vpc" {\n source = "git::https://example.com/vpc.git"\n}
Reference a module in a Git repo
module.vpc.vpc_id
Reference a module's output
terraform init -upgrade
Upgrade modules to the latest allowed version
terraform get
Download and update modules

Remote Backends 6

terraform {\n backend "s3" {\n bucket = "my-tf-state"\n key = "prod/terraform.tfstate"\n region = "us-east-1"\n }\n}
Store state in an S3 backend
terraform {\n backend "s3" {\n dynamodb_table = "tf-lock"\n }\n}
DynamoDB state lock against concurrency
data "terraform_remote_state" "db" {\n backend = "s3"\n config = {\n bucket = "shared-state"\n key = "db/terraform.tfstate"\n }\n}
Read remote state across configurations
data.terraform_remote_state.db.outputs.db_endpoint
Reference a remote state output
terraform init -reconfigure
Reconfigure the backend (migrate state)
terraform init -migrate-state
Migrate the state file to a new backend

Advanced Features 9

terraform workspace new staging
Create a new workspace
terraform workspace list
List all workspaces
terraform workspace select staging
Switch to a workspace
resource "aws_instance" "web" {\n for_each = var.subnets\n}
for_each creates resources from a set
resource "aws_instance" "web" {\n count = var.instance_count\n}
count creates resources by number
depends_on = [aws_s3_bucket.data]
Explicit dependency to fix creation order
lifecycle {\n create_before_destroy = true\n}
Lifecycle: create before destroy
lifecycle {\n prevent_destroy = true\n}
Lifecycle: prevent accidental destroy
lifecycle {\n ignore_changes = [tags]\n}
Lifecycle: ignore changes to specific attributes

Debugging 8

terraform console
Interactive console to test expressions
terraform graph | dot -Tpng > graph.png
Generate a resource dependency graph
terraform show
Readable dump of the current state
terraform show -json
JSON state output for jq
TF_LOG=DEBUG terraform apply
Enable DEBUG-level logging
TF_LOG=TRACE terraform plan
TRACE-level logging, most verbose
TF_LOG_PATH=./tf.log terraform apply
Write logs to a file
export TF_LOG=WARN
Set log level (TRACE/DEBUG/INFO/WARN/ERROR)

Parameter matrix

ParameterEffectExample
init初始化工作目录,下载 Provider 与模块terraform init
init -upgrade升级模块到当前允许的最新版本terraform init -upgrade
init -reconfigure重新配置后端(迁移状态)terraform init -reconfigure
init -migrate-state把状态文件迁移到新后端terraform init -migrate-state
plan -var="k=v"命令行传入变量值terraform plan -var="region=us-west-2"
apply -auto-approve跳过人工确认,CI 场景使用terraform apply -auto-approve
validate / fmt校验配置一致性 / 统一代码格式terraform fmt
state list / show列出状态中的资源 / 查看单个资源详情terraform state list
state mv <src> <dst>移动资源在状态中的地址terraform state mv aws_instance.a aws_instance.b
state rm <res>只从状态移除,不销毁真实资源terraform state rm aws_s3_bucket.old
import <addr> <id>把已有资源纳管进 Terraformterraform import aws_instance.web i-0123456789
taint / untaint <res>标记损坏下次重建 / 取消标记terraform taint aws_instance.web
output / output -json查看输出值 / 以 JSON 形式查看terraform output -json
workspace new/select/list创建 / 切换 / 列出工作空间terraform workspace select staging
count / for_each按计数或按集合批量创建资源count = var.instance_count
console交互式控制台,测试表达式terraform console
TF_LOG / TF_LOG_PATH设置日志级别(TRACE/DEBUG/INFO/WARN/ERROR)与输出路径TF_LOG_PATH=./tf.log terraform apply

Common pitfalls

Symptomapply 报 state 已被占用 / 锁超时,多人同时操作时互相阻塞

Cause远程后端没有配置状态锁,或只用了 S3 而没加锁表。

Fix在 backend "s3" 里补 `dynamodb_table = "tf-lock"`,然后 `terraform init -migrate-state`。

Symptomplan 里出现大量并非本次改动造成的差异

Cause远端资源被人手工改过,本地 state 与实际出现漂移。

Fix先用 `terraform state list` 和 `terraform state show <资源>` 确认,再 `terraform apply` 让状态与配置重新对齐;重大差异先 `terraform plan -out` 存档审阅。

Symptom换了资源名后 Terraform 想「销毁旧的 + 创建新的」,而旧资源不能删

Cause资源的地址变了,Terraform 无法判断二者是同一个东西。

Fix用 `terraform state mv <旧地址> <新地址>` 把资源在新地址下认领,不要靠复制配置硬改。

Symptom资源已经手工删掉,apply 却在报创建冲突

Causestate 里仍记录着这个已不存在的资源。

Fix用 `terraform state rm <资源>` 从状态中摘除(该命令不删真实资源),再重新 apply。

Symptom输出里暴露数据库密码、密钥等敏感信息

Causestate 文件本身就包含资源的全部属性,包括敏感字段;把它提交到 Git 就会扩散。

Fix把敏感值放进 variable 且不设 default,通过环境变量或 CI secret 注入;state 目录加入 .gitignore,并开启远程后端加密。

SymptomCI 里 apply 卡在等待确认,流水线超时

Cause没有加自动批准标志,命令停在交互确认。

Fix在非交互环境一律使用 `terraform apply -auto-approve`,并确保流水线已经完成 `init` 与 `plan` 审阅环节。

Symptom把 count 改成 for_each(或反过来)后 Terraform 要销毁并重建整批资源

Cause两者的资源地址形式不同,切换即等于换了身份。

Fix在切换前用 `terraform state mv` 尽量迁移已有资源地址,或接受重建代价时先评估停机窗口。

Troubleshooting

  1. 1确认本地状态里到底有哪些资源

    terraform state list

    输出的资源地址是排查「缺失资源 / 多余资源」的第一手依据。

  2. 2查看单个资源的实际状态属性

    terraform state show aws_instance.web

    用于对比配置与真实资源差异,定位漂移来源。

  3. 3把状态输出成 JSON 供 grep/jq 分析

    terraform show -json

    适合在脚本里统计资源数量、比对 ID,或与 plan 结果做差异对比。

  4. 4交互式验证表达式是否算对

    terraform console

    输入 var.region、local.name 之类表达式,直接看求值结果,比改完 plan 再试快得多。

  5. 5打开详细日志并落盘

    TF_LOG=DEBUG TF_LOG_PATH=./tf.log terraform plan

    日志里能看到 Provider 实际发出的 API 请求与响应,是排查「资源外部系统改不了」的关键。

  6. 6用代码格式检查暴露潜在语法问题

    terraform fmt -check -diff

    CI 中作为门禁使用,输出差异即为不符合规范的文件片段。

Command Examples

Review the diff with plan before applying

terraform plan -out=tfplan terraform show tfplan

plan -out 存成文件后可以 terraform show tfplan 复核,再 terraform apply tfplan 执行同一份计划,保证执行的就是你审过的那版。

Output

  # aws_instance.web will be updated in-place
  ~ resource "aws_instance" "web" {
      ~ instance_type = "t2.micro" -> "t3.medium"
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Detect drift between reality and state

terraform plan -detailed-exitcode echo $?

退出码 0=无变更、1=有错误、2=有变更且非破坏性。这个约定让它能直接用在 CI 里做基础设施漂移检测。

Output

2

Make Terraform forget a resource

terraform state rm aws_instance.web terraform plan

state rm 只改状态不动真实资源,plan 随即显示要重建——这正是「删掉重建」而非误删。直接用 destroy 会真删掉机器。

Output

  # aws_instance.web will be created
  + resource "aws_instance" "web" {
      + id = "i-0abc123"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Common Pitfalls

  • Always review plan before apply, and never pass -auto-approve to a plan containing destroys without reading it.
  • State contains sensitive data and must not be committed; production needs a remote backend with locking enabled.
  • terraform destroy removes every managed resource including databases; use -target or remove the resource from config to drop one.
  • Changing an for_each key destroys and recreates the whole resource — keys must be stable identifiers.
  • Run terraform workspace show before acting; operating on the wrong workspace between prod and staging is a real incident.
  • state rm makes a hand-created resource look missing and get recreated — use it to correct drift, not to hide it.

Tips

  • terraform plan never modifies resources — use it freely
  • Keep secrets in variables; never hardcode them in .tf files
  • Use terraform fmt to standardize formatting — essential for teams
  • The state file holds sensitive data; never commit it to Git
  • Use a remote backend (e.g. S3 + DynamoDB) for collaboration and state locking

FAQ

Why run terraform plan separately from apply?

plan computes and displays the diff without touching anything; apply performs the create, change or destroy. Running plan first inserts a human checkpoint, which matters most for changes that rebuild or delete resources. A stronger workflow is to save the plan to a file and re-apply that exact plan, guaranteeing you execute precisely what you reviewed.

What is the difference between terraform state rm and terraform destroy?

destroy actually deletes the cloud resource and drops it from state; state rm only removes the entry from the state file while leaving the real resource running. They solve different problems. When state has drifted from reality, state rm makes Terraform forget the resource so it does not plan a destroy-then-recreate. Use destroy only when you actually want to reclaim the infrastructure.

When should I switch Terraform workspaces?

Use one workspace per environment — staging, prod — created with terraform workspace new staging, switched with select, listed with list. Note that all workspaces share the same backend state file, so you must ensure resource names do not collide across them. Because workspaces are easy to get wrong, always run terraform workspace show before applying to confirm which environment you are actually in.

Should I use count or for_each to create resources in bulk?

Use count when the instances are identical and only the quantity differs; use for_each when each instance needs different configuration, such as its own subnet or size. A crucial caveat: changing an for_each key destroys and recreates that entire resource, so design keys to be stable identifiers. Likewise, reordering a list used with count shifts indices and triggers recreation.

Should the Terraform state file be committed to Git?

No. State records the mapping to real resources, contains sensitive data, and produces painful merge conflicts. Configure a remote backend (backend "s3" or similar) so it lives in object storage or Terraform Cloud, keeping only configuration in the repository. Teams should also enable state locking — a DynamoDB lock for S3, or Terraform Cloud — or two concurrent applies will overwrite each other.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us