Almost every real AWS incident that isn't a code bug is an IAM problem — a permission too broad, a policy that grants more than intended, a role assumed by something that shouldn't be able to. IAM is the single most load-bearing service in AWS, and it's worth understanding properly before touching anything else.
4 min read
IAM (Identity and Access Management) controls who (or what) can do what, to which resources, in an AWS account. Four concepts do almost all of the work:
A policy is a JSON document with one or more statements, each specifying an Effect (Allow/Deny), Action (which API calls), and Resource (which specific things those actions apply to):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::my-app-uploads/*"
}
]
}This statement allows reading and writing objects in exactly one bucket (my-app-uploads) — not all S3 buckets in the account, not the ability to delete the bucket itself, not any other service. Every real IAM incident traces back to one of these three fields being broader than intended — most commonly Resource: "*" where a specific ARN was actually needed.
Least privilege means granting exactly the permissions a task needs, nothing more — the opposite instinct from "grant broad access now, narrow it later if there's a problem," which in practice almost never actually gets narrowed later. A policy scoped to one bucket, one table, one function is more work to write than "Resource": "*", but it's the difference between a compromised credential leaking one bucket's contents versus the entire account's data.
An IAM role has no permanent password or access key — instead, something (a user, an EC2 instance, a Lambda function, another AWS account) assumes the role and receives short-lived, automatically-expiring credentials. This is the same pattern covered in the CI/CD domain's OIDC lesson (GitHub Actions assuming a role instead of storing a static AWS key) — the general principle: anywhere a static, long-lived access key could be used instead, a role providing temporary credentials is safer, because a leaked temporary credential expires on its own, while a leaked static key remains valid until someone notices and manually rotates it.
resource "aws_iam_role" "lambda_s3_reader" {
name = "lambda-s3-reader"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy" "s3_read" {
role = aws_iam_role.lambda_s3_reader.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = ["s3:GetObject"]
Resource = "arn:aws:s3:::my-app-uploads/*"
}]
})
}The assume_role_policy (the trust policy) controls who is allowed to assume this role at all; the attached policy controls what the role can do once assumed — two separate, both-necessary layers.
AmazonS3ReadOnlyAccess), convenient but often broader than a specific use case actually needs (that particular one grants read access to every bucket in the account, not just one).For anything beyond the most casual learning account, writing a scoped customer-managed policy (like the S3 example above) instead of reaching for a broad AWS-managed one is worth the extra few minutes.
A less commonly used but important concept: a permissions boundary sets the maximum permissions an identity can ever have, regardless of what policies are later attached to it — it doesn't grant anything by itself, it caps what other policies are allowed to grant. This matters for delegated administration: letting a team lead create IAM roles for their own team, while guaranteeing none of those roles can ever exceed a boundary that (for example) blocks access to billing or account-level settings.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. What is the key difference between an IAM user and an IAM role?
2. Which field in an IAM policy statement is most commonly the source of an overly broad grant?
3. Why are IAM roles generally preferred over long-lived access keys for services and automation?
4. What does an IAM permissions boundary do?
AWS