“Least privilege” is one of those phrases that gets thrown around constantly in cloud security. It sounds simple. It’s not.
Here’s how I actually approach it when I’m reviewing or writing IAM policies.
Start With What the Resource Actually Does
Before you write a single line of policy, answer this: what does this role/user/service actually need to do? Not what it might need someday. What it does right now.
A Lambda function that reads from DynamoDB and writes to SQS needs exactly:
dynamodb:GetItem(orQuery/Scanif needed)sqs:SendMessage
That’s it. Not dynamodb:*. Not sqs:*. Definitely not *.
Use IAM Access Analyzer
AWS IAM Access Analyzer has a feature that generates least-privilege policies based on CloudTrail activity. It’s not perfect, but it’s a solid starting point.
aws accessanalyzer start-policy-generation \
--policy-generation-details '{"principalArn":"arn:aws:iam::123456789012:role/MyRole"}' \
--cloud-trail-details '{"accessRole":"arn:aws:iam::123456789012:role/AccessAnalyzerRole","trails":[{"cloudTrailArn":"arn:aws:cloudtrail:us-east-1:123456789012:trail/MyTrail","allRegions":true}],"startTime":"2026-01-01T00:00:00Z","endTime":"2026-03-01T00:00:00Z"}'
Let it run for a few weeks in production, then generate the policy. You’ll be surprised how much you can cut.
Resource-Level Restrictions
Always scope to specific resources where possible. This is the difference between:
"Resource": "*"
and:
"Resource": "arn:aws:s3:::my-specific-bucket/*"
The second one means a compromised role can only touch that one bucket. The first one means game over.
Condition Keys Are Underused
Conditions let you add guardrails that most people skip. For example, requiring MFA for sensitive actions:
{
"Effect": "Allow",
"Action": "iam:DeleteRole",
"Resource": "*",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
Or restricting to specific source IPs, VPCs, or requiring specific tags on resources.
The Mindset Shift
Stop thinking about what to allow and start thinking about what to deny. Write the minimal allow policy, then ask yourself: if this credential was compromised, what’s the blast radius? If the answer is “a lot,” you haven’t gone far enough.