IAM is the gatekeeper of your AWS account. Every API call made to AWS goes through IAM authorization. Think of it as a bouncer at a club — it checks your ID (authentication) and your guest list (authorization) before letting you do anything.
| Entity | What it is | Real-World Analogy |
|---|---|---|
| User | A person or service with long-term credentials | An employee badge |
| Group | Collection of users sharing the same permissions | A department (e.g., "Developers") |
| Role | Temporary identity assumed by AWS services or users | A contractor's day pass |
| Policy | JSON document defining permissions | A rulebook |
AWS Account
├── Identity-based policies → attached to User/Group/Role
├── Resource-based policies → attached to S3 bucket, SQS queue, etc.
├── Permission boundaries → max permissions a role/user can have
├── SCP (Service Control Policies) → org-level guardrails
└── Session policies → passed at assume-role time
Explicit DENY → wins always
↓
SCP allows? → No = DENY
↓
Resource policy allows? → Yes = ALLOW (cross-account)
↓
Identity policy allows? → Yes = ALLOW
↓
Default = DENY
Scenario: A Lambda function needs to read from DynamoDB table orders and write to S3 bucket order-backups.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadOrdersTable",
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": "arn:aws:dynamodb:us-east-1:123456789:table/orders"
},
{
"Sid": "WriteToBackupBucket",
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": "arn:aws:s3:::order-backups/*"
}
]
}Key: The Lambda execution role has this policy attached — Lambda assumes the role, gets temporary credentials, makes API calls. No hardcoded secrets.
Every role has TWO policies:
Trust Policy (who can assume this role):
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}]
}Permission Policy (what the role can do): the JSON above.
Scenario: Account A's Lambda needs to read Account B's DynamoDB.
Account A: Lambda → assumes RoleA → calls sts:AssumeRole on RoleB (Account B)
Account B: RoleB trust policy allows Account A, permission policy allows DynamoDB reads
STS issues temporary credentials (AccessKeyId + SecretAccessKey + SessionToken).
| API | Use Case |
|---|---|
AssumeRole |
Switch to another role (same or cross-account) |
AssumeRoleWithWebIdentity |
Federation with Google/Facebook/Cognito |
AssumeRoleWithSAML |
Federation with corporate SSO (ADFS, Okta) |
GetSessionToken |
MFA enforcement, temporary escalation |
GetFederationToken |
Create temp creds for broker-based federation |
Credential duration: 15 min – 12 hours for AssumeRole (36 hours max with DurationSeconds override for some scenarios).
{
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "us-east-1"
},
"Bool": {
"aws:MultiFactorAuthPresent": "true"
},
"IpAddress": {
"aws:SourceIp": "203.0.113.0/24"
}
}
}Common condition keys for exam:
aws:RequestedRegion— restrict actions to specific regionaws:SourceVpc/aws:SourceVpce— enforce VPC endpoint usages3:prefix— restrict S3 access to specific folder prefixdynamodb:LeadingKeys— user can only access their own DynamoDB items
{
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${aws:userid}"]
}
}
}This means user johndoe can only read DynamoDB rows where the partition key equals johndoe. Perfect for multi-tenant apps.
Problem: You want developers to be able to create IAM roles for their Lambda functions, but you don't want them to create roles with admin access.
Solution: Permission Boundary
{
"Effect": "Allow",
"Action": ["iam:CreateRole", "iam:AttachRolePolicy"],
"Resource": "*",
"Condition": {
"StringEquals": {
"iam:PermissionsBoundary": "arn:aws:iam::123456789:policy/DevBoundary"
}
}
}Now developers CAN create roles, but those roles can NEVER exceed DevBoundary.
| Practice | Why |
|---|---|
| Use IAM Roles over IAM Users for services | Roles use temporary credentials, no rotation needed |
| Apply least privilege | Minimize blast radius if credentials leak |
| Use Groups to assign permissions | Easier to manage: change group, all members updated |
| Enable MFA for all human users | Root account + all privileged users |
| Rotate access keys every 90 days | Limit exposure window for long-term credentials |
| Use AWS Organizations SCPs as guardrails | Prevent entire accounts from doing dangerous things |
| Tag IAM roles with team/purpose | Enables auditing and cost allocation |
| Anti-Pattern | Why It's Bad | Fix |
|---|---|---|
| Hardcoding AWS credentials in code | Keys in GitHub = compromised instantly | Use IAM roles + instance profiles |
| Using root account for daily work | Root bypasses all policies, no MFA sometimes | Create admin IAM user, lock root |
"Action": "*" on "Resource": "*" |
Gives full AWS access | Write specific actions and resources |
| Sharing IAM users across team members | Can't audit who did what | One user per person |
| Long-term credentials for EC2/Lambda | Credentials sit in memory/disk | EC2 instance profile, Lambda execution role |
| No permission boundary on developer IAM | Developer can grant themselves admin | Set permission boundaries |
- IAM is global — not regional. Users and roles exist across all regions.
- Inline vs Managed Policies: AWS Managed = AWS maintains them. Customer Managed = you control. Inline = embedded in entity (not reusable, deleted with entity).
- When you see "403 Forbidden" on exam — think: missing IAM permission OR S3 bucket policy conflict OR resource policy conflict.
- Cross-account S3: bucket policy must explicitly allow the other account; AND the IAM user/role in the other account must also have permission.
sts:AssumeRoleappears in almost every cross-account or service-to-service question.PassRole: When creating a service (e.g., Lambda), you neediam:PassRoleto give the role TO the service.- Access Analyzer: Tells you which resources are exposed to external accounts — comes up in security questions.
Q: A developer's Lambda gets AccessDenied when calling DynamoDB. What's the fix? → Add DynamoDB permissions to the Lambda execution role (not the developer's IAM user).
Q: You need EC2 to call S3 without storing credentials. How? → Attach an IAM instance profile (role) to the EC2 instance.
Q: Developer created an IAM user for an app running on EC2. How to improve security? → Remove the IAM user, use an EC2 instance profile with an IAM role instead.
Q: How to allow users to see only their own S3 "folder"?
→ Use an IAM policy with condition s3:prefix = ${aws:username}/.