The EntityAlreadyExists error occurs when Terraform attempts to create an IAM Role that already exists in AWS. This typically happens when the resource exists in AWS but is missing from your Terraform state file. Resolve it by importing the existing role, deleting it from AWS and recreating it, or checking for state file mismatches.
The EntityAlreadyExists error indicates that Terraform tried to create an AWS IAM Role with a name that already exists in your AWS account. This is AWS's way of preventing duplicate named resources. The root cause is usually a mismatch between your Terraform state file and actual AWS resources. The role exists in AWS but your .tfstate file doesn't have a record of it. When Terraform tries to create what it thinks is a new resource, AWS rejects the request because the name is already taken. This commonly happens when a previous Terraform apply partially failed—the role was created in AWS, but Terraform exited before saving it to the state file. Subsequent runs then see a non-existent resource in state and attempt to create it again, hitting this error.
First, verify the role actually exists in your AWS account:
aws iam get-role --role-name your-role-nameIf this returns role details, the role exists. If it returns "NoSuchEntity", the role doesn't exist but your state thinks it will be created.
Check your Terraform state to see if it has a record of the role:
terraform state list | grep aws_iam_role
terraform state show aws_iam_role.your_role_nameIf the role doesn't appear in state list, Terraform has no record of it.
If the role exists in AWS but not in your state, import it. This tells Terraform to track it:
terraform import aws_iam_role.your_role_name your-role-nameReplace your_role_name with the resource name in your .tf file and your-role-name with the actual role name in AWS. After importing, terraform plan should show no changes needed.
If you prefer Terraform to create the role fresh (not import it), delete it from AWS:
# Remove inline policies first (if any)
aws iam list-role-policies --role-name your-role-name
# Delete each inline policy
aws iam delete-role-policy --role-name your-role-name --policy-name policy-name
# Delete the role
aws iam delete-role --role-name your-role-nameThen terraform apply will create it fresh. Warning: this deletes the role entirely from AWS.
If the role keeps getting orphaned, the inline_policy might be invalid:
# Check what policies the role has
aws iam list-role-policies --role-name your-role-name
aws iam get-role-policy --role-name your-role-name --policy-name policy-nameAWS creates the role before validating policies. If the policy is malformed, the role exists but stays out of Terraform state. Fix your inline_policy JSON in Terraform:
resource "aws_iam_role" "example" {
name = "example-role"
inline_policy {
name = "example-policy"
# Ensure JSON is valid
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "s3:GetObject"
Resource = "arn:aws:s3:::bucket-name/*"
}]
})
}
}If multiple team members apply Terraform simultaneously without state locking:
1. Set up Remote State with locking (Terraform Cloud, S3 with DynamoDB):
terraform {
backend "s3" {
bucket = "terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}2. Ensure DynamoDB table exists for state locking
3. Team members must pull latest state: terraform refresh
4. Always run terraform plan before apply to catch conflicts
In CI/CD pipelines, make sure state files are saved between runs:
# GitHub Actions example
- name: Terraform Apply
run: terraform apply -auto-approve
# Save state file as artifact or to remote backend
- name: Upload State
uses: actions/upload-artifact@v2
with:
name: terraform-state
path: terraform.tfstateBetter: use a remote backend so state is always centralized and accessible.
State File Strategy: Always use a remote backend (Terraform Cloud, S3, or Postgres) in production. This prevents state loss, enables team collaboration, provides locking, and offers versioning.
Import vs Delete: Importing existing resources is safer for production environments where roles have been assigned to users/services. Deleting and recreating can briefly break those permissions. Choose import if the role is already in use.
Inline Policies: AWS validates inline policies only after the role is created. This can cause the role to exist in AWS but fail to be tracked by Terraform. Always validate your policy JSON with aws iam validate-custom-attribute-policy or use jsonencode() in Terraform to ensure valid JSON structure.
Module Reuse: If using modules that create shared roles, ensure each module's resource has a unique name or is created only once. Use dependency injection—have the root module create the role and pass it to modules as a data source rather than having modules create their own.
Name Prefixes: To avoid conflicts in shared AWS accounts, use name_prefix instead of name:
resource "aws_iam_role" "example" {
name_prefix = "my-app-" # AWS appends random suffix
}This prevents naming collisions when multiple teams deploy the same infrastructure.
Error: Error rendering template: template not found
How to fix "template not found" error in Terraform
Error: Error generating private key
How to fix 'Error generating private key' in Terraform
Error creating Kubernetes Service: field is immutable
How to fix "field is immutable" errors in Terraform
Error: Error creating local file: open: permission denied
How to fix "Error creating local file: permission denied" in Terraform
Error: line endings have changed from CRLF to LF
Line endings have changed from CRLF to LF in Terraform