DEV Community

Francis Eytan Dortort
Francis Eytan Dortort

Posted on • Originally published at dortort.com

Terraform at Scale: Folders, Workspaces, or Services?

TL;DR

  • Terraform structures must match the type of divergence across environments: value-based (sizes, counts) or structural (providers, topology, IAM boundaries).
  • Folder-per-environment is safe and explicit but risks drift without strong module discipline.
  • Workspaces support value-based differences but are operationally weak for structurally divergent or highly isolated environments.
  • Per-service root modules scale best in microservice organizations.
  • Service-aligned workspaces offer a hybrid approach but carry operational risks.
  • Environment generators (Terragrunt/codegen) provide maximal parity and DRYness but add tooling complexity.
  • Environment parity is achieved through module logic, not directory layout.

Terraform becomes difficult to manage as teams introduce multiple microservices and long-lived environments like dev, staging, and prod. A sustainable Terraform architecture balances:

  • Environment parity
  • Strong isolation
  • Service-level autonomy
  • DRY logic
  • Support for divergence
  • Predictable promotion flows

Choosing the right pattern is primarily about understanding the nature of your environment differences and the structure of your engineering organization.


Best Practices

Strong State Isolation

Dev operations must be structurally incapable of impacting prod.

Minimize Blast Radius

Decompose monolithic state files. Smaller root modules ensure that a bad apply in one service cannot accidentally destroy resources in another.

DRY Logic Through Modules

All environment logic should live in modules to prevent drift.

Maintain Environment Parity Where Required

Staging and prod should behave equivalently except for intended differences.

Support Intentional Divergence

Differences must be expressed cleanly, either as variables or structural changes.

Predictable Promotion Workflows

Promotion paths should be deterministic and low risk.

These practices drive the evaluation of the Terraform patterns below.


Value Divergence vs. Structural Divergence

A critical distinction for choosing a pattern:

Value Divergence
Differences in parameters: instance size, feature flags, scaling limits.
Workspaces handle these well.

Structural Divergence
Differences in topology, provider configurations, IAM boundaries, backends, or additional resources.
Workspaces struggle here because they share a single main.tf and provider configuration. If dev requires an AWS Provider in Account A and prod requires Account B, Workspaces require complex conditional logic. Folder-based layouts handle this natively by having distinct provider blocks for each environment.

  • Critical Example: If dev lives in AWS Account A and prod in AWS Account B, the Terraform provider block often needs distinct configurations (e.g. allowed account IDs). Workspaces share a single main.tf and provider block, making multi-account deployments brittle or hacky. Folder-based layouts handle this natively.

This distinction explains why patterns differ more than expressiveness alone would suggest.


Terraform Architectural Patterns

Folder-per-Environment

infra/
  modules/
    app/
      main.tf
      variables.tf
      outputs.tf
  envs/
    dev/
      main.tf
      backend.tf
      variables.tf
    staging/
      main.tf
      backend.tf
      variables.tf
    prod/
      main.tf
      backend.tf
      variables.tf
Enter fullscreen mode Exit fullscreen mode

Pros

  • Strong isolation
  • Clear boundaries
  • Simple CI/CD setup
  • Explicit divergence

Cons

  • Potential for configuration drift
  • Folder duplication
  • Less ergonomic for ephemeral environments

Example
envs/prod/main.tf:

module "app" {
  source        = "../../modules/app"
  instance_size = "m5.large"
  environment   = "prod"
}
Enter fullscreen mode Exit fullscreen mode

Single Root Module + Workspaces

infra/
  main.tf
  variables.tf
Enter fullscreen mode Exit fullscreen mode

Pros

  • High parity
  • Very DRY
  • Ideal for ephemeral environments
  • Compact codebase

Cons

  • Weak isolation
  • Not operationally suited for structural divergence (different topologies, providers, IAM boundaries)
  • Harder CI/CD
  • Increased risk of workspace misuse

Workspaces are best for environments that differ only by variable values, not structure.

Example
CLI usage:

terraform workspace select prod
terraform apply -var-file=prod.tfvars
Enter fullscreen mode Exit fullscreen mode

Per-Service Root Modules

infra/
  service-a/
    dev/
      main.tf
      variables.tf
      outputs.tf
      terraform.tfvars
    prod/
      main.tf
      variables.tf
      outputs.tf
      terraform.tfvars
  service-b/
    dev/
      main.tf
      variables.tf
      outputs.tf
      terraform.tfvars
    prod/
      main.tf
      variables.tf
      outputs.tf
      terraform.tfvars
Enter fullscreen mode Exit fullscreen mode

Pros

  • Small blast radius
  • Strong service autonomy
  • Clear ownership boundaries
  • Good fit for microservice scale

Cons

  • More folders to manage
  • Requires consistent module use

Service-Aligned Workspaces (The Hybrid)

services/
  billing/
    main.tf       # Single root for billing
    variables.tf
    terraform.tfvars
  auth/
    main.tf       # Single root for auth
    variables.tf
    terraform.tfvars
Enter fullscreen mode Exit fullscreen mode

How it works: Each service has a single root module that uses Terraform Workspaces to target different environments. This combines the "Per-Service" organization of (C) with the "DRY" nature of (B).

Pros

  • Redundant environment folders are eliminated
  • Logic defined once per service
  • High consistency within a service

Cons

  • Inherits the operational risks of Workspaces
  • Structural divergence (different providers for Dev/Prod) is painful
  • Requires disciplined review to prevent workspace misuse

Environment Generators & Wrappers (Terragrunt / CDKTF)

live/
  dev/
    networking/
      terragrunt.hcl
    compute/
      terragrunt.hcl
  prod/
    networking/
      terragrunt.hcl
    compute/
      terragrunt.hcl
modules/
  networking/
    main.tf
    variables.tf
    outputs.tf
  compute/
    main.tf
    variables.tf
    outputs.tf
Enter fullscreen mode Exit fullscreen mode

Pros

  • Maximum DRY
  • Maximum parity
  • Rapid environment creation
  • Scalable for large organizations

Cons

  • Additional tooling complexity
  • Debugging requires awareness of generation layers

Best Practice Alignment Matrix

Best Practice Folder-per-Env Workspaces Per-Service Roots Service Workspaces Env Generator
1. Operational Safety (Isolation) ●●● ●●● ●●●
2. Minimize Blast Radius ●●● ●●●● ●●●● ●●●
3. DRY Logic Through Modules ●● ●●● ●● ●●● ●●●●
4. Maintain Environment Parity ●● ●●●● ●● ●●●● ●●●●
5. Support Intentional Divergence ●●● ●● ●●● ●● ●●
6. Predictable Promotion Workflow ●●● ●● ●●● ●● ●●●

Note: "Service Workspaces" (D) scores similarly to "Workspaces" (B) for isolation and divergence because it relies on the same underlying mechanism, despite being organized by service.


Decision Tree

Mermaid Diagram


Takeaways

  • Folder-per-environment remains a safe and understandable pattern for teams that prioritize isolation, especially when environments diverge structurally.
  • Workspaces are best for simple, uniform, or ephemeral environments—not for structurally divergent or strongly isolated ones.
  • Per-service root modules align naturally with microservices, balancing autonomy with isolation.
  • Service-aligned workspaces reduce folder duplication but carry the operational risks of workspaces.
  • Environment generators enable maximum DRY and parity but introduce additional tooling.
  • Parity is enforced by modules, not directory layout.

Further Reading

Terraform Documentation & Official Guidance

Industry Articles & Guides

Community Discussion & Prior Art

Tooling

Top comments (0)