Problem Context

You have a single Terraform configuration and you need to deploy it to dev, staging, and production with different variable values. The temptation is to use Terraform workspaces โ€” a built-in feature for maintaining multiple state files from a single configuration. But workspaces are frequently misunderstood and misused, leading to environments that are supposed to be isolated but share more than they should.

Understanding when workspaces are the right tool โ€” and when separate directories or backends serve you better โ€” is one of the most important Terraform architectural decisions for multi-environment infrastructure.

๐Ÿค” Sound familiar?
  • You're about to use workspaces for dev/staging/prod but aren't sure if that's the right approach
  • A workspace plan showed it would delete production resources because you were in the wrong workspace
  • Your dev workspace shares some resources with production and you don't know which
  • You're not sure how workspaces relate to Terraform Cloud environments

Know when to use workspaces โ€” and when not to. Both decisions matter.

How Workspaces Work

Terraform workspaces maintain separate state files for the same configuration directory. The default workspace is called default. Every terraform apply modifies the state of the current workspace only.

# List workspaces
terraform workspace list

# Create and switch to a new workspace
terraform workspace new staging

# Switch to an existing workspace
terraform workspace select production

# Show current workspace
terraform workspace show

# Delete a workspace (must be empty of resources)
terraform workspace delete staging

You can reference the current workspace inside your configuration:

locals {
  is_production = terraform.workspace == "production"
}

resource "azurerm_service_plan" "main" {
  name     = "asp-myapp-${terraform.workspace}"
  sku_name = local.is_production ? "P2v3" : "B2"
  ...
}

Workspace-Based Environment Strategy

Workspaces work well for ephemeral, short-lived environments โ€” feature branch environments, review apps, temporary testing environments. Each developer creates a workspace, deploys, and destroys it when done:

# CI: create feature branch environment
WORKSPACE="feature-${PR_NUMBER}"
terraform workspace new $WORKSPACE || terraform workspace select $WORKSPACE
terraform apply -var="environment=$WORKSPACE" -auto-approve

# CI: destroy on PR merge
terraform workspace select $WORKSPACE
terraform destroy -auto-approve
terraform workspace select default
terraform workspace delete $WORKSPACE

When NOT to Use Workspaces for Environments

HashiCorp itself recommends against using workspaces as a primary environment promotion mechanism. The reasons:

  • Single backend: All workspace state files are in the same storage location. A misconfigured IAM policy can expose production state to dev users.
  • Shared provider configuration: All workspaces use the same providers.tf. You can't easily use different service principals per environment.
  • Human error: terraform workspace select production && terraform applylooks identical to dev commands. It's easy to run apply against the wrong workspace.
  • Configuration parity: It's hard to guarantee that dev and prod use the same code version when you're switching workspaces in a single directory.

The Alternative: Directory-Per-Environment

infra/
โ”œโ”€โ”€ modules/
โ”‚   โ”œโ”€โ”€ app-service/
โ”‚   โ””โ”€โ”€ networking/
โ”œโ”€โ”€ environments/
โ”‚   โ”œโ”€โ”€ dev/
โ”‚   โ”‚   โ”œโ”€โ”€ main.tf          # Calls modules
โ”‚   โ”‚   โ”œโ”€โ”€ backend.tf       # Separate state per env
โ”‚   โ”‚   โ”œโ”€โ”€ terraform.tfvars # Dev-specific values
โ”‚   โ”‚   โ””โ”€โ”€ providers.tf     # Dev service principal
โ”‚   โ”œโ”€โ”€ staging/
โ”‚   โ”‚   โ””โ”€โ”€ ...
โ”‚   โ””โ”€โ”€ production/
โ”‚       โ””โ”€โ”€ ...

Benefits: strict isolation at the filesystem level, separate backends (separate IAM for state access), clear blast radius, independent deployment pipelines.

# Different state keys for complete isolation
# environments/production/backend.tf
terraform {
  backend "azurerm" {
    resource_group_name  = "rg-terraform-state"
    storage_account_name = "stterraformstate001"
    container_name       = "tfstate"
    key                  = "production/main.tfstate"
  }
}

# environments/dev/backend.tf
terraform {
  backend "azurerm" {
    resource_group_name  = "rg-terraform-state"
    storage_account_name = "stterraformstate001"
    container_name       = "tfstate"
    key                  = "dev/main.tfstate"
  }
}

Terraform Cloud Workspaces

Terraform Cloud workspaces are a different concept from CLI workspaces. Each Terraform Cloud workspace has its own:

  • State file
  • Variables (including secrets)
  • Run history
  • Access controls
  • VCS trigger configuration

Terraform Cloud workspaces map well to environments โ€” they're the right place for dev/staging/prod isolation.

# Connecting a directory to a Terraform Cloud workspace
# terraform.tf
terraform {
  cloud {
    organization = "myorg"
    workspaces {
      name = "myapp-production"   # One workspace per environment
    }
  }
}

Decision Guide

Use workspaces when:
  โœ… Creating short-lived feature/preview environments
  โœ… Testing configuration changes before promoting to long-lived envs
  โœ… Environments are structurally identical, just need separate state

Use separate directories when:
  โœ… Long-lived environments (dev, staging, prod)
  โœ… Environments need different provider configs or service principals
  โœ… Environment state must be strictly isolated for compliance
  โœ… Different teams own different environments

Production Checklist

  • Do not use CLI workspaces for long-lived dev/staging/prod environments โ€” use separate directories
  • Use workspaces for ephemeral PR/feature environments where parity with prod is required
  • Always display the current workspace in your terminal prompt when working with Terraform
  • Add workspace name validation in variables.tf if using workspaces for environments
  • In CI, set TF_WORKSPACE environment variable explicitly rather than relying on local state
  • For Terraform Cloud, create one workspace per environment with separate variable sets per workspace