# Migrating from Terraform Workspaces

Terraform workspaces solve a simple problem: deploy the same code to multiple environments using one state backend. Atmos can adopt that workspace-backed state while moving environment configuration into explicit stack files.

## How Workspaces Fit With Atmos

Workspaces are a valid way to organize Terraform state. When migrating to Atmos, the important shift is making configuration explicit in stacks while preserving the state layout that already works for your team.

### 1. **Shared Backend Configuration**

Terraform workspaces share backend configuration and differentiate state by workspace name. Atmos can keep that model, or configure separate backends per stack when a team explicitly wants that operating model.

```hcl
# All workspaces use this backend configuration
terraform {
  backend "s3" {
    bucket = "my-terraform-state"  # Same bucket for dev, staging, prod
    key    = "terraform.tfstate"   # Workspace adds the environment-specific path
    region = "us-east-1"
  }
}
```

If your team wants independent backend configuration per environment, use the optional separate-backend migration path below.

### 2. **Configuration Often Lives in Code**

Workspace-based repos often put environment-specific choices in Terraform expressions. Atmos moves those choices into stack YAML:

**File:** `variables.tf`

```hcl
variable "instance_type" {
  default = "t3.micro"
}

locals {
  # Workspace-specific logic
  instance_type = terraform.workspace == "prod" ? "m5.large" :
                  terraform.workspace == "staging" ? "t3.medium" :
                  "t3.micro"

  enable_monitoring = terraform.workspace == "prod" ? true : false

  # This can grow as environments diverge
}
```

### 3. **Hidden State**

Which workspaces exist? What resources do they contain? You have to inspect the state backend or track it manually.

```bash
$ terraform workspace list
  default
  dev
  staging
  prod
  old-test-workspace  # Is this safe to delete?
  johns-experiment    # What is this?
```

### 4. **Component Versioning**

Workspaces usually run the same root module version for every environment. Atmos stacks can point environments at different component versions when a rollout needs that control.

### 5. **Optional Backend Changes**

Teams that want separate backend configuration can migrate workspaces into per-stack backends. This is optional and should be planned carefully because it moves state.

## How Atmos Helps

Atmos separates **code** (Terraform components) from **configuration** ([stack YAML files](/stacks/)). This gives you:

- **Workspace-compatible state** - Keep existing workspace-backed state, or choose separate [backend configuration](/stacks/remote-state)
- **Environment-specific config** - No giant case statements
- **Visible configuration** - Stacks are files you can search, version, review
- **Component reuse** - Share code, customize config via [inheritance](/howto/inheritance)
- **Optional backend changes** - Move state backends only when your team chooses that model

## Migration Strategy

### Before: Workspace-Based Setup

**File:** `main.tf`

```hcl
terraform {
  backend "s3" {
    bucket = "terraform-state"
    key    = "vpc/terraform.tfstate"
    region = "us-east-1"
  }
}

variable "environment" {
  default = "dev"
}

variable "cidr_block" {
  default = "10.0.0.0/16"
}

locals {
  # Workspace-specific logic
  cidr_block = terraform.workspace == "prod" ? "10.100.0.0/16" :
               terraform.workspace == "staging" ? "10.50.0.0/16" :
               "10.0.0.0/16"
}

resource "aws_vpc" "main" {
  cidr_block = local.cidr_block

  tags = {
    Environment = terraform.workspace
  }
}
```

**Deploy:**

```bash
terraform workspace select prod
terraform apply  # Which VPC am I deploying? Not obvious!
```

### After: Atmos Stacks

**Step 1: Create reusable component (generic code)**

**File:** `components/terraform/vpc/main.tf`

```hcl
# No workspace logic!
variable "cidr_block" {
  description = "VPC CIDR block"
  type        = string
}

variable "environment" {
  description = "Environment name"
  type        = string
}

resource "aws_vpc" "main" {
  cidr_block = var.cidr_block

  tags = {
    Environment = var.environment
  }
}
```

**Step 2: Create environment-specific stacks (config)**

**File:** `stacks/prod.yaml`

```yaml
terraform:
  backend_type: s3
  backend:
    s3:
      bucket: terraform-state-prod  # Isolated backend
      key: terraform.tfstate
      region: us-east-1

components:
  terraform:
    vpc:
      backend:
        s3:
          workspace_key_prefix: vpc
      vars:
        cidr_block: "10.100.0.0/16"
        environment: prod
```

**File:** `stacks/dev.yaml`

```yaml
terraform:
  backend_type: s3
  backend:
    s3:
      bucket: terraform-state-dev  # Separate backend!
      key: terraform.tfstate
      region: us-east-1

components:
  terraform:
    vpc:
      backend:
        s3:
          workspace_key_prefix: vpc
      vars:
        cidr_block: "10.0.0.0/16"
        environment: dev
```

**Deploy:**

```bash
atmos terraform apply vpc -s prod  # Crystal clear: VPC in prod
atmos terraform apply vpc -s dev   # VPC in dev
```

No workspace selection. No hidden state. Just explicit, declarative configuration.

Learn more: [`atmos terraform apply`](/cli/commands/terraform/apply) | [`atmos terraform plan`](/cli/commands/terraform/plan)

## Step-by-Step Migration

### 1. Extract Workspace Logic

Identify all workspace-specific logic in your Terraform code:

**Before:**

```hcl
locals {
  instance_type     = terraform.workspace == "prod" ? "m5.large" : "t3.small"
  enable_monitoring = terraform.workspace == "prod" ? true : false
  backup_retention  = terraform.workspace == "prod" ? 30 : 7
}
```

**After (convert to variables):**

**File:** `components/terraform/app/variables.tf`

```hcl
variable "instance_type" {
  description = "EC2 instance type"
  type        = string
}

variable "enable_monitoring" {
  description = "Enable CloudWatch monitoring"
  type        = bool
}

variable "backup_retention" {
  description = "Backup retention in days"
  type        = number
}
```

### 2. Create Stack Configurations

For each workspace, create a stack file:

**File:** `stacks/prod.yaml`

```yaml
components:
  terraform:
    app:
      vars:
        instance_type: m5.large
        enable_monitoring: true
        backup_retention: 30
```

**File:** `stacks/dev.yaml`

```yaml
components:
  terraform:
    app:
      vars:
        instance_type: t3.small
        enable_monitoring: false
        backup_retention: 7
```

### 3. Choose a State Backend Strategy

Atmos can use your existing workspace-backed state. Moving state to separate backends is optional and requires careful planning to avoid state loss.

**Option A: Keep Workspace State (Easiest)**

You can keep using workspace-based state with Atmos:

**File:** `stacks/prod.yaml`

```yaml
terraform:
  backend_type: s3
  backend:
    s3:
      bucket: terraform-state  # Same bucket
      key: vpc/terraform.tfstate
      region: us-east-1
      workspace_key_prefix: env  # Uses workspace structure

components:
  terraform:
    vpc:
      settings:
        terraform:
          workspace: prod  # Selects workspace "prod"
```

This lets you migrate incrementally without touching state.

**Option B: Move to Separate Backends (Optional)**

If your team explicitly wants independent backend configuration per environment, migrate each workspace to its own backend:

1. **Export state from workspace:**
   ```bash
   terraform workspace select prod
   terraform state pull > prod.tfstate
   ```

2. **Configure new backend:**

   **File:** `stacks/prod.yaml`
   ```yaml
   terraform:
     backend_type: s3
     backend:
       s3:
         bucket: terraform-state-prod  # New bucket
         key: vpc.tfstate
         region: us-east-1
   ```

3. **Initialize and push state:**
   ```bash
   atmos terraform init vpc -s prod
   terraform state push prod.tfstate
   ```

4. **Verify:**
   ```bash
   atmos terraform plan vpc -s prod  # Should show no changes
   ```

### 4. Remove Workspace Logic

Clean up your Terraform code:

**Remove:**

```hcl
# DELETE workspace references
locals {
  env = terraform.workspace  # Remove
}

# DELETE workspace conditionals
count = terraform.workspace == "prod" ? 1 : 0  # Remove
```

**Replace with variables:**

```hcl
variable "environment" {
  description = "Environment name"
  type        = string
}

variable "create_feature" {
  description = "Whether to create optional feature"
  type        = bool
  default     = false
}
```

### 5. Update CI/CD

**Before:**

```bash
# Old CI/CD
terraform workspace select $ENV
terraform plan
terraform apply -auto-approve
```

**After:**

```bash
# New CI/CD
atmos terraform plan $COMPONENT -s $STACK
atmos terraform apply $COMPONENT -s $STACK -auto-approve
```

**GitHub Actions example:**

```yaml
- name: Deploy VPC
  run: |
    atmos terraform apply vpc -s ${{ matrix.stack }}
  strategy:
    matrix:
      stack: [dev, staging, prod]
```

## Handling Common Patterns

### Pattern 1: Workspace-Specific Resources

**Before:**

```hcl
resource "aws_instance" "monitoring" {
  count = terraform.workspace == "prod" ? 1 : 0
  # ...
}
```

**After:**

**File:** `components/terraform/monitoring/main.tf`

```hcl
variable "enabled" {
  type    = bool
  default = false
}

resource "aws_instance" "this" {
  count = var.enabled ? 1 : 0
  # ...
}
```

**File:** `stacks/prod.yaml`

```yaml
components:
  terraform:
    monitoring:
      vars:
        enabled: true
```

**File:** `stacks/dev.yaml`

```yaml
components:
  terraform:
    monitoring:
      vars:
        enabled: false
```

### Pattern 2: Workspace in Tags

**Before:**

```hcl
tags = {
  Environment = terraform.workspace
}
```

**After:**

**File:** `components/terraform/vpc/main.tf`

```hcl
variable "environment" {
  type = string
}

tags = {
  Environment = var.environment
}
```

**File:** `stacks/prod.yaml`

```yaml
vars:
  environment: production

components:
  terraform:
    vpc:
      vars:
        environment: '{{ .vars.environment }}'
```

### Pattern 3: Workspace-Specific Data Sources

**Before:**

```hcl
data "aws_ami" "app" {
  most_recent = true

  filter {
    name   = "name"
    values = [terraform.workspace == "prod" ? "prod-ami-*" : "dev-ami-*"]
  }
}
```

**After:**

**File:** `components/terraform/app/main.tf`

```hcl
variable "ami_prefix" {
  type = string
}

data "aws_ami" "app" {
  most_recent = true

  filter {
    name   = "name"
    values = ["${var.ami_prefix}-*"]
  }
}
```

**File:** `stacks/prod.yaml`

```yaml
components:
  terraform:
    app:
      vars:
        ami_prefix: prod-ami
```

## Migration Checklist

- \[ ] Audit all `terraform.workspace` references in code
- \[ ] Convert workspace conditionals to variables
- \[ ] Create Atmos `atmos.yaml` configuration
- \[ ] Create stack files for each workspace
- \[ ] Keep workspace-backed state, unless your team explicitly chooses separate backends
- \[ ] If moving state, test the migration in dev first
- \[ ] Verify `atmos terraform plan` shows no changes
- \[ ] Update CI/CD pipelines
- \[ ] Update team documentation
- \[ ] Update workspace commands to stack-based Atmos commands where appropriate

* **Explicit configuration** - Environment settings live in stack files
* **Workspace-compatible state** - Keep existing workspace-backed state
* **Environment-specific settings** - No complex conditionals
* **Better code review** - Stack changes visible in YAML diffs
* **Reusable components** - Same code, different configs
* **Easier testing** - Deploy different component versions per stack

## Common Questions

### Can I keep using workspaces with Atmos?

Yes. Atmos supports workspace-based backends via `workspace` settings. Migrating state to separate backends is optional, not required or recommended by default.

### Do I need to migrate everything at once?

No. Migrate incrementally, one component at a time. You can keep workspace-backed state throughout the migration.

### What about Terraform Cloud workspaces?

Terraform Cloud workspaces are different from OSS workspaces. They are more similar to Atmos stacks because each workspace carries its own state and configuration. Migration is similar but simpler.

## Get Help

Migrating from workspaces? We're here to help:

- **[Slack Community](/community/slack)** - Ask migration questions
- **[Office Hours](/community/office-hours)** - Live support for complex migrations
- **[GitHub Discussions](https://github.com/cloudposse/atmos/discussions)** - Share your migration story

## Next Steps

Ready to get started?

- **[Quick Start](/quick-start/simple)** - Build your first Atmos stack
- **[Core Concepts](/learn/why-atmos)** - Understand Atmos fundamentals
- **[Stack Configuration](/stacks/)** - Advanced YAML features
