When we’re under pressure, the fastest solution often wins. Someone needs access, something is failing, and a deadline is approaching. So we do the classic move: grant wide permissions “temporarily.”
The problem is that temporary permissions have a habit of becoming permanent.
Least privilege isn’t about being paranoid. It’s about being intentional. We grant only what’s needed, so mistakes stay small, and security remains predictable.
What does least privilege actually mean?
Least privilege means:
- Only the actions needed (not everything)
- Only the resources needed (not “any resource”)
- Only when needed (not forever)
- Only for the right identity (role, user, or service)
With these points in mind, I always ask myself the following questions:
- What does this workload need to do?
- Where does it need to do it?
- What should never be allowed?
This mindset matters because IAM is not just about security; it’s also about reliability.
A role with too much power can break more things, faster.
Why Least Privilege Matters at Scale
In small environments, wide permissions might not cause immediate problems.
At scale, they usually do.
Here’s what least privilege protects you from:
- A larger blast radius: one compromised key can impact everything
- Accidental deletions: someone runs the wrong command in production
- Shadow access: old roles retain permissions nobody remembers granting
- Audit headaches: difficult questions like “Why does this role have this access?”
When permissions are tight, debugging becomes easier as well.
If something fails, we know the access boundary is real and meaningful.
Where Teams Usually Go Wrong
These are the most common patterns we see in real AWS environments:
- Wildcards like:*
- Policies copied from the internet and never cleaned up
- A single role reused across multiple systems
- “Temporary” permissions that quietly become permanent
- No separation between deployment permissions and runtime permissions
This happens to good teams as well. It usually comes from moving fast.
The fix isn’t blame; the fix is adopting the right patterns.
Bad Policy vs Good Policy Examples
Example 1: S3 access
❌ Bad policy (too broad)
This allows any S3 action on any bucket.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
]
}
Why This Is Risky
- Can delete any bucket
- Can read data that should be private
- No limits on specific paths or environments
✅ Good Policy (Tight and Practical)
This policy grants read-only access to a single bucket, limited to a specific prefix.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListBucketInPrefix",
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::my-app-data",
"Condition": {
"StringLike": {
"s3:prefix": ["public/*"]
}
}
},
{
"Sid": "ReadObjectsInPrefix",
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-app-data/public/*"
}
]
}
Why This Is Better
- Limited to a single bucket
- Limited to a specific path
- No delete permissions
Example 2: Lambda Logging
❌ Bad Policy
This policy allows writing logs anywhere, which is unnecessary and risky.
{
"Effect": "Allow",
"Action": "logs:*",
"Resource": "*"
}
✅ Good Policy
This policy allows only log stream creation and log writing for one specific log group.
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/my-function:*"
}
A Simple System to Design IAM the Right Way
Here’s a pattern that works in real projects:
Separate roles
- Use different roles for different responsibilities:
- One for deployment
- One for runtime
- One for monitoring
Start narrow, expand only when needed
- When an action fails, add only the minimum permission required to fix it.
Use guardrails
- Apply SCPs and permission boundaries to enforce “this must never happen” rules.
Review regularly
- If a permission hasn’t been used in months, remove it.
- Least privilege is not a one-time setup.
- It’s a maintenance habit.
🧪 Mini Project: Least-Privilege IAM Role for Lambda + S3
(Terraform Coming-Soon)
Goal
We will create:
- One S3 bucket
- One Lambda execution role
- One least-privilege IAM policy
- Attach the policy to the role
- The Lambda will be able to:
- Read only from s3://bucket/public/*
- Write only to s3://bucket/results/*
- Write logs to CloudWatch
If this article helped you, here’s what you can do next:
Follow me on X and YouTube for more AWS, DevOps, and Terraform content that’s beginner-friendly.
Leave a comment with your thoughts, your own AWS journey, or questions you’d like me to cover next.
Coming soon: a GitHub repository where I’ll share resources and examples for the Mini Project: Least-Privilege IAM Role for Lambda + S3 (Terraform), so you can practice hands-on.
Top comments (0)