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

Terraform State Surgery & Import Prompt

Perform Terraform state operations — terraform state mv/rm/import, replace, large-scale imports via import block.

Target user
Senior Terraform engineers managing state drift
Difficulty
Advanced
Tools
Claude, ChatGPT

The prompt

You are a senior Terraform engineer who has manually surgically edited state — moving resources between modules, importing existing infrastructure, removing tracking without destroy.

I will provide:
- The surgery use case (refactor, import, drift)
- Current state of the resource(s)
- Target state

Your job:

1. **State commands**:
   - **`terraform state list`** — show all
   - **`terraform state show <addr>`** — detail
   - **`terraform state mv <src> <dst>`** — relocate
   - **`terraform state rm <addr>`** — stop tracking (no destroy)
   - **`terraform import <addr> <id>`** — start tracking existing
   - **`terraform state replace-provider`** — provider migration
2. **For `state mv`** (refactor):
   - Module rename: `terraform state mv module.old module.new`
   - Per-resource: `terraform state mv aws_instance.web aws_instance.app`
   - Preserves resource, updates address
   - **Backup state first**
3. **For `state rm`**:
   - Resource stays in cloud, leaves state
   - For "this resource is now managed elsewhere"
   - For drift you want to ignore
4. **For `import`**:
   - Existing resource → state
   - Doesn't modify cloud
   - Subsequent plan shows mismatch (config vs real)
   - Need matching config
5. **For `import` block (1.5+)**:
   - Declarative import in `.tf` file
   - Survives plan/apply cycle
   - Better for many imports
6. **For large-scale import**:
   - Generate config via `terraform plan -generate-config-out=`
   - Iterate to refine
7. **For backup**:
   - `terraform state pull > backup.tfstate`
   - Or use S3 versioning
8. **For `moved` block** (1.1+):
   - In code instead of CLI
   - `moved { from = ..., to = ... }`
   - Auto-applied; no state mv needed

Mark DESTRUCTIVE: state rm of resources that need destroying, import without config (next apply destroys), state mv without backup, replacing state with stale copy.

---

Use case: [refactor / import / drift]
Resource(s):
```hcl
[PASTE current config]
```
Target state: [DESCRIBE]

Why this prompt works

State surgery is high-stakes. This prompt walks safe patterns.

How to use it

  1. Backup first always.
  2. Use moved block when refactoring.
  3. Import + verify plan matches.
  4. Document changes.

Useful commands

# Backup
terraform state pull > backup-$(date +%F).tfstate

# List
terraform state list

# Detail
terraform state show aws_instance.web

# Move
terraform state mv aws_instance.web aws_instance.app
terraform state mv 'module.old' 'module.new'

# Remove (stop tracking; keep cloud resource)
terraform state rm aws_instance.legacy

# Import
terraform import aws_instance.imported i-0123456789abcdef0
terraform import 'module.network.aws_vpc.main' vpc-0abc123

# Replace provider (e.g., when forking)
terraform state replace-provider \
    registry.terraform.io/hashicorp/aws \
    registry.opentofu.org/opentofu/aws

Patterns

Refactoring with moved block (preferred)

# main.tf

# OLD code (delete):
# resource "aws_instance" "web" { ... }

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

# Migration directive (1.1+):
moved {
  from = aws_instance.web
  to   = aws_instance.app
}

Run terraform plan — shows “moved” rather than destroy/create.

Import block (1.5+)

# Declarative import
import {
  to = aws_vpc.main
  id = "vpc-0abc123def456"
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  # Config must match the real resource for clean plan
}
# Generate config from imported resource
terraform plan -generate-config-out=generated.tf
# Review generated.tf; refine

Bulk import (script)

#!/bin/bash
# Import all EC2 instances tagged Environment=production

aws ec2 describe-instances \
    --filters "Name=tag:Environment,Values=production" \
    --query "Reservations[*].Instances[*].[InstanceId,Tags[?Key=='Name'].Value | [0]]" \
    --output text | \
    while read -r INSTANCE_ID NAME; do
        ADDR="aws_instance.${NAME//-/_}"
        terraform import "$ADDR" "$INSTANCE_ID"
    done

State mv example (modularize)

# Before: flat resources
# resource "aws_vpc" "main" { ... }
# resource "aws_subnet" "public" { ... }

# After: under network module
# module "network" { source = "./modules/network" }

terraform state mv aws_vpc.main module.network.aws_vpc.main
terraform state mv aws_subnet.public module.network.aws_subnet.public

Provider replacement (OpenTofu migration)

# Backup
terraform state pull > pre-migration.tfstate

# Replace
terraform state replace-provider \
    registry.terraform.io/hashicorp/aws \
    registry.opentofu.org/opentofu/aws

# Verify
terraform plan         # should be no changes

Common findings this catches

  • State rm on resource you wanted destroyed — destroy it from cloud manually too.
  • Import config doesn’t match — plan shows changes; refine config.
  • Bulk import without naming strategy — addressing collisions.
  • moved block missing for production rename — state mv before apply.
  • Backup not made before surgery — risky.
  • State surgery during active apply → wait for lock.
  • Provider replacement leaves duplicate registries → cleanup.

When to escalate

  • Cross-state moves (state pull / push) — coordinated.
  • Major refactor across many modules — staged.
  • Production state corruption — 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.