All cheatsheets

Cheatsheets

Terraform

init/plan/apply, state & backends, modules, variables, for_each, and drift.

Visualize

Terraform — plan → apply → state

plan previews a diff against state; apply reconciles real infrastructure to match the config.

plan · config vs state
aws_instance.webcreate · ami = "ami-0c1a"
aws_security_group.sgupdate · ingress 80 → 443
aws_instance.legacydestroy · no longer in config
+1 · ~1 · -1 to apply
state · real infra

apply writes resources here

plan is a preview: a diff of config against the current state — nothing has changed yet.

65 entries

Core workflow10

terraform init

Initialize a working directory

terraform plan

Preview changes without applying them

terraform apply

Create or update infrastructure to match config

terraform destroy

Tear down all managed infrastructure

terraform fmt -recursive

Rewrite files to the canonical HCL style

terraform validate

Check config for syntax and internal consistency

terraform show

Render state or a saved plan in human-readable form

terraform output

Read root-module output values

terraform plan -target=...

Limit operations to specific resources

terraform apply -replace=ADDR

Force-recreate one resource (modern taint)

State11

terraform.tfstate

What state IS — Terraform's source of truth

backend "s3" { ... }

Remote state in S3 with DynamoDB locking

cloud { organization = ... }

Terraform Cloud / HCP remote state + runs

terraform state list

List every resource tracked in state

terraform state show ADDR

Show one resource's tracked attributes

terraform state mv SRC DST

Rename/move a resource in state (no infra change)

terraform state rm ADDR

Forget a resource without destroying it

terraform import ADDR ID

Bring an existing resource under management

import { to = ...; id = ... }

Config-driven import (Terraform 1.5+)

state locking

Why concurrent applies must be serialized

sensitive data in state

State stores secrets in plaintext — protect it

Providers & resources9

required_providers { ... }

Declare and pin which providers to use

version = "~> 5.40"

Version constraint operators (~>, >=, ranges)

provider "aws" { region = ... }

Configure a provider

provider "aws" { alias = "west" }

Multiple provider configs via aliases

resource "type" "name" { ... }

Declare a managed resource

data "type" "name" { ... }

Read existing data without managing it

resource addressing

How to name any resource: type.name[key]

depends_on = [aws_iam_role.r]

Declare an explicit dependency

implicit dependency graph

References define apply order automatically

Variables, outputs & locals11

variable "name" { type = ... }

Declare an input variable

validation { condition = ... }

Reject invalid variable values early

variable "x" { sensitive = true }

Mark a variable as sensitive

terraform.tfvars

Default variable values file

variable precedence

Which value wins when set in multiple places

output "name" { value = ... }

Expose a value after apply

output "x" { sensitive = true }

Redact an output from CLI display

locals { name = expr }

Named intermediate values (DRY expressions)

"${var.project}-${var.env}"

String interpolation

for / lookup / coalesce / try

Everyday expressions & built-in functions

cond ? a : b

Conditional (ternary) expression

Modules6

root vs child module

Every directory of .tf files is a module

module "name" { source = ... }

Call a reusable module

source = "..." (local/git/registry)

Where module code comes from

module variables & outputs

Pass values in, read results out

module composition

Wire modules together via I/O

for_each on a module

Instantiate a module multiple times

Meta-arguments8

count = N

Create N copies, indexed 0..N-1

for_each = toset([...]) / map

Create one instance per set/map key (stable)

count vs for_each

When to pick which (the core decision)

dynamic "block" { for_each = ... }

Generate repeated nested blocks

lifecycle { create_before_destroy }

Build the replacement before deleting the old

lifecycle { prevent_destroy = true }

Guard a resource against deletion

lifecycle { ignore_changes = [...] }

Stop fighting out-of-band changes to specific attributes

provisioner "remote-exec" { ... }

Run scripts on a resource — last resort

Operations & safety10

drift detection

Plan reveals config-vs-reality drift

terraform apply -refresh-only

Reconcile state with reality (no infra change)

terraform workspace

Multiple states from one config (and its limits)

TF_VAR_ secrets

Feed secrets via environment, not files

secrets handling

Keep credentials out of code, VCS, and logs

CI/CD: plan on PR, apply on merge

The safe pipeline pattern

footgun: wrong workspace / var-file

Destroying prod via the wrong context

footgun: count index shift

Removing a list item churns later resources

footgun: monolithic state

One giant state file is slow and risky

footgun: provider version drift

Unpinned providers change under you