Terraform workspaces and prod guardrails — terraform.workspace under Pro depth
Named state slots for spikes. Preconditions for prod refusals. File layout for durable envs.
<!-- hal:authoritative:yaml -->
*A workspace is a named state slot under one configuration. terraform.workspace is the string you can branch on. Prod still needs a wall.*
§I — Frame
07-31 already argued, as Ops, that durable environments prefer file layout over workspace names. That argument stands. This Cert lesson does not reverse it.
Pro exams still test workspaces. Lab 13 asks you to replace hardcoded environment assumptions with terraform.workspace, shape values from the active workspace, and add a guardrail that blocks an invalid prod configuration. Lab 05 joins workspace selection to terraform_remote_state and blocks t3.micro in prod. Those are exam-shaped skills. Learn them. Then keep Brikman's critique in mind when you promote revenue traffic.
Today's Ops lesson uses separate S3 keys for network and app stacks. That is file layout. Today's Cert lesson shows what workspaces are for when the exam (or a disposable spike) asks for them.
§II — Foundations: workspace mechanics
Terraform starts with workspace default. Commands:
terraform workspace list
terraform workspace new spike-42
terraform workspace select spike-42
terraform workspace show
With an S3 backend, each workspace stores a separate state object under the backend's workspace prefixing rules. Brikman walks an EC2 example: same configuration, new workspace, new instance, separate state. Useful for a quick isolated test on the same configuration. Weak as a promotion boundary because IAM usually covers the whole bucket.
The interpolation that makes workspaces useful inside HCL is the string terraform.workspace:
locals {
instance_type = terraform.workspace == "prod" ? "m5.large" : "t3.micro"
name_prefix = "app-${terraform.workspace}-"
}
resource "aws_instance" "app" {
ami = var.ami_id
instance_type = local.instance_type
tags = {
Name = "${local.name_prefix}web"
Env = terraform.workspace
}
lifecycle {
precondition {
condition = terraform.workspace != "prod" || var.instance_type != "t3.micro"
error_message = "prod refuses t3.micro"
}
}
}
Lab 13's tasks map onto that shape: stop hardcoding local.environment = "dev", derive behavior from terraform.workspace, surface the active workspace in outputs, block unsafe prod configuration with a precondition.
Lab 05's broken starter hardcodes local.environment = "dev" and keeps a fake network_outputs map keyed by environment. The repair path the README implies: use terraform.workspace, consume upstream values via terraform_remote_state, block t3.micro in prod. That is the Pro combination punch: workspace string plus remote_state plus guardrail.
§III — Mechanism: what the exam wants versus what production wants
Exam wants: you can create and select workspaces; you know terraform.workspace; you can branch instance sizes; you can attach preconditions/checks; you understand remote state still works per workspace.
Production wants: stage and prod as separate root modules (or separate directories) with separate keys, separate IAM where possible, and promotion by module version pin. Brikman spends Isolation via Workspaces to earn Isolation via File Layout.
Hold both. On the exam, pick workspace answers when the question names workspaces. On the job, default to file layout for anything a teammate depends on. Use workspaces for disposable spikes that die the same day.
Guardrails matter in both worlds. A precondition that refuses t3.micro in prod is valid HCL whether prod is a workspace name or a directory name selected by CI. Lab 13's prod safety task trains the reflex. Port the reflex to file-layout stacks by checking var.environment the same way.
§III.D — Sample stem walkthroughs
Stem A. A team uses one configuration and three workspaces: dev, stage, prod. They need larger instances only in prod. Which expression selects the type?
Answer shape: ternary or map keyed by terraform.workspace. Not a backend change. Not a provider alias.
Stem B. Same team wants separate IAM so prod state cannot be written by the dev role. Are workspaces enough?
Answer shape: No. Same bucket and backend credentials usually apply. Prefer separate state locations and roles (file layout / separate accounts).
Stem C. A configuration interpolates key = "app/${terraform.workspace}.tfstate" inside backend "s3". What happens?
Answer shape: Invalid. Backend configuration does not evaluate that expression. Use partial config or static keys per root module.
Stem D. Lab-style: plan in prod with t3.micro should fail. Where do you put the condition?
Answer shape: resource precondition (or check) comparing terraform.workspace and the instance type. Variable validation alone is insufficient unless environment is also a variable.
Walk these four aloud. They cover most of today's Pro surface.
§IV — Worked Example: repairing Lab 05 / Lab 13 shapes
Lab 05 broken pattern (simplified from on-disk starter):
locals {
environment = "dev"
}
locals {
network_outputs = {
dev = {
vpc_id = "vpc-dev-1234567890abcdef0"
# ...
}
prod = {
vpc_id = "vpc-prod-1234567890abcdef0"
# ...
}
}
}
output "selected_environment" {
value = local.environment
}
Repair toward:
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "acme-tf-state"
key = "network/${terraform.workspace}/terraform.tfstate"
region = "us-east-1"
}
}
output "selected_environment" {
value = terraform.workspace
}
output "vpc_id" {
value = data.terraform_remote_state.network.outputs.vpc_id
}
variable "instance_type" {
type = string
}
resource "aws_instance" "app" {
# ...
instance_type = var.instance_type
lifecycle {
precondition {
condition = terraform.workspace != "prod" || var.instance_type != "t3.micro"
error_message = "prod refuses t3.micro"
}
}
}
Notice the remote_state key still interpolates terraform.workspace. That is legal inside data config. It would be illegal inside a backend block. Ops Fact two still holds.
For Lab 13, focus on the guardrail and the output that prints terraform.workspace. Validate. Plan in dev. Select prod. Plan again with an illegal type. Confirm the precondition fails before apply.
§II.B — Commands and state objects under S3
When the backend is s3, Terraform stores non-default workspace state under a prefix that includes the workspace name. Exact key layout depends on Terraform version and backend implementation details, but the operational fact is stable: terraform workspace new creates another state object in the same bucket. terraform workspace select points subsequent plans at that object. Deleting a workspace does not always delete cloud resources. Destroy first if the workspace owned real infrastructure.
Exam traps:
- Thinking workspaces create separate backends. They do not. One backend configuration, many state objects.
- Thinking
terraform.workspaceworks insidebackendblocks. It does not. - Thinking switching workspaces changes your working directory layout. It changes which state file the same
.tffiles talk to. - Forgetting
defaultstill exists and still receives applies if you never select the spike workspace.
Practice the trap answers out loud once. Then move on.
§III.B — Preconditions, checks, and validation (Lab 13 depth)
Lab 13 success criteria mention variable validation, preconditions, and check blocks. Know the split:
- Variable validation runs early on input values. Good for format and allowed sets.
- Preconditions on resources run during plan/apply against expressions that can see other resources and
terraform.workspace. - Check blocks express advisory assertions that show up as warnings/failures depending on version and flags without always blocking the same way a precondition does.
For prod refusing t3.micro, a resource precondition keyed on terraform.workspace is the clearest Pro drill. Variable validation alone cannot see the workspace string unless you also pass environment as a variable (file-layout style). Workspaces push you toward precondition expressions.
Example check block sibling (advisory posture):
check "prod_name_prefix" {
assert {
condition = terraform.workspace != "prod" || startswith(aws_instance.app.tags["Name"], "app-prod-")
error_message = "prod Name tag must use app-prod- prefix"
}
}
Do not confuse check blocks with terraform test. Different instruments.
§VI.B — Notes you should keep next to the labs
Write these lines in your Bootcamp notebook:
terraform workspace showbefore every destroy.terraform.workspaceis readable in resources, data, and locals. Not in backend blocks.- Preconditions see workspace. Use them for prod refusals.
- Remote state config may interpolate workspace. Backend may not.
- Durable envs: directories and keys. Disposable envs: workspaces.
- Brikman pp.163-170 is the critique that keeps you honest after the exam.
If you only memorize syntax, you will pass some stems and fail the design stems. If you only memorize the critique, you will fail the syntax stems. Carry both.
When Lab 13 and Lab 05 both pass validate/plan for the expected scenarios, stop. Do not expand the lab into a multi-account import project tonight. Maghrib will point the lab-ref back at these two directories. That is the completion contract for tonight.
§III.C — Remote state plus workspaces (Lab 05 depth)
Lab 05 wants both workspace awareness and remote_state. That combination is where people invent dynamic backend keys. Refuse that invention. Keep the backend static. Let remote_state config.key interpolate terraform.workspace if you must mirror workspace-named producer keys.
Better durable design (Ops lesson): producer keys like network/prod/terraform.tfstate selected by CI environment variables via partial backend config on each root module, not by workspace switching on a single root module. Cert still requires you to be fluent in the workspace version of the story for exam day.
When a question shows a workspace named prod and a remote_state block, ask: does the producer also use workspaces, or does the producer use file layout with keys that only happen to contain the word prod? The answer changes which key string is correct.
§IV.B — Drill sequence (thirty minutes)
- Open Lab 13. Replace hardcoded environment with
terraform.workspace. - Add output
workspace = terraform.workspace. - Add precondition blocking illegal prod instance types.
terraform validateandterraform planin default/dev.terraform workspace new prod(or select) and plan with a bad type; confirm failure.- Open Lab 05. Remove fake network map. Add remote_state. Wire outputs.
- Re-read Brikman pp.163-170. Write four sentences: what workspaces are good for; what they are bad for; how remote_state differs from backend; how a precondition differs from a variable validation.
That drill is the Cert product for today. It cites real Bootcamp labs only.
§IV.C — Mapping Associate objectives to Pro depth
Associate objectives covering state and modules expect workspace vocabulary. Pro-depth scenarios expect you to choose isolation strategy under constraints: shared CI role, single bucket, need for a temporary preview environment, need for hard prod separation. The correct Pro answer is often "workspaces for the preview, file layout for prod," not "workspaces for everything because the CLI is convenient."
Write that sentence into your notes. It is the judgment call the multiple-choice stems circle without naming.
§V — Connection to Prior Lessons and Today's Quatro
07-31 Ops: file layout preferred for durable envs. Cite it. Do not re-litigate the whole GCP shirt.
08-18 Cert: remote operations and force-unlock. Different Pro skill.
09-02 Cert: replace_triggered_by. Different Pro skill.
Today Ops: remote_state across AWS stacks with static keys. Prefer that pattern when both stacks are durable.
Today Dev: census the plan. A workspace switch that changes instance_type will show up as update or replace in the census. Use that as a review aid when practicing Lab 13.
Today Go: list backend objects. Workspace-prefixed keys appear as additional objects under the bucket. Knowing that helps you see why IAM on the whole bucket is a blunt wall.
Also refuse to put the words that belong in the manifest into the lesson body. The mechanism is workspaces. The referent is Lab 13 and Lab 05. The tome is Brikman Chapter 3. That is enough naming.
If an exam stem shows terraform.workspace used to select a different module source URI, be suspicious. Module sources that change by workspace complicate the dependency graph and surprise reviewers. Prefer same module source, different variable values.
§V.B — Failure modes specific to workspace drills
Applied to the wrong workspace. You meant to destroy a spike and you were still on default. Habit: terraform workspace show before destroy. Put it in the same muscle memory as git status before commit.
Orphaned cloud objects after workspace delete. Deleting the workspace removes Terraform's pointer. Cloud resources can remain. Destroy first.
Shared variable defaults that assume dev. A default instance_type = "t3.micro" is fine until someone selects prod and forgets to override. The precondition is the backstop. Defaults are not.
CI that never selects the workspace. Pipelines that only run in default will not catch prod guardrails. Add an explicit terraform workspace select (or TF_WORKSPACE) in the prod job.
Backend re-init surprises. Changing backend configuration while multiple workspaces exist requires care. Read the init migration prompts. Do not click through blindly.
§V.C — How this Cert shirt supports the Ops remote_state shirt
Ops uses static keys and separate directories. Cert shows the exam's workspace tool and its guardrails. Together they teach judgment: which isolation tool for which lifetime. The remote_state data source appears in both stories. Backend expression rules appear in both stories. The difference is whether environment identity lives in a directory name or a workspace name.
When you review a pull request that introduces terraform.workspace into a long-lived prod stack, ask for the lifetime argument. If the stack is durable, ask why file layout was rejected. If the stack is a spike, approve the workspace and ask for a destroy job.
§VI — Closing
Know workspace commands cold. Use terraform.workspace for exam-shaped branching. Add prod guardrails with preconditions. Prefer file layout when the environment outlives the afternoon. Keep remote_state keys honest either way.
One last exam tip: if a stem mentions both workspaces and moved blocks, solve isolation first, then refactor addresses. Workspace selection does not replace state moves. Different instruments.
§VI.B — Notes you should keep next to the labs
Write these lines in your Bootcamp notebook:
terraform workspace showbefore every destroy.terraform.workspaceis readable in resources, data, and locals. Not in backend blocks.- Preconditions see workspace. Use them for prod refusals.
- Remote state config may interpolate workspace. Backend may not.
- Durable envs: directories and keys. Disposable envs: workspaces.
- Brikman pp.163-170 is the critique that keeps you honest after the exam.
If you only memorize syntax, you will pass some stems and fail the design stems. If you only memorize the critique, you will fail the syntax stems. Carry both.
When Lab 13 and Lab 05 both pass validate/plan for the expected scenarios, stop. Do not expand the lab into a multi-account import project tonight. Maghrib will point the lab-ref back at these two directories. That is the completion contract for tonight.
Related
- Prior arc: Terraform workspaces and environment isolation
- Vendor hub: Cross-References/domains/01-Earth-DevOps
- Grounding tome: Brikman (Ch 3, Isolation via Workspaces, pp. 163-170)
- Paired Ops: Archmagus-Stack/01-Earth-DevOps/Synthesis-Lessons/2026-09-05-terraform-remote-state-data-source-across-aws-network-and-app-stacks/lesson.md