TerraformPilot

Troubleshooting

Fix Terraform Error - AssumeRole AccessDenied

Fix the Terraform AssumeRole AccessDenied error for cross-account deployments. Covers trust policies, STS permissions, MFA, and external ID configuration.

LLuca Berton2 min read

Quick Answer

#

Your IAM identity can't assume the target role. Either the role's trust policy doesn't list your identity as a principal, your identity lacks sts:AssumeRole permission, or the role requires MFA/external ID conditions you haven't met.

The Error

#
Error: error configuring Terraform AWS Provider: IAM Role (arn:aws:iam::123456789012:role/terraform-deploy)
  cannot be assumed.
 
There are a number of possible causes of this - the most common are:
  * The credentials used in order to assume the role are invalid
  * The IAM Role (arn:aws:iam::123456789012:role/terraform-deploy) does not exist
  * The IAM Role (arn:aws:iam::123456789012:role/terraform-deploy) does not have a valid trust policy
Error: AccessDenied: User: arn:aws:iam::111111111111:user/ci-user
  is not authorized to perform: sts:AssumeRole on resource:
  arn:aws:iam::222222222222:role/terraform-deploy

What Causes This Error

#

1. Trust Policy Doesn't Allow Your Principal

#

The role in the target account must explicitly trust your IAM identity:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "AWS": "arn:aws:iam::111111111111:user/ci-user"
    },
    "Action": "sts:AssumeRole"
  }]
}

2. Missing sts:AssumeRole Permission

#

Your IAM identity needs permission to call sts:AssumeRole in its own account.

3. MFA Required

#

The trust policy has an MFA condition but you're not passing an MFA token.

4. External ID Required

#

The trust policy requires an ExternalId condition — common for third-party access.

5. Wrong Role ARN

#

Typo in the account ID, role name, or using the wrong region's role.

How to Fix It

#

Solution 1: Fix the Trust Policy (Target Account)

#

In the target account (222222222222), update the role's trust policy:

# Check the current trust policy
aws iam get-role --role-name terraform-deploy \
  --query 'Role.AssumeRolePolicyDocument' --profile target-account
 
# Update to allow the source account
cat > trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "AWS": [
        "arn:aws:iam::111111111111:root",
        "arn:aws:iam::111111111111:role/ci-pipeline"
      ]
    },
    "Action": "sts:AssumeRole"
  }]
}
EOF
 
aws iam update-assume-role-policy \
  --role-name terraform-deploy \
  --policy-document file://trust-policy.json \
  --profile target-account

Solution 2: Grant sts:AssumeRole (Source Account)

#

In the source account (111111111111), give your user/role permission to assume:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::222222222222:role/terraform-deploy"
  }]
}

Solution 3: Configure the Provider with assume_role

#
provider "aws" {
  region = "us-east-1"
 
  assume_role {
    role_arn     = "arn:aws:iam::222222222222:role/terraform-deploy"
    session_name = "terraform-ci"
    external_id  = var.external_id  # If required by trust policy
  }
}

Solution 4: Handle MFA Requirements

#

If the role requires MFA, you can't use it directly in CI. Options:

# Option A: Remove MFA condition for CI roles (recommended for automation)
# In the trust policy, don't require MFA for the CI role
 
# Option B: Use a session token with MFA (manual/local only)
# First, get temporary credentials with MFA:
# aws sts get-session-token --serial-number arn:aws:iam::111:mfa/user --token-code 123456
 
# Option C: Use separate roles — MFA for humans, no MFA for CI
provider "aws" {
  assume_role {
    role_arn = var.is_ci ? var.ci_role_arn : var.human_role_arn
  }
}

Solution 5: Test AssumeRole Manually

#
# Test assuming the role
aws sts assume-role \
  --role-arn arn:aws:iam::222222222222:role/terraform-deploy \
  --role-session-name test \
  --profile source-account
 
# Check your current identity
aws sts get-caller-identity
 
# Check what permissions you have
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::111111111111:user/ci-user \
  --action-names sts:AssumeRole \
  --resource-arns arn:aws:iam::222222222222:role/terraform-deploy

Solution 6: Debug with CloudTrail

#
# Check CloudTrail in the TARGET account for denied AssumeRole calls
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRole \
  --max-results 10 \
  --profile target-account

Cross-Account Setup Checklist

#

Both sides must be configured:

StepAccountAction
1. Create roleTarget (222)Create IAM role with trust policy
2. Trust policyTarget (222)Allow source account principal
3. Attach policiesTarget (222)Attach permissions the role needs
4. Grant AssumeRoleSource (111)Allow sts:AssumeRole on target role
5. Configure providerTerraformSet assume_role block

Troubleshooting Checklist

#
  1. ✅ Is the role ARN correct? (account ID, role name, no typos)
  2. ✅ Does the trust policy allow your IAM identity?
  3. ✅ Does your identity have sts:AssumeRole permission?
  4. ✅ Is MFA required? If so, are you providing it?
  5. ✅ Is an external ID required?
  6. ✅ Can you assume the role manually with aws sts assume-role?
  7. ✅ Check CloudTrail in the target account for the denial reason

Prevention Tips

#
  • Use IAM roles for CI/CD — not long-lived user credentials
  • Test assume-role with CLI before putting it in Terraform
  • Use OIDC federation for GitHub Actions / GitLab CI — no static credentials
  • Don't require MFA for automation roles — use separate human and CI roles
  • Document cross-account setup — both sides need changes, easy to miss one
#

Conclusion

#

AssumeRole failures need fixes on both sides: the trust policy in the target account must allow your identity, and your identity in the source account must have sts:AssumeRole permission. Test with the AWS CLI first, check CloudTrail for the exact denial reason, and use OIDC federation in CI/CD to avoid managing static credentials entirely.

#aws#iam#assume-role#cross-account

Share this article