TerraformPilot

Troubleshooting

Fix Terraform Error - IAM EntityAlreadyExists

Fix the Terraform IAM EntityAlreadyExists error for roles, users, and policies. Covers import, unique naming, cross-workspace coordination, and cleanup.

LLuca Berton3 min read

Quick Answer

#

An IAM role, user, or policy with the same name already exists in your AWS account. Import it into Terraform state, use unique names per environment, or delete the duplicate if it's orphaned.

The Error

#

When running terraform apply, you encounter:

Error: creating IAM Role (my-lambda-role):
  EntityAlreadyExists: Role with name my-lambda-role already exists.
Error: creating IAM User (deploy-user):
  EntityAlreadyExists: User with name deploy-user already exists.
Error: creating IAM Policy (s3-read-policy):
  EntityAlreadyExists: A policy called s3-read-policy already exists.
  Duplicate names are not allowed.
Error: creating IAM Instance Profile (web-profile):
  EntityAlreadyExists: Instance Profile web-profile already exists.

What Causes This Error

#

1. Manually Created IAM Entity

#

Someone created the role/user/policy through the AWS Console or CLI, and now Terraform tries to create the same one.

2. Another Terraform Workspace

#

A different Terraform workspace or project already manages an IAM entity with the same name. IAM names are global within an AWS account.

3. Partial Apply Failure

#

A previous terraform apply created the IAM entity in AWS but crashed before recording it in state.

4. State Was Lost

#

The state file was deleted or corrupted, but the IAM entities still exist in AWS.

5. Hard-Coded Names Without Environment Prefix

#

Using the same name across dev/staging/production in the same account.

How to Fix It

#

Solution 1: Import the Existing IAM Entity

#
# Import IAM Role
terraform import aws_iam_role.lambda my-lambda-role
 
# Import IAM User
terraform import aws_iam_user.deploy deploy-user
 
# Import IAM Policy (needs ARN)
terraform import aws_iam_policy.s3_read arn:aws:iam::123456789012:policy/s3-read-policy
 
# Import IAM Instance Profile
terraform import aws_iam_instance_profile.web web-profile
 
# Import IAM Role Policy Attachment
terraform import aws_iam_role_policy_attachment.lambda_basic \
  my-lambda-role/arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

After importing, run plan to check for drift:

terraform plan
# Update your .tf files to match the actual IAM configuration

Solution 2: Use Import Blocks (Terraform 1.5+)

#
import {
  to = aws_iam_role.lambda
  id = "my-lambda-role"
}
 
resource "aws_iam_role" "lambda" {
  name = "my-lambda-role"
 
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = { Service = "lambda.amazonaws.com" }
      Action    = "sts:AssumeRole"
    }]
  })
}

Solution 3: Use Unique Names Per Environment

#
variable "environment" {
  type = string
}
 
variable "project" {
  type    = string
  default = "myapp"
}
 
resource "aws_iam_role" "lambda" {
  name = "${var.project}-${var.environment}-lambda-role"
 
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = { Service = "lambda.amazonaws.com" }
      Action    = "sts:AssumeRole"
    }]
  })
 
  tags = {
    Project     = var.project
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

Solution 4: Use name_prefix Instead of name

#

Let AWS generate a unique suffix:

resource "aws_iam_role" "lambda" {
  name_prefix = "lambda-role-"
 
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = { Service = "lambda.amazonaws.com" }
      Action    = "sts:AssumeRole"
    }]
  })
}
 
# Creates: lambda-role-20251212abc123

name_prefix works for: aws_iam_role, aws_iam_policy, aws_iam_instance_profile, aws_iam_group.

Solution 5: Check and Delete Orphaned Entities

#

If the IAM entity shouldn't exist:

# Check if the role exists and who created it
aws iam get-role --role-name my-lambda-role
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=ResourceName,AttributeValue=my-lambda-role \
  --max-results 5
 
# Delete if orphaned (detach policies first)
aws iam list-attached-role-policies --role-name my-lambda-role
aws iam detach-role-policy --role-name my-lambda-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name my-lambda-role
 
# Then apply normally
terraform apply

Solution 6: Find Who Manages the Entity

#
# Check if it's in another Terraform state
# Look for the "ManagedBy" tag
aws iam list-role-tags --role-name my-lambda-role
 
# Search your S3 state buckets
aws s3 ls s3://terraform-state/ --recursive | grep tfstate
# Download and search each state for the role name

IAM Name Scope Reference

#
IAM EntityName ScopeMax Length
IAM UserGlobal per account64 chars
IAM RoleGlobal per account64 chars
IAM PolicyGlobal per account128 chars
IAM GroupGlobal per account128 chars
Instance ProfileGlobal per account128 chars
OIDC ProviderGlobal per account2048 chars (URL)

All IAM names are global within an AWS account — you can't have two roles with the same name even in different regions.

Naming Convention Best Practice

#
locals {
  name_prefix = "${var.project}-${var.environment}"
}
 
resource "aws_iam_role" "lambda" {
  name = "${local.name_prefix}-lambda-execution"
  # e.g., "myapp-prod-lambda-execution"
}
 
resource "aws_iam_role" "ecs_task" {
  name = "${local.name_prefix}-ecs-task"
  # e.g., "myapp-prod-ecs-task"
}
 
resource "aws_iam_policy" "s3_access" {
  name = "${local.name_prefix}-s3-access"
  # e.g., "myapp-prod-s3-access"
}

Troubleshooting Checklist

#
  1. ✅ Does the IAM entity exist? (aws iam get-role --role-name NAME)
  2. ✅ Who created it? (Check CloudTrail or tags)
  3. ✅ Is it managed by another Terraform workspace?
  4. ✅ Should you import it or delete it?
  5. ✅ Are you using environment-specific names?
  6. ✅ Would name_prefix prevent future collisions?

Prevention Tips

#
  • Always include environment in IAM namesprod-lambda-role, not lambda-role
  • Use name_prefix when the exact name doesn't matter
  • Tag everything with ManagedBy = "terraform" — makes orphan detection easy
  • Use separate AWS accounts per environment — eliminates name collisions entirely
  • Coordinate naming across teams — document IAM naming conventions in a shared wiki
#

Conclusion

#

IAM EntityAlreadyExists means the name is taken within your AWS account. Import the entity if Terraform should manage it, delete it if it's orphaned, or adopt a naming convention that includes project and environment to prevent collisions. For maximum isolation, use separate AWS accounts per environment.

#aws#iam#naming

Share this article