Defining infrastructure for different environments (dev, stage, prod) is a bad practice.

Approaches

Isolation via Terraform workspaces

CLI workspaces isolation have a few drawbacks:

  • State files are stored in the same backend. May be not great when full account isolation between environments is required. But I found a workaround for that (give write access to only the specific folder in the S3 backend for the given user, store all states for all environments in the master AWS account, and deploy other workspaces in different accounts by providing different auth).
  • Visibility from the code is not great. You can’t easily understand what environments are created and what deployed there. The workaround is isolating with different branches to target different workspaces, and create envs directory in each module to provide values to the variables and improve visibility of which environments are created. Not great, but still.
  • May be error prone: you can run some terraform CLI command on the wrong environment. Can be addressed with using task or make commands that will explicitly select target environment before running any plan or apply commands.

Isolation via file layout

Allows to achieve full isolation between environments and configure each environment differently. And it’s clearer what environments are in the project and how they configured.

  • stage - for pre-production workloads (i.e., testing)
  • prod - for production workloads (i.e., user-facing apps)
  • mgmt - for DevOps tooling (e.g., bastion host, CI server)
  • global - to put resources that are used across all environments (e.g., S3, IAM)