Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AWS with AI By James Joyner IV · · 8 min read Last reviewed Jul 2026

AWS Error Guide: 'EntityAlreadyExists' — Fix IAM Role, User, and Policy Name Collisions

Quick answer

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
Free toolkit

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, or CreateInstanceProfile fails with EntityAlreadyExists on the second run of an otherwise-working script.
  • CloudFormation stack fails with ... already exists in stack or a CREATE_FAILED on an IAM resource.
  • Terraform apply errors with EntityAlreadyExists because 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-policy first and branch to update vs create, or use create ... || 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:

  1. A non-idempotent create re-run collides with its own earlier output.
  2. CloudFormation/Terraform lost track of an entity it once created (drift).
  3. Two teams or environments chose the same name in one account.
  4. A leftover, half-built entity from a failed prior run.
  5. 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.

Free download · 368-page PDF

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?

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.