Some links in this article are affiliate links. We earn a commission at no extra cost to you when you purchase through them. Full disclosure.
Click any item to expand the explanation and examples. New to Terraform? Start with what is Terraform or compare it with alternatives in Terraform vs Pulumi.
🚀 Core Commands
terraform init / plan / apply / destroy cli
# Initialize (download providers, set up backend) terraform initThe workflow:Preview changes
terraform plan
Apply changes
terraform apply terraform apply -auto-approve # Skip confirmation
Destroy everything
terraform destroy
Format code
terraform fmt terraform fmt -recursive
Validate syntax
terraform validate
Show current state
terraform show
List resources in state
terraform state list
init → plan → apply. Always plan before apply.
terraform state — manage state cli
# List all resources terraform state listShow details of a resource
terraform state show aws_instance.web
Remove resource from state (without destroying it)
terraform state rm aws_instance.web
Move/rename resource in state
terraform state mv aws_instance.web aws_instance.app
Import existing resource into state
terraform import aws_instance.web i-1234567890abcdef0
Pull remote state to local file
terraform state pull > state.json
📐 HCL Syntax
Providers and resources syntax
# Provider
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider “aws” {
region = “eu-west-1”
}
Resource
resource “aws_instance” “web” {
ami = “ami-0c55b159cbfafe1f0”
instance_type = “t3.micro”
tags = {
Name = “web-server”
Env = “production”
}
}
Variables and outputs syntax
# variables.tf
variable "region" {
type = string
default = "eu-west-1"
description = "AWS region"
}
variable “instance_type” {
type = string
default = “t3.micro”
}
variable “allowed_cidrs” {
type = list(string)
default = [“0.0.0.0/0”]
}
variable “tags” {
type = map(string)
default = {
Env = “dev”
}
}
Use variables
resource “aws_instance” “web” {
instance_type = var.instance_type
tags = var.tags
}
outputs.tf
output “instance_ip” {
value = aws_instance.web.public_ip
description = “Public IP of the web server”
}
Set variables
terraform apply -var=“region=us-east-1”
terraform apply -var-file=“prod.tfvars”
export TF_VAR_region=us-east-1
Data sources and locals syntax
# Data source — read existing resources
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = “name”
values = [“ubuntu/images/hvm-ssd/ubuntu--amd64-server-”]
}
}
resource “aws_instance” “web” {
ami = data.aws_ami.ubuntu.id
}
Locals — computed values
locals {
name_prefix = ”${var.project}-${var.environment}”
common_tags = {
Project = var.project
Environment = var.environment
ManagedBy = “terraform”
}
}
resource “aws_instance” “web” {
tags = merge(local.common_tags, { Name = ”${local.name_prefix}-web” })
}
🔁 Loops & Conditionals
count, for_each, for loops
# count — create N copies
resource "aws_instance" "web" {
count = 3
ami = "ami-123"
instance_type = "t3.micro"
tags = { Name = "web-${count.index}" }
}
for_each — create from map/set
resource “aws_iam_user” “users” {
for_each = toset([“alice”, “bob”, “carol”])
name = each.value
}
for_each with map
variable “buckets” {
default = {
assets = { acl = “private” }
logs = { acl = “private” }
public = { acl = “public-read” }
}
}
resource “aws_s3_bucket” “this” {
for_each = var.buckets
bucket = ”${var.project}-${each.key}”
}
for expression
output “instance_ips” {
value = [for i in aws_instance.web : i.public_ip]
}
Conditional
resource “aws_instance” “bastion” {
count = var.create_bastion ? 1 : 0
ami = “ami-123”
instance_type = “t3.micro”
}
Prefer for_each over count — it’s safer when removing items from the middle of a list.
📦 Modules
Using and creating modules modules
# Use a public module
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = “my-vpc”
cidr = “10.0.0.0/16”
azs = [“eu-west-1a”, “eu-west-1b”]
}
Use a local module
module “web” {
source = ”./modules/web-server”
instance_type = “t3.small”
subnet_id = module.vpc.public_subnets[0]
}
Module structure
modules/web-server/
main.tf
variables.tf
outputs.tf
🗄️ Backend (Remote State)
S3 backend backend
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "eu-west-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
Always use remote state in teams. The DynamoDB table prevents concurrent modifications.
⚡ Tips
Common patterns tips
# Target specific resource terraform plan -target=aws_instance.web terraform apply -target=aws_instance.webRefresh state (detect drift)
terraform refresh
Generate dependency graph
terraform graph | dot -Tpng > graph.png
Workspace (multiple environments)
terraform workspace new staging terraform workspace select production terraform workspace list
Lock version
terraform { required_version = ”>= 1.5.0” }
.gitignore for Terraform
.terraform/
*.tfstate
*.tfstate.backup
*.tfvars (if contains secrets)
Quick access: Raycast lets you search commands, snippets, and cheat sheets instantly from your keyboard. Free for Mac.
Related:* What is Terraform? A Simple Explanation for Developers