Skip to content

AWS Identity and Access Management (IAM)

Chapter 3: AWS Identity and Access Management (IAM)

Section titled “Chapter 3: AWS Identity and Access Management (IAM)”

Mastering Security and Access Control in AWS

Section titled “Mastering Security and Access Control in AWS”

IAM is the foundation of AWS security, enabling you to control who can do what in your AWS environment.

IAM Core Components
+------------------------------------------------------------------+
| |
| +------------------------+ |
| | IAM Service | |
| +------------------------+ |
| | |
| +--------------------+--------------------+ |
| | | | |
| v v v |
| +----------+ +----------+ +----------+ |
| | Users | | Groups | | Roles | |
| | | | | | | |
| | - People | | - User | | - AWS | |
| | who | | groups | | services| |
| | login | | - Policy | | - Cross- | |
| | | | attach | | account | |
| +----------+ +----------+ +----------+ |
| | | | |
| +--------------------+--------------------+ |
| | |
| v |
| +------------------------+ |
| | Policies | |
| | | |
| | - Identity-based | |
| | - Resource-based | |
| | - Permission | |
| | boundaries | |
| +------------------------+ |
| |
+------------------------------------------------------------------+

IAM Principals Hierarchy
+------------------------------------------------------------------+
| |
| IAM Principals |
| ============================================================ |
| |
| 1. Root User (Account Owner) |
| +----------------------------------------------------------+ |
| | - Created with AWS account | |
| | - Has full access to all resources | |
| | - Should NOT be used for daily tasks | |
| | - Enable MFA immediately! | |
| +----------------------------------------------------------+ |
| |
| 2. IAM Users |
| +----------------------------------------------------------+ |
| | - Represent people or applications | |
| | - Have long-term credentials (username/password) | |
| | - Can have access keys for programmatic access | |
| | - Should be in groups for easier management | |
| +----------------------------------------------------------+ |
| |
| 3. IAM Roles |
| +----------------------------------------------------------+ |
| | - Temporary credentials | |
| | - No long-term credentials | |
| | - Assumed by users, services, or applications | |
| | - Cross-account access | |
| +----------------------------------------------------------+ |
| |
| 4. Federated Users |
| +----------------------------------------------------------+ |
| | - External identity providers | |
| | - AWS SSO (IAM Identity Center) | |
| | - SAML 2.0 (Active Directory, Okta, etc.) | |
| | - Web Identity (Amazon Cognito, Google, Facebook) | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

IAM User Structure
+------------------------------------------------------------------+
| |
| +------------------------+ |
| | IAM User | |
| | (developer@company) | |
| +------------------------+ |
| | |
| +-----------------+-----------------+ |
| | | |
| v v |
| +----------------+ +----------------+ |
| | Console Login | | Access Keys | |
| | | | | |
| | - Username | | - Access Key ID| |
| | - Password | | - Secret Key | |
| | - MFA (rec.) | | | |
| +----------------+ +----------------+ |
| | | |
| v v |
| +----------------+ +----------------+ |
| | AWS Console | | CLI/SDK/API | |
| | (Web UI) | | (Programmatic) | |
| +----------------+ +----------------+ |
| |
| User Security Best Practices: |
| +----------------------------------------------------------+ |
| | 1. Require MFA for console access | |
| | 2. Enforce password policy | |
| | 3. Rotate access keys regularly | |
| | 4. Use groups instead of attaching policies to users | |
| | 5. Remove unused users and credentials | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
IAM User Creation Process
+------------------------------------------------------------------+
| |
| Step 1: Create User |
| +----------------------------------------------------------+ |
| | aws iam create-user \ | |
| | --user-name developer-john \ | |
| | --path /developers/ | |
| +----------------------------------------------------------+ |
| | |
| v |
| Step 2: Configure Login Profile |
| +----------------------------------------------------------+ |
| | aws iam create-login-profile \ | |
| | --user-name developer-john \ | |
| | --password TempPassword123! \ | |
| | --password-reset-required | |
| +----------------------------------------------------------+ |
| | |
| v |
| Step 3: Create Access Keys (Optional) |
| +----------------------------------------------------------+ |
| | aws iam create-access-key \ | |
| | --user-name developer-john | |
| +----------------------------------------------------------+ |
| | |
| v |
| Step 4: Add to Group |
| +----------------------------------------------------------+ |
| | aws iam add-user-to-group \ | |
| | --user-name developer-john \ | |
| | --group-name Developers | |
| +----------------------------------------------------------+ |
| | |
| v |
| Step 5: Enable MFA |
| +----------------------------------------------------------+ |
| | aws iam enable-mfa-device \ | |
| | --user-name developer-john \ | |
| | --serial-number arn:aws:iam::123456789012:mfa/john \ | |
| | --authentication-code1 123456 \ | |
| | --authentication-code2 789012 | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

IAM Groups Structure
+------------------------------------------------------------------+
| |
| +------------------------+ |
| | Organization | |
| +------------------------+ |
| | |
| +----------+----------+----------+----------+ |
| | | | | | |
| v v v v v |
| +--------+ +--------+ +--------+ +--------+ +--------+ |
| | Admins | | DevOps | |Develop-| | DBA | |Auditors| |
| | | | | | ers | | | | | |
| +--------+ +--------+ +--------+ +--------+ +--------+ |
| | | | | | |
| v v v v v |
| +--------+ +--------+ +--------+ +--------+ +--------+ |
| |Admin | |EC2, | |EC2, | |RDS, | |Read- | |
| |Policy | |S3, | |S3, | |Dynamo- | |Only | |
| | | |Lambda, | |Lambda | |DB | |Policy | |
| | | |CloudFor-| | | | | | | |
| | | |mation | | | | | | | |
| +--------+ +--------+ +--------+ +--------+ +--------+ |
| |
| Benefits of Groups: |
| +----------------------------------------------------------+ |
| | - Easier policy management | |
| | - Consistent permissions across team members | |
| | - Quick onboarding/offboarding | |
| | - Audit-friendly | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

IAM Role Types
+------------------------------------------------------------------+
| |
| 1. AWS Service Roles |
| +----------------------------------------------------------+ |
| | Service: EC2 | |
| | Role: EC2InstanceProfile | |
| | Purpose: Allow EC2 to access S3, DynamoDB, etc. | |
| | | |
| | +--------+ +--------+ +--------+ | |
| | | EC2 | --> | Role | --> | S3 | | |
| | +--------+ +--------+ +--------+ | |
| +----------------------------------------------------------+ |
| |
| 2. Cross-Account Roles |
| +----------------------------------------------------------+ |
| | Account A (Dev) Account B (Prod) | |
| | +--------+ +--------+ | |
| | | Dev | Assume | Prod | | |
| | | User | -----------> | Role | | |
| | +--------+ Role +--------+ | |
| | | |
| | Use Case: Dev team needs read access to Prod resources | |
| +----------------------------------------------------------+ |
| |
| 3. AWS Service-Linked Roles |
| +----------------------------------------------------------+ |
| | Predefined by AWS services | |
| | Cannot be modified | |
| | Examples: | |
| | - AWSServiceRoleForEC2Spot | |
| | - AWSServiceRoleForAutoScaling | |
| | - AWSServiceRoleForRDS | |
| +----------------------------------------------------------+ |
| |
| 4. Web Identity Roles |
| +----------------------------------------------------------+ |
| | External Identity Provider | |
| | +--------+ +--------+ | |
| | | Google | Federate | AWS | | |
| | | User | -----------> | Role | | |
| | +--------+ via OIDC +--------+ | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
Role Assumption Process
+------------------------------------------------------------------+
| |
| Step 1: User/Service Requests Role |
| +----------------------------------------------------------+ |
| | User: developer@company | |
| | Wants to assume: CrossAccountAdminRole | |
| +----------------------------------------------------------+ |
| | |
| v |
| Step 2: STS Verify Permissions |
| +----------------------------------------------------------+ |
| | Check: Does user have sts:AssumeRole permission? | |
| | Check: Does role trust policy allow this user? | |
| +----------------------------------------------------------+ |
| | |
| v |
| Step 3: Generate Temporary Credentials |
| +----------------------------------------------------------+ |
| | Returns: | |
| | - AccessKeyId: AKIAIOSFODNN7EXAMPLE | |
| | - SecretAccessKey: wJalrXUtnFEMI/K7MDENG/... | |
| | - SessionToken: AQoDYXdzEJr... | |
| | - Expiration: 2026-02-16T12:00:00Z | |
| +----------------------------------------------------------+ |
| | |
| v |
| Step 4: Use Temporary Credentials |
| +----------------------------------------------------------+ |
| | User can now access resources defined in role policy | |
| | Credentials expire after session duration | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

IAM Policy Types
+------------------------------------------------------------------+
| |
| 1. Identity-Based Policies (Attached to Principals) |
| +----------------------------------------------------------+ |
| | Managed Policies (AWS or Customer) | |
| | +----------------------------------------------------+ | |
| | | - Reusable across users/roles | | |
| | | - Version controlled | | |
| | | - AWS Managed: AdministratorAccess, PowerUser... | | |
| | +----------------------------------------------------+ | |
| | | |
| | Inline Policies | |
| | +----------------------------------------------------+ | |
| | | - Embedded in user/role/group | | |
| | | - Deleted when principal is deleted | | |
| | | - Use for one-off permissions | | |
| | +----------------------------------------------------+ | |
| +----------------------------------------------------------+ |
| |
| 2. Resource-Based Policies (Attached to Resources) |
| +----------------------------------------------------------+ |
| | Examples: | |
| | - S3 Bucket Policy | |
| | - SQS Queue Policy | |
| | - Lambda Function Policy | |
| | - SNS Topic Policy | |
| | | |
| | Can specify WHO can access the resource | |
| +----------------------------------------------------------+ |
| |
| 3. Permission Boundaries |
| +----------------------------------------------------------+ |
| | - Sets maximum permissions for a principal | |
| | - Used with SCPs for permission boundaries | |
| | - Does NOT grant permissions | |
| +----------------------------------------------------------+ |
| |
| 4. Service Control Policies (SCPs) |
| +----------------------------------------------------------+ |
| | - AWS Organizations feature | |
| | - Sets permission guardrails | |
| | - Applied to OUs or accounts | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
{
"Version": "2012-10-17", // Policy version (always use this)
"Id": "S3ReadPolicy", // Optional identifier
"Statement": [ // Array of statements
{
"Sid": "AllowS3ReadAccess", // Optional statement ID
"Effect": "Allow", // Allow or Deny
"Principal": { // Who the policy applies to
"AWS": "arn:aws:iam::123456789012:user/developer"
},
"Action": [ // What actions
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [ // Which resources
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
],
"Condition": { // Optional conditions
"IpAddress": {
"aws:SourceIp": "192.0.2.0/24"
}
}
}
]
}
Policy Evaluation Flow
+------------------------------------------------------------------+
| |
| Start: Request Made |
| | |
| v |
| +---------------------+ |
| | Explicit Deny? | |
| | (Any policy) | |
| +----------+----------+ |
| | |
| +------------+------------+ |
| | | |
| v v |
| (Yes) (No) |
| | | |
| v v |
| +----------+ +---------------------+ |
| | DENY | | Explicit Allow? | |
| | Request | +----------+----------+ |
| +----------+ | |
| +---------+---------+ |
| | | |
| v v |
| (Yes) (No) |
| | | |
| v v |
| +----------+ +------------------+ |
| | ALLOW | | Default Deny | |
| | Request | | (Implicit) | |
| +----------+ +------------------+ |
| |
| Order of Evaluation: |
| 1. Explicit DENY in any policy -> DENY |
| 2. Explicit ALLOW in any policy -> ALLOW |
| 3. No explicit ALLOW -> IMPLICIT DENY |
| |
+------------------------------------------------------------------+

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:Get*",
"s3:List*"
],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAllWithMFA",
"Effect": "Allow",
"Action": "*",
"Resource": "*",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
},
{
"Sid": "DenyWithoutMFA",
"Effect": "Deny",
"NotAction": [
"iam:GetUser",
"iam:ListMFADevices",
"iam:EnableMFADevice"
],
"Resource": "*",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"192.0.2.0/24",
"203.0.113.0/24"
]
}
}
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*",
"Condition": {
"DateGreaterThan": {
"aws:CurrentTime": "2026-02-01T09:00:00Z"
},
"DateLessThan": {
"aws:CurrentTime": "2026-02-01T18:00:00Z"
}
}
}
]
}
// Trust Policy (in Account B - Prod)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ACCOUNT-A-ID:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "unique-external-id"
}
}
}
]
}
// Permission Policy (in Account B - Prod)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::prod-bucket",
"arn:aws:s3:::prod-bucket/*"
]
}
]
}

IAM Security Checklist
+------------------------------------------------------------------+
| |
| 1. Root Account Security |
| +----------------------------------------------------------+ |
| | [ ] Lock away root credentials | |
| | [ ] Enable MFA on root account | |
| | [ ] Don't use root for daily tasks | |
| | [ ] Delete root access keys | |
| +----------------------------------------------------------+ |
| |
| 2. User Management |
| +----------------------------------------------------------+ |
| | [ ] Use groups for permission assignment | |
| | [ ] Require MFA for console access | |
| | [ ] Enforce strong password policy | |
| | [ ] Rotate credentials regularly | |
| | [ ] Remove unused users | |
| +----------------------------------------------------------+ |
| |
| 3. Role Management |
| +----------------------------------------------------------+ |
| | [ ] Use roles for cross-account access | |
| | [ ] Use roles for EC2/Lambda services | |
| | [ ] Implement external ID for third-party | |
| | [ ] Set appropriate session duration | |
| +----------------------------------------------------------+ |
| |
| 4. Policy Management |
| +----------------------------------------------------------+ |
| | [ ] Follow least privilege principle | |
| | [ ] Use managed policies for reuse | |
| | [ ] Use conditions for additional security | |
| | [ ] Regular policy review and audit | |
| +----------------------------------------------------------+ |
| |
| 5. Monitoring & Auditing |
| +----------------------------------------------------------+ |
| | [ ] Enable CloudTrail | |
| | [ ] Use IAM Access Analyzer | |
| | [ ] Set up IAM credential reports | |
| | [ ] Monitor for unused permissions | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
Least Privilege Approach
+------------------------------------------------------------------+
| |
| BAD: Too Permissive |
| +----------------------------------------------------------+ |
| | { | |
| | "Effect": "Allow", | |
| | "Action": "*", <-- Allows EVERYTHING! | |
| | "Resource": "*" <-- On ALL resources! | |
| | } | |
| +----------------------------------------------------------+ |
| |
| GOOD: Least Privilege |
| +----------------------------------------------------------+ |
| | { | |
| | "Effect": "Allow", | |
| | "Action": [ | |
| | "s3:GetObject", <-- Only specific actions | |
| | "s3:ListBucket" | |
| | ], | |
| | "Resource": [ | |
| | "arn:aws:s3:::my-bucket", <-- Only specific bucket| |
| | "arn:aws:s3:::my-bucket/*" | |
| | ] | |
| | } | |
| +----------------------------------------------------------+ |
| |
| Process to Determine Least Privilege: |
| +----------------------------------------------------------+ |
| | 1. Start with no permissions | |
| | 2. Identify required actions | |
| | 3. Add minimum required permissions | |
| | 4. Test and iterate | |
| | 5. Use IAM Access Analyzer to verify | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

IAM Access Analyzer
+------------------------------------------------------------------+
| |
| Purpose: Identify resources shared with external entities |
| |
| +----------------------------------------------------------+ |
| | Access Analyzer | |
| | | |
| | Scans: | |
| | - S3 Bucket Policies | |
| | - IAM Roles (trust policies) | |
| | - KMS Key Policies | |
| | - Lambda Function Policies | |
| | - SQS Queue Policies | |
| | | |
| | Findings: | |
| | +------------------+------------------+ | |
| | | Resource | Shared With | | |
| | +------------------+------------------+ | |
| | | s3://my-bucket | Account: 999999 | | |
| | | Role: CrossAcct | Account: 888888 | | |
| | | KMS: my-key | Public: * | <-- ALERT! | |
| | +------------------+------------------+ | |
| +----------------------------------------------------------+ |
| |
| Actions: |
| +----------------------------------------------------------+ |
| | 1. Review findings | |
| | 2. Verify intended or unintended | |
| | 3. Remediate unintended access | |
| | 4. Archive resolved findings | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Terminal window
# List all users
aws iam list-users --query 'Users[*].UserName' --output table
# Create a new user
aws iam create-user --user-name developer-john
# Create access keys for a user
aws iam create-access-key --user-name developer-john
# Create a group
aws iam create-group --group-name Developers
# Add user to group
aws iam add-user-to-group --user-name developer-john --group-name Developers
# Attach managed policy to group
aws iam attach-group-policy \
--group-name Developers \
--policy-arn arn:aws:iam::aws:policy/PowerUserAccess
# Create a role with trust policy
aws iam create-role \
--role-name EC2InstanceRole \
--assume-role-policy-document file://trust-policy.json
# Create instance profile
aws iam create-instance-profile --instance-profile-name EC2InstanceProfile
# Add role to instance profile
aws iam add-role-to-instance-profile \
--role-name EC2InstanceRole \
--instance-profile-name EC2InstanceProfile
# Assume a role
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/CrossAccountRole \
--role-session-name "DevSession"
# Get credential report
aws iam get-credential-report --query 'Content' --output text | base64 --decode
# List MFA devices
aws iam list-mfa-devices --user-name developer-john
# Enable MFA device
aws iam enable-mfa-device \
--user-name developer-john \
--serial-number arn:aws:iam::123456789012:mfa/john-device \
--authentication-code1 123456 \
--authentication-code2 789012
import boto3
import json
# Initialize IAM client
iam = boto3.client('iam')
# Create a user
user = iam.create_user(UserName='developer-john')
print(f"Created user: {user['User']['UserName']}")
# Create a group
group = iam.create_group(GroupName='Developers')
# Attach policy to group
iam.attach_group_policy(
GroupName='Developers',
PolicyArn='arn:aws:iam::aws:policy/PowerUserAccess'
)
# Add user to group
iam.add_user_to_group(
UserName='developer-john',
GroupName='Developers'
)
# Create access keys
keys = iam.create_access_key(UserName='developer-john')
print(f"Access Key ID: {keys['AccessKey']['AccessKeyId']}")
print(f"Secret Access Key: {keys['AccessKey']['SecretAccessKey']}")
# Create a role
trust_policy = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
role = iam.create_role(
RoleName='EC2InstanceRole',
AssumeRolePolicyDocument=json.dumps(trust_policy)
)
# List all users
users = iam.list_users()
for user in users['Users']:
print(f"User: {user['UserName']}, Created: {user['CreateDate']}")

IAM Troubleshooting Guide
+------------------------------------------------------------------+
| |
| Issue 1: "Access Denied" Error |
| +----------------------------------------------------------+ |
| | Possible Causes: | |
| | - Missing policy | |
| | - Explicit deny | |
| | - Condition not met | |
| | - Resource ARN incorrect | |
| | | |
| | Debug Steps: | |
| | 1. Check IAM policy simulator | |
| | 2. Review CloudTrail for denied calls | |
| | 3. Verify resource ARNs | |
| | 4. Check conditions | |
| +----------------------------------------------------------+ |
| |
| Issue 2: "Not authorized to assume role" |
| +----------------------------------------------------------+ |
| | Possible Causes: | |
| | - Missing sts:AssumeRole permission | |
| | - Trust policy doesn't include user | |
| | - External ID mismatch | |
| | | |
| | Debug Steps: | |
| | 1. Check user has sts:AssumeRole | |
| | 2. Verify trust policy Principal | |
| | 3. Check for ExternalId condition | |
| +----------------------------------------------------------+ |
| |
| Issue 3: EC2 instance can't access S3 |
| +----------------------------------------------------------+ |
| | Possible Causes: | |
| | - No instance profile attached | |
| | - Role doesn't have S3 permissions | |
| | - S3 bucket policy denies access | |
| | | |
| | Debug Steps: | |
| | 1. Check instance has IAM role attached | |
| | 2. Verify role has S3 permissions | |
| | 3. Check S3 bucket policy | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

IAM is the single most critical AWS service for DevOps and SRE. Every permission error, every security breach, and every compliance audit starts and ends with IAM.

IAM in DevOps Daily Work
+------------------------------------------------------------------+
| |
| DevOps IAM Responsibilities: |
| |
| 1. CI/CD Pipeline Permissions |
| +----------------------------------------------------------+ |
| | - CodeBuild/GitHub Actions need IAM roles | |
| | - Least privilege for deployment roles | |
| | - Separate roles for build vs deploy stages | |
| +----------------------------------------------------------+ |
| |
| 2. Infrastructure Automation |
| +----------------------------------------------------------+ |
| | - Terraform/CloudFormation need broad permissions | |
| | - Use permission boundaries to limit scope | |
| | - Cross-account deployment roles | |
| +----------------------------------------------------------+ |
| |
| 3. Incident Response |
| +----------------------------------------------------------+ |
| | - Revoking compromised credentials immediately | |
| | - Creating break-glass access procedures | |
| | - Auditing IAM changes via CloudTrail | |
| +----------------------------------------------------------+ |
| |
| 4. Team Onboarding/Offboarding |
| +----------------------------------------------------------+ |
| | - Automating user creation with correct group membership| |
| | - Enforcing MFA enrollment | |
| | - Immediate access revocation when someone leaves | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Terminal window
# Install required tools on Arch Linux
sudo pacman -S aws-cli-v2 jq
yay -S aws-vault # Secure credential management
# aws-vault setup (recommended over plain credentials)
aws-vault add default
# Enter Access Key ID: ...
# Enter Secret Access Key: ...
# Use aws-vault for all operations (credentials never touch disk)
aws-vault exec default -- aws sts get-caller-identity
# IAM audit script - find users without MFA
#!/bin/bash
# Save as ~/bin/iam-audit.sh
echo "=== IAM Security Audit ==="
echo "Timestamp: $(date -u)"
echo ""
# Users without MFA
echo "--- Users WITHOUT MFA ---"
aws iam generate-credential-report > /dev/null 2>&1
sleep 3
aws iam get-credential-report \
--query 'Content' --output text | \
base64 --decode | \
awk -F, 'NR>1 && $4=="true" && $8=="false" {print $1}'
# Users with old access keys (>90 days)
echo ""
echo "--- Access Keys Older Than 90 Days ---"
CUTOFF=$(date -u -d '90 days ago' '+%Y-%m-%dT%H:%M:%S')
for user in $(aws iam list-users --query 'Users[*].UserName' --output text); do
aws iam list-access-keys --user-name "$user" \
--query "AccessKeyMetadata[?CreateDate<'$CUTOFF'].[UserName,AccessKeyId,CreateDate]" \
--output text 2>/dev/null
done
# Unused access keys (not used in 90 days)
echo ""
echo "--- Unused Access Keys ---"
for user in $(aws iam list-users --query 'Users[*].UserName' --output text); do
for key in $(aws iam list-access-keys --user-name "$user" --query 'AccessKeyMetadata[*].AccessKeyId' --output text); do
last_used=$(aws iam get-access-key-last-used --access-key-id "$key" --query 'AccessKeyLastUsed.LastUsedDate' --output text)
if [ "$last_used" == "None" ] || [ "$last_used" \< "$CUTOFF" ]; then
echo "User: $user, Key: $key, Last Used: $last_used"
fi
done
done
/etc/systemd/system/iam-key-rotation.service
# Systemd timer to rotate access keys automatically
[Unit]
Description=IAM Access Key Rotation Reminder
[Service]
Type=oneshot
ExecStart=/usr/local/bin/check-key-age.sh
User=akash
# /etc/systemd/system/iam-key-rotation.timer
[Unit]
Description=Check IAM key age weekly
[Timer]
OnCalendar=Mon *-*-* 09:00:00
Persistent=true
[Install]
WantedBy=timers.target
# Enable timer
sudo systemctl enable --now iam-key-rotation.timer

Scenario 1: Credential Leak Incident Response

Section titled “Scenario 1: Credential Leak Incident Response”
Credential Leak Response Playbook
+------------------------------------------------------------------+
| |
| Detection: AWS access key found in public GitHub repo |
| |
| IMMEDIATE ACTIONS (first 5 minutes): |
| +----------------------------------------------------------+ |
| | 1. Deactivate the exposed access key | |
| | aws iam update-access-key \ | |
| | --access-key-id AKIAEXAMPLE \ | |
| | --status Inactive --user-name leaked-user | |
| | | |
| | 2. Check CloudTrail for unauthorized API calls | |
| | aws cloudtrail lookup-events \ | |
| | --lookup-attributes \ | |
| | AttributeKey=AccessKeyId,\ | |
| | AttributeValue=AKIAEXAMPLE | |
| | | |
| | 3. Revoke all sessions for the user | |
| | (Attach deny-all inline policy) | |
| +----------------------------------------------------------+ |
| |
| INVESTIGATION (next 30 minutes): |
| +----------------------------------------------------------+ |
| | 1. Check what resources were accessed/created | |
| | 2. Look for new IAM users/roles created by attacker | |
| | 3. Check for cryptocurrency mining instances | |
| | 4. Review S3 buckets for data exfiltration | |
| | 5. Check for Lambda functions (common attack vector) | |
| +----------------------------------------------------------+ |
| |
| REMEDIATION: |
| +----------------------------------------------------------+ |
| | 1. Delete the compromised access key | |
| | 2. Create new key and rotate in applications | |
| | 3. Delete any unauthorized resources | |
| | 4. Enable GuardDuty if not already active | |
| | 5. Write postmortem and update procedures | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Scenario 2: Least Privilege for CI/CD Pipeline

Section titled “Scenario 2: Least Privilege for CI/CD Pipeline”
// Terraform deployment role - least privilege
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "TerraformStateAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::terraform-state-bucket/*"
},
{
"Sid": "TerraformStateLock",
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:DeleteItem"
],
"Resource": "arn:aws:dynamodb:*:*:table/terraform-locks"
},
{
"Sid": "DeploymentPermissions",
"Effect": "Allow",
"Action": [
"ec2:*",
"ecs:*",
"ecr:*",
"elasticloadbalancing:*",
"autoscaling:*",
"cloudwatch:*",
"logs:*",
"sns:*"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": ["us-east-1", "us-west-2"]
}
}
},
{
"Sid": "DenyDangerous",
"Effect": "Deny",
"Action": [
"iam:CreateUser",
"iam:DeleteUser",
"iam:CreateAccessKey",
"organizations:*"
],
"Resource": "*"
}
]
}

IssueCauseSolution
”Access Denied” on API callMissing permission in policyUse IAM Policy Simulator or CloudTrail to identify
Cannot assume roleTrust policy doesn’t allow your principalCheck the role’s trust policy
Instance can’t access S3No instance profile attachedAttach IAM role via instance profile
MFA not workingClock skew on MFA deviceResync MFA device in IAM console
Cross-account access deniedExternal ID mismatch or missingVerify external ID in trust policy
Policy too largeExceeds 6,144 character limitUse managed policies or split into multiple policies
Terminal window
# Debug IAM issues from terminal
# Check your current identity
aws sts get-caller-identity
# Simulate a policy (test permissions without executing)
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/developer \
--action-names s3:GetObject \
--resource-arns arn:aws:s3:::my-bucket/file.txt
# Check what permissions a role has
aws iam list-attached-role-policies --role-name MyRole
aws iam list-role-policies --role-name MyRole # inline policies
# Find the last time an access key was used
aws iam get-access-key-last-used --access-key-id AKIAEXAMPLE
# Decode authorization failure message
aws sts decode-authorization-message --encoded-message "<encoded message>" | jq '.DecodedMessage | fromjson'

IAM Anti-Patterns
+------------------------------------------------------------------+
| |
| ❌ Mistake 1: Using Root Account for Daily Work |
| +----------------------------------------------------------+ |
| | Problem: Root has unrestricted access, no MFA enforced | |
| | Impact: Single compromise = full account takeover | |
| | Fix: Create admin IAM user, lock root, enable MFA | |
| +----------------------------------------------------------+ |
| |
| ❌ Mistake 2: Wildcard Permissions (Action: "*") |
| +----------------------------------------------------------+ |
| | Problem: Giving "*" on actions or resources | |
| | Impact: Blast radius of compromise is entire account | |
| | Fix: Use IAM Access Analyzer to scope down permissions | |
| +----------------------------------------------------------+ |
| |
| ❌ Mistake 3: Long-lived Access Keys |
| +----------------------------------------------------------+ |
| | Problem: Access keys that never rotate, shared in Slack | |
| | Impact: Keys leak into repos, logs, or chat history | |
| | Fix: Use IAM roles + STS, rotate keys every 90 days | |
| +----------------------------------------------------------+ |
| |
| ❌ Mistake 4: Not Using Permission Boundaries |
| +----------------------------------------------------------+ |
| | Problem: Delegated admins can escalate their own perms | |
| | Impact: Users can grant themselves more access | |
| | Fix: Set permission boundaries on all delegated users | |
| +----------------------------------------------------------+ |
| |
| ❌ Mistake 5: Embedding Credentials in Application Code |
| +----------------------------------------------------------+ |
| | Problem: AWS keys hardcoded in source code | |
| | Impact: Keys exposed in version control | |
| | Fix: Use IAM roles, Secrets Manager, or env variables | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

  1. Q: Explain the IAM policy evaluation logic.

    • A: AWS evaluates all policies. (1) If any policy has an explicit DENY, the request is denied. (2) If any policy has an explicit ALLOW, the request is allowed. (3) If no policy explicitly allows, the request is implicitly denied. Additionally, SCPs from Organizations and permission boundaries further constrain what’s allowed.
  2. Q: What’s the difference between IAM users and IAM roles?

    • A: Users have long-term credentials (passwords, access keys) for people/applications. Roles provide temporary credentials via STS and are assumed by services, cross-account access, or federated users. In DevOps, prefer roles over users whenever possible.
  3. Q: How do you implement least privilege at scale?

    • A: Start with zero permissions and add as needed. Use IAM Access Analyzer to identify unused permissions. Use CloudTrail to audit actual API calls. Implement permission boundaries. Use AWS managed policies as starting points. Review permissions quarterly.
  1. Q: An EC2 instance needs to access an S3 bucket in another account. How?

    • A: Two approaches: (1) Resource-based: Add a bucket policy in Account B allowing the EC2 role ARN from Account A. (2) Role-based: Create a cross-account role in Account B with S3 permissions; have the EC2 instance assume that role via STS. The role-based approach is preferred for centralized control.
  2. Q: A developer accidentally committed AWS credentials to GitHub. What do you do?

    • A: Immediately: (1) Deactivate the key, (2) Check CloudTrail for unauthorized usage, (3) Revoke all active sessions. Then: (4) Delete the key and create a new one, (5) Clean up any unauthorized resources, (6) Remove credentials from git history using git filter-branch or BFG, (7) Enable GuardDuty, (8) Conduct postmortem.

Exam Tip

  1. Root Account: Never use for daily tasks; always enable MFA
  2. IAM Users vs Roles: Users for people, roles for services and temporary access
  3. Policy Evaluation: Explicit deny > Explicit allow > Implicit deny
  4. Least Privilege: Always grant minimum required permissions
  5. Groups: Use groups to manage permissions, not individual users
  6. MFA: Required for sensitive operations, can be enforced via policy
  7. Cross-Account: Use roles, not users, for cross-account access
  8. Instance Profiles: Required for EC2 to assume roles
  9. Access Analyzer: Identifies resources shared externally

Chapter 4: AWS CLI and SDKs


Last Updated: March 2026