Terraform & OpenTofu Guide
# Terraform: brew install terraform # OpenTofu (OSS fork): brew install opentofu # Windows: download from terraform.io/downloads
Terraform lets you describe your entire infrastructure in declarative HCL (HashiCorp Configuration Language) files. `terraform plan` shows what will change, `terraform apply` executes it, `terraform destroy` tears it down. State files track real-world resources.
OpenTofu is the open-source fork of Terraform (after HashiCorp switched to BSL license). It's API-compatible with Terraform providers and modules. Most Terraform code works with OpenTofu without changes — just replace `terraform` with `tofu`.
Modules (`terraform-aws-vpc`) are reusable infrastructure packages. Use remote state (S3 + DynamoDB, or Terraform Cloud) for team collaboration. Terraform workspaces manage multiple environments (dev/staging/prod). GUI: Terraform Cloud, OpenTofu via Terragrunt for DRY configurations.
Basic Workflow
terraform init # Download providers terraform plan -out plan.tfplan # Preview changes echo "yes" | terraform apply plan.tfplan # or: terraform apply -auto-approve # Skip confirmation terraform destroy -auto-approve # Tear everything down
# OpenTofu uses identical HCL syntax # Just replace the command: tofu init tofu plan tofu apply # Test migration from Terraform: # 1. Backup .terraform/ # 2. tofu init -migrate-state # 3. tofu plan # Should show no changes
Providers
# main.tf
terraform {
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
}
}
provider "aws" {
region = "us-west-2"
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "HelloWorld"
}
}output "instance_ip" {
value = aws_instance.web.public_ip
description = "The public IP of the web server"
}
# After apply:
terraform output instance_ip
terraform output -jsonState Management
# backend.tf erraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-west-2"
dynamodb_table = "terraform-locks" # For state locking
encrypt = true
}
}
# State commands:
terraform state list # List all resources
terraform state show aws_instance.web # Show resource details
terraform state rm aws_instance.web # Remove from state (not destroy)
terraform import aws_instance.web i-12345 # Import existing resourceModules
# variables.tf
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Must be dev, staging, or prod."
}
}
# Use: var.instance_type
# Set: terraform apply -var="instance_type=t3.large"
# Or via terraform.tfvars:
# instance_type = "t3.large"module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-2a", "us-west-2b"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
private_subnets = ["10.0.10.0/24", "10.0.20.0/24"]
enable_nat_gateway = true
tags = { Environment = "dev" }
}Workspaces
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
terraform workspace select dev
# Use in config:
resource "aws_instance" "web" {
tags = { Environment = terraform.workspace }
}
# Different variable files per workspace:
terraform apply -var-file="dev.tfvars"
terraform apply -var-file="prod.tfvars"