Using terraform.workspace with Terraform Cloud

Lich Nguyen
3 min readMay 24, 2020

--

Terraform Cloud Workflow

Have you ever tried Terraform Cloud before? Have you tried all the integration, collaboration features nicely setup on Terraform Cloud? Have you ever been so eager running all your configuration in Terraform Cloud and then suddenly realized it is not reading variable from your desired terraform.workspace?

Let’s see what we can do to fix this.

Using terraform.workspace to setup multi environment

If you already know how to use terraform.workspace to switch between multi environment on the same configuration, then you can skip this section.

When working with Terraform, we usually want to use the same configuration but with different variables for each environment (dev, test, prod). There are many technique to archive that, one of them is leverage on terraform.workspace

To use terraform.workspace, first, we create the workspace corresponding to our environments. For example here I can create 3 workspace: dev, test and prod using the command: terraform workspace new dev .

I can switch between workspace using the command: terraform workspace select dev/test/prod

Also, the Terraform state of each workspace is separated and managed by the workspace select command.

Now, we can define resource as below example, that will switch to use the variables corresponding to current environment workspace

# Variables files
---- variables.tf ------
variable name {
description = "Environment"
type = map(string)
default = {
dev = "dev"
stg = "stg"
prod = "prod"
}
}
# AWS ECS cluster configuration file
---- ecs.tf ------
module "ecs" {
source = "../modules/ecs/"

name = var.name[terraform.workspace]
}

As you can see, the variable name is a map of key and value type, with the key is the env we defined with terraform.workspace. Or in other word, we are mapping the variables with terraform.workspace

This is a very smart way to use different variables for each environment and not repeating a lot of code.

Use terraform.workspace with Terraform Cloud

Terraform Cloud is a cloud version of Terraform Enterprise, which promote collaborative IAC (Infrastructure-As-Code), make it easier for multi teams to work on Terraform.

The workspace concept on TF Cloud is not the same as local terraform.workspace.

Locally, configuration codes are organized into directories, each directory has configuration, variables and state file. TF Cloud doesn’t have a directory structure, instead, those configuration, variables and state file are stored in a workspace, or you can say a cloud directory.

In local environment, terraform.workspace is just a way to switch state file.

In TF Cloud, we would like to have one workspace per environment per configuration. We shall organize the ECS example above into 3 workspace: ecs-dev, ecs-stg, ecs-prod

How do we match local workspace with TF Cloud workspace?

How can I configure that, locally, when I am inside ecs configuration, terrafom.workspace=dev, I would work with ecs-dev on the cloud? This is archive-able using workspace prefix in backend config.

terraform {
backend "remote" {
hostname = "app.terraform.io"
organization = "my-org"

workspaces {
prefix = "ecs-"
}
}
}

With this, Terraform will always know which remote workspace you are working with by combining the prefix with the local terraform.workspace

Terraform Cloud run Plan/Apply on its own machine, which is great to ensure consistency and a single source of truth for all the Plan/Apply. However, because of this, we can’t set terraform.workspace before each run, thus it is always set to default value default. This is not what we want.

We have to modify our code a bit, to make it compatible with both running locally and on TF Cloud.

variable "TFC_WORKSPACE_NAME" {
type = string
default = ""
}

locals {
# If your backend is not Terraform Cloud, the value is ${terraform.workspace}
# otherwise the value retrieved is that of the TFC_WORKSPACE_NAME with trimprefix
workspace = var.TFC_WORKSPACE_NAME != "" ? trimprefix("${var.TFC_WORKSPACE_NAME}", "ecs-") : "${terraform.workspace}"
}
  • TFC_WORKSPACE_NAME is a special variable that auto set by TF Cloud, it is the current TF Cloud workspace.
  • If I run locally, use terraform.workspace
  • Else in the cloud, use TFC_WORKSPACE_NAME without the prefix ecs

In configuration code, switch to use local.workspace instead:

module "ecs" {
source = "../modules/ecs/"

name = var.name[local.workspace]
}

This is done.

Now you can have the best of both world, use Terraform Cloud in remote execution mode, and still able to switch between terraform.workspace on your local environment without any issue.

--

--