Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Terraform By James Joyner IV · · 8 min read

Terraform Error: 'removed' block still declared in configuration

Quick answer

Fix Terraform's 'Removed resource still present' error: a removed block must target a resource whose resource/module declaration has actually been deleted from your config.

  • #terraform
  • #iac
  • #troubleshooting
  • #errors
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Exact Error Message


│ Error: Removed resource still present

│   on removed.tf line 1:
│    1: removed {

│ This statement declares a removal of the resource aws_s3_bucket.legacy,
│ but this resource is still declared in the configuration.

│ Remove the resource block for aws_s3_bucket.legacy from the configuration
│ before removing it from the state with a removed block.

You may also see the variant Cannot remove module.<name> because it is still declared when the removed block targets a whole module call that has not been deleted.

What It Means

The removed block (introduced in Terraform 1.7) tells Terraform to drop a resource or module from state without destroying the real infrastructure. It is the declarative replacement for terraform state rm.

For that to be valid, the corresponding resource or module block must no longer exist in your configuration. Terraform refuses to process a removed block while the same address is still declared, because that would be contradictory: you are simultaneously asking Terraform to manage the resource and to forget it. The fix is always to delete the actual declaration, leaving only the removed block behind.

Common Causes

  • You added a removed block but forgot to delete the matching resource "aws_s3_bucket" "legacy" { ... } declaration.
  • The declaration was commented out rather than deleted — Terraform still parses removed correctly but a stray duplicate resource block remains elsewhere.
  • A module still instantiates the resource internally, while you added the removed block at the root.
  • You targeted the wrong address (typo in resource type or name) so it collides with a live declaration.
  • A for_each/count instance was removed individually but the parent resource block is still present.

Diagnostic Commands

Confirm which addresses currently exist in configuration versus state:

terraform state list | grep legacy

Search the codebase for the still-present declaration:

grep -rn 'aws_s3_bucket" "legacy"' *.tf modules/

Run a validate to see the parser’s view of both blocks:

terraform validate

Run a plan to reproduce the exact error and confirm the address in the message:

terraform plan

Step-by-Step Resolution

  1. Read the error carefully and note the exact address it names (here, aws_s3_bucket.legacy).

  2. Find and delete the resource declaration for that address. Do not comment it out — remove the whole block:

# DELETE this entire block
resource "aws_s3_bucket" "legacy" {
  bucket = "my-legacy-bucket"
}
  1. Keep only the removed block, and make sure lifecycle.destroy = false so the real bucket is preserved:
removed {
  from = aws_s3_bucket.legacy

  lifecycle {
    destroy = false
  }
}
  1. If the resource lives inside a module, delete it from the module too, or move the removed block into that module and target the correct nested address:
removed {
  from = module.storage.aws_s3_bucket.legacy

  lifecycle {
    destroy = false
  }
}
  1. Re-run the plan. Terraform should now report that the resource will be removed from state only:
terraform plan
Plan: 0 to add, 0 to change, 0 to destroy, 1 to forget.
  1. Apply, then delete the now-unused removed block in a later commit once the state has been reconciled:
terraform apply

Prevention

  • Treat removed as a two-part change: delete the declaration and add the block in the same commit.
  • Always include lifecycle { destroy = false } when you want to keep the real infrastructure — omitting it destroys the resource on apply.
  • Use grep or your editor’s project search to confirm no other file re-declares the address before applying.
  • Remove removed blocks once they have served their purpose; leaving them indefinitely clutters the config and can mask future re-additions.
  • When refactoring modules, prefer moved blocks for renames and reserve removed for genuine ownership handoffs. If you are unsure which refactor block fits, the Terraform refactoring prompts can generate a checklist for your exact change.
  • Moved object still exists — the analogous problem for moved blocks when both endpoints are declared.
  • Resource already managed by Terraform — an import block colliding with an existing declaration.
  • Reference to undeclared resource — you deleted a declaration that other expressions still reference.
  • Cannot remove module ... still declared — the module-call variant of this same error.

Frequently Asked Questions

Does a removed block destroy my real infrastructure? Only if you omit lifecycle { destroy = false }. With destroy = false, Terraform forgets the resource but leaves the actual cloud object untouched.

Why not just run terraform state rm? removed blocks are declarative and reviewable in version control, so the intent is captured in your codebase and applied consistently across a team, unlike an imperative one-off command.

Can I keep the resource block commented out instead of deleting it? Yes — commented HCL is ignored by the parser, so a fully commented-out declaration satisfies the requirement. Deleting it is cleaner and avoids confusion.

Do I have to remove the removed block afterwards? Not immediately, but it is good hygiene to delete it in a follow-up commit once the state is reconciled, since it no longer does anything. For more refactoring walkthroughs, see the Terraform guides.

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.