Terraform Advanced: Modules, Workspaces, and State Management

Advanced Terraform: Scaling Your Infrastructure

As your infrastructure grows, you need more sophisticated ways to manage it. This post covers advanced Terraform concepts that help you scale and maintain complex infrastructure effectively.

Terraform Modules

Modules are containers for multiple resources that are used together. Think of them as reusable infrastructure components.

Creating a Module

Here’s an example of a VPC module:

# modules/vpc/main.tf
variable "vpc_cidr" {
  type = string
}

variable "environment" {
  type = string
}

variable "public_subnet_cidrs" {
  type = list(string)
}

resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr
  
  tags = {
    Name        = "${var.environment}-vpc"
    Environment = var.environment
  }
}

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 = data.aws_availability_zones.available.names[count.index]
  
  tags = {
    Name = "${var.environment}-public-${count.index + 1}"
  }
}

output "vpc_id" {
  value = aws_vpc.main.id
}

output "public_subnet_ids" {
  value = aws_subnet.public[*].id
}

Using a Module

module "vpc" {
  source = "./modules/vpc"
  
  vpc_cidr            = "10.0.0.0/16"
  environment         = var.environment
  public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24"]
}

# Reference module outputs
resource "aws_instance" "web" {
  subnet_id = module.vpc.public_subnet_ids[0]
}

Workspaces

Workspaces let you manage multiple states for the same configuration. They’re perfect for managing different environments:

# Create and list workspaces
terraform workspace new dev
terraform workspace new prod
terraform workspace list

# Switch between workspaces
terraform workspace select prod

# Show current workspace
terraform workspace show

Use workspace names in your configuration:

locals {
  environment = terraform.workspace
  
  instance_type = {
    dev  = "t2.micro"
    prod = "t2.small"
  }
}

resource "aws_instance" "app" {
  instance_type = local.instance_type[local.environment]
  
  tags = {
    Environment = local.environment
  }
}

Remote State Management

Backend Configuration

Store your state remotely for better collaboration and security:

# backend.tf
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "global/s3/terraform.tfstate"
    region         = "us-west-2"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

State Management Commands

# Import existing resources
terraform import aws_instance.web i-1234567890abcdef0

# Show state
terraform show

# List resources
terraform state list

# Move resources
terraform state mv aws_instance.app aws_instance.web

# Remove resources from state
terraform state rm aws_instance.old

Advanced HCL (HashiCorp Configuration Language)

Dynamic Blocks

resource "aws_security_group" "example" {
  name = "dynamic-example"

  dynamic "ingress" {
    for_each = var.service_ports
    content {
      from_port   = ingress.value
      to_port     = ingress.value
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }
}

Complex Expressions

locals {
  # Map transformation
  instance_tags = {
    for key, value in var.tags :
    key => upper(value)
    if key != "Environment"
  }
  
  # List comprehension
  public_instance_ids = [
    for instance in aws_instance.public :
    instance.id if instance.public_ip != ""
  ]
  
  # Conditional expression
  instance_type = var.environment == "prod" ? "t2.medium" : "t2.micro"
}

Provider Configuration

Multiple Provider Configurations

# Configure AWS providers for different regions
provider "aws" {
  region = "us-west-2"
  alias  = "west"
}

provider "aws" {
  region = "us-east-1"
  alias  = "east"
}

# Use specific provider configurations
resource "aws_instance" "west_coast" {
  provider = aws.west
  ami      = "ami-123456"
}

resource "aws_instance" "east_coast" {
  provider = aws.east
  ami      = "ami-789012"
}

Best Practices for Large-Scale Infrastructure

  1. State Organization
    • Use workspaces or separate state files for different environments
    • Consider using partial configurations for sensitive values
  2. Module Strategy
    • Create small, focused modules
    • Version your modules
    • Use consistent interface patterns
  3. Code Organization
    infrastructure/
    β”œβ”€β”€ modules/
    β”‚   β”œβ”€β”€ vpc/
    β”‚   β”œβ”€β”€ ecs/
    β”‚   └── rds/
    β”œβ”€β”€ live/
    β”‚   β”œβ”€β”€ prod/
    β”‚   β”‚   β”œβ”€β”€ main.tf
    β”‚   β”‚   β”œβ”€β”€ variables.tf
    β”‚   β”‚   └── terraform.tfvars
    β”‚   └── staging/
    β”œβ”€β”€ global/
    └── shared/
    
  4. State Management
    • Use remote state storage
    • Enable state locking
    • Implement state backup strategies
  5. Security
    • Use variables for sensitive values
    • Implement least privilege access
    • Enable audit logging

Next Steps

  • Explore Terragrunt for keeping your Terraform code DRY
  • Look into tools like pre-commit hooks for Terraform
  • Consider implementing automated testing for your Terraform code
  • Learn about Terraform Enterprise features

Remember:

  • Always review plans before applying
  • Use version control for all configurations
  • Document your code and decisions
  • Implement a proper CI/CD pipeline for Terraform
  • Regular security audits of your infrastructure code
Written on July 12, 2025