Terraform Modules and Workspaces: Managing Multi-Environment AWS Infrastructure at Scale

A practical introduction to Terraform for provisioning AWS infrastructure reproducibly, with a real working configuration.

Beyond a Single main.tf

A single flat Terraform configuration works fine for a demo, but real infrastructure needs to be reused across dev, staging, and production without copy-pasting resource blocks. Modules and workspaces solve this in different, complementary ways.

Structuring a Reusable Module

modules/
  web-service/
    main.tf
    variables.tf
    outputs.tf
environments/
  dev/
    main.tf
  staging/
    main.tf
  production/
    main.tf
// modules/web-service/main.tf
resource "aws_ecs_service" "this" {
  name            = var.service_name
  cluster         = var.cluster_id
  task_definition = aws_ecs_task_definition.this.arn
  desired_count   = var.desired_count
}

resource "aws_ecs_task_definition" "this" {
  family = var.service_name
  container_definitions = jsonencode([{
    name  = var.service_name
    image = var.image
    memory = var.memory
  }])
}

Consuming the Module Per Environment

// environments/production/main.tf
module "web_service" {
  source        = "../../modules/web-service"
  service_name  = "web-prod"
  cluster_id    = data.aws_ecs_cluster.main.id
  image         = "myregistry/web:1.4.0"
  desired_count = 5
  memory        = 1024
}
// environments/dev/main.tf
module "web_service" {
  source        = "../../modules/web-service"
  service_name  = "web-dev"
  cluster_id    = data.aws_ecs_cluster.main.id
  image         = "myregistry/web:latest"
  desired_count = 1
  memory        = 256
}

Workspaces: A Lighter-Weight Alternative

For infrastructure that’s structurally identical across environments and only differs in variable values, workspaces avoid duplicating directory structures entirely:

terraform workspace new staging
terraform workspace new production
terraform workspace select production
terraform apply -var-file="production.tfvars"

Workspaces share the same configuration but maintain separate state files — useful for near-identical environments, less suited when environments genuinely diverge structurally (e.g., production has a multi-AZ database and dev doesn’t).

Modules vs Workspaces: When to Use Which

Situation Approach
Environments have structural differences Separate directories + shared modules
Environments are nearly identical, differ only in scale/values Workspaces
Reusing the same resource pattern across many services Modules

Remote State Per Environment

// environments/production/backend.tf
terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "production/terraform.tfstate"
    region = "us-east-1"
  }
}

Conclusion

Modules make infrastructure patterns reusable; workspaces or separate environment directories keep environments isolated. Most teams outgrow workspaces-only setups as production infrastructure diverges from dev — plan for separate environment directories with shared modules from the start if you expect that divergence.