Infrastructure as Code (IaC) Principles
Infrastructure as Code is the practice of managing and provisioning infrastructure through machine-readable definition files rather than manual processes or interactive configuration tools. IaC brings software engineering practices — version control, code review, testing, and CI/CD — to infrastructure.
IaC Core Principles
- Idempotency: Applying the same configuration multiple times produces the same result
- Immutability: Replace resources rather than modifying them in place
- Declarative: Describe the desired end state, not the steps to get there
- Version Control: All infrastructure code lives in Git alongside application code
- Automation: Infrastructure changes are applied through CI/CD, not manual commands
Terraform Modules
Modules are the key building block for reusable, composable Terraform code. A module is a container for multiple resources that are used together. Instead of copying infrastructure code, you define it once as a module and instantiate it multiple times with different parameters.
# Module directory structure:
# modules/
# vpc/
# main.tf
# variables.tf
# outputs.tf
# eks-cluster/
# main.tf
# variables.tf
# outputs.tf
# rds/
# main.tf
# variables.tf
# outputs.tf
# modules/vpc/main.tf
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project}-${var.environment}-vpc"
}
}
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project}-${var.environment}-public-${count.index + 1}"
"kubernetes.io/role/elb" = "1"
}
}
resource "aws_subnet" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
tags = {
Name = "${var.project}-${var.environment}-private-${count.index + 1}"
"kubernetes.io/role/internal-elb" = "1"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project}-${var.environment}-igw"
}
}
resource "aws_nat_gateway" "main" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public[0].id
tags = {
Name = "${var.project}-${var.environment}-nat"
}
}
resource "aws_eip" "nat" {
domain = "vpc"
}
Using Modules
# main.tf - Using modules
module "vpc" {
source = "./modules/vpc"
project = var.project_name
environment = var.environment
vpc_cidr = "10.0.0.0/16"
public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
private_subnet_cidrs = ["10.0.10.0/24", "10.0.20.0/24", "10.0.30.0/24"]
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
}
module "eks" {
source = "./modules/eks-cluster"
cluster_name = "${var.project_name}-${var.environment}"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
node_count = var.environment == "production" ? 3 : 1
instance_types = ["t3.medium"]
}
module "rds" {
source = "./modules/rds"
identifier = "${var.project_name}-${var.environment}-db"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
instance_class = var.environment == "production" ? "db.r6g.large" : "db.t3.micro"
multi_az = var.environment == "production"
}
Terraform Workspaces
# Create and manage workspaces
terraform workspace list
terraform workspace new staging
terraform workspace new production
terraform workspace select production
# Use workspace name in configuration
# main.tf
locals {
environment = terraform.workspace
instance_count = {
development = 1
staging = 2
production = 3
}
}
resource "aws_instance" "web" {
count = local.instance_count[local.environment]
instance_type = local.environment == "production" ? "t3.large" : "t3.micro"
# ...
}
State Management Best Practices
# Remote state with locking
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "projects/my-app/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-state-locks"
encrypt = true
}
}
# State commands
terraform state list # List all resources
terraform state show aws_s3_bucket.website # Show resource details
terraform state mv aws_s3_bucket.old aws_s3_bucket.new # Rename/move
terraform state rm aws_s3_bucket.website # Remove from state (not from cloud)
terraform import aws_s3_bucket.website my-existing-bucket # Import existing resource
IaC Tool Comparison
| Tool | Language | State | Best For |
|---|---|---|---|
| Terraform | HCL | Stateful | Multi-cloud infrastructure |
| Pulumi | TypeScript, Python, Go | Stateful | Developers who prefer real languages |
| CloudFormation | YAML/JSON | AWS-managed | AWS-only infrastructure |
| CDK | TypeScript, Python | AWS-managed | AWS with programming languages |
| Ansible | YAML | Stateless | Configuration management, provisioning |
Key Takeaways
- IaC brings software engineering practices to infrastructure management
- Terraform modules enable reusable, composable infrastructure components
- Always store state remotely with locking for team collaboration
- Use workspaces or separate state files to manage multiple environments
- Apply infrastructure changes through CI/CD pipelines, not manual commands