AWS IAM Roles Explained: Trust Policy, Permissions & Examples

An IAM role is an AWS identity that grants temporary credentials instead of permanent access keys. You assign a role to a service, workload, or user, and AWS issues short-lived credentials automatically. Roles are the recommended way to grant access for EC2 instances, Lambda functions, ECS tasks, CI/CD pipelines, and cross-account workflows.

This guide explains what roles are, how their trust and permissions policies work together, and when to use a role instead of an IAM user or access key. By the end, you will understand the role assumption flow, know how to set up an EC2 instance profile, and have clear guidance on which identity type to choose for any workload.

Simple explanation

An IAM role is a set of permissions that is not tied to a specific person or permanent password. Instead of giving your application a username and password that never change, you give it a role. When the application needs access, AWS checks the role, issues a short-lived pass, and the pass expires on its own.

This matters because permanent credentials can leak. If an access key ends up in a Git repository, anyone who finds it has access until someone manually revokes it. With a role, the credentials expire in minutes to hours. Even if they leak, the window of exposure is small.

Why IAM roles matter

Permanent credentials are one of the most common sources of AWS security incidents. Long-lived access keys get committed to repositories, pasted into chat, or left in decommissioned servers. Roles eliminate this category of risk.

  • No long-lived credentials in code. Applications never store permanent keys. The AWS SDK retrieves temporary credentials from the instance metadata service or the Lambda runtime automatically.
  • Automatic rotation. AWS refreshes the temporary credentials before they expire. You do not need to build credential rotation logic.
  • Reduced blast radius. If temporary credentials leak, they expire within minutes to hours. An attacker cannot use them indefinitely.
  • Clean delegation. Roles let you grant access across AWS accounts or to external identity providers without sharing passwords or keys. Each side manages its own policies independently.
Tip

If you are building anything on AWS today, start with a role. The only time you should reach for access keys is when the workload runs outside AWS and cannot use OIDC federation. For broader context on how IAM fits into AWS, see the IAM overview.

How IAM roles work

Every IAM role has two policy documents that work together: a trust policy and one or more permissions policies. The trust policy controls who can use the role. The permissions policies control what the role can do.

Trust policy

The trust policy is a JSON document attached to the role that lists which principals are allowed to call sts:AssumeRole. A principal can be an AWS service, an IAM user, another AWS account, or an external identity provider.

Here is a trust policy that allows EC2 instances to assume the role:

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

Common principal values:

  • “Service”: “ec2.amazonaws.com” for EC2 instances
  • ”Service”: “lambda.amazonaws.com” for Lambda functions
  • ”AWS”: “arn:aws:iam::123456789012:root” for all principals in another AWS account
  • ”Federated”: “token.actions.githubusercontent.com” for GitHub Actions via OIDC

You can add conditions to a trust policy to restrict assumption further. For example, you can require that a GitHub Actions workflow runs on a specific repository branch before it can assume the role.

Permissions policy

The permissions policy works the same way as a policy on an IAM user. It lists the AWS actions and resources the role holder can access. Here is a policy granting read access to a specific S3 bucket:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-data-bucket",
        "arn:aws:s3:::my-app-data-bucket/*"
      ]
    }
  ]
}

You can attach AWS managed policies, customer managed policies, or write inline policies directly on the role. For guidance on choosing between these, see managed vs customer managed policies. For the JSON anatomy of a policy document, see IAM policy structure.

Temporary credentials and role assumption

When a principal assumes a role, AWS Security Token Service (STS) returns three values: a temporary access key ID, a secret access key, and a session token. These credentials expire after a configurable period (typically one hour, up to 12 hours via the role’s MaxSessionDuration setting).

The assumption flow works like this:

  1. A trusted principal (EC2, Lambda, a user, or an external identity) requests to assume the role.
  2. AWS checks the role’s trust policy to confirm the principal is allowed.
  3. STS issues temporary credentials scoped to the role’s permissions policies.
  4. The principal uses those credentials to make AWS API calls.
  5. The credentials expire automatically. No cleanup is needed.

For a deeper walkthrough of the AssumeRole API and cross-account patterns, see role assumption in AWS.

Instance profile vs role

EC2 instances cannot reference an IAM role directly. Instead, you create an instance profile, which is a container that holds exactly one role. You attach the instance profile to the EC2 instance at launch, and the instance metadata service (IMDS) vends temporary credentials for the role automatically.

When you create a role in the AWS Console and select “EC2” as the trusted entity, the console creates the instance profile for you. When using the CLI or Terraform, you must create the instance profile separately.

Note

Lambda, ECS, and other services do not use instance profiles. They reference the role ARN directly in their configuration. The instance profile concept is specific to EC2.

Common use cases

EC2 instance profile

An EC2 instance assumes a role through its instance profile. Any application running on the instance can call AWS APIs without storing credentials. The AWS SDK reads temporary credentials from IMDS automatically.

Lambda execution role

Every Lambda function requires an execution role. The role grants the function permission to write logs to CloudWatch, read from DynamoDB, publish to SNS, or whatever the function needs. Lambda assumes the role on every invocation.

Cross-account access

A role in Account B can trust principals in Account A. Users or services in Account A call sts:AssumeRole to get temporary credentials for Account B. This avoids sharing IAM users or access keys between accounts. It is the standard pattern in multi-account setups.

GitHub Actions and OIDC federation

GitHub Actions can assume a role using OpenID Connect (OIDC) federation instead of storing long-lived access keys as repository secrets. The trust policy validates the OIDC token from GitHub and grants temporary credentials for the deployment workflow.

EKS with IAM Roles for Service Accounts (IRSA)

Pods running in EKS can assume IAM roles through IRSA. Each Kubernetes service account maps to an IAM role, so individual pods get only the permissions they need instead of sharing a single node-level role.

Tip

The pattern is always the same: a trusted entity assumes a role, gets temporary credentials, and uses them until they expire. Whether it is EC2, Lambda, GitHub Actions, or a Kubernetes pod, the flow is identical. Learn it once and you understand all of these use cases.

IAM roles vs IAM users vs access keys

This table helps you decide which identity type fits your situation. The short version: use roles for workloads, users for humans, and avoid access keys unless you have no alternative.

IAM roleIAM userAccess keys
Intended forServices, workloads, cross-account access, federationIndividual humans needing console or CLI accessProgrammatic access (attached to an IAM user)
Credential typeTemporary (STS)Permanent password, optional access keysPermanent key pair (access key ID + secret)
Credential lifetimeMinutes to 12 hours, auto-expiresUntil manually rotated or deletedUntil manually rotated or deleted
Common use casesEC2, Lambda, ECS, CI/CD, cross-accountConsole login, local CLI usageLegacy scripts, external systems without federation
RecommendedYes, for all workloadsYes, for human access with MFAAvoid inside AWS; use only when roles are not possible
Key riskOver-permissioned trust or permissions policiesStale accounts, missing MFALeaked keys grant persistent access

For a side-by-side walkthrough, see IAM users vs roles. For details on how access keys work and why they carry risk, see access keys explained.

Real example: EC2 reading from S3

This walkthrough creates a role that allows an EC2 instance to read objects from S3, then launches an instance with that role attached via an instance profile. All commands use the AWS CLI.

Step 1: Create the role with a trust policy

# Save the trust policy to a file
cat > trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "ec2.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

# Create the role
aws iam create-role \
  --role-name MyEC2S3ReadRole \
  --assume-role-policy-document file://trust-policy.json

Step 2: Attach a permissions policy

This attaches the AWS managed AmazonS3ReadOnlyAccess policy. In production, create a least-privilege custom policy that scopes access to specific buckets.

aws iam attach-role-policy \
  --role-name MyEC2S3ReadRole \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

Step 3: Create an instance profile and add the role

# Create the instance profile (required for EC2)
aws iam create-instance-profile \
  --instance-profile-name MyEC2S3ReadProfile

# Add the role to the instance profile
aws iam add-role-to-instance-profile \
  --instance-profile-name MyEC2S3ReadProfile \
  --role-name MyEC2S3ReadRole

Step 4: Launch an EC2 instance with the profile

Replace ami-0abcdef1234567890 with a valid AMI ID for your region.

aws ec2 run-instances \
  --image-id ami-0abcdef1234567890 \
  --instance-type t3.micro \
  --iam-instance-profile Name=MyEC2S3ReadProfile \
  --count 1

Once the instance is running, any code on it can call S3 APIs without any access keys. The AWS SDK reads credentials from the instance metadata service automatically. The credentials refresh before they expire, so the application never needs to handle rotation.

Warning

The AmazonS3ReadOnlyAccess managed policy grants read access to all S3 buckets in the account. For production workloads, write a custom policy that restricts access to only the buckets and prefixes the application needs. See least privilege for guidance.

When to use a role

  • Workloads running in AWS. EC2, Lambda, ECS, EKS, CodeBuild, Step Functions. Always use a role. Never embed access keys.
  • Cross-account access. Create a role in the target account and let the source account assume it.
  • CI/CD pipelines. Use OIDC federation with GitHub Actions, GitLab, or other providers instead of storing access keys as secrets.
  • Third-party access. Grant vendors a role with scoped permissions and an external ID condition, rather than creating IAM users for them.
  • Temporary privilege escalation. A developer assumes a role with elevated permissions for a specific task, then the session expires.

Only fall back to access keys when no role-based option exists, such as on-premises scripts that cannot use OIDC federation.

Common mistakes

  1. Over-permissioning roles. Attaching AdministratorAccess to a role to “make things work” turns the role into a master key. Always scope permissions to exactly what the workload needs. Start with the principle of least privilege.
  2. Overly broad trust policies. A trust policy with "AWS": "*" lets any principal in any AWS account assume the role. Always restrict the principal to the specific service, account, or identity you intend to trust.
  3. Confusing role and instance profile. A role cannot be attached to an EC2 instance directly. You must create an instance profile, add the role to it, and reference the profile at launch. Missing this step causes authentication errors that are hard to diagnose. See fixing IAM AccessDenied errors for common symptoms.
  4. Using IAM users with keys for AWS workloads. Creating IAM users with access keys for applications running on EC2 or in containers is unnecessary and dangerous. Roles provide the same access with temporary credentials and no risk of key leakage.
  5. Reusing one role across environments. A role used in development should not have access to production resources. Create separate roles per environment with permissions scoped to that environment’s resources.
  6. Ignoring session duration defaults. Roles issue credentials that expire after one hour by default. If your workload needs longer sessions, set a higher MaxSessionDuration on the role (up to 12 hours) and request it at assumption time.

Frequently asked questions

What is the difference between an IAM role and an IAM user?

An IAM user has permanent credentials such as a password or access keys. An IAM role has no permanent credentials. Instead, a trusted entity assumes the role and receives temporary credentials that expire automatically. Roles are recommended for workloads; users are for humans who need console or CLI access.

Do IAM roles have access keys?

No. IAM roles do not have their own access keys. When something assumes a role, AWS STS issues a set of temporary credentials (access key ID, secret access key, and session token) that expire after a configurable period, typically one hour.

Can a human assume an IAM role?

Yes. A human user can assume a role if the trust policy allows their IAM user or federated identity as a principal. This is common for cross-account access, privilege escalation workflows, and SSO setups.

What is an instance profile?

An instance profile is a container that holds exactly one IAM role and associates it with an EC2 instance. EC2 cannot reference a role directly. You create an instance profile, add the role to it, and attach the profile when you launch the instance.

When should I use a role instead of access keys?

Use a role whenever the workload runs inside AWS, including EC2, Lambda, ECS, EKS, and CodeBuild. Use a role with OIDC federation for external CI/CD systems like GitHub Actions. Only use access keys when no role-based option exists, such as on-premises scripts that cannot federate.

Last verified: 30 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.