DevDocsDev Docs
IAM

IAM Roles

Delegate access with temporary security credentials

An IAM role is an identity with permission policies that can be assumed by trusted entities. Unlike users, roles don't have permanent credentials—they provide temporary security credentials.

Roles vs Users

Use roles when you need temporary credentials or when multiple entities need the same permissions. Roles are essential for AWS services, cross-account access, and federated users.

Role Components

Trust Policy

Defines who can assume the role:

trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Permission Policy

Defines what the role can do:

permission-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::my-bucket/*"
    }
  ]
}

Types of Roles

Allow AWS services to perform actions on your behalf.

Common Use Cases:

  • EC2 instances accessing S3
  • Lambda functions accessing DynamoDB
  • ECS tasks accessing Secrets Manager
EC2 Trust Policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Allow users or roles from other AWS accounts to access your resources.

Cross-Account Trust Policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::987654321098:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "unique-external-id"
        }
      }
    }
  ]
}

Always use an External ID for third-party cross-account access to prevent the "confused deputy" problem.

Allow users from external identity providers (SAML, OIDC) to assume roles.

OIDC Federation Trust Policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:*"
        }
      }
    }
  ]
}

Pre-defined roles created by AWS services with all necessary permissions.

List Service-Linked Roles
aws iam list-roles --query 'Roles[?starts_with(RoleName, `AWSServiceRoleFor`)].[RoleName]' --output table

Examples:

  • AWSServiceRoleForElasticLoadBalancing
  • AWSServiceRoleForAutoScaling
  • AWSServiceRoleForRDS

Creating Roles

Create Trust Policy File

trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Create the Role

Create Role
aws iam create-role \
  --role-name LambdaExecutionRole \
  --assume-role-policy-document file://trust-policy.json \
  --description "Execution role for Lambda functions"

Attach Permission Policies

Attach Policy
# Attach AWS managed policy
aws iam attach-role-policy \
  --role-name LambdaExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

# Or attach custom policy
aws iam put-role-policy \
  --role-name LambdaExecutionRole \
  --policy-name S3Access \
  --policy-document file://s3-policy.json

Assuming Roles

Using AWS CLI

Assume Role
aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/AdminRole \
  --role-session-name my-session \
  --duration-seconds 3600

Response:

Temporary Credentials
{
    "Credentials": {
        "AccessKeyId": "ASIAEXAMPLEID",
        "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY",
        "SessionToken": "FwoGZXIvYXdzEB...",
        "Expiration": "2024-01-15T12:30:00Z"
    },
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAEXAMPLEID:my-session",
        "Arn": "arn:aws:sts::123456789012:assumed-role/AdminRole/my-session"
    }
}

Using AWS SDK (Node.js)

assume-role.js
import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts";
import { S3Client, ListBucketsCommand } from "@aws-sdk/client-s3";

const stsClient = new STSClient({ region: "us-east-1" });

const assumeRole = async () => {
  const command = new AssumeRoleCommand({
    RoleArn: "arn:aws:iam::123456789012:role/AdminRole",
    RoleSessionName: "my-session",
    DurationSeconds: 3600,
  });

  const response = await stsClient.send(command);
  
  // Use temporary credentials
  const s3Client = new S3Client({
    region: "us-east-1",
    credentials: {
      accessKeyId: response.Credentials.AccessKeyId,
      secretAccessKey: response.Credentials.SecretAccessKey,
      sessionToken: response.Credentials.SessionToken,
    },
  });

  return s3Client;
};

Using AWS Profiles

Configure role assumption in ~/.aws/config:

~/.aws/config
[profile admin]
role_arn = arn:aws:iam::123456789012:role/AdminRole
source_profile = default
region = us-east-1

[profile admin-mfa]
role_arn = arn:aws:iam::123456789012:role/AdminRole
source_profile = default
mfa_serial = arn:aws:iam::123456789012:mfa/user
Use Profile
aws s3 ls --profile admin

Role Session Duration

Role TypeDefaultMaximum
IAM User assuming role1 hour12 hours
AWS Service1 hour12 hours
SAML Federation1 hour12 hours
OIDC Federation1 hour12 hours
Set Max Session Duration
aws iam update-role \
  --role-name AdminRole \
  --max-session-duration 43200  # 12 hours

Role Chaining

Assume one role, then assume another:

Role Chaining
# First, assume RoleA
aws sts assume-role \
  --role-arn arn:aws:iam::111111111111:role/RoleA \
  --role-session-name session1

# Then, using RoleA credentials, assume RoleB
aws sts assume-role \
  --role-arn arn:aws:iam::222222222222:role/RoleB \
  --role-session-name session2

Role chaining has a maximum session duration of 1 hour, regardless of the individual role settings.

Instance Profiles

For EC2 instances to use roles, you need an instance profile:

Create Instance Profile
# Create instance profile
aws iam create-instance-profile \
  --instance-profile-name EC2-S3-Access

# Add role to instance profile
aws iam add-role-to-instance-profile \
  --instance-profile-name EC2-S3-Access \
  --role-name EC2-S3-Role

# Attach to EC2 instance
aws ec2 associate-iam-instance-profile \
  --instance-id i-1234567890abcdef0 \
  --iam-instance-profile Name=EC2-S3-Access

Best Practices

Role Best Practices

  1. Use roles for AWS services - Never embed credentials in code
  2. Use external IDs - For third-party cross-account access
  3. Limit session duration - Use shortest practical duration
  4. Apply least privilege - Only grant necessary permissions
  5. Use conditions - Add constraints like source IP or MFA
  6. Enable CloudTrail - Log all AssumeRole calls

Troubleshooting

Common Errors

ErrorCauseSolution
AccessDeniedMissing trust policyAdd principal to trust policy
InvalidIdentityTokenToken expiredGet new federation token
MalformedPolicyDocumentInvalid JSONValidate policy syntax
PackedPolicyTooLargeSession policy too bigReduce policy size

Next Steps

On this page