# Fix AWS IAM Role Trust Policy Error

You're trying to assume a role or configure cross-account access, but AWS keeps telling you the trust policy is invalid or you're not authorized. IAM trust policies are tricky because they work backwards from regular IAM policies—instead of saying "this identity can do X," they say "this identity is allowed to become this role."

The errors range from "Invalid principal in policy" to "User is not authorized to perform: sts:AssumeRole" and often leave you guessing where the actual problem lies.

Diagnosis Commands

First, understand which error you're getting. Try assuming the role:

bash
aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/MyRole \
  --role-session-name test-session

If this fails, the error message will hint at the issue. Let's examine the trust policy:

bash
aws iam get-role \
  --role-name MyRole \
  --query 'Role.AssumeRolePolicyDocument' \
  --output json | jq .

Check if the role exists and get its basic info:

bash
aws iam get-role \
  --role-name MyRole \
  --query 'Role.[RoleName,Arn,CreateDate,MaxSessionDuration]'

Verify who you're currently authenticated as:

bash
aws sts get-caller-identity

Check if your identity has permission to assume roles:

bash
aws iam list-attached-user-policies \
  --user-name $(aws sts get-caller-identity --query Arn --output text | cut -d'/' -f2)

If you're using an instance profile or Lambda execution role, check the attached role policies:

bash
aws iam list-role-policies --role-name MyRole
aws iam list-attached-role-policies --role-name MyRole

Use the policy simulator to test if you can assume the role:

bash
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:user/my-user \
  --action-names sts:AssumeRole \
  --resource-arns arn:aws:iam::123456789012:role/MyRole

Common Causes and Solutions

Invalid Principal in Trust Policy

Error: Invalid principal in policy

The trust policy references a principal that doesn't exist or is formatted incorrectly. A valid trust policy looks like:

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}

Common mistakes:

Wrong: Account ID without full ARN ``json "Principal": { "AWS": "123456789012" }

Correct: Use full ARN or root ``json "Principal": { "AWS": "arn:aws:iam::123456789012:root" }

Wrong: Missing account in service principal ``json "Principal": { "Service": "ec2" }

Correct: Include full service name ``json "Principal": { "Service": "ec2.amazonaws.com" }

Update the trust policy:

bash
aws iam update-assume-role-policy \
  --role-name MyRole \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::123456789012:root"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }'

Cross-Account Access Denied

Error: User: arn:aws:iam::ACCOUNT-A:user/my-user is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::ACCOUNT-B:role/CrossAccountRole

For cross-account access, both sides need configuration:

In Account B (the role's account): The trust policy must allow Account A:

bash
aws iam update-assume-role-policy \
  --role-name CrossAccountRole \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::ACCOUNT-A-ID:root"
        },
        "Action": "sts:AssumeRole",
        "Condition": {}
      }
    ]
  }'

In Account A (your account): Your identity needs permission to assume the role:

bash
aws iam put-user-policy \
  --user-name my-user \
  --policy-name AssumeCrossAccountRole \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": "arn:aws:iam::ACCOUNT-B-ID:role/CrossAccountRole"
      }
    ]
  }'

To restrict to specific users in Account A, update the trust policy in Account B:

bash
aws iam update-assume-role-policy \
  --role-name CrossAccountRole \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::ACCOUNT-A-ID:user/specific-user"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }'

Service Role Trust Policy Error

Error: Invalid principal in policy when creating a service role

Service roles need the correct service principal. Here are common ones:

```json // EC2 "Principal": { "Service": "ec2.amazonaws.com" }

// Lambda "Principal": { "Service": "lambda.amazonaws.com" }

// ECS Tasks "Principal": { "Service": "ecs-tasks.amazonaws.com" }

// CloudFormation "Principal": { "Service": "cloudformation.amazonaws.com" }

// CodePipeline "Principal": { "Service": "codepipeline.amazonaws.com" }

// For services in specific regions (e.g., China) "Principal": { "Service": "ec2.amazonaws.com.cn" } ```

Create a service role:

bash
aws iam create-role \
  --role-name MyServiceRole \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "lambda.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }'

Multiple Principals

When you need to allow multiple services or accounts:

bash
aws iam update-assume-role-policy \
  --role-name MultiPrincipalRole \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": [
            "arn:aws:iam::123456789012:root",
            "arn:aws:iam::210987654321:root"
          ]
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }'

Or combine service and account principals:

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:root"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

External ID Condition

For third-party access with External ID:

bash
aws iam update-assume-role-policy \
  --role-name ThirdPartyAccessRole \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::THIRD-PARTY-ACCOUNT:root"
        },
        "Action": "sts:AssumeRole",
        "Condition": {
          "StringEquals": {
            "sts:ExternalId": "my-unique-external-id-12345"
          }
        }
      }
    ]
  }'

When assuming the role, include the External ID:

bash
aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/ThirdPartyAccessRole \
  --role-session-name third-party-session \
  --external-id my-unique-external-id-12345

MFA Condition Not Met

If the trust policy requires MFA:

json
{
  "Condition": {
    "Bool": {
      "aws:MultiFactorAuthPresent": "true"
    }
  }
}

You must use MFA when assuming the role:

```bash # Get MFA device ARN aws iam list-mfa-devices --user-name my-user

# Get session token with MFA aws sts get-session-token \ --serial-number arn:aws:iam::123456789012:mfa/my-user \ --token-code 123456

# Then assume role with the temporary credentials export AWS_ACCESS_KEY_ID=<from-session-token> export AWS_SECRET_ACCESS_KEY=<from-session-token> export AWS_SESSION_TOKEN=<from-session-token>

aws sts assume-role \ --role-arn arn:aws:iam::123456789012:role/MFARequiredRole \ --role-session-name mfa-session ```

Session Duration Issues

Error: The requested DurationSeconds exceeds the MaxSessionDuration set for this role

Check the role's max session duration:

bash
aws iam get-role \
  --role-name MyRole \
  --query 'Role.MaxSessionDuration'

If you need longer sessions, update the role:

bash
aws iam update-role \
  --role-name MyRole \
  --max-session-duration 43200  # 12 hours in seconds

Then specify the duration when assuming:

bash
aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/MyRole \
  --role-session-name long-session \
  --duration-seconds 43200

Session Name Condition

If the trust policy requires specific session name patterns:

json
{
  "Condition": {
    "StringLike": {
      "sts:RoleSessionName": "admin-*"
    }
  }
}

Use a matching session name:

bash
aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/MyRole \
  --role-session-name admin-john-session

Verification Steps

After updating the trust policy, verify it works:

```bash # View the trust policy aws iam get-role \ --role-name MyRole \ --query 'Role.AssumeRolePolicyDocument' \ --output json | jq .

# Test assuming the role aws sts assume-role \ --role-arn arn:aws:iam::123456789012:role/MyRole \ --role-session-name test-session \ --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken,Expiration]'

# Use the assumed role aws sts get-caller-identity ```

For automated testing, create a script:

```bash #!/bin/bash ROLE_ARN="arn:aws:iam::123456789012:role/MyRole" SESSION_NAME="verify-session-$(date +%s)"

CREDENTIALS=$(aws sts assume-role \ --role-arn "$ROLE_ARN" \ --role-session-name "$SESSION_NAME" \ --output json)

if [ $? -eq 0 ]; then echo "Successfully assumed role: $ROLE_ARN" echo "Session expires: $(echo $CREDENTIALS | jq -r '.Credentials.Expiration')" else echo "Failed to assume role" exit 1 fi ```

Set up CloudTrail to monitor AssumeRole calls:

bash
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRole \
  --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
  --max-items 10 \
  --query 'Events[*].[EventTime,Username,Resources[0].ResourceName]'