Terraform Workspace Guide

Blog Featured image for a blog with a title - Terraform Workspace Guide
Categories

Introduction

When building cloud infrastructure, you often need multiple versions of the same component. You need a place to test new ideas. You need an alternative location for your team to review their work. And you need a safe, stable place for your real users. Managing all these environments can get messy fast. That is what Terraform workspaces do.

In this blog, we will help you learn how to use workspace in Terraform to keep your infrastructure neat and organized. We will start from the basics and cover advanced techniques. We will also cover Terraform workspace commands for better understanding. By the end, you will know how to manage multiple environments like a professional.

Before we dive into workspaces, let’s talk about what happens when you run Terraform. Here, we will discuss the core issue, i.e., the challenges you might face while managing multiple environments.

Challenge of Managing Multiple Environments

Once you enter ‘terraform apply’, Terraform will create resources on your cloud provider. It may provision servers, build databases, or networks. But how does Terraform remember what it created? The answer is the state file. Terraform maintains a dedicated file known as terraform.tfstate. The file is a sort of notebook in which Terraform takes notes of what it creates. Next time you run Terraform, it checks this notebook to see what already exists.

Here’s where it gets interesting. Typically, you will have one state file in each project folder. Suppose you wanted three environments (such as dev, test, and prod), then you would have three folders with three copies of your code. That is a lot of copying and pasting.

So, how will you solve this problem? The answer is Terraform workspace.

Let us now move on to our main section where we will discuss about workspace in Terraform.

What is Terraform Workspace?

A Terraform workspace helps you to build, orchestrate, and control various environments, such as development, test, and prod, in a single working environment. Suppose you were developing a web application and required a development environment, a test environment, and a production environment. Without workspaces, you would end up creating separate directories or configurations for each environment, resulting in a messy setup. The workspaces allow you to maintain a single copy of your Terraform configuration files while managing multiple environments under a single roof.

Each workspace maintains a state file, in which it stores the resources managed by Terraform in the specified environment. This isolation means that no change in one working environment (such as your development environment) can affect another (such as production).

Default Workspace

When you first use Terraform, you’re already using a workspace. You just don’t know it yet. Every Terraform project starts with a workspace called “default”. It’s similar to the main branch in Git if you’re familiar with version control.

You can see this yourself. Create a new folder and add a simple Terraform file:

# main.tf
resource "local_file" "example" {
  content  = "Hello from Terraform!"
  filename = "hello.txt"
}

Now run these commands:

terraform init
terraform workspace list

You’ll see output like this:

* default

The star shows which workspace you’re currently using. Right now, it’s just default.

How Terraform Workspaces Store State

When you use the default workspace, Terraform creates a file structure like this:

image 10

But when you create a new workspace, something interesting happens. Let’s create one:

terraform workspace new development

Now your folder structure changes:

image 11

See that new terraform.tfstate.d folder? That’s where Terraform keeps the state files for all your workspaces (except default). Each workspace gets its own subfolder.

Why Use Terraform Workspace?

There are several big advantages to using workspace in Terraform. Some of these are:

Managing Multiple Environments

This is the main reason people use workspaces. Let’s say you’re building a web application. You need three environments:

  • Development: Where developers try new features
  • Staging: Where you test before going live
  • Production: Where real users access your app

Without workspaces, you’d need three folders:

image 12

That’s nine files for what’s basically the same infrastructure. If you need to change something, you have to update it in three places. It’s easy to forget one and end up with environments that don’t match.

With workspaces, you just need:

image 13

Three files instead of nine. When you need to make a change, you do it once. Much simpler.

Testing Updates Safely

Here’s a common scenario. Your boss asks you to add a new feature to your infrastructure. Maybe you need to add a caching layer to speed things up. You can’t test this directly in production. You need a safe place to experiment.

With workspaces, you can create a temporary environment:

terraform workspace new feature-cache-test
terraform apply

Now you have a complete copy of your infrastructure where you can test the new cache. If it works great, you can apply the same changes to staging and then production. If it doesn’t work, just destroy the test workspace:

terraform destroy
terraform workspace select default
terraform workspace delete feature-cache-test

No harm done. Your other environments stayed safe the whole time.

Cost Savings

Cloud resources cost money. You probably don’t need your development environment to be as powerful as production. With workspaces, you can adjust resource sizes based on the environment.

Here’s a simple example:

resource "aws_instance" "web_server" {
  instance_type = terraform.workspace == "prod" ? "t3.large" : "t3.micro"

  # ... other configuration ...
}

This code says: “If we are in the prod workspace, use a large server. Otherwise, use a micro server.” Your production environment gets the power it needs, while dev and staging save money with smaller instances.

Let us now discuss some of the most crucial workspace commands in Terraform.

Essential Terraform Workspace Commands

Let’s go through all the Terraform workspace commands you need to know. These are your daily tools for managing workspaces.

Using workspaces involves a handful of simple commands. Once you learn them, they will become a natural part of your Terraform workflow. Let’s walk through the essential commands you need to know.

First, make sure you are in a directory with a Terraform configuration. You will need to run “terraform init” before you can use any Terraform workspace commands.

Creating a New Workspace

To create a workspace, use:

terraform workspace new <WORKSPACE_NAME>

For example:

terraform workspace new staging

When you create a workspace, Terraform automatically switches to it. You are now working in the staging workspace.

Listing All Workspaces

To see all your workspaces:

terraform workspace list

You will see something like:

image 14

The star shows your current workspace.

Switching Between Workspaces

To change workspaces, use:

terraform workspace select <name>

For example:

terraform workspace select production

Always double-check which workspace you’re in before running terraform apply. It’s easy to accidentally change the wrong environment.

Showing Current Workspace

To see which workspace you are currently using:

terraform workspace show

This simply prints the workspace name:

staging

It is highly recommend running this command often, especially before making changes.

Deleting a Workspace

To remove a workspace, you no longer need:

terraform workspace delete <name>

Important: You can only delete empty workspaces. If the workspace has resources, you need to destroy them first:

terraform workspace select old-feature
terraform destroy
terraform workspace select default
terraform workspace delete old-feature

You cannot delete the default workspace.

These commands are the backbone of workspace management. Practice them in a test project to get comfortable before using them in production.

Real-World Example: Building a Multi-Environment Setup

Let’s build something real. We’ll create a simple but realistic infrastructure that works across multiple environments. This example uses AWS, but the concepts work with any cloud provider.

Step 1: Project Structure

Create a new directory for your project:

mkdir terraform-workspace-demo
cd terraform-workspace-demo

Step 2: Create the Main Configuration

Create a file called main.tf:

# main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

# Create a VPC for our environment
resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr

  tags = {
    Name        = "${var.project_name}-${terraform.workspace}-vpc"
    Environment = terraform.workspace
  }
}

# Create a subnet
resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = var.public_subnet_cidr
  availability_zone       = "${var.aws_region}a"
  map_public_ip_on_launch = true

  tags = {
    Name        = "${var.project_name}-${terraform.workspace}-public-subnet"
    Environment = terraform.workspace
  }
}

# Create an internet gateway
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name        = "${var.project_name}-${terraform.workspace}-igw"
    Environment = terraform.workspace
  }
}

# Create a route table
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }

  tags = {
    Name        = "${var.project_name}-${terraform.workspace}-public-rt"
    Environment = terraform.workspace
  }
}

# Associate route table with subnet
resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

# Create a security group
resource "aws_security_group" "web" {
  name        = "${var.project_name}-${terraform.workspace}-web-sg"
  description = "Security group for web servers"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name        = "${var.project_name}-${terraform.workspace}-web-sg"
    Environment = terraform.workspace
  }
}

# Get the latest Amazon Linux 2 AMI
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

# Create EC2 instance
resource "aws_instance" "web" {
  count = var.instance_count

  ami           = data.aws_ami.amazon_linux.id
  instance_type = var.instance_type
  subnet_id     = aws_subnet.public.id
  
  vpc_security_group_ids = [aws_security_group.web.id]

  user_data = <<-EOF
    #!/bin/bash
    yum update -y
    yum install -y httpd
    systemctl start httpd
    systemctl enable httpd
    echo "<h1>Hello from ${terraform.workspace} environment!</h1>" > /var/www/html/index.html
    echo "<p>Instance ${count.index + 1} of ${var.instance_count}</p>" >> /var/www/html/index.html
  EOF

  tags = {
    Name        = "${var.project_name}-${terraform.workspace}-web-${count.index + 1}"
    Environment = terraform.workspace
  }
}

Step 3: Define Variables

Create a file called variables.tf:

# variables.tf
variable "project_name" {
  description = "Name of the project"
  type        = string
  default     = "webapp"
}

variable "aws_region" {
  description = "AWS region to deploy resources"
  type        = string
  default     = "us-east-1"
}

variable "vpc_cidr" {
  description = "CIDR block for VPC"
  type        = string
}

variable "public_subnet_cidr" {
  description = "CIDR block for public subnet"
  type        = string
}

variable "instance_type" {
  description = "EC2 instance type"
  type        = string
}

variable "instance_count" {
  description = "Number of EC2 instances"
  type        = number
}

Step 4: Create Environment-Specific Variables

Now we’ll create variable files for each environment.

Create environments/dev.tfvars:

# environments/dev.tfvars
vpc_cidr           = "10.0.0.0/16"
public_subnet_cidr = "10.0.1.0/24"
instance_type      = "t2.micro"
instance_count     = 1

Create environments/staging.tfvars:

# environments/staging.tfvars
vpc_cidr           = "10.1.0.0/16"
public_subnet_cidr = "10.1.1.0/24"
instance_type      = "t2.small"
instance_count     = 2

Create environments/prod.tfvars:

# environments/prod.tfvars
vpc_cidr           = "10.2.0.0/16"
public_subnet_cidr = "10.2.1.0/24"
instance_type      = "t2.medium"
instance_count     = 3

Step 5: Add Outputs

Create outputs.tf:

# outputs.tf
output "instance_ips" {
  description = "Public IP addresses of web servers"
  value       = aws_instance.web[*].public_ip
}

output "instance_urls" {
  description = "URLs to access the web servers"
  value       = [for ip in aws_instance.web[*].public_ip : "http://${ip}"]
}

output "environment" {
  description = "Current environment/workspace"
  value       = terraform.workspace
}

Step 6: Deploy to Multiple Environments

Now let’s use workspaces to deploy our infrastructure.

First, initialize Terraform:

terraform init

Create and deploy the development environment:

terraform workspace new dev
terraform plan -var-file=environments/dev.tfvars
terraform apply -var-file=environments/dev.tfvars -auto-approve

Check the outputs:

terraform output

Now create staging:

terraform workspace new staging
terraform apply -var-file=environments/staging.tfvars -auto-approve

And finally, production:

terraform workspace new prod
terraform apply -var-file=environments/prod.tfvars -auto-approve

Step 7: Verify Your Environments

List all workspaces:

terraform workspace list
image 15

Switch between workspaces to see their resources:

terraform workspace select dev
terraform output

terraform workspace select staging
terraform output

terraform workspace select prod
terraform output

Each environment has different IP addresses and different numbers of servers.

Best Practices for Using Workspaces

After working with Terraform workspaces on many projects, I’ve learned some important lessons. Here are the best practices that will save you time and headaches.

1. Use Clear Naming Conventions

Pick a naming scheme and stick to it. Here are some good patterns:

Environment-based: dev, staging, prod

Feature-based: feature-payment-gateway, feature-user-auth

Team-based: team-frontend-dev, team-backend-dev

Bad names are vague or too long:

Bad: test, test2, new-test

Bad: development-environment-for-testing-the-new-payment-system-v2

2. Always Check Your Current Workspace

Before running any Terraform command that changes infrastructure, check where you are:

terraform workspace show

Even better, add this to your shell prompt. If you use bash, add this to your .bashrc:

terraform_workspace() {
  if [ -d .terraform ]; then
    echo " (tf:$(terraform workspace show 2>/dev/null))"
  fi
}
PS1='[\w$(terraform_workspace)]\$ '

Now your prompt shows the current workspace:

image 16

3. Use Variables for Environment Differences

Don’t hardcode environment-specific values. Use variables instead:

# Good
resource "aws_instance" "web" {
  instance_type = var.instance_type
}

# Bad
resource "aws_instance" "web" {
  instance_type = terraform.workspace == "prod" ? "t2.large" : "t2.micro"
}

The second approach works, but it gets messy fast as you add more environments or more differences.

4. Tag Everything with the Workspace Name

Always include the workspace name in your resource tags:

tags = {
  Name        = "${var.project_name}-${terraform.workspace}-${local.resource_name}"
  Environment = terraform.workspace
  ManagedBy   = "Terraform"
}

This makes it easy to:

  • Find resources in your cloud console
  • Set up billing alerts per environment
  • Create monitoring dashboards per environment

5. Use Workspace-Specific Variable Files

Organize your variables like this:

image 17

Then create a simple script to apply changes:

#!/bin/bash
# apply.sh
WORKSPACE=$(terraform workspace show)
terraform apply -var-file="environments/${WORKSPACE}.tfvars" "$@"

Now you can just run ./apply.sh instead of remembering the var-file path.

6. Protect Production

Add extra safety for your production workspace. Here’s a simple protection script:

#!/bin/bash
# safe-apply.sh
WORKSPACE=$(terraform workspace show)

if [ "$WORKSPACE" = "prod" ]; then
  echo "WARNING: You're about to change PRODUCTION!"
  echo "Type 'yes' to continue:"
  read confirmation
  if [ "$confirmation" != "yes" ]; then
    echo "Cancelled."
    exit 1
  fi
fi

terraform apply -var-file="environments/${WORKSPACE}.tfvars" "$@"

7. Document Your Workspaces

Keep a README file that lists all workspaces and their purposes:

# Terraform Workspaces
## Permanent Workspaces
- **dev**: Development environment for daily work
- **staging**: Pre-production testing
- **prod**: Production environment (restricted access)

## Temporary Workspaces
Create these as needed, delete when done:
- **feature-***: For testing new features
- **hotfix-***: For emergency fixes

## Creating a New Feature Workspace
```bash  
terraform workspace new feature-your-feature-name  
terraform apply -var-file=environments/dev.tfvars

Let us now discuss some of the common mistakes you can make during your projects.

Common Mistakes and How to Avoid Them

Even experienced users make these mistakes. Learn from the experience.

Mistake 1: Forgetting Which Workspace, You’re In

Problem: You think you’re in dev but you’re actually in prod. You run `terraform destroy`. Oops.

Solution:

  • Always check before running commands
  • Use the shell prompt trick mentioned earlier
  • Create wrapper scripts that show warnings

Mistake 2: Sharing State Files Incorrectly

Problem: Workspace state files on your laptop don’t help your team.

Solution: Always use remote state. Here’s an S3 example:

```hcl  
# backend.tf  
terraform {  
  backend "s3" {  
    bucket = "my-company-terraform-state"  
    key    = "projects/web-app/terraform.tfstate"  
    region = "us-east-1"  
    
    dynamodb_table = "terraform-state-lock"  
    encrypt        = true  
  }  
}  

Terraform automatically handles workspace state files in S3:

Default: projects/web-app/terraform.tfstate

Dev: projects/web-app/env:/dev/terraform.tfstate

Prod: projects/web-app/env:/prod/terraform.tfstate

Mistake 3: Workspace Drift

Problem: Over time, workspaces start to differ in unexpected ways. Someone manually changed something in staging. Someone forgot to apply a change to dev.

Solution:

Run regular drift detection:

for ws in dev staging prod; do
  echo "Checking $ws..."
  terraform workspace select $ws
  terraform plan -detailed-exitcode
done

Use CI/CD to automatically apply changes to all environments

Mistake 4: Using Workspaces for the Wrong Thing

Problem: Workspaces work best for environments of the same application. Don’t use them for completely different projects or applications.

Bad Use Cases:

  • Different applications (use separate directories)
  • Different AWS accounts (use separate backend configs)
  • Different regions (use variables instead)

Good Use Cases:

  • Dev/staging/prod of the same app
  • Feature branches
  • Different clients with the same infrastructure

Advanced Workspace Techniques

Once you’re comfortable with basics, these advanced techniques will level up your workspace game.

Dynamic Backend Configuration

You can’t use variables in backend configuration, but you can use partial configuration:

# backend.tf
terraform {
  backend "s3" {
    # Partial configuration
  }
}
done

Then initialize with different configs per workspace:

# For dev
terraform init -backend-config="key=dev/terraform.tfstate"
}

# For prod  
terraform init -backend-config="key=prod/terraform.tfstate"

Workspace-Specific Providers

Sometimes different environments need different provider settings:

# providers.tf
provider "aws" {
  region = var.aws_region
  
  assume_role {
    role_arn = var.assume_role_arn
  }
 
  default_tags {
    tags = {
      Environment = terraform.workspace
      ManagedBy   = "Terraform"
    }
  }
}

Then in your variable files:

# environments/prod.tfvars
aws_region = "us-east-1"
assume_role_arn = "arn:aws:iam::123456789012:role/TerraformProdRole"

# environments/dev.tfvars  
aws_region = "us-west-2"
assume_role_arn = "arn:aws:iam::123456789012:role/TerraformDevRole"

Conditional Resource Creation

Sometimes you only want certain resources in specific environments:

# Only create monitoring alerts in staging and prod
resource "aws_cloudwatch_metric_alarm" "high_cpu" {
  count = contains(["staging", "prod"], terraform.workspace) ? 1 : 0
  
  alarm_name          = "${var.project_name}-${terraform.workspace}-high-cpu"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "2"
  # ... rest of configuration
}

# Only create expensive resources in prod
resource "aws_rds_cluster" "database" {
  count = terraform.workspace == "prod" ? 1 : 0
  
  cluster_identifier = "${var.project_name}-${terraform.workspace}-db"
  engine            = "aurora-mysql"
  # ... rest of configuration
}

Workspace-Specific Modules

You can even use different module versions per workspace:

module "networking" {
  source = terraform.workspace == "prod" ? "./modules/networking-v2" : "./modules/networking-v1"
  
  vpc_cidr = var.vpc_cidr
  # ... other variables
}

Alternatives to Terraform Workspaces: Make the Right Choice

So when should you use workspaces versus alternatives?

Use Workspaces When:

  • Environments are mostly similar
  • You have a small to medium project
  • You want simplicity
  • Same cloud account for all environments

Use Directory Structure When:

  • Environments are very different
  • Different teams manage different environments
  • Need different Terraform versions
  • Different cloud accounts per environment

Use Terragrunt When:

  • You have many similar projects
  • Complex dependencies between modules
  • Large team needing standards
  • Want advanced features like hooks

Frequently Asked Questions

Q1. What are workspaces in Terraform?

Workspace in Terraform let you use one codebase for different environments. Each workspace has its own state file. This keeps your development, staging, and production setups separate and clean.

Q2. What is the difference between terraform modules and workspaces?

Modules are for reusing code blocks. They help you build things faster. Workspaces manage different states for that code. They allow you to deploy the same code to other locations.

Q3. What can you use workspaces for?

Use workspaces to manage different environments, such as dev and prod. They are great for testing changes without affecting your main setup. They also help manage different customer deployments.

Q4. What is the difference between Terragrunt and workspaces?

Workspaces manage state files for different environments within one configuration. Terragrunt is a tool that helps you write cleaner, reusable code and manage remote state across many modules.

Conclusion

Terraform workspaces are a powerful feature that solves a real problem. They enable you to maintain multiple environments without having to replicate code. They are also embedded into Terraform and thus need no additional installations. They are easy to comprehend but versatile enough to be applied in complicated situations.

Start small. Create dev and prod workspaces for your next project. Use variable files to manage the differences. Tag your resources with the workspace name. As you become more comfortable, you can add more advanced techniques.

The next time you find yourself copying Terraform code to create a new environment, stop. Create a workspace instead. Your future self will thank you.

And if you’re looking to take your skills to the next level maybe go beyond infrastructure provisioning and dive into full-stack automation now’s a great time to explore an Ansible and Terraform course. It’s a solid way to learn how these tools work together in real-world workflows.

Get in touch

Blog
Looking For Networking Training- PyNetLabs

Popular Courses

Automation

(FRESHERS / EXPERIENCED)

Network Expert

Automation

(FRESHERS / EXPERIENCED)

Network Expert

Automation

(FRESHERS / EXPERIENCED)

Network Expert

Automation

(FRESHERS / EXPERIENCED)

Network Expert

Automation

(FRESHERS / EXPERIENCED)

Network Expert

Leave a Reply

Your email address will not be published. Required fields are marked *