šŸŽ‰ DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Terraform Workspace State Mismatch: How to Fix Wrong Environment Deploys

Terraform applying to the wrong environment because workspace state is confused? Here's how to diagnose, fix, and prevent workspace state mismatches.

DevOpsBoys4 min read
Share:Tweet

Terraform workspaces are meant to keep dev/staging/prod state separate. When they get confused, you risk running production config against a staging environment or vice versa. Here's how to catch and fix it.

Symptoms of a State Mismatch

bash
# You think you're in dev, but plan shows production resources
terraform plan
# ...shows destroying prod RDS instances you didn't expect
 
# Or workspace shows wrong resources
terraform show | grep environment
# Shows "production" when you expected "development"
 
# State file references wrong backend
terraform state list | head -20
# Shows resources with prod-* prefix when you're supposedly in dev workspace

Step 1: Check Which Workspace You're In

bash
# Current workspace
terraform workspace show
 
# List all workspaces
terraform workspace list
# Output:
#   default
# * dev           <- currently selected (marked with *)
#   staging
#   production

Never trust your memory. Always run terraform workspace show before applying. Make it a habit.

Step 2: Verify the State is What You Expect

bash
# List all resources in current state
terraform state list
 
# Count resources per environment prefix
terraform state list | grep -c "^module.vpc"
 
# Get details of a specific resource
terraform state show aws_db_instance.main
 
# Should show values matching your current workspace (e.g., instance type, tags)

If terraform state show aws_db_instance.main shows production-size instances when you're in dev, your state is wrong.

Fix 1: You're in the Wrong Workspace

The simplest case — you're applying to the wrong workspace:

bash
# Switch to correct workspace
terraform workspace select staging
 
# Verify
terraform workspace show  # should show: staging
 
# Now re-run plan to confirm
terraform plan

Fix 2: State File Points to Wrong Backend Location

If your backend config uses workspace-aware paths:

hcl
# backend.tf
terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "env/${terraform.workspace}/terraform.tfstate"
    region = "us-east-1"
  }
}

The state key should resolve to env/dev/terraform.tfstate when in dev workspace.

bash
# Check what state is currently stored in S3
aws s3 ls s3://my-terraform-state/env/dev/
aws s3 ls s3://my-terraform-state/env/production/
 
# Download and inspect state file
aws s3 cp s3://my-terraform-state/env/dev/terraform.tfstate ./dev-state.json
cat dev-state.json | jq '.resources[0].instances[0].attributes.tags'
# Check if tags say environment = "dev" or environment = "production"

If the state file in env/dev/ contains production resources, someone applied to the wrong workspace at some point.

Fix 3: Moving Resources Between State Files

If resources ended up in the wrong state, you need to move them:

bash
# First, create a backup of both states
terraform state pull > backup-current-state.json
 
# Move a resource from current state to another workspace's state
# (Terraform doesn't support cross-workspace state moves directly)
# Instead: use state mv with a combined approach
 
# Step 1: Remove from wrong workspace
terraform workspace select dev
terraform state rm aws_db_instance.main
 
# Step 2: Switch to correct workspace
terraform workspace select production
 
# Step 3: Import the resource into correct state
terraform import aws_db_instance.main db-abcd1234efgh
 
# Step 4: Verify
terraform plan  # should show 0 changes if import was correct

Fix 4: Workspace Isolation via Variable Files

The cleanest pattern is to use workspace-aware variable files instead of relying on workspace magic:

hcl
# variables.tf
locals {
  env = terraform.workspace
  
  config = {
    dev = {
      instance_type = "t3.micro"
      min_capacity  = 1
      max_capacity  = 2
      db_class      = "db.t3.micro"
    }
    staging = {
      instance_type = "t3.small"
      min_capacity  = 1
      max_capacity  = 3
      db_class      = "db.t3.small"
    }
    production = {
      instance_type = "t3.large"
      min_capacity  = 2
      max_capacity  = 10
      db_class      = "db.r6g.large"
    }
  }
  
  current = local.config[local.env]
}

Now every resource uses local.current.instance_type and automatically gets the right value per workspace. A mismatch is immediately obvious in terraform plan.

Fix 5: Guard Rails in CI/CD

Prevent wrong-workspace applies in your pipeline:

bash
#!/bin/bash
# deploy.sh - called from CI/CD
set -euo pipefail
 
EXPECTED_ENV="${1:-}"
CURRENT_WORKSPACE=$(terraform workspace show)
 
if [ "$CURRENT_WORKSPACE" != "$EXPECTED_ENV" ]; then
  echo "ERROR: Expected workspace '$EXPECTED_ENV' but current is '$CURRENT_WORKSPACE'"
  echo "Run: terraform workspace select $EXPECTED_ENV"
  exit 1
fi
 
echo "Workspace verified: $CURRENT_WORKSPACE"
terraform plan -out=tfplan

In GitHub Actions:

yaml
- name: Verify workspace before apply
  run: |
    WORKSPACE=$(terraform workspace show)
    if [ "$WORKSPACE" != "${{ inputs.environment }}" ]; then
      echo "Wrong workspace! Expected ${{ inputs.environment }}, got $WORKSPACE"
      exit 1
    fi
  working-directory: ./infrastructure

Fix 6: Use Terragrunt for Better Isolation

If workspace confusion is a recurring problem, consider Terragrunt — it uses directory structure for environment separation instead of workspaces:

environments/
ā”œā”€ā”€ dev/
│   ā”œā”€ā”€ terragrunt.hcl    # dev-specific config
│   └── vpc/
│       └── terragrunt.hcl
ā”œā”€ā”€ staging/
│   └── ...
└── production/
    └── ...

No workspace switching — you cd to the environment directory. Misapplying to the wrong env requires you to actively be in the wrong directory, which is much harder to do accidentally.

Prevention Checklist

  • Add terraform workspace show to your prompt ($PS1) in terminal
  • Always run terraform plan and review before terraform apply
  • Use workspace-aware tags on all resources (tags = { env = terraform.workspace })
  • Set up CI/CD approval gates for production workspace applies
  • Store state in workspace-aware S3 paths
  • Consider Terragrunt if workspace confusion is frequent

Resources: Terraform workspaces | Terragrunt

šŸ”§

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments