IAM: Identity & Access
Identity and Access Management (IAM) is the control plane for who can do what to which resources in your AWS account. Every API call - from the console, CLI, or SDK - is authenticated and authorized by IAM before anything happens. Get IAM right and the rest of AWS becomes a safe playground; get it wrong and you’ve handed out the keys to the kingdom.
The four core entities
IAM revolves around four building blocks.
- Users — a long-lived identity for a person or legacy application, with optional console password and/or access keys.
- Groups — a collection of users. You attach policies to the group, and membership grants those permissions. Groups are for managing humans, not machines.
- Roles — an identity with permissions but no permanent credentials. Trusted entities (an EC2 instance, a Lambda function, another account, a federated user) assume the role and receive temporary credentials.
- Policies — JSON documents that define permissions. They attach to users, groups, or roles.
The principle of least privilege
Grant only the permissions required to perform a task, and nothing more. Start from zero and add specific permissions as needs arise - never start from * and try to subtract.
Least privilege limits blast radius. If a credential leaks, the damage is bounded by exactly what that identity could do. A leaked admin key is a catastrophe; a leaked read-only-one-bucket key is an inconvenience.
Use IAM Access Analyzer and the “Last accessed” data in the console to prune permissions that are never used.
Anatomy of a policy
A policy is a JSON document of statements. Each statement has an Effect (Allow/Deny), Action(s), Resource(s), and optional Condition(s).
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadWriteOneBucket",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::devcraftly-uploads/*"
},
{
"Sid": "ListThatBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::devcraftly-uploads",
"Condition": {
"StringLike": { "s3:prefix": ["public/*"] }
}
}
]
}
This grants object read/write on a single bucket and listing of only the public/ prefix. Note the two different ARN forms: object-level actions target bucket/*, while bucket-level actions target the bucket itself.
Evaluation rule: an explicit Deny always wins. By default everything is denied; an
Allowopens access; any matchingDenyoverrides all allows. Use explicit denies for guardrails (e.g. “never delete production logs”).
Roles vs users
| Aspect | IAM User | IAM Role |
|---|---|---|
| Credentials | Long-lived (password / access keys) | Temporary, auto-rotated (STS) |
| Best for | Individual humans | Applications, services, cross-account, federation |
| On EC2/Lambda | Discouraged (keys on disk) | Strongly preferred |
| Rotation | Manual | Automatic |
Whenever code running on AWS needs to call AWS, attach a role - never bake in user access keys. An EC2 instance profile or a Lambda execution role delivers short-lived credentials transparently.
MFA
Multi-factor authentication adds a second factor on top of the password. Require it everywhere humans sign in.
- Mandatory on the root user and all privileged IAM users.
- Enforce it with a policy condition like
"aws:MultiFactorAuthPresent": "true"so sensitive actions are blocked unless MFA was used. - Prefer hardware keys (FIDO2/WebAuthn) or authenticator apps over SMS.
Access key best practices
Access keys are the number-one source of AWS account compromise. Treat them as radioactive.
- Don’t create them when a role will do (EC2, Lambda, ECS, CodeBuild all support roles).
- Never commit keys to source control; use
git-secretsor similar pre-commit scanning. - Rotate keys regularly and delete unused ones.
- Scope them with least-privilege policies - never attach
AdministratorAccessto a service account. - Use IAM Identity Center (SSO) for human access to get short-lived credentials by default.
Best Practices
- Lock away the root user with MFA; operate as a least-privilege IAM admin.
- Attach policies to groups and roles, not directly to individual users.
- Default to roles and temporary credentials; avoid long-lived access keys.
- Write tightly scoped policies; reserve
*resources for read-only or genuinely global actions. - Enable CloudTrail to audit every IAM action, and review IAM Access Analyzer findings.
- Use permission boundaries and Service Control Policies (SCPs) to cap maximum permissions across teams and accounts.