.tfstate files to ensure that a mistake in the Development environment doesn’t accidentally delete the Production database?1. The Multi-Environment Problem#
Imagine you have a beautifully written main.tf file that provisions a complete microservices architecture. You run terraform apply, and it deploys perfectly to your AWS account.
Now, your manager asks you to deploy a “Staging” clone of this exact architecture.
If you just change the variables in terraform.tfvars from prod to staging and run terraform apply in the same directory, Terraform will destroy your Production environment and replace it with Staging. Why? Because both environments are fighting over the exact same .tfstate file!
To solve this, we must isolate the state files. There are two industry-standard ways to achieve this: Workspaces and Directory Isolation.
2. Approach A: Terraform Workspaces#
Terraform Workspaces allow you to use a single directory of HCL code, but maintain multiple, entirely separate .tfstate files underneath the hood.
Practice: Managing Workspaces#
Create a new directory named terraform-workspaces:
mkdir terraform-workspaces
cd terraform-workspacesCreate a main.tf file:
provider "aws" {
region = "us-east-1"
}
# Notice we use the built-in 'terraform.workspace' variable!
resource "aws_s3_bucket" "app_data" {
bucket = "my-company-data-${terraform.workspace}-12345"
}By default, every Terraform directory starts in the default workspace.
terraform init
terraform workspace listExpected Terminal Output:
* defaultLet’s create two new workspaces: staging and production.
terraform workspace new staging
terraform workspace new productionVerify your current workspace:
terraform workspace show(Should output production).
Now, switch back to staging and apply the code:
terraform workspace select staging
terraform apply -auto-approveTerraform just created a bucket named my-company-data-staging-12345.
Now, switch to production and apply the identical code:
terraform workspace select production
terraform apply -auto-approveTerraform just created a second bucket named my-company-data-production-12345.
The Pros and Cons of Workspaces#
| Pros | Cons |
|---|---|
Extremely DRY: You only maintain one set of .tf files. | High Risk: It is very easy to forget which workspace you are currently in. Running terraform destroy in the wrong workspace is a frequent cause of outages. |
| Easy Branching: Perfect for spinning up temporary QA environments tied to Git branches. | No Variable Isolation: You have to manage complex if/else logic in your code to handle differences (e.g., Prod needs 10 servers, Staging needs 1). |
HashiCorp officially recommends against using CLI Workspaces as the primary mechanism for separating Production and Non-Production environments due to the high risk of human error.
3. Approach B: Directory Isolation (The Industry Standard)#
The safer, more robust approach favored by Enterprise Platform Teams is Directory Isolation. Instead of using hidden workspaces, you physically separate the environments into different folders.
Practice: Structuring for Directory Isolation#
Create a new directory structure like this:
terraform-environments/
├── modules/
│ └── web-app/ # Reusable child module (from Episode 9)
│ ├── main.tf
│ └── variables.tf
├── staging/ # Staging Root Module
│ ├── main.tf
│ └── backend.tf
└── production/ # Production Root Module
├── main.tf
└── backend.tfThe Staging Configuration#
In staging/main.tf, you call the module and pass Staging-specific values:
# staging/main.tf
module "web_app" {
source = "../modules/web-app"
environment = "staging"
instance_type = "t3.micro" # Save money in staging
replica_count = 1
}In staging/backend.tf, you configure an isolated state file:
# staging/backend.tf
terraform {
backend "s3" {
bucket = "my-company-terraform-state"
key = "staging/terraform.tfstate" # Isolated path!
region = "us-east-1"
}
}The Production Configuration#
In production/main.tf, you call the exact same module, but with Production values:
# production/main.tf
module "web_app" {
source = "../modules/web-app"
environment = "production"
instance_type = "m5.large" # High performance for prod
replica_count = 5 # High availability
}In production/backend.tf, you ensure the state file is completely segregated:
# production/backend.tf
terraform {
backend "s3" {
bucket = "my-company-terraform-state"
key = "production/terraform.tfstate" # Completely different path!
region = "us-east-1"
}
}The Pros and Cons of Directory Isolation#
| Pros | Cons |
|---|---|
Zero Confusion: You know exactly which environment you are applying because you must physically cd into the folder. | WET Code (Not DRY): You have to copy-paste the provider and backend blocks into every single environment folder. |
Access Control: You can use AWS IAM policies to restrict Junior Developers from accessing the production/ S3 state key. | Update Overhead: If you add a new variable to the module, you must update main.tf in all environment folders. |
Troubleshooting & Common Errors#
Workspace already exists- Root Cause: You ran
terraform workspace new <name>but someone already created it in the remote backend. - Solution: Use
terraform workspace select <name>instead.
- Root Cause: You ran
Error configuring the backend: State data found- Root Cause: When setting up Directory Isolation, you copied the
.terraformhidden folder fromstaging/intoproduction/. - Solution: Never copy the
.terraformdirectory or local.tfstatefiles between environment folders. Always run a freshterraform initin every new environment folder.
- Root Cause: When setting up Directory Isolation, you copied the
Conclusion & Next Steps#
You now understand the architectural trade-offs between Workspaces and Directory Isolation. For enterprise deployments, Directory Isolation provides the strict blast-radius containment required for Production safety.
However, running terraform apply manually from your laptop is a massive security risk, regardless of which architecture you choose.
In Episode 12: CI/CD Automation with Terraform Cloud and GitHub Actions, we will permanently confiscate Terraform execution rights from your laptop and shift all deployments to automated GitOps pipelines!

