What Terraform Does
- Declarative Config: Define desired state, Terraform figures out how to get there
- Execution Plans: Preview exactly what will change before applying
- Resource Graph: Automatic dependency resolution and parallel creation
- State Management: Track real-world resources in state files (local or remote)
- Modules: Reusable infrastructure components for DRY configuration
- Multi-Cloud: Manage AWS, Azure, GCP, Kubernetes, DigitalOcean, and more from one tool
- Import: Bring existing resources under Terraform management
- Workspaces: Manage multiple environments (dev/staging/prod) from one codebase
Architecture
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ .tf files │────▶│ Terraform │────▶│ Cloud APIs │
│ (HCL config)│ │ Core │ │ AWS │
└──────────────┘ │ (Go) │ │ Azure │
└──────┬───────┘ │ GCP │
│ │ K8s │
┌──────┴───────┐ │ 3000+ more │
│ State File │ └──────────────┘
│ (JSON) │
└──────────────┘Basic Example
main.tf (AWS EC2 Instance)
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 = "web-server"
Env = "production"
}
}
resource "aws_security_group" "web" {
name = "web-sg"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
output "instance_ip" {
value = aws_instance.web.public_ip
}Workflow
# Initialize (download providers)
terraform init
# Preview changes
terraform plan
# Apply changes
terraform apply
# Get outputs
terraform output instance_ip
# Destroy everything
terraform destroyVariables & Modules
Variables
# variables.tf
variable "environment" {
type = string
default = "dev"
}
variable "instance_count" {
type = number
default = 1
}
variable "tags" {
type = map(string)
default = {
Owner = "devops"
}
}
# Using variables
resource "aws_instance" "web" {
count = var.instance_count
ami = "ami-..."
instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
tags = merge(var.tags, {
Environment = var.environment
})
}Modules
# Use a module
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", "us-west-2c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = false
}State Management
Local State (Default)
terraform.tfstate # Current state
terraform.tfstate.backup # Previous stateRemote State (Production)
# backend.tf
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-west-2"
dynamodb_table = "terraform-locks" # State locking
encrypt = true
}
}Benefits of remote state:
- Team collaboration (shared state)
- State locking (prevent concurrent modifications)
- Encrypted storage
- Versioning and backup
Multi-Cloud Example
# AWS + Cloudflare + Kubernetes
provider "aws" { region = "us-west-2" }
provider "cloudflare" { api_token = var.cf_token }
provider "kubernetes" {
config_path = "~/.kube/config"
}
# Create AWS EC2 instance
resource "aws_instance" "web" { ... }
# Create Cloudflare DNS record pointing to EC2
resource "cloudflare_record" "web" {
zone_id = var.cf_zone_id
name = "www"
value = aws_instance.web.public_ip
type = "A"
}
# Deploy to Kubernetes
resource "kubernetes_deployment" "app" {
metadata { name = "my-app" }
spec {
replicas = 3
template {
spec {
container {
image = "myapp:latest"
name = "app"
}
}
}
}
}Terraform vs Alternatives
| Feature | Terraform | OpenTofu | Pulumi | CloudFormation |
|---|---|---|---|---|
| Open Source | BSL | MPL-2.0 | Apache-2.0 | No |
| Language | HCL | HCL | Python/TS/Go/C# | YAML/JSON |
| Multi-cloud | Yes | Yes | Yes | AWS only |
| Providers | 3000+ | Same as TF | 100+ | AWS services |
| State backend | Multiple | Multiple | Cloud (free) | AWS managed |
| Community | Very large | Growing | Large | AWS focused |
OpenTofu — The Open Source Fork
Note: In August 2023, HashiCorp changed Terraform's license from MPL-2.0 to BSL. The Linux Foundation and community created OpenTofu as an open-source fork. OpenTofu is drop-in compatible with Terraform 1.5.x and maintains MPL-2.0 licensing.
# Use OpenTofu instead (drop-in replacement)
brew install opentofu
tofu init
tofu plan
tofu applyFAQ
Q: What's the difference between Terraform and Ansible? A: Terraform is primarily for infrastructure provisioning (creating cloud resources like VMs, networks, databases). Ansible is primarily for configuration management (installing software and configuring existing machines). They often work together: Terraform creates the servers, Ansible configures them.
Q: Does the BSL license affect usage? A: For most users, no. The BSL restriction is that you can't offer Terraform as a competing commercial product (like building your own HashiCorp Cloud competitor). Personal and internal enterprise use are completely unrestricted. If you're worried about licensing, use OpenTofu.
Q: What if I lose my state file? A: The state file is crucial — losing it means Terraform no longer knows which resources it manages. Always use a remote state backend (S3, Azure Blob, GCS) with versioning enabled, and back up state files regularly.
Sources & Credits
- GitHub: hashicorp/terraform — 48.1K+ ⭐ | BSL
- Website: terraform.io
- OpenTofu: opentofu.org