Sunday, November 16, 2025

Terraform Workspaces: Managing Multiple Environments

Terraform Workspaces: Managing Multiple Environments
Terraform Workspaces Environments State Management DevOps Multi-Environment

Terraform Workspaces: Managing Multiple Environments

Published on: November 3, 2023 | Author: DevOps Engineering Team

Multi-Environment Management with Workspaces

Welcome to Part 8 of our Terraform Mastery Series! Learn how to efficiently manage multiple environments like development, staging, and production using Terraform workspaces. Master workspace commands, environment-specific configurations, and best practices for scalable infrastructure management.

What are Terraform Workspaces?

Terraform workspaces allow you to manage multiple distinct state files within a single Terraform configuration. Each workspace maintains its own isolated state, enabling you to manage different environments without code duplication.

Workspace Benefits

  • Isolated State: Separate state files for each environment
  • Code Reuse: Single configuration for multiple environments
  • Quick Switching: Easy environment context switching
  • Collaboration: Team members can work in different workspaces
  • Testing: Safe testing without affecting production

Common Workspace Use Cases

  • Development, Staging, Production environments
  • Feature branch deployments
  • Regional deployments (us-east, eu-west, etc.)
  • Customer-specific deployments
  • Blue-green deployment strategies

Workspace Environment Flow

Development
dev workspace
Staging
staging workspace
Production
prod workspace

Same Terraform code, different state files

Default Workspace

Every Terraform configuration starts with a default workspace. When no workspace is explicitly selected, you're working in the default workspace. It's recommended to rename this to something more descriptive like "dev" or "staging".

Workspace Management Commands

Terraform provides a set of workspace commands for creating, selecting, and managing workspaces. Here's the complete workflow:

Workspace Command Structure

terraform workspace
show|list|new|select|delete
workspace_name

Basic Workspace Operations

# Check current workspace
$ terraform workspace show
default

# List all workspaces
$ terraform workspace list
* default

# Create new workspace
$ terraform workspace new development
Created and switched to workspace "development"!

# Switch between workspaces
$ terraform workspace select staging
Switched to workspace "staging".

# Delete a workspace (must switch away first)
$ terraform workspace select default
$ terraform workspace delete old_workspace
Deleted workspace "old_workspace"!

# Verify current workspace
$ terraform workspace show
staging

Basic Workflow: Create → Switch → Manage → Delete workspaces as needed.

Complete Environment Setup

# Initialize Terraform configuration
$ terraform init

# Create environment workspaces
$ terraform workspace new dev
$ terraform workspace new staging
$ terraform workspace new prod

# Switch to development workspace
$ terraform workspace select dev

# Deploy to development
$ terraform plan -out dev.tfplan
$ terraform apply dev.tfplan

# Promote to staging
$ terraform workspace select staging
$ terraform plan -out staging.tfplan
$ terraform apply staging.tfplan

# Deploy to production
$ terraform workspace select prod
$ terraform plan -out prod.tfplan
$ terraform apply prod.tfplan

# Verify deployments
$ for env in dev staging prod; do
    terraform workspace select $env
    echo "=== $env ==="
    terraform output instance_ip
  done

Environment Promotion: Deploy through environments using workspace switching.

Advanced Workspace Features

# Create workspace with custom state file
$ terraform workspace new eu-west --state=eu-west.tfstate

# Show workspace-specific outputs
$ terraform output -json | jq '.'

# Workspace-aware planning
$ terraform plan -var-file="$(terraform workspace show).tfvars"

# Bulk workspace operations
#!/bin/bash
# Apply to all workspaces
for workspace in $(terraform workspace list | grep -v '*'); do
  terraform workspace select "$workspace"
  terraform apply -auto-approve
done

# Workspace-specific destroy
$ terraform workspace select feature-branch
$ terraform destroy -auto-approve
$ terraform workspace select default
$ terraform workspace delete feature-branch

# Check workspace in automation scripts
CURRENT_WS=$(terraform workspace show)
if [ "$CURRENT_WS" == "prod" ]; then
  echo "Production deployment - requiring manual approval"
  terraform apply
else
  terraform apply -auto-approve
fi

Advanced Usage: Custom state files, bulk operations, and automation integration.

Environment Management Strategies

Different environments require different configurations and resource sizes. Here's how to manage environment-specific requirements with workspaces.

Development

  • Instance Size: t3.micro
  • Node Count: 1
  • Database: Small, no backups
  • Monitoring: Basic
  • Cost: Minimal
  • Access: Full developer access

Staging

  • Instance Size: t3.small
  • Node Count: 2
  • Database: Medium, daily backups
  • Monitoring: Standard
  • Cost: Moderate
  • Access: Limited team access

Production

  • Instance Size: m5.large
  • Node Count: 3+
  • Database: Large, continuous backups
  • Monitoring: Comprehensive
  • Cost: Production-grade
  • Access: Restricted access

Environment-Specific Configuration

# Define environment-specific variables
variable "environment" {
  description = "Deployment environment"
  type        = string
}

variable "instance_sizes" {
  type = map(string)
  default = {
    "dev"     = "t3.micro"
    "staging" = "t3.small"
    "prod"    = "m5.large"
  }
}

variable "node_counts" {
  type = map(number)
  default = {
    "dev"     = 1
    "staging" = 2
    "prod"    = 3
  }
}

# Use workspace-aware configuration
locals {
  environment = terraform.workspace
  instance_type = var.instance_sizes[local.environment]
  node_count = var.node_counts[local.environment]
  is_production = local.environment == "prod"
}

# Create resources with environment-specific settings
resource "aws_instance" "web" {
  count = local.node_count
  
  ami           = "ami-0c02fb55956c7d316"
  instance_type = local.instance_type
  
  tags = {
    Name        = "${local.environment}-web-${count.index}"
    Environment = local.environment
  }
}

Conditional Resource Creation

# Only create monitoring in staging and production
resource "aws_cloudwatch_dashboard" "main" {
  count = contains(["staging", "prod"], terraform.workspace) ? 1 : 0
  
  dashboard_name = "${terraform.workspace}-dashboard"
  dashboard_body = jsonencode({
    widgets = [
      # Monitoring configuration
    ]
  })
}

# Production-only resources
resource "aws_db_instance" "primary" {
  count = terraform.workspace == "prod" ? 1 : 0
  
  identifier     = "prod-primary"
  instance_class = "db.m5.large"
  multi_az       = true
  backup_retention_period = 35
}

# Development-specific resources (cheaper alternatives)
resource "aws_db_instance" "development" {
  count = terraform.workspace == "dev" ? 1 : 0
  
  identifier     = "dev-db"
  instance_class = "db.t3.micro"
  backup_retention_period = 1
}

# Environment-specific database configuration
locals {
  database_config = {
    "dev" = {
      instance_class = "db.t3.micro"
      storage        = 20
      backup_days   = 1
    }
    "staging" = {
      instance_class = "db.t3.medium"
      storage        = 100
      backup_days   = 7
    }
    "prod" = {
      instance_class = "db.m5.large"
      storage        = 500
      backup_days   = 35
    }
  }
  
  current_db_config = local.database_config[terraform.workspace]
}

Workspace-Specific Configurations

Use workspace-specific variable files and conditional logic to customize configurations for each environment.

Workspace Configuration Builder

Select a workspace to see the corresponding configuration:

Select a workspace to see environment-specific configuration...

Workspace-Specific Variable Files

# File structure for workspace configurations
# .
# ├── main.tf
# ├── variables.tf
# ├── outputs.tf
# ├── dev.tfvars
# ├── staging.tfvars
# └── prod.tfvars

# dev.tfvars - Development configuration
instance_type = "t3.micro"
instance_count = 1
database_size = 20
enable_monitoring = false
domain_name = "dev.example.com"

# staging.tfvars - Staging configuration
instance_type = "t3.small"
instance_count = 2
database_size = 100
enable_monitoring = true
domain_name = "staging.example.com"

# prod.tfvars - Production configuration
instance_type = "m5.large"
instance_count = 3
database_size = 500
enable_monitoring = true
domain_name = "example.com"

# Apply with workspace-specific variables
$ terraform workspace select dev
$ terraform apply -var-file="dev.tfvars"

# Or use automatic variable file loading
$ terraform apply -var-file="$(terraform workspace show).tfvars"

Dynamic Configuration with Locals

# Define all environment configurations in one place
locals {
  environments = {
    "dev" = {
      instance_type    = "t3.micro"
      instance_count   = 1
      database_size    = 20
      monitoring       = false
      domain           = "dev.example.com"
      certificate_arn  = "arn:aws:acm:...dev"
      min_capacity     = 1
      max_capacity     = 2
    }
    "staging" = {
      instance_type    = "t3.small"
      instance_count   = 2
      database_size    = 100
      monitoring       = true
      domain           = "staging.example.com"
      certificate_arn  = "arn:aws:acm:...staging"
      min_capacity     = 1
      max_capacity     = 4
    }
    "prod" = {
      instance_type    = "m5.large"
      instance_count   = 3
      database_size    = 500
      monitoring       = true
      domain           = "example.com"
      certificate_arn  = "arn:aws:acm:...prod"
      min_capacity     = 2
      max_capacity     = 10
    }
  }
  
  environment = terraform.workspace
  config = local.environments[local.environment]
}

# Use dynamic configuration in resources
resource "aws_instance" "app" {
  count = local.config.instance_count
  
  ami           = "ami-0c02fb55956c7d316"
  instance_type = local.config.instance_type
  
  tags = {
    Environment = local.environment
    AutoScalingGroup = "app-${local.environment}"
  }
}

# Conditional resources based on environment
resource "aws_autoscaling_group" "app" {
  min_size = local.config.min_capacity
  max_size = local.config.max_capacity
  
  tag {
    key                 = "Environment"
    value               = local.environment
    propagate_at_launch = true
  }
}

Workspaces with Remote Backends

When using remote backends like S3, Azure Storage, or Terraform Cloud, workspaces provide even more powerful collaboration features.

Remote Backend Benefits

  • State Locking: Prevent concurrent modifications
  • Team Collaboration: Multiple users can work safely
  • State Versioning: Rollback to previous states
  • Access Control: Granular permissions per workspace
  • Audit Trail: Track who changed what and when

Backend Configuration

terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "terraform.tfstate"
    region = "us-east-1"
    
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

With workspaces, state files are stored as: env:/<workspace>/terraform.tfstate

State File Organization

With remote backends and workspaces, state files are automatically organized:
env:/dev/terraform.tfstate
env:/staging/terraform.tfstate
env:/prod/terraform.tfstate
This keeps your states separate and manageable.

Workspace Best Practices

Naming Conventions

  • Use descriptive names: dev, staging, prod
  • Avoid special characters in workspace names
  • Use consistent naming across projects
  • Consider feature branch names: feature-auth
  • Include region if applicable: us-east-prod

Security & Access Control

  • Restrict production workspace access
  • Use different AWS accounts per environment
  • Implement least privilege principles
  • Audit workspace usage regularly
  • Use remote backends with encryption

Operational Excellence

  • Always use terraform plan before apply
  • Implement CI/CD pipelines per environment
  • Monitor costs per workspace
  • Clean up unused workspaces regularly
  • Document workspace purposes and owners

Development Workflow

  • Develop in feature workspaces
  • Test in staging before production
  • Use automation for workspace management
  • Implement approval gates for production
  • Maintain separate state backups

Real-World Implementation

Here's a complete example showing a real-world multi-environment setup with workspaces.

🏗️

Complete Multi-Environment Setup

# terraform.tf - Backend configuration
terraform {
  required_version = ">= 1.0"
  
  backend "s3" {
    bucket         = "my-company-terraform-state"
    key            = "web-app/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-state-locks"
    encrypt        = true
  }
}

# variables.tf - Environment configuration
variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "us-east-1"
}

variable "environment_config" {
  description = "Environment-specific configuration"
  type = map(object({
    instance_type  = string
    instance_count = number
    database_size  = number
    domain_name    = string
    min_capacity   = number
    max_capacity   = number
  }))
  default = {
    "dev" = {
      instance_type  = "t3.micro"
      instance_count = 1
      database_size  = 20
      domain_name    = "dev.myapp.com"
      min_capacity   = 1
      max_capacity   = 2
    }
    "staging" = {
      instance_type  = "t3.small"
      instance_count = 2
      database_size  = 100
      domain_name    = "staging.myapp.com"
      min_capacity   = 1
      max_capacity   = 4
    }
    "prod" = {
      instance_type  = "m5.large"
      instance_count = 3
      database_size  = 500
      domain_name    = "myapp.com"
      min_capacity   = 2
      max_capacity   = 10
    }
  }
}

# main.tf - Resource configuration
provider "aws" {
  region = var.aws_region
}

locals {
  environment = terraform.workspace
  config = var.environment_config[local.environment]
  
  common_tags = {
    Project     = "web-app"
    Environment = local.environment
    ManagedBy   = "terraform"
  }
}

# Create VPC (shared across environments)
module "vpc" {
  source = "terraform-aws-modules/vpc/aws"
  
  name = "${local.environment}-vpc"
  cidr = "10.0.0.0/16"
  
  azs             = ["us-east-1a", "us-east-1b"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]
  
  tags = local.common_tags
}

# Create EC2 instances
resource "aws_instance" "web" {
  count = local.config.instance_count
  
  ami           = "ami-0c02fb55956c7d316"
  instance_type = local.config.instance_type
  subnet_id     = module.vpc.public_subnets[count.index % length(module.vpc.public_subnets)]
  
  tags = merge(local.common_tags, {
    Name = "${local.environment}-web-${count.index}"
  })
}

# Create database (production has enhanced features)
resource "aws_db_instance" "postgres" {
  identifier     = "${local.environment}-db"
  instance_class = local.config.instance_type
  allocated_storage = local.config.database_size
  
  # Production-specific settings
  backup_retention_period = local.environment == "prod" ? 35 : 7
  multi_az = local.environment == "prod"
  deletion_protection = local.environment == "prod"
  
  tags = local.common_tags
}

Workspace Alternatives

While workspaces are powerful, they're not always the best solution. Here are alternative approaches for managing multiple environments.

Approach Pros Cons Best For
Workspaces Simple, built-in, state isolation Limited to one backend, no code differences Same infrastructure, different scales
Separate Directories Complete code separation, different backends Code duplication, maintenance overhead Fundamentally different environments
Terragrunt DRY configuration, powerful abstractions Additional tool, learning curve Complex multi-account setups
Terraform Cloud Enhanced collaboration, VCS integration Vendor lock-in, cost Enterprise teams, compliance needs

Key Takeaways

  • Workspaces provide isolated state files within a single configuration
  • Use terraform.workspace to access current workspace name
  • Implement environment-specific configurations using maps and conditionals
  • Combine workspaces with remote backends for team collaboration
  • Follow naming conventions and security best practices
  • Use conditional resources for environment-specific features
  • Consider alternative approaches when workspaces don't fit your needs

In our next tutorial, we'll explore Terraform Modules Deep Dive, where you'll learn how to create reusable, composable infrastructure components that can be shared across your organization.


No comments:

Post a Comment

Shell Scripting Mastery - DevOps Automation Guide

Shell Scripting Mastery - DevOps Automation Guide Shell Scripting for DevOps Published: ...