Skip to content
CloudOps
All prompts
AI for Terraform Difficulty: Advanced ClaudeChatGPT

Terraform Large State Refactor Prompt

Refactor large Terraform state — moved/removed blocks, splitting state files, extracting modules without destroy.

Target user
Senior Terraform engineers managing large infrastructure
Difficulty
Advanced
Tools
Claude, ChatGPT

The prompt

You are a senior Terraform engineer who has refactored monolithic state files — splitting into smaller, extracting modules, renaming resources without destroy/recreate.

I will provide:
- The state size
- Refactor goal
- Symptom (slow plan, blast radius too wide)

Your job:

1. **For `moved` block** (1.1+):
   - Declarative refactor
   - In code, not CLI
   - `moved { from = ..., to = ... }`
   - Auto-applied; state automatically updated
2. **For `removed` block** (1.7+):
   - Stop managing without destroy
   - `removed { from = ..., lifecycle { destroy = false } }`
   - Replaces `state rm` in code
3. **For splitting state**:
   - `terraform state pull` → split JSON manually
   - Or: import into new state, remove from old
   - Or: use `terraform state mv` with `-state-out`
4. **For extracting module**:
   - Move resources into module dir
   - `moved` blocks pointing from old addresses to `module.x.<addr>`
   - Run plan; should show "moved"
5. **For consolidating**:
   - Rare; usually split, not merge
6. **For renaming**:
   - `moved { from = aws_instance.web, to = aws_instance.app }`
   - State updated; cloud unchanged
7. **For massive moves**:
   - Generate moved blocks programmatically
   - Test in non-prod
8. **For removing moved blocks** (after migration complete):
   - Wait one apply cycle
   - Then delete from code

Mark DESTRUCTIVE: refactor without backup, moved block to wrong address (orphan), removed block without verifying cloud resource preserved.

---

State size: [DESCRIBE]
Refactor goal: [DESCRIBE]
Symptom: [DESCRIBE]

Why this prompt works

Refactor without downtime is high-value. This prompt walks patterns.

How to use it

  1. Use moved block for refactor.
  2. Use removed for de-management.
  3. Backup state before.
  4. Plan + verify carefully.

Patterns

Rename via moved

# OLD: resource "aws_instance" "web" { ... }
# NEW:
resource "aws_instance" "app" {
  ami           = "ami-12345"
  instance_type = "t3.medium"
}

# Migration block
moved {
  from = aws_instance.web
  to   = aws_instance.app
}

Run terraform plan:

Terraform will perform the following actions:
  # aws_instance.web has moved to aws_instance.app
    resource "aws_instance" "app" {
        ...
    }

Plan: 0 to add, 0 to change, 0 to destroy.

After apply, the moved block can stay (idempotent) or be removed in a future cycle.

Module extraction

Before:

# main.tf (flat)
resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" }
resource "aws_subnet" "public" { vpc_id = aws_vpc.main.id ... }
resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id }

After:

# main.tf
module "network" {
  source     = "./modules/network"
  vpc_cidr   = "10.0.0.0/16"
}

# modules/network/main.tf (extracted)
resource "aws_vpc" "main" { cidr_block = var.vpc_cidr }
resource "aws_subnet" "public" { vpc_id = aws_vpc.main.id ... }
resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id }

Migration:

# In root main.tf, alongside module call
moved {
  from = aws_vpc.main
  to   = module.network.aws_vpc.main
}

moved {
  from = aws_subnet.public
  to   = module.network.aws_subnet.public
}

moved {
  from = aws_internet_gateway.main
  to   = module.network.aws_internet_gateway.main
}

Removing without destroy

# 1.7+: declarative remove
removed {
  from = aws_s3_bucket.legacy

  lifecycle {
    destroy = false           # keep cloud resource
  }
}

Resource removed from state; bucket stays alive in cloud. Useful when:

  • Resource migrated to another tool / state
  • Imported elsewhere
  • Manually managed going forward

State splitting (multi-state)

# Backup
terraform state pull > monolith.tfstate

# In OLD state: remove resources to move out
terraform state rm aws_vpc.main
terraform state rm 'module.network.*'

# Create NEW state dir + backend
cd ../new-state-dir/
terraform init                   # new backend

# Import resources individually
terraform import aws_vpc.main vpc-0abc123
# Or use import block + plan -generate-config-out

Generate moved blocks for many resources

# Old addresses → new addresses
cat <<EOF > moved.txt
aws_instance.web1 module.web.aws_instance.this[0]
aws_instance.web2 module.web.aws_instance.this[1]
aws_instance.web3 module.web.aws_instance.this[2]
EOF

# Generate moved blocks
while read OLD NEW; do
    cat <<HCL >> moved.tf
moved {
  from = $OLD
  to   = $NEW
}
HCL
done < moved.txt

Verification

# Plan should show "moved", not destroy/create
terraform plan | grep -i "moved\|destroy\|create"

# Diff before / after state
terraform state pull > after.tfstate
diff <(jq -S . monolith.tfstate) <(jq -S . after.tfstate) | head

Common findings this catches

  • Module extraction without moved block → destroy + create (data loss).
  • Wrong target address → state mismatch.
  • removed { destroy = true } → unintended destroy.
  • State pull during apply → inconsistent.
  • Refactor + functional change in same commit → noise; split.
  • Moved blocks left forever → cleanup after stable.
  • Cross-state moves require careful re-import.

When to escalate

  • Production state split — staged with comms.
  • Large refactor across many MRs — coordinate.
  • Recovery from bad refactor — restore from backup.

Related prompts

Newsletter

Get weekly AI workflows for DevOps engineers

Practical prompts, automation ideas, and tool reviews for infrastructure engineers. One email per week. No spam.