AWS Error Guide: 'EntityAlreadyExists' — Fix IAM Role, User, and Policy Name Collisions
Fix EntityAlreadyExists in AWS IAM: resolve role, user, and policy name collisions from re-runs, non-idempotent creates, and CloudFormation or Terraform drift.
- #aws
- #cloud
- #troubleshooting
- #errors
Stuck on this AWS with AI 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.
Overview
IAM entity names — roles, users, groups, instance profiles, and managed-policy names — must be unique per account (policies are unique per path + name). When you try to create one that already exists, IAM rejects the call with EntityAlreadyExists rather than overwriting the existing entity. Unlike a data-plane error, this is a safety feature: IAM will not silently replace an identity that other resources may already trust.
You will see it from the CLI or SDK:
An error occurred (EntityAlreadyExists) when calling the CreateRole operation: Role with name app-exec-role already exists.
The managed-policy variant looks like:
An error occurred (EntityAlreadyExists) when calling the CreatePolicy operation: A policy called app-boundary already exists. Duplicate names are not allowed.
It typically occurs on a re-run of a provisioning script that is not idempotent, a CloudFormation/Terraform stack that lost track of a resource it once created, or two teams choosing the same name in one account.
Symptoms
CreateRole,CreateUser,CreatePolicy, orCreateInstanceProfilefails withEntityAlreadyExistson the second run of an otherwise-working script.- CloudFormation stack fails with
... already exists in stackor aCREATE_FAILEDon an IAM resource. - Terraform
applyerrors withEntityAlreadyExistsbecause the entity exists in the account but not in state. - The name works in one account/Region setup but collides in a shared account.
aws iam create-role --role-name app-exec-role \
--assume-role-policy-document file://trust.json
An error occurred (EntityAlreadyExists) when calling the CreateRole operation: Role with name app-exec-role already exists.
Common Root Causes
1. A non-idempotent create re-run
A setup script calls create-role unconditionally, so the second invocation collides with the role the first one made.
grep -n "create-role" setup.sh
8:aws iam create-role --role-name app-exec-role --assume-role-policy-document file://trust.json
There is no “get-or-create” guard, so any re-run fails.
2. CloudFormation/Terraform state lost track of the entity
The IaC tool created the role earlier, but the state/stack was deleted or the resource was imported incorrectly, so the tool now tries to create a role that already lives in the account.
terraform state list | grep aws_iam_role.app || echo "not in state"
aws iam get-role --role-name app-exec-role --query 'Role.Arn' --output text
not in state
arn:aws:iam::REDACTED:role/app-exec-role
The role exists in AWS but not in Terraform state — a classic drift that produces EntityAlreadyExists on apply.
3. A name collision across teams or environments
Two stacks or two teams pick the same fixed name (ci-role, admin) in one shared account, so whichever runs second collides.
aws iam get-role --role-name ci-role --query 'Role.[Arn,CreateDate]' --output text
arn:aws:iam::REDACTED:role/ci-role 2026-05-02T12:00:00+00:00
The role predates your run — someone else owns that name.
4. A leftover entity from a failed previous run
A prior deployment created the role but failed before finishing, leaving a partial entity that the retry now collides with.
aws iam list-attached-role-policies --role-name app-exec-role \
--query 'AttachedPolicies' --output text
The role exists but has no policies attached — a half-built leftover from an aborted run.
5. Reusing a name still inside its path or with an instance profile
Instance-profile and role names collide independently; deleting a role but leaving its instance profile (or vice versa) blocks recreation.
aws iam list-instance-profiles-for-role --role-name app-exec-role 2>/dev/null \
|| aws iam get-instance-profile --instance-profile-name app-exec-role --query 'InstanceProfile.Arn'
arn:aws:iam::REDACTED:instance-profile/app-exec-role
The instance profile survived a partial cleanup and now owns the name.
Diagnostic Workflow
Step 1: Confirm the entity really exists and inspect it
aws iam get-role --role-name app-exec-role \
--query 'Role.[Arn,CreateDate]' --output text 2>&1
If this returns an ARN, the name is taken; the CreateDate tells you whether it is yours (recent) or pre-existing.
Step 2: Determine whether it is managed by IaC
terraform state list 2>/dev/null | grep app-exec-role
aws cloudformation describe-stack-resources \
--physical-resource-id app-exec-role --query 'StackResources[].StackName' --output text 2>/dev/null
If a stack or state owns it, fix the drift there — do not delete it out from under IaC.
Step 3: Check for an orphaned instance profile
aws iam get-instance-profile --instance-profile-name app-exec-role \
--query 'InstanceProfile.Roles[].RoleName' --output text 2>/dev/null
An instance profile with no roles is a leftover blocking the name.
Step 4: Decide idempotent-update vs recreate
aws iam list-attached-role-policies --role-name app-exec-role
aws iam get-role --role-name app-exec-role --query 'Role.AssumeRolePolicyDocument'
If the existing role is correct, switch your script to update it; only delete/recreate if it is a genuine leftover with nothing depending on it.
Example Root Cause Analysis
A CI pipeline that bootstrapped a per-service role started failing on every run with EntityAlreadyExists on CreateRole. It had worked the first time.
get-role confirmed the role existed and was recent — the pipeline’s own earlier run had made it:
aws iam get-role --role-name svc-payments-role --query 'Role.CreateDate' --output text
2026-07-08T09:14:00+00:00
The bootstrap script called create-role unconditionally with no guard, so the second pipeline run collided with the role from the first. The fix was to make the step idempotent — try to update, create only if absent:
if aws iam get-role --role-name svc-payments-role >/dev/null 2>&1; then
aws iam update-assume-role-policy --role-name svc-payments-role \
--policy-document file://trust.json
else
aws iam create-role --role-name svc-payments-role \
--assume-role-policy-document file://trust.json
fi
Reruns now converged instead of failing, and the trust policy stayed authoritative from the file.
Prevention Best Practices
- Make identity provisioning idempotent:
get-role/get-policyfirst and branch to update vs create, or usecreate ... || update ...guards. - Let CloudFormation or Terraform own IAM entities end-to-end, and
terraform import(or a CFN import) existing entities into state instead of recreating them. - Namespace names per environment/team (
prod-payments-exec,dev-payments-exec) to avoid collisions in shared accounts. - Clean up instance profiles and roles together; a partial delete leaves a name reserved and blocks recreation.
- On managed policies, prefer versioning (
create-policy-version+set-default-policy-version) over delete-and-recreate so the ARN and attachments survive.
Quick Command Reference
# Does the entity already exist?
aws iam get-role --role-name <name> --query 'Role.[Arn,CreateDate]' --output text
# Is it owned by Terraform / CloudFormation?
terraform state list | grep <name>
aws cloudformation describe-stack-resources --physical-resource-id <name>
# Idempotent create-or-update for a role trust policy
aws iam get-role --role-name <name> >/dev/null 2>&1 \
&& aws iam update-assume-role-policy --role-name <name> --policy-document file://trust.json \
|| aws iam create-role --role-name <name> --assume-role-policy-document file://trust.json
# Find and remove an orphaned instance profile blocking the name
aws iam get-instance-profile --instance-profile-name <name>
aws iam delete-instance-profile --instance-profile-name <name>
# Update a managed policy via a new version instead of recreating
aws iam create-policy-version --policy-arn <arn> --policy-document file://p.json --set-as-default
Conclusion
EntityAlreadyExists means the IAM name is already taken and IAM refuses to overwrite it. The usual root causes:
- A non-idempotent create re-run collides with its own earlier output.
- CloudFormation/Terraform lost track of an entity it once created (drift).
- Two teams or environments chose the same name in one account.
- A leftover, half-built entity from a failed prior run.
- An orphaned instance profile still reserving the role name.
Confirm the entity with get-role/get-policy, decide whether to update it or clean up a leftover, and make provisioning idempotent (or import it into IaC) so reruns converge instead of colliding.
Fixed it? Get 500 AWS with AI & 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.
Did this fix your issue?
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.