AWS IAM Roles vs Policies vs Users – Complete Guide
AWS Identity and Access Management (IAM) is the foundation of security in AWS. Understanding the differences between IAM Users, IAM Roles, and IAM Policies is critical for both real-world AWS security and AWS certification exams. This guide provides a comprehensive comparison of these three core IAM components, explains how they work together, and covers best practices for 2025-2026.
Long-term credentials
(Access Key / Password)
Temporary credentials
(AssumeRole → STS)
Collection of Users
(attach policies to group)
Attached to User/Role/Group
Attached to S3/SQS/KMS
Max permissions cap
Organization guardrail
IAM Overview – The Big Picture
IAM controls who (authentication) can do what (authorization) on which resources in your AWS account. Every single AWS API call passes through IAM for evaluation before reaching the target service.
The three core building blocks are:
- IAM Users – Identities representing people or applications with long-term credentials
- IAM Roles – Identities with temporary credentials that can be assumed by trusted entities
- IAM Policies – JSON documents that define permissions (allow/deny actions on resources)
| Component | What It Is | Credentials | Primary Use Case |
|---|---|---|---|
| IAM User | An identity (person or app) | Long-term (password, access keys) | Legacy human access, service accounts |
| IAM Role | An assumable identity | Temporary (STS tokens, 1-12 hrs) | Services, cross-account, federation |
| IAM Policy | A permissions document | N/A (attached to identities/resources) | Define what actions are allowed/denied |
IAM Users
What Are IAM Users?
An IAM User is an identity within your AWS account that represents a person or application. Each IAM user has:
- A unique name within the AWS account
- Long-term credentials – either a password (for console access) or access keys (for programmatic access)
- A unique ARN – e.g.,
arn:aws:iam::123456789012:user/jayendra
IAM User Credentials
| Credential Type | Usage | Rotation |
|---|---|---|
| Password | AWS Console sign-in | Configurable via password policy |
| Access Key ID + Secret Access Key | CLI, SDK, API calls | Manual (max 2 active keys per user) |
| MFA Device | Additional authentication factor | Virtual (TOTP) or hardware |
When to Use IAM Users
- Third-party integrations that cannot use IAM roles or OIDC federation
- Break-glass emergency access when federation is unavailable
- Very small teams (1-2 people) without AWS Organizations
IAM Groups
IAM Groups are collections of IAM users that simplify permission management:
- Attach policies to a group; all users in the group inherit those permissions
- A user can belong to up to 10 groups
- Groups cannot be nested (no group within a group)
- Groups are not identities – they cannot be referenced in resource-based policies or assume roles
IAM Roles
What Are IAM Roles?
An IAM Role is an identity with specific permissions that can be assumed by trusted entities. Unlike users, roles do not have long-term credentials. When an entity assumes a role, AWS STS (Security Token Service) provides temporary security credentials consisting of:
- Access Key ID
- Secret Access Key
- Session Token
- Expiration timestamp (default 1 hour, configurable 15 min to 12 hours)
Role Components
| Component | Purpose | Example |
|---|---|---|
| Trust Policy | Defines WHO can assume the role | EC2 service, another account, OIDC provider |
| Permissions Policy | Defines WHAT the role can do | Read S3, write DynamoDB |
| Permission Boundary | Sets maximum permissions ceiling | Restrict to specific services only |
| Session Duration | How long credentials are valid | 1 hour (default), max 12 hours |
Trust Policy Example
|
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } |
This trust policy allows the EC2 service to assume this role, enabling EC2 instances to use the role’s permissions.
Types of IAM Roles
| Role Type | Trusted Entity | Use Case |
|---|---|---|
| Service Role | AWS Service (EC2, Lambda, ECS) | EC2 instance accessing S3, Lambda accessing DynamoDB |
| Cross-Account Role | Another AWS account | Account A accessing resources in Account B |
| Federation Role | Identity Provider (SAML/OIDC) | Corporate SSO users accessing AWS |
| Service-Linked Role | Specific AWS service | Pre-defined by AWS, cannot modify permissions |
Cross-Account Access with Roles
Cross-account roles enable secure access between AWS accounts without sharing long-term credentials:
- Account B (resource owner) creates a role with a trust policy allowing Account A
- Account A (requester) grants its users/roles permission to call
sts:AssumeRoleon Account B’s role - User in Account A calls
AssumeRoleand receives temporary credentials for Account B
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// Trust policy in Account B's role { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111111111111:root" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "unique-external-id" } } } ] } |
Role Chaining
Role chaining occurs when a role assumes another role. Key limitations:
- Maximum session duration is limited to 1 hour (regardless of role’s max session setting)
- CloudTrail logs each AssumeRole call in the chain
- The final role’s permissions apply (not a union of all roles in the chain)
IAM Roles Anywhere
Launched in 2022 and enhanced through 2025, IAM Roles Anywhere extends IAM roles to workloads outside AWS using X.509 certificates. On-premises servers, IoT devices, and other non-AWS workloads can obtain temporary AWS credentials without long-term access keys.
IAM Policies
What Are IAM Policies?
IAM Policies are JSON documents that define permissions. They specify which actions are allowed or denied on which resources under what conditions. Policies do not grant access on their own – they must be attached to an identity (user, group, role) or a resource.
Policy Structure
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowS3ReadAccess", "Effect": "Allow", "Action": [ "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*" ], "Condition": { "IpAddress": { "aws:SourceIp": "192.168.1.0/24" } } } ] } |
Policy Elements
| Element | Required | Description |
|---|---|---|
| Version | Yes | Always use “2012-10-17” (current version) |
| Statement | Yes | Array of permission statements |
| Sid | No | Statement identifier (optional description) |
| Effect | Yes | Allow or Deny |
| Principal | Resource policies only | Who the policy applies to |
| Action | Yes | API actions (e.g., s3:GetObject) |
| Resource | Yes | ARN of resources the policy applies to |
| Condition | No | When the policy is in effect |
Types of IAM Policies
Identity-Based Policies vs Resource-Based Policies
| Aspect | Identity-Based Policies | Resource-Based Policies |
|---|---|---|
| Attached to | Users, Groups, Roles | Resources (S3 bucket, SQS queue, KMS key) |
| Principal element | Not used (implied by attachment) | Required (specifies who gets access) |
| Cross-account | Requires role assumption | Can grant access directly to another account |
| Types | Managed and Inline | Inline only (embedded in resource) |
| Examples | AmazonS3ReadOnlyAccess | S3 Bucket Policy, SQS Queue Policy, KMS Key Policy |
AWS Managed vs Customer Managed vs Inline Policies
| Type | Created By | Reusable | Versioning | Use When |
|---|---|---|---|---|
| AWS Managed | AWS | Yes (across accounts) | Yes (AWS updates) | Standard permissions for common use cases |
| Customer Managed | You | Yes (within account) | Yes (up to 5 versions) | Custom, reusable permissions specific to your org |
| Inline | You | No (1:1 with identity) | No | Strict 1:1 relationship, deleted with identity |
Permission Boundaries
A permission boundary is an advanced feature that sets the maximum permissions an IAM entity (user or role) can have. It acts as a guardrail:
- The effective permissions = intersection of identity-based policies AND permission boundary
- Even if an identity-based policy grants
s3:*, if the permission boundary only allowss3:GetObject, the effective permission is onlys3:GetObject - Used for delegation – allow developers to create roles but only within defined boundaries
- Permission boundaries do NOT grant permissions on their own
Service Control Policies (SCPs)
SCPs are organization-level policies in AWS Organizations that set permission guardrails across all accounts:
- Apply to all IAM users and roles in member accounts (including root user for service actions)
- Do NOT grant permissions – they only restrict what’s allowed
- Effective permissions = SCP ∩ Identity-based policies ∩ Permission boundaries
- Can be attached at Organization root, OU, or account level
- Do NOT affect the management account
- As of 2025, SCPs support the full IAM policy language including conditions in Allow statements and NotAction
Resource Control Policies (RCPs)
Introduced in 2024, Resource Control Policies (RCPs) complement SCPs by controlling access to resources rather than identities:
- Restrict which principals (including external accounts) can access resources in your organization
- Applied at Organization root, OU, or account level
- Complement SCPs (identity guardrails) with resource-level guardrails
IAM Policy Evaluation Logic
When an AWS API call is made, IAM evaluates all applicable policies in a specific order:
- Explicit Deny – If ANY policy explicitly denies the action → DENIED (always wins)
- Resource Control Policies (RCPs) – Organization-level resource guardrails
- Service Control Policies (SCPs) – Organization-level identity guardrails
- Resource-Based Policies – If the resource policy allows the caller → may ALLOW (same account)
- Permission Boundaries – Must allow the action for it to proceed
- Session Policies – If using assumed role with session policy, must allow
- Identity-Based Policies – Must explicitly allow the action
Same-Account vs Cross-Account Evaluation
| Scenario | Requirements |
|---|---|
| Same account | Either identity-based OR resource-based policy can grant access |
| Cross-account | BOTH the identity-based policy (in caller’s account) AND resource-based policy (in resource’s account) must allow access |
How Users, Roles, and Policies Work Together
Visual Hierarchy
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
AWS Organization ├── Service Control Policies (SCPs) ──── Guardrails for all accounts │ ├── AWS Account │ ├── IAM Users ─────────────── Long-term identities │ │ ├── Identity-based Policies (attached) │ │ ├── Group Policies (inherited) │ │ └── Permission Boundary (ceiling) │ │ │ ├── IAM Roles ─────────────── Assumable identities │ │ ├── Trust Policy (who can assume) │ │ ├── Permissions Policies (what it can do) │ │ └── Permission Boundary (ceiling) │ │ │ └── Resources ─────────────── S3 buckets, KMS keys, etc. │ └── Resource-based Policies (who can access this resource) │ └── Resource Control Policies (RCPs) ── Guardrails for resources |
Common Patterns
Pattern 1: EC2 Instance Accessing S3
- Create an IAM Role with a trust policy for
ec2.amazonaws.com - Attach an identity-based policy granting S3 permissions to the role
- Attach the role to the EC2 instance (Instance Profile)
- Application on EC2 uses temporary credentials automatically via instance metadata
Pattern 2: Lambda Function Accessing DynamoDB
- Create an IAM Role with a trust policy for
lambda.amazonaws.com - Attach policies granting DynamoDB access
- Assign the role as the Lambda execution role
- Lambda automatically assumes the role on each invocation
Pattern 3: Cross-Account S3 Access
- Option A (Role-based): Create a role in Account B, trust Account A, grant S3 access
- Option B (Resource-based): Add a bucket policy in Account B allowing Account A’s principal
Pattern 4: Developer Permission Delegation
- Admin creates a permission boundary policy (e.g., only allow S3 and DynamoDB actions)
- Admin grants developer permission to create roles WITH the permission boundary
- Developer creates roles freely, but those roles can never exceed the boundary’s permissions
Best Practices (2025-2026)
1. Use IAM Identity Center for Human Access
- Replace IAM users with IAM Identity Center (formerly AWS SSO)
- Connect your existing IdP (Okta, Entra ID, Google Workspace)
- Provides temporary credentials with automatic token refresh
- Single sign-on to all AWS accounts and applications
- No additional AWS charge for IAM Identity Center
2. Enforce Least Privilege
- Start with minimal permissions and add as needed
- Use IAM Access Analyzer to generate policies based on actual usage (CloudTrail)
- Review and remove unused permissions regularly
- Use last accessed information to identify unused services
- Prefer specific actions over wildcards (
s3:GetObjectnots3:*)
3. Require MFA
- Enable MFA for all IAM users (especially those with console access)
- Use policy conditions to require MFA for sensitive operations:
"Condition": {"Bool": {"aws:MultiFactorAuthPresent": "true"}} - Prefer FIDO2/WebAuthn security keys over TOTP virtual MFA
4. Use Roles Over Users
- EC2 instances → use Instance Profiles (IAM Roles)
- Lambda functions → use Execution Roles
- Cross-account access → use IAM Roles with AssumeRole
- On-premises workloads → use IAM Roles Anywhere
- CI/CD pipelines → use OIDC federation (GitHub Actions, GitLab)
5. Implement Guardrails at Scale
- Use SCPs to prevent dangerous actions organization-wide (e.g., deny leaving organization, deny disabling CloudTrail)
- Use Permission Boundaries for delegation (developers creating their own roles)
- Use RCPs to restrict external access to your resources
- Tag-based access control (ABAC) for scalable permissions management
6. Monitor and Audit
- Enable CloudTrail in all accounts and regions
- Use IAM Access Analyzer to detect external and unused access
- Review IAM credential reports regularly
- Set up alerts for root account usage
When to Use Each – Decision Guide
| Scenario | Use | Why |
|---|---|---|
| Human accessing AWS Console | IAM Identity Center | Centralized, temporary credentials, SSO |
| EC2 instance needs AWS API access | IAM Role (Instance Profile) | Temporary credentials, auto-rotated |
| Lambda function accessing DynamoDB | IAM Role (Execution Role) | Service role, no static credentials |
| Account A accessing Account B resources | IAM Role (Cross-Account) | Secure delegation without sharing keys |
| Third-party SaaS needing AWS access | IAM Role (External ID) | Prevents confused deputy, no shared secrets |
| CI/CD pipeline (GitHub Actions) | IAM Role (OIDC Federation) | No stored AWS secrets in CI system |
| On-premises server accessing S3 | IAM Roles Anywhere | X.509 certs, temporary credentials |
| Restricting max permissions for developers | Permission Boundary | Delegation without privilege escalation |
| Organization-wide security guardrails | SCPs | Prevent dangerous actions across all accounts |
| Allow external account to access S3 bucket | Resource-Based Policy | Principal retains own permissions |
| Emergency break-glass access | IAM User (with MFA) | When federation/SSO is unavailable |
AWS Certification Exam Tips
- Explicit Deny always wins – No matter what allows exist, a single deny overrides everything
- Roles for services, users for legacy – If the question mentions EC2/Lambda needing access, the answer involves a role, not access keys
- Cross-account = role OR resource policy – Know when each is appropriate. Resource-based policies let the caller keep their original permissions
- Permission boundaries don’t grant permissions – They only restrict. Effective = identity policy ∩ boundary
- SCPs don’t affect management account – Common exam trick question
- External ID prevents confused deputy – Required for third-party cross-account access
- Role chaining limits session to 1 hour – Even if role’s max is 12 hours
- Groups are NOT identities – Cannot be principal in a resource policy, cannot assume roles
- IAM is global – Not region-specific (but STS endpoints can be regional)
- AssumeRoleWithWebIdentity vs AssumeRoleWithSAML – Web identity for OIDC (Cognito, GitHub), SAML for enterprise IdPs
IAM Roles vs Policies vs Users – Practice Questions
Question 1
A company wants its EC2 instances to securely access objects in an S3 bucket without storing credentials on the instances. What is the recommended approach?
- Create an IAM user, generate access keys, and store them in environment variables on the EC2 instance
- Create an IAM role with S3 permissions and associate it with the EC2 instance via an instance profile
- Add a bucket policy allowing all EC2 instances in the VPC to access the bucket
- Enable public access on the S3 bucket for the specific objects needed
Show Answer
Answer: B –
Explanation: IAM roles attached to EC2 instances (via instance profiles) provide temporary credentials that are automatically rotated. This eliminates the need to store long-term credentials. Option A uses long-term credentials which is a security risk. Option C is not how bucket policies work (VPC endpoint policies could restrict, but not via bucket policy to EC2). Option D removes security entirely.
Question 2
An organization uses AWS Organizations with multiple accounts. They want to ensure NO user in any member account can disable CloudTrail, regardless of their IAM permissions. What should they implement?
- An IAM policy attached to all users denying cloudtrail:StopLogging
- A permission boundary on all roles denying cloudtrail:StopLogging
- A Service Control Policy (SCP) denying cloudtrail:StopLogging on member accounts
- A resource-based policy on the CloudTrail trail denying StopLogging
Show Answer
Answer: C –
Explanation: SCPs provide organization-wide guardrails that cannot be overridden by any IAM policy within member accounts. Option A requires attaching to every user and can be removed by admins. Option B requires applying to every role and doesn’t cover users. Option D is not supported by CloudTrail. Only SCPs provide mandatory, centralized enforcement across accounts.
Question 3
A developer in Account A (111111111111) needs to access a DynamoDB table in Account B (222222222222). The solution must NOT require the developer to give up their Account A permissions while accessing Account B’s table. What approach should be used?
- Create a cross-account IAM role in Account B and have the developer assume it
- Create an IAM user in Account B for the developer with DynamoDB permissions
- Add a resource-based policy on the DynamoDB table granting access to Account A’s developer
- Use VPC peering between the accounts to access DynamoDB
Show Answer
Answer: C –
Explanation: Resource-based policies allow cross-account access while the principal retains their original account permissions. When you assume a role (Option A), you temporarily give up your original permissions and can only use the assumed role’s permissions. DynamoDB does not support resource-based policies directly, but this question tests the concept. In practice, for DynamoDB cross-account, you would use a role. However, for the exam concept being tested: resource-based policies = keep original permissions; role assumption = adopt role’s permissions only.
Note: In real-world scenarios, DynamoDB does NOT support resource-based policies, so cross-account IAM roles would be the correct approach for DynamoDB specifically. This question tests conceptual understanding of the difference between roles and resource-based policies.
Question 4
A company wants to allow its development team to create IAM roles for their Lambda functions, but ensure those roles can never have more permissions than a predefined set (S3 and DynamoDB access only). What should the security team implement?
- An SCP restricting the development account to S3 and DynamoDB only
- A permission boundary policy that allows only S3 and DynamoDB actions, required when developers create roles
- AWS managed policies that only include S3 and DynamoDB permissions
- Inline policies on each developer’s IAM user restricting role creation
Show Answer
Answer: B –
Explanation: Permission boundaries are designed for this exact delegation pattern. The security team creates a permission boundary policy allowing only S3 and DynamoDB. They then grant developers permission to create roles only if those roles have this permission boundary attached (using iam:PermissionsBoundary condition key). This ensures developers can self-service while preventing privilege escalation. Option A would restrict the entire account, not just developer-created roles. Options C and D don’t prevent developers from attaching broader policies.
Question 5
A company’s security policy requires that all API calls from IAM users must use Multi-Factor Authentication (MFA). They want users who haven’t authenticated with MFA to only be able to manage their own MFA device. Which policy approach achieves this?
- Attach a deny policy that denies all actions except MFA management unless
aws:MultiFactorAuthPresentis true - Remove all permissions from users and only grant them through roles that require MFA to assume
- Use an SCP to deny all actions unless MFA is present
- Configure the AWS account password policy to require MFA
Show Answer
Answer: A –
Explanation: The standard pattern uses a policy with two statements: (1) Allow MFA self-management actions always, and (2) Deny all other actions with a condition "BoolIfExists": {"aws:MultiFactorAuthPresent": "false"}. This forces users to set up and authenticate with MFA before they can perform any other actions. Option B works but is overly complex. Option C would affect all principals including service roles which don’t use MFA. Option D only enforces MFA at console sign-in, not for API/CLI calls.
Related Posts
- AWS IAM – Identity Access Management
- AWS IAM Role
- AWS IAM Roles vs Resource Based Policies
- AWS IAM Access Management
- AWS IAM Best Practices
- IAM Role – Identity Providers and Federation






