What is AWS IAM? Roles, Policies, Users, and Groups Explained Simply
IAM is how AWS decides who can do what. Here's a plain-English explanation of users, groups, roles, and policies ā with real examples of how they're used together.
Every AWS action ā launching an EC2 instance, reading an S3 bucket, calling a Lambda ā goes through IAM first. IAM decides: "Is this entity allowed to do this thing?"
Here's how it works.
The Four Core Concepts
IAM User ā A person (or application) with long-term credentials (username/password, access keys).
IAM Group ā A collection of users. Assign policies to the group instead of to each user individually.
IAM Role ā An identity that can be assumed temporarily. No permanent credentials. Used by AWS services, EC2 instances, Lambda functions, and cross-account access.
IAM Policy ā A JSON document that defines what actions are allowed or denied, on which resources.
IAM Users
An IAM user gets credentials that don't expire:
- Console password ā for AWS Management Console
- Access key + secret key ā for CLI and API calls
# Create a user
aws iam create-user --user-name devops-engineer
# Create access keys for the user
aws iam create-access-key --user-name devops-engineer
# Returns: AccessKeyId + SecretAccessKey (save these ā shown only once)
# List users
aws iam list-usersWhen to use IAM Users:
- Human engineers who need AWS Console access
- Legacy applications that can't use roles (use sparingly)
When NOT to use IAM Users:
- EC2 instances (use IAM Roles instead)
- Lambda functions (use IAM Roles)
- EKS pods (use IRSA ā IAM Roles for Service Accounts)
- CI/CD pipelines (use OIDC + IAM Role where possible)
IAM Groups
Groups let you manage multiple users' permissions together.
# Create a group
aws iam create-group --group-name DevOpsEngineers
# Attach a policy to the group
aws iam attach-group-policy \
--group-name DevOpsEngineers \
--policy-arn arn:aws:iam::aws:policy/AmazonEKSClusterPolicy
# Add user to group
aws iam add-user-to-group \
--group-name DevOpsEngineers \
--user-name devops-engineerThe user inherits all permissions from their groups.
IAM Policies
A policy is a JSON document with permissions. It has:
- Effect:
AlloworDeny - Action: What API calls are allowed (e.g.,
s3:GetObject,ec2:*) - Resource: Which specific resources (e.g., a specific S3 bucket, or
*for all)
Example ā allow reading a specific S3 bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-company-logs",
"arn:aws:s3:::my-company-logs/*"
]
}
]
}Example ā deny deleting anything:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"ec2:TerminateInstances",
"rds:DeleteDBInstance",
"s3:DeleteBucket"
],
"Resource": "*"
}
]
}Key rule: Deny always wins over Allow. Even if another policy allows an action, an explicit Deny blocks it.
Policy types:
- AWS Managed Policies ā Pre-built by AWS (e.g.,
AmazonS3FullAccess,AdministratorAccess) - Customer Managed Policies ā You create and manage
- Inline Policies ā Attached directly to a user/group/role (avoid these ā hard to manage)
IAM Roles
A role is the most powerful and most-used IAM concept in modern AWS.
How a role works:
- You create a role with a trust policy (who can assume it) and permission policies (what they can do)
- An entity (EC2, Lambda, developer, other AWS account) "assumes" the role
- AWS returns temporary credentials (valid 1ā12 hours)
- The entity uses those credentials until they expire
EC2 Instance Role (most common):
# Create a role that EC2 can assume
aws iam create-role \
--role-name ec2-app-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "ec2.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
# Attach permissions to the role
aws iam attach-role-policy \
--role-name ec2-app-role \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
# Create instance profile and attach role
aws iam create-instance-profile --instance-profile-name ec2-app-profile
aws iam add-role-to-instance-profile \
--instance-profile-name ec2-app-profile \
--role-name ec2-app-role
# Attach to EC2 instance at launch or modify
aws ec2 associate-iam-instance-profile \
--instance-id i-1234567890abcdef0 \
--iam-instance-profile Name=ec2-app-profileNow your EC2 instance code can call AWS APIs without hardcoded credentials:
import boto3
# Credentials automatically from instance role ā no keys needed
s3 = boto3.client('s3')
s3.get_object(Bucket='my-bucket', Key='config.json')Trust Policies ā Who Can Assume the Role
The trust policy defines who can assume a role:
// Lambda can assume this role
{
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}// Another AWS account can assume this role (cross-account)
{
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::987654321:root"},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "unique-external-id-123"
}
}
}]
}// GitHub Actions CI/CD can assume this role (OIDC)
{
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/main"
}
}
}]
}Least Privilege ā The Golden Rule
Never give more permissions than needed.
ā Bad: Attach AdministratorAccess to EC2 instances
ā
Good: Create a role with only the specific S3 bucket and specific actions needed
ā Bad: Give developers AdministratorAccess
ā
Good: Give developers access to specific services, deny delete actions
ā Bad: Use long-term access keys in CI/CD
ā
Good: Use OIDC to assume a role with minimal CI/CD permissions
Quick Reference
| Concept | Has credentials | Used by | Duration |
|---|---|---|---|
| User | Yes (permanent) | Humans | Permanent |
| Group | No | Groups users | N/A |
| Role | No (temporary) | Services, apps | 1ā12 hours |
| Policy | No | Attached to users/roles | N/A |
For AWS IAM, security, and SAA-C03 exam prep, KodeKloud has hands-on AWS labs that cover IAM deep dives, cross-account access, and IRSA for Kubernetes.
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam ā just practical engineering content.
Related Articles
AWS IAM Permission Denied Errors ā How to Fix Every Variant (2026)
Getting 'Access Denied' or 'is not authorized to perform' errors in AWS? Here's how to diagnose and fix every IAM permission issue ā EC2, EKS, Lambda, S3, and CLI.
AWS VPC Networking: The Complete Guide for DevOps Engineers (2026)
Understand AWS VPC from the ground up ā subnets, route tables, security groups, NACLs, VPC peering, Transit Gateway, and real-world architectures for production workloads.
How to Set Up HashiCorp Vault for Secrets Management from Scratch (2026)
HashiCorp Vault is the industry standard for secrets management. This step-by-step guide shows you how to install Vault, configure it, and integrate it with Kubernetes.