Skip to content

IAM

First PublishedByAtif Alam

IAM (Identity and Access Management) controls who can access what in your AWS account. Every API call to AWS is authenticated and authorized through IAM. Getting IAM right is the foundation of AWS security.

┌──────────────────────────────────────────────────┐
│ AWS Account │
│ │
│ ┌─────────┐ ┌─────────┐ ┌──────────────┐ │
│ │ Users │ │ Groups │ │ Roles │ │
│ │ (people) │ │(teams) │ │ (services/ │ │
│ └────┬─────┘ └────┬─────┘ │ temp access)│ │
│ │ │ └──────┬───────┘ │
│ └──────┬───────┘ │ │
│ ▼ ▼ │
│ ┌────────────────────────────────┐ │
│ │ Policies (JSON) │ │
│ │ "Allow s3:GetObject on bucket" │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
ConceptWhat It Is
UserAn identity for a person or application. Has long-term credentials (password, access keys).
GroupA collection of users (e.g. developers, ops). Policies attached to a group apply to all its members.
RoleAn identity with temporary credentials. Used by AWS services (EC2, Lambda), cross-account access, or federated users. No permanent password or access keys.
PolicyA JSON document that defines permissions (allow or deny specific actions on specific resources).
Terminal window
# Create a user
aws iam create-user --user-name alice
# Create access keys (for CLI/API access)
aws iam create-access-key --user-name alice
# Enable console access (set a password)
aws iam create-login-profile --user-name alice --password 'TempP@ss123!' --password-reset-required
  • Don’t use the root account for daily work. Create IAM users instead.
  • One user per person — never share credentials.
  • Use access keys only when needed — prefer roles for services, SSO for humans.
  • Rotate access keys regularly and delete unused ones.
  • Enable MFA on every user, especially the root account.

Groups simplify permission management — attach a policy to a group once, and every member inherits it.

Terminal window
# Create a group
aws iam create-group --group-name developers
# Add user to group
aws iam add-user-to-group --user-name alice --group-name developers
# Attach a policy to the group
aws iam attach-group-policy --group-name developers \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

Common group patterns:

GroupTypical Policies
adminsAdministratorAccess
developersPowerUserAccess (everything except IAM) or custom
read-onlyReadOnlyAccess
billingBilling + CostExplorerAccess

Roles provide temporary credentials via the AWS Security Token Service (STS). They’re safer than long-term access keys because credentials expire automatically.

Use CaseExample
EC2 instance roleAn EC2 server needs to read from S3 — attach a role, no access keys needed
Lambda execution roleA Lambda function needs to write to DynamoDB
Cross-account accessAccount A lets a role in Account B access its S3 bucket
Federated accessUsers from your company’s SSO (Okta, Azure AD) assume a role
EKS pod roleA Kubernetes pod needs AWS API access (IAM Roles for Service Accounts)
Terminal window
# Create a role that EC2 can assume
aws iam create-role --role-name MyEC2Role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "ec2.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
# Attach a policy to the role
aws iam attach-role-policy --role-name MyEC2Role \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
# Create an instance profile (needed to attach role to EC2)
aws iam create-instance-profile --instance-profile-name MyEC2Profile
aws iam add-role-to-instance-profile --instance-profile-name MyEC2Profile --role-name MyEC2Role

Every role has two types of policies:

Policy TypeQuestion It AnswersExample
Trust policyWho can assume this role?"Principal": {"Service": "ec2.amazonaws.com"}
Permission policyWhat can the role do?"Action": "s3:GetObject" on arn:aws:s3:::my-bucket/*

Policies are JSON documents that define permissions. They follow an Evaluate → Deny by default → Explicit allow → Explicit deny wins model.

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3Read",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}
]
}
FieldMeaning
VersionAlways "2012-10-17" (the current policy language version)
StatementArray of permission rules
SidOptional identifier for the statement
Effect"Allow" or "Deny"
ActionAWS API actions (e.g. s3:GetObject, ec2:RunInstances, *)
ResourceARNs of the resources the actions apply to (* = all)
ConditionOptional conditions (IP range, time, MFA required, etc.)
TypeAttached ToManaged By
AWS managedUser/group/roleAWS (e.g. AmazonS3ReadOnlyAccess)
Customer managedUser/group/roleYou (reusable across the account)
InlineSingle user/group/roleYou (embedded, not reusable)
Resource-basedA resource (S3 bucket, SQS queue)You (who can access this resource from outside)
Permission boundaryUser/roleYou (sets maximum permissions — a “guardrail”)
Service control policy (SCP)AWS Organization OU/accountOrg admin (restricts what an entire account can do)
Request arrives
Is there an explicit Deny? ──► Yes ──► DENIED
No
Is there an explicit Allow? ──► Yes ──► ALLOWED
No
DENIED (implicit deny — default)

Explicit deny always wins, even if another policy says allow.

Allow EC2 start/stop in a specific region:

{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["ec2:StartInstances", "ec2:StopInstances"],
"Resource": "*",
"Condition": {
"StringEquals": {"aws:RequestedRegion": "us-east-1"}
}
}]
}

Deny all actions without MFA:

{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyWithoutMFA",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"BoolIfExists": {"aws:MultiFactorAuthPresent": "false"}
}
}]
}

The principle of least privilege means granting only the permissions needed to perform a task — nothing more.

  1. Start with zero permissions and add as needed.
  2. Use AWS managed policies as a starting point, then narrow down.
  3. Use IAM Access Analyzer to review what permissions are actually used vs granted.
  4. Scope resources — don’t use "Resource": "*" when you know the specific bucket/table/queue.
  5. Use conditions — restrict by IP, region, time, or MFA.
  6. Review regularly — remove unused users, roles, and permissions.
Terminal window
# Generate a policy based on actual usage (last 90 days of CloudTrail)
aws accessanalyzer generate-access-policy --principal-arn arn:aws:iam::123456789012:role/MyRole

This shows you what the role actually used, so you can trim the policy.

MFA adds a second factor (usually a TOTP app like Google Authenticator) to password-based logins.

Terminal window
# Create a virtual MFA device
aws iam create-virtual-mfa-device --virtual-mfa-device-name alice-mfa \
--outfile /tmp/qrcode.png --bootstrap-method QRCodePNG
# Enable MFA for a user (after scanning QR and getting two consecutive codes)
aws iam enable-mfa-device --user-name alice \
--serial-number arn:aws:iam::123456789012:mfa/alice-mfa \
--authentication-code1 123456 --authentication-code2 789012
  1. Root account — Enable MFA immediately. Use a hardware key if possible.
  2. All human users — Enforce with a policy that denies actions without MFA.
  3. Sensitive operations — Require MFA for deleting resources, changing IAM, etc.
PracticeWhy
Don’t use root accountRoot has unrestricted access; use IAM users/roles instead
Enable MFA everywhereProtects against credential theft
Use roles, not access keysTemporary credentials are safer
Apply least privilegeMinimize blast radius of compromised credentials
Use groups for permissionsEasier to manage than per-user policies
Tag users and rolesTrack ownership and purpose
Review with Access AnalyzerFind unused permissions and over-privileged roles
Use SCPs in AWS OrganizationsGuardrails across multiple accounts
  • Users have long-term credentials; roles have temporary credentials. Prefer roles whenever possible.
  • Policies are JSON documents with Effect/Action/Resource. Explicit deny always wins.
  • Groups simplify permission management — attach policies to groups, add users to groups.
  • Least privilege means starting with zero permissions and adding only what’s needed.
  • MFA is non-negotiable on the root account and recommended for all human users.