Terraform and OpenTofu: Cloud Infrastructure as Code
Terraform and OpenTofu are open-source declarative Infrastructure as Code (IaC) engines that enable provisioning, updating, and managing cloud resources (AWS, Azure, GCP, Kubernetes) using HashiCorp Configuration Language (HCL2).
⚡ Quick Dive
CLI Command Cheat Sheet
| Command | Action | Example |
|---|---|---|
tofu init / terraform init |
Initialize working directory & download provider plugins | tofu init -upgrade |
tofu plan |
Generate and preview execution plan against remote API | tofu plan -out=tfplan |
tofu apply |
Apply changes to reach desired state | tofu apply tfplan |
tofu destroy |
Tear down and destroy all tracked infrastructure | tofu destroy -target=module.staging |
tofu fmt |
Rewrites config files to canonical formatting | tofu fmt -recursive |
tofu validate |
Verifies configuration syntax and internal consistency | tofu validate |
tofu state list |
List all managed resources inside the state file | tofu state list |
tofu output |
Extract value of output variables from state | tofu output -raw cluster_endpoint |
Production Project Structure
infrastructure/
├── main.tf # Root module resources and provider bindings
├── variables.tf # Input variable type definitions & defaults
├── outputs.tf # Output values exported from the state
├── terraform.tf # Required providers and remote backend configuration
├── terraform.tfvars # Environment-specific variable assignments
└── modules/ # Reusable local modules
├── vpc/
└── eks/
📖 Extended Guide
1. Remote State & Distributed State Locking
In team environments, never store terraform.tfstate on local developer machines. Use a remote backend with distributed state locking to prevent race conditions during concurrent runs.
AWS S3 + DynamoDB Remote Backend Configuration (terraform.tf):
terraform {
required_version = ">= 1.8.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.50"
}
}
backend "s3" {
bucket = "mycompany-terraform-state-prod"
key = "core-infra/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-state-locks"
encrypt = true
}
}
2. Core HCL2 Blocks & Idioms
# variables.tf
variable "environment" {
type = string
description = "Target deployment environment"
default = "production"
}
variable "vpc_cidr" {
type = string
description = "CIDR block for VPC"
validation {
condition = can(cidrnetmask(var.vpc_cidr))
error_message = "Must be a valid IPv4 CIDR block."
}
}
# main.tf
locals {
common_tags = {
Environment = var.environment
ManagedBy = "Terraform"
}
}
# Data source: query existing cloud metadata
data "aws_availability_zones" "available" {
state = "available"
}
# Resource definition
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = merge(local.common_tags, {
Name = "${var.environment}-vpc"
})
lifecycle {
# Prevent accidental destruction of critical infrastructure
prevent_destroy = true
}
}
# outputs.tf
output "vpc_id" {
value = aws_vpc.main.id
description = "ID of the provisioned VPC"
}
3. Resource Lifecycle Customization
HCL lifecycle blocks control how Terraform manages resource replacement:
create_before_destroy = true: Creates the replacement resource before terminating the old one (essential for zero-downtime DNS, load balancers, and certificates).prevent_destroy = true: Rejects anyterraform destroyor plan that would delete this critical resource (databases, main VPCs).ignore_changes = [ tags["UpdatedBy"] ]: Prevents Terraform from reverting changes made by external autoscalers or security tools.