Skip to content

Workspaces and Environment Isolation


Once state becomes shared, the next problem is isolation. Development and production should not accidentally point at the same state, use the same credentials, or share the same blast radius.

Terraform CLI workspaces can create multiple state instances from one configuration, but they are not a universal answer to environment separation. This chapter explains what they actually do, where they help, and where separate configurations are safer.

What Is a Terraform CLI Workspace?

A Terraform CLI workspace lets one Terraform working directory use multiple state files.

Instead of:

One directory
    β”‚
    └── one state

you can have:

One directory
    β”‚
    β”œβ”€β”€ dev state
    β”œβ”€β”€ staging state
    └── production state

The configuration remains the same while the selected workspace determines which state Terraform operates against.

The default workspace is called:

default

You can see the current workspace with:

terraform workspace show

List available workspaces:

terraform workspace list

Create one:

terraform workspace new dev

Switch to another:

terraform workspace select production

Why Workspaces Exist

Suppose you have a small infrastructure configuration that is intentionally identical across environments.

You might want:

dev
staging
production

to use the same Terraform code while maintaining separate state.

CLI workspaces can provide that separation.

The important mental model is:

             Same Configuration
                    β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β–Ό           β–Ό           β–Ό
       dev       staging     production
        β”‚           β”‚           β”‚
     state A      state B      state C

The configuration is shared.

The state is separate.

Creating and Switching Workspaces

Start with the default workspace:

terraform workspace show

Create a development workspace:

terraform workspace new dev

Create staging:

terraform workspace new staging

List them:

terraform workspace list

You might see:

  default
* dev
  staging

The * indicates the currently selected workspace.

Switch to staging:

terraform workspace select staging

Then:

terraform workspace show

returns:

staging

Terraform commands now operate against the state associated with that workspace.

Workspace-Specific Values

The current workspace is available through:

terraform.workspace

For example:

locals {
  instance_name = "web-${terraform.workspace}"
}

If the workspace is:

dev

the value becomes:

web-dev

If it is:

production

the value becomes:

web-production

You can also use a map:

variable "instance_size" {
  type = map(string)

  default = {
    dev        = "t3.micro"
    staging    = "t3.small"
    production = "t3.medium"
  }
}

resource "aws_instance" "web" {
  instance_type = var.instance_size[terraform.workspace]

  # ...
}

This can be convenient for small configurations.

Warning

Workspace selection is part of the Terraform execution context. A command run in the wrong workspace can affect the wrong environment while still producing a completely valid Terraform plan.

Workspaces and Remote State

When using a backend that supports workspaces, the backend generally stores separate state for different CLI workspaces.

For the S3 backend, the default workspace has the configured key, while non-default workspaces use a workspace-specific path based on the backend’s workspace key prefix.

Conceptually:

S3 bucket
β”‚
β”œβ”€β”€ default state
β”‚
β”œβ”€β”€ dev state
β”‚
β”œβ”€β”€ staging state
β”‚
└── production state

This means workspaces can provide state separation without creating completely separate Terraform configurations.

The Major Limitation: Workspaces Are Not Security Boundaries

This is where beginners often overestimate workspaces.

A workspace gives you different state.

It does not automatically give you:

  • Different AWS credentials
  • Different IAM permissions
  • Different Git branches
  • Different approval requirements
  • Different administrators
  • Different backend buckets
  • A hard production access boundary

For example:

workspace = dev
workspace = production

can still use the same AWS identity if you configure them that way.

That means:

State isolation
      β‰ 
Security isolation

This distinction is critical for production environments.

Environment Isolation Is Bigger Than State

A robust environment boundary may look like:

Development
β”œβ”€β”€ configuration
β”œβ”€β”€ state
β”œβ”€β”€ credentials
└── permissions

Production
β”œβ”€β”€ configuration
β”œβ”€β”€ state
β”œβ”€β”€ credentials
└── permissions

Production may require:

  • Different AWS accounts
  • Different roles
  • Different credentials
  • Different state
  • Different approval rules
  • Fewer people with write access

A CLI workspace by itself does not provide those controls.

Separate Directories

Another common design is:

terraform/
β”œβ”€β”€ dev/
β”‚   β”œβ”€β”€ main.tf
β”‚   └── backend.tf
β”œβ”€β”€ staging/
β”‚   β”œβ”€β”€ main.tf
β”‚   └── backend.tf
└── production/
    β”œβ”€β”€ main.tf
    └── backend.tf

Each directory can have its own state boundary and backend configuration.

This can make environment differences explicit.

For example:

dev
  └── cheaper instance sizes

production
  └── larger instances + stricter controls

The downside is that duplicated configuration can drift.

That is where modules become useful.

You can keep reusable infrastructure in modules while maintaining environment-specific root configurations:

modules/
└── application/

environments/
β”œβ”€β”€ dev/
β”œβ”€β”€ staging/
└── production/

Each environment can call the same module with different values.

This gives you stronger boundaries while retaining reuse.

Workspaces Versus Directories

Consider the two models.

CLI Workspaces

one configuration
        β”‚
        β”œβ”€β”€ dev state
        β”œβ”€β”€ staging state
        └── production state

Good when:

  • Configuration is genuinely shared
  • Environments differ mostly by values
  • The team understands workspace selection
  • Strong environment-specific permissions are not required from the workspace mechanism itself

Separate Root Configurations

dev/
staging/
production/

Good when:

  • Environments have meaningful configuration differences
  • Production needs stricter review
  • Different credentials or accounts are used
  • You want environment boundaries to be visible in Git
  • Different teams own different environments

Neither is universally correct.

The important question is:

What boundary do you actually need?

A Practical Environment Decision

A useful way to think about the choice is:

RequirementCLI WorkspacesSeparate Root Configurations
Same configurationExcellentPossible through modules
Separate stateYesYes
Different credentialsNot inherentlyEasy
Different AWS accountsPossible, but requires careNatural
Different approval policyNot inherentlyEasier
Strong production boundaryWeakStronger
Small repeated environmentsConvenientMore files
Large organizational separationUsually awkwardUsually clearer

CLI Workspaces and HCP Terraform Workspaces Are Different

The word “workspace” is overloaded.

Terraform CLI workspaces are local Terraform functionality. HCP Terraform workspaces are objects managed by HCP Terraform. They are not interchangeable.

A CLI workspace means multiple state instances associated with one Terraform working directory.

An HCP Terraform workspace is more like a managed unit containing configuration, variables, state, runs, permissions, and execution settings.

HashiCorp explicitly distinguishes the two. HCP Terraform workspaces are required for managing infrastructure in HCP Terraform and participate in its access-control model, while CLI workspaces are optional and isolate multiple state files within a working directory.

When CLI Workspaces Are a Good Fit

Imagine a small service deployed to several nearly identical temporary environments:

feature-a
feature-b
feature-c

The infrastructure structure is almost identical.

Workspaces can be convenient because you can create and destroy state instances without duplicating the root configuration.

Another reasonable case is a small team with several identical low-risk environments where the workspace mechanism is well understood.

When Not to Use CLI Workspaces

Be cautious when:

  • Production requires separate credentials
  • Production has different administrators
  • Environments use different cloud accounts
  • Configuration differs substantially
  • Accidental workspace selection would be dangerous
  • Different environments need independent CI/CD pipelines
  • The state boundary needs to map directly to organizational ownership

In those situations, separate root configurations are often clearer.

A Production Mental Model

Do not think:

production = workspace "production"

Think:

Production
β”‚
β”œβ”€β”€ state boundary
β”œβ”€β”€ cloud-account boundary
β”œβ”€β”€ credential boundary
β”œβ”€β”€ permission boundary
β”œβ”€β”€ approval boundary
└── operational ownership

A workspace can be one part of that design.

It is not the whole design.

The Goal of Environment Isolation

The purpose of environment separation is to reduce blast radius.

If development breaks:

development
   β”‚
   └── failure

you want the failure to remain there.

If production state, credentials, and infrastructure are all separated appropriately:

development ──X── production

then an accidental development operation has fewer ways to affect production.

That is much more valuable than simply having three different state filenames.

What Comes Next?

Once multiple environments exist, the Terraform repository itself becomes part of the operational system.

You now need to answer questions such as:

  • What should Git contain?
  • What must never be committed?
  • Where should secrets come from?
  • What does sensitive = true actually protect?

Those are the problems addressed in the next chapter.

Last updated on