IAM (Identity and Access Management) controls who can do what on which resources. It’s the lock and key system of the cloud. Get it wrong, and we’re one misconfigured policy away from a data breach. Get it right, and even a compromised credential can’t do much damage.
Core Concepts
In simple language: Users are people, Roles are hats anyone can wear temporarily, Policies are the rulebook, and Groups are teams that share the same rulebook.
Principle of Least Privilege
This is the golden rule of IAM: give the minimum permissions needed to do the job, and nothing more.
A developer who deploys containers doesn’t need permission to delete the VPC. A Lambda function that reads from S3 doesn’t need permission to write to DynamoDB. Every extra permission is an extra attack surface.
Policy Structure
A policy is just a JSON document with three main fields:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-app-bucket",
"arn:aws:s3:::my-app-bucket/*"
]
}
]
}
- Effect —
AlloworDeny(Deny always wins if there’s a conflict) - Action — what operations are permitted (
s3:GetObject,ec2:StartInstances) - Resource — which specific resources the policy applies to (ARNs)
The tighter we scope the Action and Resource, the safer we are.
The Assume Role Pattern
Instead of giving applications long-lived access keys, we use roles. An EC2 instance or Lambda function “assumes” a role and gets temporary credentials that expire automatically.
# instead of this (bad — long-lived keys in env vars):
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=wJalr...
# we do this (good — EC2 instance profile with a role):
# 1. Create a role with the needed permissions
# 2. Attach the role to the EC2 instance
# 3. The AWS SDK automatically uses the instance's role credentials
# No keys in code or environment. Credentials rotate automatically.
This is how it works across providers:
- AWS — Instance Profiles, Lambda execution roles
- GCP — Service Accounts
- Azure — Managed Identities
MFA (Multi-Factor Authentication)
Every human user should have MFA enabled. Period. Especially the root/admin account. A stolen password with MFA is useless. A stolen password without MFA is a catastrophe.
# enforce MFA with a policy condition
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "true"
}
}
Common Mistakes
Overly permissive policies — using "Action": "*" and "Resource": "*" gives full admin access. Never do this for application roles.
Long-lived credentials — access keys that never rotate. Use roles with temporary credentials instead.
Not using groups — attaching policies directly to users means managing permissions one by one. Use groups: put all developers in a “Developers” group, attach the policy to the group.
Ignoring the root account — the root account can do anything and can’t be restricted. Lock it down with MFA, use it only for billing, and create admin IAM users for daily work.
No audit logging — without CloudTrail (AWS) or Cloud Audit Logs (GCP), we have no idea who did what. Always enable it.
In simple language, IAM is about answering three questions for every request: Who are we? What are we trying to do? Are we allowed? Getting this right is the single most impactful security practice in the cloud.