cheatsheet
Name origin: Terraform = "to transform the earth" (Latin: terra + forma). HashiCorp chose the name because the tool transforms infrastructure declarations into real cloud resources. Created by Mitchell Hashimoto, first released in 2014.
Core Workflow
terraform init # Download providers, init backend
terraform plan # Preview changes (dry-run)
terraform apply # Apply changes
terraform destroy # Tear down all resources — DANGEROUS: destroys everything
# Gotcha: use -target for surgical destruction: terraform destroy -target=aws_instance.web
# CI-safe apply
terraform plan -out= plan.tfplan
terraform apply plan.tfplan
Configuration Basics
# Provider
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
# Resource
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = { Name = "web-server" }
}
# Variable
variable "instance_type" {
type = string
default = "t3.micro"
}
# Output
output "public_ip" {
value = aws_instance.web.public_ip
}
# Data source (read-only lookup)
data "aws_ami" "ubuntu" {
most_recent = true
owners = [ "099720109477" ]
filter {
name = "name"
values = [ "ubuntu/images/hvm-ssd/ubuntu-jammy-*" ]
}
}
Remote Backend (State)
terraform {
backend "s3" {
bucket = "my-tf-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
Modules
module "vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
name = "production"
}
# Reference module output
resource "aws_subnet" "web" {
vpc_id = module.vpc.vpc_id
}
State Commands
terraform state list # List all resources
terraform state show aws_instance.web # Show resource details
terraform state mv old_name new_name # Rename resource
terraform state rm aws_instance.web # Remove from state (doesn't destroy)
terraform import aws_instance.web i-123 # Import existing resource
terraform force-unlock <LOCK_ID> # Unlock stuck state
Lifecycle Rules
resource "aws_instance" "web" {
lifecycle {
prevent_destroy = true # Block accidental destroy
create_before_destroy = true # Zero-downtime replacement
ignore_changes = [ tags ] # Don't track tag drift
}
}
# Rename without destroy
moved {
from = aws_instance.web
to = aws_instance.app
}
Expressions
# Conditional
instance_type = var.env == "prod" ? "m5.xlarge" : "t3.micro"
# For each
resource "aws_iam_user" "users" {
for_each = toset ([ "alice", "bob", "carol" ])
name = each.key
}
# Count
resource "aws_instance" "web" {
count = var.instance_count
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = { Name = "web-${count.index}" }
}
# Dynamic blocks
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = "tcp"
cidr_blocks = ingress.value.cidrs
}
}
Common Flags
Flag
Purpose
-auto-approve
Skip confirmation prompt
-target=resource
Apply to specific resource
-replace=resource
Force recreation
-var="key=val"
Set a variable
-var-file=file
Load variables from file
-parallelism=N
Concurrent operations (default 10)
-refresh=false
Skip state refresh
Troubleshooting
Error
Fix
State lock
terraform force-unlock <id> (verify no one else running)
Provider version
Pin version in required_providers
Dependency cycle
Use depends_on or restructure resources
Destroy then create
Add create_before_destroy = true
Drift detected
terraform refresh or fix in config
Pages that link here
March 27, 2026 01:19:38
March 6, 2026 10:37:01