Complete guide to AWS IAM — Understanding users, groups, policies, roles, and how they work together to secure your AWS account.
IAM Components Explained
IAM Core Concepts (Users, Groups, Policies, Roles)
flowchart LR Root["🔒 Root User<br/>(account owner)"] subgraph IAM["IAM Building Blocks"] U["👤 Users"] G["👥 Groups"] R["🎭 Roles"] P["📋 Policies"] end U -->|"member of"| G P -->|"attached to"| U P -->|"attached to"| G P -->|"attached to"| R U -->|"assume role (STS)"| R F["🌐 Federated Identities<br/>(Identity Center/SAML/OIDC)"] -->|"assume role"| R S["⚙️ AWS Services<br/>(EC2/Lambda/etc.)"] -->|"assume service role"| R Root -.->|"break-glass only"| IAM
In practice:
- Users represent individual IAM identities (long-term credentials if needed).
- Groups organize users so you assign permissions once to many users.
- Policies are permission documents attached to users, groups, or roles.
- Roles provide temporary credentials and are the preferred pattern for services, federation, and cross-account access.
1. Root User
What It Is
The root user is the account owner identity created when you first sign up for AWS.
Characteristics
| Attribute | Value |
|---|---|
| Login | Email address used during signup |
| Access | Complete, unrestricted access to ALL resources and settings |
| Permissions | Not governed by IAM identity policies (organization guardrails can still apply) |
| Best Practice | Lock away; use only for emergencies |
Root User Responsibilities
Only the root user can perform these actions:
- Change AWS account settings
- Close the AWS account
- Change the root password
- Retrieve root account security credentials
- Enable MFA Delete for S3
Critical Security Practice: Enable MFA on your root account immediately and never use it for daily operations.
2. IAM Users
What They Are
IAM users represent individuals or applications that need to interact with AWS resources.
User Credentials
| Credential Type | Purpose | Where It’s Used |
|---|---|---|
| Console Password | Sign in to AWS Management Console | Browser-based access |
| Access Key ID + Secret | Programmatic access to AWS APIs | CLI, SDKs, scripts |
| MFA Device | Additional security layer | Optional but recommended |
Creating a User
# AWS CLI example
aws iam create-user --user-name john-developer
aws iam create-login-profile --user-name john-developer --password TempPassword123!
aws iam create-access-key --user-name john-developerUser States
| State | Description |
|---|---|
| Active | User can sign in and make API calls |
| Inactive | User exists but cannot access (password/access keys disabled) |
Best Practices for Users
- One user per person — No shared accounts
- Force password reset on first login — For console access
- Enable MFA — Required for security best practices
- Set password policy — Enforce complexity and rotation
- Don’t embed access keys — Use IAM roles for applications
3. IAM Groups
What They Are
Groups are collections of IAM users that share the same permissions.
Key Characteristics
| Characteristic | Detail |
|---|---|
| Nesting | Groups cannot contain other groups — only users |
| Membership | A user can belong to multiple groups |
| Permissions | Attached as policies (managed or inline) |
| Simplification | Manage permissions at group level, not per user |
Why Use Groups?
Without Groups (Chaotic):
❌ Each user needs policies assigned individually
flowchart TB U1["User 1"] --> PA1["Policy A"] U1 --> PB1["Policy B"] U2["User 2"] --> PA2["Policy A"] U2 --> PB2["Policy B"] U3["User 3"] --> PA3["Policy A"] U3 --> PB3["Policy B"]
With Groups (Organized):
flowchart TB subgraph Organized["✅ Group-Based Management"] G["👥 Developers Group<br/>Policies: A + B"] G --> U1["User 1"] G --> U2["User 2"] G --> U3["User 3"] end
Common Group Patterns
| Group Name | Permissions | Typical Members |
|---|---|---|
| Administrators | AdministratorAccess | DevOps engineers, Sysadmins |
| Developers | PowerUserAccess + custom | Software developers |
| ReadOnly | ReadOnlyAccess | Business analysts, auditors |
| Support | Limited specific permissions | Support staff |
4. IAM Policies
What They Are
Policies are JSON documents that define permissions.
Policy Structure
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "OptionalIdentifier",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": ["192.0.2.0/24", "203.0.113.0/24"]
}
}
}
]
}Policy Elements Explained
| Element | Required | Description | Examples |
|---|---|---|---|
| Version | Yes | Policy syntax version | Always "2012-10-17" |
| Statement | Yes | Container for policy rules | Array of statement objects |
| Sid | No | Statement identifier (optional) | "AllowS3Read" |
| Effect | Yes | Allow or Deny | "Allow" or "Deny" |
| Action | Yes | Specific AWS API calls | "s3:GetObject" or "s3:*" |
| Resource | Yes* | Target AWS resources | "arn:aws:s3:::bucket/*" |
| Condition | No | Optional constraints | IP address, time, tags |
* Policy statements must include Resource or NotResource.
* For actions that do not support resource-level permissions (such as iam:ChangePassword), use Resource: "*" in the statement.
Action Syntax
| Pattern | Meaning | Example |
|---|---|---|
service:action | Specific action | s3:GetObject |
service:* | All actions in service | s3:* |
* | All AWS actions | * (admin access) |
Resource ARN Format
arn:partition:service:region:account-id:resource
| Part | Example | Description |
|---|---|---|
| partition | aws | Standard AWS partition |
| service | s3 | The AWS service |
| region | us-east-1 | Region (optional for some services) |
| account-id | 123456789012 | 12-digit AWS account ID |
| resource | bucket-name/object-key | Specific resource identifier |
Common Wildcards and Condition Operators
| Type | Description | Example |
|---|---|---|
* (wildcard) | Matches multiple characters in action/resource patterns | s3:* or arn:aws:s3:::* |
? (wildcard) | Matches a single character in patterns | user:? matches user:a |
StringEquals | String equality match in Condition | Restrict to a specific VPC endpoint ID |
ArnLike | Pattern match for ARNs in Condition | Restrict assumed roles by ARN pattern |
Bool / BoolIfExists | Boolean condition checks | Require MFA with aws:MultiFactorAuthPresent |
Managed vs Inline Policies
| Aspect | Managed Policies | Inline Policies |
|---|---|---|
| Location | Standalone entity | Embedded in user/group/role |
| Reusability | Can attach to multiple entities | Only for one entity |
| Versioning | Supported | Not supported |
| AWS-Managed | Available (pre-built by AWS) | Not available |
| Customer-Managed | Available | Available |
| Best For | Reusable permission sets | Entity-specific permissions |
AWS Managed Policies (Examples)
| Policy Name | Description | Use With |
|---|---|---|
AdministratorAccess | Full access to all AWS services | Trusted administrators |
PowerUserAccess | All permissions except IAM management | Developers who don’t manage users |
ReadOnlyAccess | Read-only access to all AWS services | Auditors, analysts |
SecurityAudit | Read access commonly used for security review | Security and audit teams |
5. IAM Roles
What They Are
Roles are identities with temporary credentials that you assume when needed.
Why Use Roles Instead of Users?
| Scenario | Use Role Because… |
|---|---|
| EC2 accessing S3 | No need to manage/rotate long-term access keys |
| Lambda accessing DynamoDB | Automatic credential rotation |
| Cross-account access | No need to create users in other accounts |
| Federated access | External users get temporary credentials |
| Service-to-service | AWS services need permissions to act on your behalf |
Trust Policy (Who Can Assume the Role)
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}Permission Policy (What the Role Can Do)
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-app-bucket/*"
}Common Role Use Cases
1. EC2 Instance Profile
# Create role with trust policy for EC2
aws iam create-role \
--role-name MyEC2Role \
--assume-role-policy-document file://trust-policy.json
# Attach permissions
aws iam attach-role-policy \
--role-name MyEC2Role \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
# Create instance profile
aws iam create-instance-profile --instance-profile-name MyEC2Profile
# Add role to instance profile
aws iam add-role-to-instance-profile \
--instance-profile-name MyEC2Profile \
--role-name MyEC2Role
# Attach to EC2 instance
aws ec2 associate-iam-instance-profile \
--instance-id i-1234567890abcdef0 \
--iam-instance-profile Name=MyEC2Profile2. Cross-Account Access
Account A (123456789012) → Account B (987654321098)
flowchart LR Bob["👤 User Bob"] -->|"Assume Role"| Role["🎭 Role"] Role --> Resources["📦 Resources"]
Account A (User’s Policy):
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::987654321098:role/CrossAccountRole"
}Account B (Role’s Trust Policy):
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "sts:AssumeRole"
}3. Service-Linked Roles
- Automatically created by AWS services
- Pre-defined permissions required by the service
- No need to manually manage
Examples:
AWSServiceRoleForOrganizations— AWS OrganizationsAWSServiceRoleForTrustedAdvisor— Trusted AdvisorAWSServiceRoleForConfig— AWS Config
IAM Policy Evaluation Logic
The Decision Flow
flowchart TD Request["📨 Request Arrives"] --> Default["1️⃣ Default DENY<br/>(implicit deny)"] Default --> CheckDeny["2️⃣ Check for explicit DENY<br/>in all applicable policies"] CheckDeny --> DenyFound{Deny found?} DenyFound -->|Yes| Deny1["❌ DENY"] DenyFound -->|No| CheckAllow["3️⃣ Check for explicit ALLOW<br/>in all applicable policies"] CheckAllow --> AllowFound{Allow found?} AllowFound -->|Yes| Allow["✅ ALLOW"] AllowFound -->|No| Deny2["❌ DENY<br/>(implicit deny)"]
Policy Sources Checked
| Source | Type | Scope |
|---|---|---|
| Identity-based policies | Attached to user/group/role | What this identity can do |
| Resource-based policies | Attached to AWS resource | Who can access this resource |
| IAM Permissions Boundaries | Maximum permissions for an entity | Cannot exceed this boundary |
| SCP | AWS Organizations | Organization-level restrictions |
| Session Policies | Passed when assuming role | Temporary restriction during session |
Explicit Deny Always Wins
Policy A: Allow S3 read
Policy B: Allow S3 write
Policy C: Deny S3 delete
Policy D: Allow S3 delete
Result: User can read and write S3, but CANNOT delete
(Explicit Deny in Policy C overrides Allow in D)
Common IAM Scenarios
Scenario 1: Developer Needs Full EC2, Read-Only RDS
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"rds:Describe*",
"rds:List*"
],
"Resource": "*"
}
]
}Scenario 2: Limit S3 Access to Specific Bucket
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::my-app-bucket"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::my-app-bucket/*"
}
]
}Scenario 3: Require MFA for Sensitive Operations
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAllUsers",
"Effect": "Allow",
"Action": ["iam:ChangePassword", "iam:GetAccountPasswordPolicy"],
"Resource": "*"
},
{
"Sid": "AllowViewAccountInfo",
"Effect": "Allow",
"Action": [
"iam:GetAccountSummary",
"iam:ListVirtualMFADevices"
],
"Resource": "*"
},
{
"Sid": "RequireMFAForSensitive",
"Effect": "Deny",
"NotAction": [
"iam:ChangePassword",
"iam:GetAccountPasswordPolicy"
],
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}IAM Security Best Practices
| Practice | Implementation |
|---|---|
| Enable MFA on root | Console → IAM → Security credentials → Activate MFA |
| Delete root access keys | Never use root credentials programmatically |
| Individual IAM users | Create user for each person; no shared accounts |
| Use groups for permissions | Attach policies to groups, not individual users |
| Least privilege | Grant only minimum required permissions |
| Rotate credentials | Set password policy; rotate access keys regularly |
| Use roles for apps | Never hardcode access keys; use IAM roles |
| Enable CloudTrail | Log all IAM API calls for auditing |
| Review access regularly | Use IAM Access Analyzer to find unintended access |
| Set password policy | Enforce complexity, expiration, history |
IAM Limits and Quotas
| Resource | Default Limit |
|---|---|
| IAM users per account | 5,000 |
| IAM groups per account | 500 |
| IAM roles per account | 1,000 |
| Managed policies per account | 1,500 |
| Inline policies per entity | Limited by size (2,048 characters for users, 10,240 for roles) |
| User groups per user | 10 |
| Policies per group | 10 |
| Access keys per user | 2 |
TL;DR
- Root User = Account owner, lock away with MFA, never use daily
- IAM Users = Individual identities for people/apps with credentials
- IAM Groups = Collections of users with shared permissions (simplifies management)
- Policies = JSON documents defining permissions (Effect, Action, Resource, Condition)
- Roles = Temporary credentials for services, cross-account, federation
- Managed Policies = Reusable, versioned; Inline Policies = Entity-specific
- Evaluation Logic: Default Deny → Check Allow → Check Deny (Deny always wins)
- Best Practices: MFA everywhere, least privilege, groups for permissions, roles for apps
Resources
IAM User Guide Comprehensive IAM documentation.
IAM Policy Elements Reference Detailed policy syntax and options.
IAM Policy Examples Common policy patterns by use case.
IAM Best Practices Official AWS security recommendations.
IAM Access Analyzer Tool to verify that your resources grant only intended access.