Every Terraform project eventually faces the same question: how do I run the same infrastructure for dev, staging, and production without copy-pasting my configuration three times? The two answers you’ll see in the wild are workspaces and separate directories, and picking the wrong one is a mistake that follows teams for years. The HashiCorp Terraform Associate (003) exam expects you to know what a workspace actually is, how it isolates state, how to reference it in configuration, and — critically — when not to reach for one.
This guide covers CLI workspaces end to end with commands you can run today, then compares them honestly against the directory-per-environment pattern most production teams settle on. If you want the full exam blueprint first, start with the Terraform Associate exam guide for 2026 and the 30-day study plan. Since workspaces are really a form of state isolation, it also pays to be solid on state management before you dive in.
What a Terraform Workspace Actually Is
A workspace is a named instance of state stored inside a single backend. Every Terraform configuration starts with exactly one workspace called default, and you usually don’t notice it because you never had to create it. When you add more workspaces, each one gets its own separate state file while continuing to use the same backend, the same configuration files, and the same credentials.
That last sentence is the whole exam, so read it twice. Workspaces do not give you separate code. They do not give you separate backends. They do not give you separate cloud accounts or credentials. All they give you is a separate terraform.tfstate per name, so that terraform apply in the dev workspace tracks a different set of real resources than terraform apply in the prod workspace — from the exact same directory.
Think of a workspace as answering one question: “which copy of the state am I operating on right now?” Everything else — which region, which instance size, which tags — is something you have to wire up in configuration using the workspace name. Terraform won’t do it for you.
The CLI Workspace Commands
All workspace management happens through the terraform workspace subcommand. Here is the complete set you need for the exam and for daily work:
| Command | What it does |
|---|---|
terraform workspace list | List all workspaces; * marks the current one |
terraform workspace new dev | Create a workspace named dev and switch to it |
terraform workspace select prod | Switch to an existing workspace |
terraform workspace show | Print the name of the current workspace |
terraform workspace delete dev | Delete a workspace (must be empty and not current) |
A typical first session looks like this:
# You start in the default workspace
terraform workspace show
# default
# Create and switch to a dev workspace in one step
terraform workspace new dev
# Created and switched to workspace "dev"!
# Create staging and prod too
terraform workspace new staging
terraform workspace new prod
# See them all
terraform workspace list
# default
# dev
# staging
# * prod
# Move between them
terraform workspace select dev
A few rules the exam likes to test:
- You cannot delete the
defaultworkspace. It always exists. - You cannot delete the workspace you’re currently in — select another one first.
- By default you cannot delete a workspace that still has resources in its state. Terraform stops you unless you pass
-force, which is a footgun because it orphans real infrastructure. - Creating a new workspace switches you into it immediately; there’s no separate “create then select” step required.
Where Workspace State Actually Lives
This is the detail that separates people who use workspaces from people who understand them. The location of each workspace’s state depends entirely on the backend.
With the local backend, the default workspace uses the familiar terraform.tfstate in your working directory. Every other workspace gets a file under a special directory:
terraform.tfstate.d/
├── dev/
│ └── terraform.tfstate
├── staging/
│ └── terraform.tfstate
└── prod/
└── terraform.tfstate
With a remote backend like S3, all workspaces share the same bucket, but non-default workspaces are stored under a key prefix (env:/ by default, configurable via workspace_key_prefix). So a single S3 backend transparently holds every workspace’s state, keyed by name. This is exactly why workspaces share a backend: they were designed to be lightweight variations that live side by side in one storage location.
The practical takeaway for the exam: switching workspaces does not touch your .tf files or your backend configuration — it only changes which state Terraform reads and writes.
Referencing the Workspace in Configuration with terraform.workspace
A workspace is useless unless your configuration reacts to it. Terraform exposes the current workspace name through the terraform.workspace expression, and you build environment-specific behavior on top of it.
The cleanest, most maintainable pattern is a lookup map in locals — this keeps all environment differences in one readable block instead of scattering if logic through your resources:
locals {
env = terraform.workspace
# Per-environment settings in one place
settings = {
dev = {
instance_type = "t3.micro"
instance_count = 1
enable_backups = false
}
staging = {
instance_type = "t3.small"
instance_count = 2
enable_backups = true
}
prod = {
instance_type = "m5.large"
instance_count = 4
enable_backups = true
}
}
# Resolve the block for the active workspace
config = local.settings[local.env]
}
resource "aws_instance" "app" {
count = local.config.instance_count
ami = var.ami_id
instance_type = local.config.instance_type
tags = {
Name = "app-${local.env}-${count.index}"
Environment = local.env
}
}
Now terraform workspace select prod && terraform apply builds four m5.large instances, while dev builds a single t3.micro — from identical code. Naming resources with ${local.env} also prevents collisions when the same account hosts more than one environment. If the count/for_each mechanics here are fuzzy, the count vs. for_each guide breaks them down, and the variables, outputs & locals guide covers the locals block in depth.
You can also use terraform.workspace directly in a resource — for example, only creating an expensive resource outside of dev:
resource "aws_cloudwatch_metric_alarm" "prod_only" {
count = terraform.workspace == "prod" ? 1 : 0
# ...
}
When Workspaces Are the Right Tool
Workspaces shine when you need multiple, nearly identical copies of the same infrastructure that can safely share one backend and one set of credentials. Good fits include:
- Ephemeral test or feature environments — spin up a throwaway copy of a stack per feature branch, then destroy it.
- Regional duplicates — the same architecture deployed to several regions from one config.
- Personal sandboxes — each engineer gets a named workspace to experiment without stepping on shared state.
The common thread: these are low-risk, short-lived, or structurally identical variations where the blast radius of a mistake is small and where sharing credentials is acceptable.
When Workspaces Are the Wrong Tool (The Part Everyone Gets Wrong)
Here is the nuance the exam rewards and the one that bites real teams: HashiCorp explicitly recommends against using CLI workspaces to separate strongly-isolated environments like production from non-production.
The reason is structural. Because all workspaces share the same backend and the same credentials, three problems appear the moment prod is involved:
- One wrong
selecthits production. If you forget which workspace you’re in,terraform applyfrom your dev directory can modify prod. There’s no account boundary to stop you — the credentials are the same. - You can’t vary credentials or backends per environment. Real organizations put prod in a separate cloud account with separate access. Workspaces can’t express that; the backend and provider auth are fixed across all of them.
- The environment difference is invisible in the code. Nothing in the
.tffiles tells a reviewer “this is prod.” The difference lives in your shell’s current workspace, which no pull request can capture.
For anything where a mistake is expensive — production especially — the recommended pattern is separate root modules per environment.
The Directory-Per-Environment Pattern
Instead of one directory with three workspaces, you keep one directory per environment, each with its own backend configuration, and factor the shared infrastructure into a reusable module:
environments/
├── dev/
│ ├── main.tf # calls the shared module
│ ├── backend.tf # points at the dev state
│ └── terraform.tfvars # dev-specific values
├── staging/
│ ├── main.tf
│ ├── backend.tf
│ └── terraform.tfvars
└── prod/
├── main.tf
├── backend.tf
└── terraform.tfvars
modules/
└── app/
├── main.tf
├── variables.tf
└── outputs.tf
Each environment’s main.tf is thin — it just calls the module with the right inputs:
# environments/prod/main.tf
module "app" {
source = "../../modules/app"
environment = "prod"
instance_type = "m5.large"
instance_count = 4
}
This gives you what workspaces can’t: a separate backend per environment (so prod state can live in a separate, tightly-controlled account), the ability to use different credentials, and — best of all — an environment difference that is explicit in code and visible in every code review. The tradeoff is a little duplication in the thin main.tf files, which is a small price for isolating production. The modules guide walks through building the reusable modules/app piece.
Workspaces vs. Directories at a Glance
| Dimension | CLI Workspaces | Directory per Environment |
|---|---|---|
| State isolation | Yes (one per workspace) | Yes (one backend per dir) |
| Separate backend | No — shared | Yes |
| Separate credentials/account | No | Yes |
| Environment visible in code | No (lives in shell) | Yes |
| Risk of hitting prod by accident | Higher | Lower |
| Setup overhead | Very low | Moderate |
| Best for | Ephemeral / identical copies | Strongly isolated prod/non-prod |
CLI Workspaces vs. HCP Terraform (Terraform Cloud) Workspaces
One more distinction the exam loves to slip in: the word “workspace” means two different things depending on context.
- A CLI workspace (everything above) is a named state instance inside a single backend, managed with
terraform workspace. - A workspace in HCP Terraform / Terraform Cloud is the primary organizational unit — it’s a first-class object that bundles a configuration, its state, its variables, its run history, and its access controls, typically mapped one-to-one with a working directory or repository.
They are not the same concept, and they don’t nest the way you’d expect. In HCP Terraform, the standard practice is to create a separate workspace per environment (like app-dev, app-prod), which naturally gives you the isolation CLI workspaces lack. If a question mentions “Terraform Cloud” or “HCP Terraform,” it’s asking about the organizational unit, not the terraform workspace command.
Common Exam Traps and Pitfalls
- “Workspaces give you separate configurations.” False — same code, different state only.
- “You should use workspaces to separate dev and prod.” The exam-safe answer is that this is not recommended for strongly-isolated environments; separate configurations/backends are preferred.
- Forgetting
terraform.workspace. Creating workspaces alone changes nothing about the resources — you must reference the name in configuration to get different behavior. - Deleting the default workspace. Not possible; it always exists.
- Assuming a remote backend needs separate buckets per workspace. It doesn’t — one backend holds all workspaces, keyed by a prefix.
- Confusing CLI workspaces with Terraform Cloud workspaces. Different concepts; know both definitions.
Put Workspaces Into Muscle Memory
Workspace questions are quick points if you’ve actually run the commands. Spin up a folder with a local backend, create dev and prod workspaces, drop in the locals lookup-map pattern above, and watch how terraform plan changes as you select between them. Then look inside terraform.tfstate.d/ to see the separate state files with your own eyes — that single observation locks in the concept better than any amount of reading.
When you’re ready to test yourself under exam conditions, Sailor.sh’s Terraform Associate mock exams include workspace and multi-environment items — including the “when should you not use a workspace” scenarios that trip people up — each with an explanation of why the answer is correct. You can gauge where you stand first with the free Terraform Associate practice questions, then reinforce the CLI side with the Terraform commands cheat sheet. Treating environment management as something you practice rather than read about is what turns it into reliable points on exam day.
Frequently Asked Questions
Do Terraform workspaces create separate copies of my code?
No. Every workspace uses the exact same .tf files, the same backend, and the same credentials. The only thing that changes between workspaces is which state file Terraform reads and writes. To get different infrastructure per workspace, you must reference terraform.workspace in your configuration.
Where is the state stored for a non-default workspace?
With the local backend, it lives under terraform.tfstate.d/<workspace-name>/terraform.tfstate. With a remote backend such as S3, all workspaces share the same backend but non-default workspaces are stored under a key prefix (env:/ by default). The default workspace keeps the plain terraform.tfstate location.
Should I use workspaces for dev, staging, and production?
For strongly-isolated environments — production especially — HashiCorp recommends against it, because workspaces share one backend and one set of credentials, making an accidental production change easy. Use separate root modules/directories (or separate HCP Terraform workspaces) per environment instead. Workspaces are better suited to ephemeral or structurally identical copies of the same stack.
How do I know which workspace I’m currently in?
Run terraform workspace show to print the current name, or terraform workspace list to see all workspaces with a * marking the active one. You can also surface terraform.workspace in outputs or resource tags so the environment is visible in your plan.
Can I delete the default workspace?
No. The default workspace always exists and cannot be deleted. You also can’t delete the workspace you’re currently in, or (without -force) a workspace that still tracks resources in its state.
What’s the difference between a CLI workspace and a Terraform Cloud workspace?
A CLI workspace is a named state instance inside a single backend, managed with the terraform workspace command. A workspace in HCP Terraform (Terraform Cloud) is the primary organizational unit that bundles a configuration, its state, variables, run history, and permissions — usually one per environment. They share a name but are different concepts, and the exam expects you to distinguish them.
How do I make a resource only exist in one environment?
Use a conditional count driven by the workspace name, for example count = terraform.workspace == "prod" ? 1 : 0. This creates the resource only in the prod workspace and skips it everywhere else, which is handy for prod-only alarms, backups, or replicas.
Ready to make multi-environment questions a strength? Drill realistic, explained items with the Sailor.sh Terraform Associate mock exams, then map your full prep with the Terraform Associate exam guide.