Terraform Error Guide: 'A resource with the ID ... already exists' on apply
Fix Terraform's 'already exists' / 'already managed' errors: import existing cloud resources, remove duplicate state entries, and reconcile out-of-band creation.
- #terraform
- #troubleshooting
- #errors
- #import
Overview
This family of errors means Terraform tried to create a resource that already exists — either in the cloud provider or in Terraform’s own state. The provider rejects the create because the name/ID is taken, or Terraform itself refuses because the address is already managed. The object exists; what is missing is the link between the real resource and your state.
You will see one of these on terraform apply:
Error: creating S3 Bucket (my-app-logs): BucketAlreadyOwnedByYou: Your previous
request to create the named bucket succeeded and you already own it.
with aws_s3_bucket.logs,
on storage.tf line 3, in resource "aws_s3_bucket" "logs":
3: resource "aws_s3_bucket" "logs" {
Or, when the address is already in state:
Error: Resource already managed by Terraform
Terraform is already managing a remote object for aws_s3_bucket.logs. To import
to this address you must first remove the existing object from the state.
The fix is almost never to delete the real resource — it is to import the existing object into state, or to remove a stale/duplicate state entry so the addresses line up.
Symptoms
applyfails withalready exists,AlreadyOwnedByYou,EntityAlreadyExists, or409 Conflict.terraform importfails withResource already managed by Terraform.- A resource was created in the console (out of band) and now collides with config.
- State was lost/recreated, so Terraform tries to re-create things that already exist.
terraform apply
Error: creating IAM Role (app-task-role): EntityAlreadyExists: Role with name
app-task-role already exists.
Common Root Causes
1. The resource was created out of band
Someone made the bucket/role/record in the console or CLI, and now Terraform’s create collides with it.
aws iam get-role --role-name app-task-role --query 'Role.Arn' -f text 2>/dev/null
arn:aws:iam::123456789012:role/app-task-role
The role exists in AWS but not in Terraform state — import it instead of creating.
2. State was lost or rebuilt from scratch
The backend was reinitialized, a workspace was recreated, or local state was deleted, so Terraform thinks nothing exists.
terraform state list | grep aws_s3_bucket.logs
(no output)
Empty state plus existing cloud resources guarantees create-collisions on the next apply.
3. Duplicate address already managed (import target taken)
You ran terraform import against an address that already has a state entry.
terraform import aws_s3_bucket.logs my-app-logs
Error: Resource already managed by Terraform
Terraform is already managing a remote object for aws_s3_bucket.logs.
The address is occupied — remove the old entry first, or import to a different address.
4. A renamed resource left the old one in state
Renaming the resource label creates a new address; Terraform plans to create the new one and may collide with the live object still tied to the old address.
terraform state list | grep aws_iam_role
aws_iam_role.app
Config now says aws_iam_role.task — use terraform state mv instead of a destroy/create.
5. A globally-unique name clashes with someone else’s
S3 bucket names and some other resources are globally unique; the name may be owned by another account entirely.
aws s3api head-bucket --bucket my-app-logs 2>&1
An error occurred (403) when calling the HeadBucket operation: Forbidden
A 403 (not 404) means the name is taken by an account you do not control — you must rename.
6. Two configurations manage the same object
Two root modules/workspaces both declare the same resource, so whichever applies second collides.
grep -rn 'my-app-logs' ../*/*.tf
../shared/storage.tf:3: bucket = "my-app-logs"
Two configs own one bucket — consolidate ownership into one module.
Diagnostic Workflow
Step 1: Confirm the object actually exists in the cloud
# example for an IAM role
aws iam get-role --role-name <NAME> 2>&1
A successful lookup confirms this is an import/reconcile problem, not a true name clash.
Step 2: Check whether the address is in state
terraform state list | grep '<ADDRESS>'
terraform state show '<ADDRESS>' 2>/dev/null
Not in state -> import it. Already in state -> it is a duplicate/rename case.
Step 3: Find the import ID for the resource type
# the import ID format is documented per resource; e.g. S3 = bucket name
terraform plan -generate-config-out=generated.tf 2>/dev/null || true
Confirm the exact ID format (bucket name, role name, ARN, composite key) before importing.
Step 4: Import, rename, or remove as appropriate
# existing object, empty address -> import
terraform import aws_iam_role.app app-task-role
# renamed resource -> move within state (no destroy/create)
terraform state mv aws_iam_role.app aws_iam_role.task
# stale/duplicate state entry -> remove (does NOT delete the cloud object)
terraform state rm aws_s3_bucket.logs
Step 5: Plan to confirm zero creates for that resource
terraform plan -target=aws_iam_role.app
A clean plan (no + create) for the resource confirms state and reality are reconciled.
Example Root Cause Analysis
A new pipeline fails on the first apply of a fresh workspace:
Error: creating IAM Role (app-task-role): EntityAlreadyExists: Role with name
app-task-role already exists.
The role exists in AWS but not in this workspace’s state:
aws iam get-role --role-name app-task-role --query 'Role.Arn' -f text
terraform state list | grep app
arn:aws:iam::123456789012:role/app-task-role
(no output)
The role was originally created by an older state that was discarded when the team migrated backends. The object is real and correct — Terraform just has no record of it. The fix is to import, not recreate.
terraform import aws_iam_role.app app-task-role
aws_iam_role.app: Importing from ID "app-task-role"...
Import successful!
A follow-up plan shows the role as managed with no changes, and the apply proceeds.
terraform plan -target=aws_iam_role.app
No changes. Your infrastructure matches the configuration.
Prevention Best Practices
- Never delete the real cloud resource to make the error go away — import it so you keep the live object and its history.
- Use
terraform import(orimport {}blocks in newer Terraform) whenever a resource exists but is not in state; reservestate rm/state mvfor duplicates and renames. - Protect the state backend (versioning, no manual deletes) so state is never lost and forced into re-create collisions.
- Forbid out-of-band creation in managed accounts; if it happens, import promptly so config stays the source of truth.
- For globally-unique names, suffix with account/region/random so two teams never collide.
- For triage, the free incident assistant can turn an “already exists” error into the right import/move/remove command. More patterns in the Terraform guides.
Quick Command Reference
# Does the object exist in the cloud?
aws iam get-role --role-name <NAME> 2>&1 # (per-resource lookup)
# Is the address in state?
terraform state list | grep '<ADDRESS>'
# Import an existing object into an empty address
terraform import <ADDRESS> <IMPORT_ID>
# Rename within state (no destroy/create)
terraform state mv <OLD_ADDRESS> <NEW_ADDRESS>
# Remove a stale/duplicate state entry (does NOT delete the cloud object)
terraform state rm <ADDRESS>
# Confirm no creates remain
terraform plan -target=<ADDRESS>
Conclusion
An already exists / already managed error means Terraform tried to create something that is already there. The usual root causes:
- The resource was created out of band (console/CLI).
- State was lost or rebuilt, so Terraform re-creates existing objects.
- The import target address is already managed.
- A renamed resource left the old address in state.
- A globally-unique name is owned elsewhere (a true clash).
- Two configurations manage the same object.
Confirm the object exists, check whether the address is in state, then import, state mv, or state rm to line addresses up with reality — and almost never delete the live resource.
Download the Free 500-Prompt DevOps AI Toolkit
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.