# Terraform workflow: migrate state from local to a remote S3 backend

## When

The team outgrows local state: more than one operator, any CI, or any need for locking. Do this once per workspace, deliberately.

## Steps

1. Commit everything. `git status` clean. The config must be stable while you move its state.
2. Back up: `terraform state pull > state-backup-local-[date].json`. Verify the file is non-empty and valid JSON.
3. Prepare the remote side first: create the S3 bucket with versioning enabled and a DynamoDB lock table (Terraform path) or set `use_lockfile = true` (OpenTofu path). The backend cannot migrate into infrastructure that does not exist.
4. Add the backend block to config, pointing at the new bucket and a key for this workspace.
5. Run `terraform init -migrate-state`. Terraform detects state in both places and asks which to keep. Answer: the local one (it has the higher serial; confirm by comparing).
6. `terraform plan`. It must be empty or show only expected changes. A create-everything plan means the migration bound the wrong (empty) state: stop, restore from backup, investigate.
7. Only after a clean plan: delete the local `terraform.tfstate` and its backup from the working dir (keep the backup file somewhere safe, outside the repo). Add `*.tfstate*` to gitignore if not already there.

## Rules for agents

1. Never skip the backup. Migration prompts are easy to answer wrong under time pressure.
2. Migrate one workspace at a time. Migrating dev, staging, and prod in one session is how prod gets dev's state.
3. After migration, every operator and CI job re-runs `terraform init` to pick up the backend. Anyone still on local state will diverge silently.
4. Verify locking works: run two plans concurrently and confirm one waits. An unmigrated lock config is a corruption risk, not a cosmetic issue.
