Managing infrastructure across varied environments can be a real puzzle. You’ve got your development, staging, and production setups, each with their own needs. It’s easy to feel like you’re herding cats. But what if there was a way to keep all of these configurations separate and tidy, without the chaos? That’s where Terraform Workspaces come into play. They are a game-changer for intermediate Terraform users looking to streamline their multi-environment setups.
What are Terraform Workspaces?
Terraform Workspaces are like separate containers within your Terraform project. They allow you to maintain multiple, distinct instances of your infrastructure using the same code. Think of it as having multiple projects within one, each with its own set of variables and state file. In essence, they let you use the exact same Terraform configuration for multiple environments.
You can easily switch between your dev, staging, and prod configurations by changing your active workspace. Workspaces keep your infrastructure code clean and well organized by eliminating the need for complex branching strategies to manage different environments. Instead of having separate folders or complex variable files, you use the same base code and simply switch between workspaces. This is especially helpful for teams that manage different stages of the SDLC, where they can quickly switch between development and production while keeping their base code the same.
Workspaces are not a replacement for modules; they serve two different purposes. Modules are for grouping your resources into reusable components, which you then use in different parts of your infrastructure code. Workspaces are about managing different versions of your whole setup. So, you’d use modules within a workspace to organize your resources, while different workspaces would represent different environments that use those modules.
Why Use Terraform Workspaces?
If you have ever managed multiple environments, then you know the struggle of keeping everything straight. Workspaces address some real-world issues.
First, they reduce the chance of mistakes, because all environments are built from the same base code. When you manage multiple configurations in separate files or branches, it is easy to accidentally deploy development resources to a production environment. Workspaces keep all those environments separate and managed at a high level, reducing those risks. Second, workspaces are a lot cleaner than the alternatives. Instead of having duplicated code with slight differences, you can have one code base and use workspaces to manage the differences between environments. This makes it much easier to read, maintain, and debug your code.
Terraform Workspaces also allow you to use the same modules across multiple environments. If you organize your infrastructure using modules, workspaces let you reuse these modules without modification. Because workspaces keep environment specific variables separate, you can use the same module across many workspaces without the hassle of having different version for each environment.
Let’s dig into some use cases.
Use Case 1: Managing Multiple Environments (Dev, Staging, Prod)
Most organizations have at least three environments: development, staging, and production. Without workspaces, you often find yourself managing these with separate folders, duplicate files, or complex branching. Workspaces streamline this by allowing you to switch environments with a single command, and they keep all the configurations separate. For instance, you might have a production workspace with higher resource limits and a development workspace with lower limits. You can use the same modules and code, and switch between them without any complications.
Use Case 2: Feature Branch Deployments
With workspaces, you can spin up dedicated environments for each feature branch, allowing your teams to test code in isolation. You’d create a workspace per feature branch, where every workspace has the resources and settings it needs. Once a feature is merged, the workspace can be destroyed with ease.
Use Case 3: Testing Infrastructure Changes in Isolation
Before you deploy any changes into production, you may need to test these changes in a non-production environment. With workspaces, you can set up a dedicated test environment, apply your changes, and test them without any risks to your existing setup. If you need to test specific modifications, you can set up a new workspace with the new code, test your new setup, and destroy the workspace when you are done. This allows for safe experimentation.
Use Case 4: Managing Separate User Groups or Departments
If you’re managing resources for different user groups or departments, workspaces can keep all configurations separate. Let’s say you have one department needing access to databases and another that requires access to container clusters. Instead of having one big shared set of configurations, you can create a workspace for each department and give each team access to their workspace only. This provides much needed separation to avoid mix-ups.
How to Use Terraform Workspaces
Let’s walk through how to use workspaces effectively. It’s not too hard, but there are a few things you need to know.
Step 1: Basic Workspace Commands
To start, there are a few key commands:
terraform workspace list
: Lists all existing workspaces in your project.terraform workspace show
: Shows the currently active workspace.terraform workspace select <workspace_name>
: Switches to the specified workspace.terraform workspace new <workspace_name>
: Creates a new workspace with the given name.terraform workspace delete <workspace_name>
: Deletes the specified workspace.
These are the primary commands to manage workspaces in Terraform.
Step 2: Creating a Workspace
To create a workspace, use the terraform workspace new <workspace_name>
command, where <workspace_name>
is the name you want to give the workspace. For instance, to create a workspace called dev
, you would execute:
terraform workspace new dev
This creates a new workspace and makes it the active one. Now, when you execute terraform commands, they will run in the context of this workspace.
Step 3: Switching Between Workspaces
To switch to a workspace, use the terraform workspace select <workspace_name>
command. To switch to a workspace named prod
, you would execute:
terraform workspace select prod
This command changes the active workspace. Subsequent Terraform commands will apply to the newly activated workspace.
Step 4: Understanding the State Files
Each workspace has its own state file, keeping your different environments separate. Terraform stores the state for each workspace in different folders. This is important, because your state file stores crucial information about your infrastructure, so keeping these files separate is key to avoid issues. You may store the state files in a local file system, or in a remote backend service, such as AWS S3, Azure Storage, or HashiCorp Terraform Cloud.
The state file location is determined by your backend configuration. If you are using a local backend, the state files will be located in different directories with your project. If you’re using a remote backend, you may need to set the workspace name as part of your key. For AWS S3, your key would need to use a format similar to this: state/<workspace_name>/terraform.tfstate
.
Step 5: Setting Different Variables Per Workspace
One of the best aspects of workspaces is that you can set different variable values for each workspace. This allows you to customize resource limits, configuration settings, and more for each of your environments. You can use the same variable file for all environments and provide values for each workspace. For instance, you can use the same variable file for your dev
and prod
environments, and specify different instance sizes, database connection strings, or resource limits for each.
There are a few ways to configure environment-specific variables:
-
Using Terraform variables files: create variable files named
terraform.tfvars
in your project or in a specific directory per workspace. Useterraform.tfvars
file in the project root for default values for all workspaces, and createterraform.tfvars
files in subdirectories to provide specific values for each workspace. To read these files, run:terraform plan -var-file=terraform.tfvars
. -
Using environment variables: Set environment variables such as
TF_VAR_<variable_name>
. This method is very helpful in CI/CD pipelines, since the configuration can be read by the process automatically. -
Using CLI flags: Set variables using the
-var
flag in the CLI. This is helpful when running tests and manual configurations. For instance, you can set a variable likeinstance_size
usingterraform plan -var="instance_size=t2.small"
. -
Using Input variables: Define input variables in the
variables.tf
file. This approach makes your configuration modular and reusable.
Using any of the methods above will allow you to override the default values specified in the variables.tf
file. If the variable is not defined in a workspace specific file, or by an environment variable, the default value will be used. This is quite useful, as it allows you to use reasonable defaults, while also allowing you to modify the values in specific environments.
Step 6: Example Code
Let’s illustrate all of the above concepts with a real code example, for an AWS setup with a S3 backend. First, create a main.tf
file with this content:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "state/${terraform.workspace}/terraform.tfstate"
region = "us-west-2"
}
}
provider "aws" {
region = "us-west-2"
}
resource "aws_instance" "example" {
ami = "ami-0c55b9527c59980f9"
instance_type = var.instance_size
tags = {
Name = "my-instance-${terraform.workspace}"
}
}
Next, create a variables.tf
file to define your variables:
variable "instance_size" {
description = "The size of the EC2 instance"
default = "t2.micro"
}
Now, create different terraform.tfvars
files:
Create a terraform.tfvars
file in your project directory for default values, it should contain these:
instance_size = "t2.micro"
Create a subdirectory named dev
, create a terraform.tfvars
file inside that directory, and add these variables:
instance_size = "t2.small"
Create another subdirectory named prod
, create another terraform.tfvars
file, and add these variables:
instance_size = "t2.xlarge"
Now, you can initialize the project with:
terraform init
Create your workspaces:
terraform workspace new dev
terraform workspace new prod
Now, you can plan your dev
environment:
terraform plan -var-file=dev/terraform.tfvars
You’ll see that the instance type is t2.small
.
If you plan for the prod
environment:
terraform workspace select prod
terraform plan -var-file=prod/terraform.tfvars
The instance type is t2.xlarge
.
In summary, you have one common base configuration, and each workspace has its own settings, all managed by the same code base. The state files are also stored separately in your S3 bucket.
Advanced Techniques for Workspaces
Once you’re comfortable with the basics, you might explore some advanced techniques. They can further improve how you use workspaces.
Using Workspace Variables in Modules
You can use the name of the currently active workspace within your modules. The terraform.workspace
variable in your configuration files can help you customize module behavior for different workspaces. It enables you to create dynamic configurations in your module by referencing the name of the active workspace.
You may have a module to provision a database, and you would need a different number of nodes depending on the workspace being used, this can be done by using the terraform.workspace
variable.
resource "aws_rds_cluster" "my_cluster" {
count = terraform.workspace == "prod" ? 3 : 1
...
}
In this example, the number of cluster nodes will vary depending on whether you are in the prod
workspace or any other.
Automating Workspace Selection in CI/CD
In CI/CD pipelines, it’s essential to select the correct workspace automatically. You may automate workspace selection using environment variables. You can configure the CI/CD pipeline to set an environment variable (e.g. TF_WORKSPACE
) and read it using the -state
flag of Terraform to select the proper workspace. This is very helpful when using a tool such as GitHub Actions, GitLab CI, or Jenkins.
For instance, you can use the TF_WORKSPACE
variable within your pipeline to manage your configuration. Set the TF_WORKSPACE
to the name of the workspace for each run and Terraform will automatically select the correct workspace.
Here’s an example of how to set that up:
- Set an environment variable in your CI/CD pipeline For example, you may set
TF_WORKSPACE=dev
for a dev run andTF_WORKSPACE=prod
for the production run. - Use the variable when running terraform commands. Pass the variable using the
-state
option. For example,terraform workspace select $TF_WORKSPACE
Avoiding Common Pitfalls
Even though workspaces are a powerful feature, there are still common mistakes people make when using them. Let’s review some of the common pitfalls.
First, some may try to use workspaces as a way to manage different types of resources. This is the incorrect use of workspaces, you should use modules to organize different types of resources. Do not confuse workspaces with modules, their purpose is quite different.
Another pitfall is to overuse workspaces. Keep workspaces for actual different deployments that will not have conflicts with each other. If you’re using a workspace per feature branch, and these branches do not use overlapping resources, then you’re fine. However, if you are using a workspace to test a change in the database schema, and another workspace is trying to write into that same database, then you have a conflict waiting to happen. You should avoid the use of workspaces when you’re working with resources that share the same underlying structure or configuration.
Finally, some users may fail to use the correct backend configuration. It’s easy to mix the state files when using local backends or improperly configured S3 backends. Make sure you properly configure your backend with the correct key, by using the terraform.workspace
variable. Do not leave state files in a local backend when working on a team. This will generate a lot of problems and conflicts, so it is best to avoid using local backends when working with more than one developer.
Alternatives to Workspaces
While workspaces are great, they are not always the best option. You may consider some alternatives.
Terraform Modules
Modules are reusable blocks of code that define one or more infrastructure components. Modules allow you to bundle common configurations that you can reuse in many parts of your code. They’re useful for abstraction, and they can help you organize your code. If you’re managing common resources across many environments, you can use modules to reduce code duplication.
Environment Variables
You can use environment variables to configure parts of your Terraform code. These are helpful when you need to inject secrets, access keys, or runtime settings into your code. They are a simple way to pass configuration values at runtime without hardcoding them in your code. Environment variables can be used in combination with workspaces, you can use them to select a workspace and set its configurations.
Separate Terraform Projects
For more complex setups, you may decide to use separate Terraform projects. This is very common if you need to manage different infrastructures that are completely separated from each other. This would be the case if, for instance, you’re managing different cloud providers, such as AWS and Azure, since they have completely different resources and configurations.
Choosing the Right Approach
So when should you use workspaces? If you have different environments that use the same modules and the same configuration, workspaces are a great option. They can manage configuration drift between environments, and they keep code clean and well organized. If you have completely different configurations, and you need an isolated project, then you should consider the use of different Terraform projects. When you need reusable blocks of code, you can use modules. When you need to pass runtime secrets and access keys, you can use environment variables. You do not have to use only one approach, you may combine them to fit the needs of your projects.
If you are starting with Terraform, using workspaces as soon as possible will be a great habit that you will be glad to have once you move into more advanced configurations. If you’re already using Terraform, you may migrate your current configuration into workspaces. You will thank yourself once you move into more advanced environments. You should use all of these techniques, and mix and match them, to use all of the best practices to streamline your Terraform setup.
Time to Master Your Terraform Environments
Terraform Workspaces are a powerful tool to keep your infrastructure setups organized. With the right knowledge, they will help you manage your environments with much needed efficiency. They allow you to keep your infrastructure code clean and well-structured, while also reducing the risk of errors. You should now understand how to create, switch, and use workspaces. You also learned how to properly configure your state files, and you also know how to inject workspace specific variables to customize your resources. Take these techniques and apply them to your day to day work with Terraform, you will see how they enhance your ability to manage multiple environments with ease. They will also empower you to implement changes more efficiently and reduce errors. So now you know, you can stop herding cats, and start mastering your Terraform environments.