Ravindra BagaleCourses & study guides मराठी

Chapter 34: Terraform State Remote S3 Backend Locking and Workspaces

34.4 Move local state to S3 safely

Story. An Instagram-style feature team started a prototype with local state and then needed to share it. Copying the file into S3 by hand risks overwriting someone's newer copy. Terraform can migrate it for you: add the backend block and run init with -migrate-state. Terraform copies the state and checks that nothing is lost.

local tfstate add backend.tf init -migrate -state S3 object delete local copy
Add the backend block, run init -migrate-state, verify, then remove the local file.

Steps

  1. Save the current resource list.
  2. Add backend.tf.
  3. Run terraform init -migrate-state and answer yes.
  4. Compare the lists.
  5. Run terraform plan and expect No changes.
  6. Delete the local terraform.tfstate files.
terraform state list > before.txt
terraform init -migrate-state
terraform state list > after.txt
diff before.txt after.txt
terraform plan

What each line does

  • terraform state list > before.txt — Saves the list of tracked resources before the move.
  • terraform init -migrate-state — Reads the new backend block and copies the local state into S3.
  • terraform state list > after.txt — Lists resources again, now read from S3.
  • diff before.txt after.txt — Shows nothing if the move kept every resource.
  • terraform plan — Must show no changes, which proves state and AWS still agree.

When it is green.

Do you want to copy existing state to the new backend?
  Enter a value: yes
Successfully configured the backend "s3"!
No changes. Your infrastructure matches the configuration.

When it is red.

Error: Failed to get existing workspaces: S3 bucket "badge-tfstate-..." does not exist.

Fix. The bootstrap bucket was not created yet or is in another region. Apply the bootstrap project first and match the region in backend.tf.

What: every term in plain English

  • migrate-state — The init flag that copies existing state from the old backend to the new one.
  • state backup before migration — A dated copy of terraform.tfstate taken just before you change backends.
  • reconfigure — The init flag that ignores the old backend without copying state. Wrong choice for a live project.
  • backend change detection — Terraform notices backend.tf changed and refuses to plan until you run init.
  • state mv — Moves one address inside state or into another state file.
  • state push — Uploads a local file as the remote state. A last-resort tool.
  • freeze window — A short time when nobody runs apply, so the move happens on a quiet state.

Example 1: Move a local project to S3 in five commands

A Freshworks-style team had one engineer applying from his laptop for months. The day he went on leave, they moved state to S3. This is the exact order.

  1. Tell the team: no applies for 15 minutes.
  2. Plan and confirm zero changes.
  3. Copy the local state with the date.
  4. Add backend.tf.
  5. Run init -migrate-state and answer yes.
terraform plan | tail -1
cp terraform.tfstate state-backup-2026-10-25.tfstate
# add backend.tf now
terraform init -migrate-state

Output

No changes. Your infrastructure matches the configuration.
Initializing the backend...
Do you want to copy existing state to the new backend?
  Pre-existing state was found while migrating the previous "local" backend to the
  newly configured "s3" backend. No existing state was found in the newly
  configured "s3" backend. Do you want to copy this state to the new "s3"
  backend? Enter "yes" to copy and "no" to start with an empty state.

  Enter a value: yes

Successfully configured the backend "s3"!

What it shows: Starting from zero changes means any change you see later is caused by the move, not by old work.

Example 2: Prove the move worked

Never trust a migration without proof. Compare resource counts and plan again.

  1. Count addresses from the backup.
  2. Count addresses from remote state.
  3. Plan.
  4. Rename the local file so nobody uses it by mistake.
jq '.resources | length' state-backup-2026-10-25.tfstate
terraform state list | wc -l
terraform plan | tail -1
mv terraform.tfstate terraform.tfstate.migrated-do-not-use

Output

7
7
No changes. Your infrastructure matches the configuration.

What it shows: Same count, zero changes. Keep the backup in a private, encrypted place for a few weeks, then delete it.

Example 3: Move state between two keys in the same bucket

A Myntra project was renamed from shop-api to catalog-api. The key path had to change without touching AWS.

  1. Change the key in backend.tf.
  2. Run init -migrate-state.
  3. Check both keys in S3.
  4. Delete the old object after a week.
terraform {
  backend "s3" {
    bucket       = "badge-tfstate-123456789012"
    key          = "catalog-api/dev/terraform.tfstate"   # was shop-api/dev
    region       = "ap-south-1"
    encrypt      = true
    use_lockfile = true
  }
}
terraform init -migrate-state
aws s3 ls s3://badge-tfstate-123456789012/ --recursive | grep tfstate

Output

Successfully configured the backend "s3"!
2026-10-25 11:02:10       5120 catalog-api/dev/terraform.tfstate
2026-10-24 18:40:55       5120 shop-api/dev/terraform.tfstate

What it shows: The old object stays until you remove it. Leave it for a week as an extra safety copy.

Example 4: Move state back to local for a training lab

Sometimes you want to stop paying for a test project or hand it over. Removing the backend block and migrating brings state home.

  1. Delete backend.tf.
  2. Run init -migrate-state.
  3. Check that terraform.tfstate exists locally.
rm backend.tf
terraform init -migrate-state
ls -l terraform.tfstate

Output

Do you want to copy existing state to the new backend?
  Enter a value: yes
Successfully unset the backend "s3". Terraform will now operate locally.
-rw-r--r-- 1 ravi ravi 5120 Oct 25 11:10 terraform.tfstate

What it shows: The same flag works in both directions. Use local state only for labs.

Command cheat sheet

Command What it does When to use it
terraform init -​migrate-​state Copies state to the new backend Any backend change on a live project
terraform init -​reconfigure Ignores old backend, no copy Fresh start only
cp terraform.​tfstate state-​backup-​DATE.​tfstate Dated backup Before every migration
terraform state list | wc -​l Counts addresses Compare before and after
terraform state pull Reads current state Backup of remote state
terraform state push FILE Overwrites remote state Last resort recovery only

Verify: did it work?

  1. Counts match

    jq '.resources | length' state-backup-2026-10-25.tfstate; terraform state list | wc -l
    

    Output

    7
    7
    
  2. Plan is clean

    terraform plan -detailed-exitcode; echo $?
    

    Output

    No changes. Your infrastructure matches the configuration.
    0
    

When it is red 1

Error: Backend initialization required, please run "terraform init"

Cause. You changed backend.tf and ran plan before init.

Fix, step by step

  1. Run terraform init -migrate-state.
  2. Answer yes to copy state.
  3. Plan again.

When it is red 2

Plan: 7 to add, 0 to change, 0 to destroy.  (right after migration)

Cause. You ran init -reconfigure or answered no, so remote state is empty.

Fix, step by step

  1. Do not apply.
  2. Run terraform state push state-backup-2026-10-25.tfstate.
  3. Plan and confirm zero changes.

When it is red 3

Error: Error acquiring the state lock (during migrate)

Cause. Someone else ran plan or apply during your freeze window.

Fix, step by step

  1. Ask the team to stop.
  2. Wait for their run to finish.
  3. Run init -migrate-state again.

Cleanup

# after two to four weeks of clean plans
shred -u state-backup-2026-10-25.tfstate terraform.tfstate.migrated-do-not-use

Ravindra Bagale's Tip

Always migrate from a zero-change plan. If the plan is not clean before the move, finish or revert that work first.

Practice tasks

Easy Back up first

Why: Build the habit.

What to do: Copy local state with the date in the name.

How:
  1. Use cp.

Check: Backup file exists.

Easy Migrate

Why: Do the move.

What to do: Add backend.tf and run init -migrate-state.

How:
  1. Answer yes.

Check: Backend s3 configured.

Medium Prove it

Why: Trust only proof.

What to do: Compare counts and plan.

How:
  1. Use jq and wc -l.

Check: Same counts, no changes.

Medium Rename key

Why: Handle a project rename.

What to do: Change the key and migrate again.

How:
  1. List the bucket.

Check: Both keys listed.

Easy Say it in two lines

Why: If you can say it simply, you understand it.

What to do: Write two short lines about this lesson for a fresher.

How:
  1. Write the problem it solves.
  2. Write the one command or file you use.
  3. Read it aloud.

Check: A fresher understands it without a question.

Easy Start a safe lab folder

Why: Practice needs a throwaway place.

What to do: Make a new folder with versions.tf and a random_pet resource, so no AWS cost.

How:
  1. mkdir tf-lab and cd into it.
  2. Add the hashicorp/random provider and one random_pet.
  3. Run terraform init and terraform apply.

Check: Apply complete! with 1 added.

Medium Break it on purpose

Why: You must know the red output by face.

What to do: Cause one error from the red part of this lesson and fix it.

How:
  1. Follow one red section.
  2. Copy the exact message to your notes.
  3. Write the fix in one line.

Check: You fixed it without searching the web.

Medium Run it twice

Why: A second run must be quiet.

What to do: Run the same plan two times with no edits.

How:
  1. Run terraform plan.
  2. Run it again.
  3. Explain any change it still shows.

Check: The second plan says No changes, or you can explain each line.

Medium Write a runbook page

Why: At 2 a.m. nobody remembers flags.

What to do: Write a one-page runbook for this lesson.

How:
  1. Title, when to use, exact commands, how to check, how to undo.

Check: A teammate can follow it alone.

Medium Run it from a pipeline

Why: Real teams run Terraform from CI, not from laptops.

What to do: Run the main command of this lesson inside a GitHub Actions or Jenkins job.

How:
  1. Add a job with hashicorp/setup-terraform or a Terraform Docker image.
  2. Give it AWS access through an IAM role, never a key in the file.
  3. Run init, then the command from this lesson.

Check: The job log shows the same output you saw on your laptop.

Medium Check the cost

Why: Labs left running cost real money.

What to do: Find what this lesson would cost per day if you forgot to destroy it.

How:
  1. Open the AWS Pricing Calculator.
  2. Add each resource from your plan.
  3. Write the daily cost in your notes.

Check: You know the daily cost and you ran terraform destroy.

Hard Draw the flow top to bottom

Why: A drawing shows the order of work.

What to do: Draw this lesson as boxes from top to bottom.

How:
  1. Start at your code, go through plan and state, end at AWS.
  2. Mark where each command reads or writes.

Check: Each box matches a real step you ran.

Hard Explain it like an interview

Why: Interviewers want real stories.

What to do: Record a 60 second answer about this lesson.

How:
  1. Say the problem, the command, and one error you hit.

Check: Your answer has a real command and a real error message.

Homework: try at home

Try at home

Write a migration checklist with eight lines, from "announce freeze" to "delete local backup", and keep it in your notes.

Recap

  • Start from a zero-change plan.
  • Take a dated backup of local state.
  • Use init -migrate-state, never -reconfigure, on live projects.
  • Prove with counts and a clean plan.
  • Old copies stay for a while, then are deleted safely.

FAQ

What if I answered no by mistake?

Remote state is empty. Push your backup with terraform state push and plan again.

Does migration touch AWS resources?

No. Only the state file moves.

Can I migrate while Jenkins runs?

No. Pause the pipelines during the freeze window.

Can I move from one bucket to another?

Yes, change bucket in backend.tf and use -migrate-state.

Why rename the old local file?

So nobody runs Terraform against the outdated copy.

Interview questions

  1. How do you move local state to S3?

    Clean plan, backup, add backend, init -migrate-state, then verify.

  2. Difference between -migrate-state and -reconfigure?

    migrate-state copies state; reconfigure starts with whatever is in the new backend.

  3. How do you prove a migration?

    Same resource count and a zero-change plan.

  4. How do you recover if remote state is empty after a move?

    terraform state push with the backup file.

  5. Why a freeze window?

    To avoid a lock clash or a half-moved state.

Quick quiz

Q1. Which init flag copies state?

-migrate-state

Q2. First check before migration?

A zero-change plan

Q3. Does migration change AWS resources?

No

Q4. Command to upload a backup as remote state?

terraform state push

Q5. What should you do with the old local file?

Rename it, then delete it later.

Samajla ka?

Ravi Sir's Cards for this lesson

मराठी cards