Terraform Cheatsheet

Quick reference for the most commonly used Terraform commands and HCL patterns for managing infrastructure as code.

Core Workflow

terraform init
terraform plan
terraform plan -out=tfplan
terraform apply
terraform apply tfplan
terraform apply -auto-approve
terraform destroy
terraform destroy -target=aws_instance.web

State Management

terraform state list
terraform state show <resource>
terraform state mv <src> <dst>
terraform state rm <resource>
terraform import <resource> <id>
terraform refresh
terraform output
terraform output -json

Workspace

terraform workspace list
terraform workspace new <name>
terraform workspace select <name>
terraform workspace show
terraform workspace delete <name>

Format & Validate

terraform fmt
terraform fmt -recursive
terraform validate
terraform version
terraform providers
terraform providers lock

Variables & Outputs

# Pass var via CLI
terraform apply -var="region=us-east-1"

# Use var file
terraform apply -var-file="prod.tfvars"

# Environment variable
export TF_VAR_region=us-east-1

# Show outputs
terraform output vpc_id

Debugging

TF_LOG=DEBUG terraform plan
TF_LOG=TRACE terraform apply 2>&1 | tee debug.log
terraform plan -refresh=false
terraform apply -refresh=false
terraform force-unlock <lock-id>
terraform taint <resource>    # deprecated, use -replace
terraform apply -replace=<resource>

Remote State — S3 Backend

terraform {
  backend "s3" {
    bucket         = "my-tf-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

Remote State — GCS Backend

terraform {
  backend "gcs" {
    bucket = "my-tf-state"
    prefix = "prod/terraform"
  }
}

Module Usage

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"

  name = "my-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["us-east-1a", "us-east-1b"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]
}

Terraform Cloud

# Login
terraform login

# Remote runs
terraform apply -input=false

# Sentinel policy check
terraform plan -json | sentinel apply

Workflow Examples

Complete New Project Setup

End-to-end flow for provisioning infrastructure from scratch.

# Step 1 — Write your provider configuration
cat > main.tf <<'EOF'
terraform {
  required_version = ">= 1.6"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.region
}
EOF

# Step 2 — Initialise: download providers and modules
terraform init

# Step 3 — Review planned changes before touching real infra
terraform plan -out=tfplan

# Step 4 — Apply the saved plan (no interactive prompt)
terraform apply tfplan

# Step 5 — Verify outputs
terraform output

# Step 6 — Confirm resources in AWS
aws ec2 describe-instances --filters "Name=tag:ManagedBy,Values=terraform"

Module Development Workflow

# Create module directory structure
mkdir -p modules/my-module
touch modules/my-module/{main.tf,variables.tf,outputs.tf}

# Typical variables.tf
cat > modules/my-module/variables.tf <<'EOF'
variable "name" {
  description = "Resource name prefix"
  type        = string
}

variable "environment" {
  description = "Deployment environment (dev/staging/prod)"
  type        = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "environment must be dev, staging, or prod."
  }
}

variable "tags" {
  description = "Additional tags to apply to all resources"
  type        = map(string)
  default     = {}
}
EOF

# Typical outputs.tf
cat > modules/my-module/outputs.tf <<'EOF'
output "id" {
  description = "The resource ID"
  value       = aws_resource.this.id
}
EOF

# Reference the local module from a root config
module "example" {
  source      = "./modules/my-module"
  name        = "myapp"
  environment = "prod"
  tags        = { Team = "platform" }
}

# Re-initialise to pick up the new module
terraform init
terraform plan

State Migration — Local to S3

Safely move your state file from local storage to a remote S3 backend.

# Step 1 — Ensure local state is clean and up to date
terraform plan   # Should show no changes

# Step 2 — Add the S3 backend block to main.tf
# (see Remote State — S3 Backend card above)

# Step 3 — Create the S3 bucket and DynamoDB table first
# (do this in a separate Terraform config or via AWS CLI)
aws s3api create-bucket --bucket my-tf-state --region us-east-1
aws dynamodb create-table \
  --table-name terraform-locks \
  --attribute-definitions AttributeName=LockID,AttributeType=S \
  --key-schema AttributeName=LockID,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST \
  --region us-east-1

# Step 4 — Re-initialise; Terraform will offer to copy the state
terraform init
# Answer "yes" when prompted to copy existing state to the new backend

# Step 5 — Verify the remote state is intact
terraform state list

# Step 6 — Remove the local state file (optional but recommended)
rm terraform.tfstate terraform.tfstate.backup

Handling Infrastructure Drift

Detect and reconcile differences between your Terraform state and real infrastructure.

# Step 1 — Run plan to detect drift
terraform plan
# Terraform compares state to live infra and reports differences

# Step 2a — Drift caused by a manual change you want to keep
#           Import the changed resource so state matches reality
terraform import aws_security_group.web sg-0abc123def456

# Step 2b — Drift you want to reverse (revert to declared config)
#           Simply apply — Terraform will correct the resource
terraform apply

# Step 2c — Resource is broken and needs full replacement
terraform apply -replace=aws_instance.web

# Step 3 — Force a re-read of all live resources (skips plan cache)
terraform refresh

# Step 4 — If state is locked due to a crashed run, force-unlock
terraform force-unlock <lock-id>

# Step 5 — Confirm no more drift
terraform plan   # Should output: "No changes. Infrastructure is up-to-date."
Tip: Always use terraform plan -out=tfplan in CI/CD pipelines and then terraform apply tfplan to guarantee that exactly what was reviewed gets applied — no surprises from infra changes between plan and apply.
Warning: terraform destroy permanently deletes all managed resources. In production, prefer terraform destroy -target=<resource> to destroy specific resources, and always review the plan output carefully.

kubectl Cheatsheet  |  AWS CLI Cheatsheet