Ravindra BagaleCourses & study guides मराठी

Chapter 37: Terraform Import Moved and Removed Blocks

37.3 removed block — stop managing without deleting

Story. An Instagram-style team handed a CloudWatch log group to another team's Terraform. Simply deleting the block would make the next apply delete the log group and its history. A removed block with destroy = false tells Terraform to forget the resource but leave it running in AWS. It replaces risky manual terraform state rm in reviewed code.

resource in state removed {} destroy = false state forgets it AWS keeps it
removed with destroy = false drops the resource from state and leaves it in AWS.
removed {
  from = aws_cloudwatch_log_group.app

  lifecycle {
    destroy = false
  }
}

What each line does

  • removed { — Declares that this address should no longer be managed here.
  • from = aws_cloudwatch_log_group.app — The address to forget; the resource block itself is deleted from code.
  • lifecycle { destroy = false } — Keep the real log group in AWS; only remove it from state.

When it is green.

  # aws_cloudwatch_log_group.app will no longer be managed by Terraform
Plan: 0 to add, 0 to change, 0 to destroy.

When it is red.

  # aws_cloudwatch_log_group.app will be destroyed
  # (because aws_cloudwatch_log_group.app is not in configuration)
Plan: 0 to add, 0 to change, 1 to destroy.

Fix. You deleted the resource block but forgot the removed block. Do not apply. Add the removed block with destroy = false and plan again.

What: every term in plain English

  • removed block — Tells Terraform to stop managing an address.
  • destroy = false — Keep the real object in AWS. Only forget it in state.
  • destroy = true — Delete the real object too. Same as deleting the block, but clearer.
  • hand-off — Giving a resource to another team or another state file.
  • state rm — The old CLI way to forget a resource. No review.
  • not in configuration — Plan text that means: the block is gone, so Terraform will destroy it.
  • prevent_destroy — A lifecycle setting that blocks any plan that would delete the resource.

Example 1: Hand a log group to another team

An Instagram-style platform team gave the app log group to the app team. The logs must stay.

  1. Delete the aws_cloudwatch_log_group.app block from main.tf.
  2. Add the removed block.
  3. Plan and apply.
  4. Check the log group still exists.
removed {
  from = aws_cloudwatch_log_group.app

  lifecycle {
    destroy = false
  }
}
terraform plan
terraform apply
terraform state list
aws logs describe-log-groups --log-group-name-prefix /app/web --query "logGroups[].logGroupName"

Output

  # aws_cloudwatch_log_group.app will no longer be managed by Terraform, but will not be destroyed

Plan: 0 to add, 0 to change, 0 to destroy.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
[
    "/app/web"
]

What it shows: state list no longer shows the log group, but AWS still has it.

Example 2: Move a bucket between two state files

The bucket must leave the platform state and join the data team state. removed in one, import in the other.

  1. In platform repo: delete the block, add removed with destroy = false, apply.
  2. In data repo: add an import block and the resource, apply.
# platform repo
removed {
  from = aws_s3_bucket.events
  lifecycle {
    destroy = false
  }
}
# data repo
import {
  to = aws_s3_bucket.events
  id = "demo-events-1234"
}

resource "aws_s3_bucket" "events" {
  bucket = "demo-events-1234"
}
cd platform && terraform apply
cd ../data && terraform apply

Output

platform: Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
data:     Apply complete! Resources: 1 imported, 0 added, 0 changed, 0 destroyed.

What it shows: Order matters: forget first, then import. Never let two states manage the same bucket.

Example 3: Forget a whole module

A legacy module created a VPC that the network team now owns.

  1. Delete the module call.
  2. Add removed for the module address.
  3. Plan.
removed {
  from = module.legacy_vpc
  lifecycle {
    destroy = false
  }
}
terraform plan

Output

  # module.legacy_vpc.aws_vpc.this will no longer be managed by Terraform, but will not be destroyed
  # module.legacy_vpc.aws_subnet.a will no longer be managed by Terraform, but will not be destroyed

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

What it shows: All resources inside the module are forgotten together.

Example 4: Guard with prevent_destroy

A Paytm-style team wanted a hard stop so nobody deletes the payments bucket by mistake.

  1. Add prevent_destroy to the bucket.
  2. Delete the block by mistake and plan.
  3. See the error.
resource "aws_s3_bucket" "payments" {
  bucket = "demo-payments-1234"
  lifecycle {
    prevent_destroy = true
  }
}
terraform plan -destroy

Output

Error: Instance cannot be destroyed
  on main.tf line 1:
Resource aws_s3_bucket.payments has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed.

What it shows: prevent_destroy stops deletion. To hand it off, use removed with destroy = false instead.

Command cheat sheet

Command What it does When to use it
removed { from = A lifecycle { destroy = false } } Forget, keep in AWS Hand-off
removed { from = A lifecycle { destroy = true } } Forget and delete Clear intent to delete
terraform state rm A Old CLI forget Emergency only, after state pull backup
terraform state pull Save a copy of state Before any state change
lifecycle { prevent_​destroy = true } Block deletes Critical data

Verify: did it work?

  1. Gone from state

    terraform state list
    

    Output

    (address not listed)
    
  2. Still in AWS

    aws s3api head-bucket --bucket demo-events-1234; echo $?
    

    Output

    0
    

When it is red 1

  # aws_cloudwatch_log_group.app will be destroyed
  # (because aws_cloudwatch_log_group.app is not in configuration)
Plan: 0 to add, 0 to change, 1 to destroy.

Cause. You deleted the block without a removed block.

Fix, step by step

  1. Stop. Do not apply.
  2. Add removed with destroy = false.
  3. Plan again.

When it is red 2

Error: Removed resource block still exists
This statement declares that aws_s3_bucket.events was removed, but it is still declared in a resource block.

Cause. The resource block is still in code.

Fix, step by step

  1. Delete the resource block.
  2. Keep only the removed block.

When it is red 3

Error: creating S3 Bucket (demo-events-1234): BucketAlreadyOwnedByYou

Cause. The new state tried to create instead of import.

Fix, step by step

  1. Add an import block for the bucket.
  2. Plan again and expect 1 to import.

Cleanup

aws logs delete-log-group --log-group-name /app/web
aws s3 rb s3://demo-events-1234 --force

Practice tasks

Easy Forget one bucket

Why: Basic hand-off.

What to do: Remove a bucket from state with destroy = false.

How:
  1. Delete block, add removed, apply.

Check: Bucket still listed in S3.

Medium Move between states

Why: Two teams.

What to do: Move a bucket from folder A state to folder B state.

How:
  1. removed in A.
  2. import in B.

Check: B plan shows no changes.

Medium See the danger

Why: Learn the red plan.

What to do: Delete a block without removed and read the plan.

How:
  1. Do not apply.

Check: You saw 1 to destroy.

Easy Add prevent_destroy

Why: Safety net.

What to do: Protect one bucket and run plan -destroy.

How:
  1. Read the error.

Check: Plan fails with the lifecycle error.

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

Pick a resource another team should own. Write the removed block in your repo and the import block in theirs, and test both with plan only.

Recap

  • Deleting a block means destroy.
  • removed with destroy = false forgets but keeps.
  • For hand-offs: removed first, then import in the new state.
  • removed replaces state rm in reviewed code.
  • prevent_destroy blocks deletes of key data.

FAQ

Which version do I need?

Terraform 1.7 or later for removed blocks.

Can I delete the removed block later?

Yes, after it has been applied in every environment.

Is state rm still useful?

Only in emergencies. Take terraform state pull first.

Does removed work on modules?

Yes, use the module address.

What if both states manage one bucket?

They will fight. Each apply may undo the other. Always remove first, then import.

Interview questions

  1. How do you stop managing a resource without deleting it?

    A removed block with lifecycle destroy = false, then apply.

  2. How do you move a resource between two state files?

    removed with destroy = false in the old state, then an import block in the new one.

  3. removed vs state rm?

    removed is code, reviewed and repeatable. state rm is a one-off command on one machine.

  4. What does not in configuration mean in a plan?

    The block was deleted, so Terraform will destroy the object.

  5. How do you protect a critical bucket?

    lifecycle prevent_destroy = true, plus versioning and backups.

Quick quiz

Q1. What keeps the object in AWS?

destroy = false.

Q2. Minimum version for removed?

Terraform 1.7.

Q3. Order for a hand-off?

removed first, then import.

Q4. What blocks deletes?

prevent_destroy.

Q5. Old CLI way to forget?

terraform state rm.

Lab

  1. Create one S3 bucket by hand in the console with a unique name.
  2. Write an import block and run terraform plan -generate-config-out=generated.tf.
  3. Clean up generated.tf and apply; confirm 1 imported.
  4. Rename the resource and add a moved block; confirm 0 to destroy.
  5. Add a removed block with destroy = false, apply, then delete the bucket by hand in the console to avoid cost.

Practice task

Write the moved blocks needed to change aws_instance.worker[0] and [1] into for_each keys blue and green.

Ravindra Bagale's Tip

If a plan shows a destroy you did not expect after a refactor, the answer is almost always a missing moved block.

In short

  • import blocks adopt existing resources through a reviewed plan.
  • -generate-config-out drafts config for imports.
  • moved renames and relocates without destroy.
  • removed with destroy = false forgets a resource but keeps it in AWS.

Samajla ka?

Ravi Sir's Cards for this lesson

मराठी cards