Terraform Error: 'removed' block still declared in configuration
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
Stuck on this Terraform error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
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
removedblock but forgot to delete the matchingresource "aws_s3_bucket" "legacy" { ... }declaration. - The declaration was commented out rather than deleted — Terraform still parses
removedcorrectly but a stray duplicate resource block remains elsewhere. - A module still instantiates the resource internally, while you added the
removedblock at the root. - You targeted the wrong address (typo in resource type or name) so it collides with a live declaration.
- A
for_each/countinstance 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
-
Read the error carefully and note the exact address it names (here,
aws_s3_bucket.legacy). -
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"
}
- Keep only the
removedblock, and make surelifecycle.destroy = falseso the real bucket is preserved:
removed {
from = aws_s3_bucket.legacy
lifecycle {
destroy = false
}
}
- If the resource lives inside a module, delete it from the module too, or move the
removedblock into that module and target the correct nested address:
removed {
from = module.storage.aws_s3_bucket.legacy
lifecycle {
destroy = false
}
}
- 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.
- Apply, then delete the now-unused
removedblock in a later commit once the state has been reconciled:
terraform apply
Prevention
- Treat
removedas 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
grepor your editor’s project search to confirm no other file re-declares the address before applying. - Remove
removedblocks once they have served their purpose; leaving them indefinitely clutters the config and can mask future re-additions. - When refactoring modules, prefer
movedblocks for renames and reserveremovedfor genuine ownership handoffs. If you are unsure which refactor block fits, the Terraform refactoring prompts can generate a checklist for your exact change.
Related Errors
Moved object still exists— the analogous problem formovedblocks when both endpoints are declared.Resource already managed by Terraform— animportblock 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.
Fixed it? Get 500 Terraform & 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.
Stuck on this? Start guided troubleshooting
Open an interactive diagnostic session with this error already loaded. Work a step-by-step plan, record what each check returns, land on a root cause, and export a clean incident summary — no account needed to start.
Did this fix your issue?
Solved it a different way?
Share the fix that worked for you — reviewed, then published to help the next engineer.
That looks like it may contain a secret (key, token, password, or connection string). Please remove it — a note with a detected secret can’t be published.
Thanks — that helps. Published notes appear after a quick review.
Trending errors this week
The error guides other engineers are actually reading right now.
- 1mount: wrong fs type, bad option, bad superblock
- 2Docker 'failed to set up container networking': Fix the Bridge and IP Pool
- 3Docker 'failed to create shim task': How to Fix the containerd Runtime Error
- 4Transport endpoint is not connected
- 5modprobe: FATAL: Module not found
- 6mount: wrong fs type, bad option, bad superblock
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.