Building Least-Privilege IAM Policies Without Breaking Everything
Most IAM policies are wildly over-permissioned because tightening them is scary. Here's how I scope cloud permissions down safely — and use AI to draft and audit least-privilege policies.
- #security
- #hardening
- #iam
- #aws
- #least-privilege
- #ai
Almost every cloud account I’ve audited has the same problem: IAM policies are far broader than the workloads actually need. Not out of malice — out of fear. Tightening a policy risks breaking production, so the safe-feeling move is "Action": "*" and move on. Multiply that across a few years and you have an account where a single compromised credential can do nearly anything.
Least privilege is the antidote, and it’s not glamorous, but it’s the control that most reduces blast radius. After 25 years of scoping permissions, here’s how I tighten IAM without breaking things, and how I use AI to draft and audit policies safely.
Why least privilege actually matters
The point isn’t compliance theater. It’s that least privilege is your blast-radius limiter. When a credential leaks — and credentials leak — the damage is bounded by what that identity could do. An app role that can only read one S3 prefix is a contained incident. An app role with AdministratorAccess is a catastrophe.
Every wildcard you remove is a path the attacker no longer has.
Start from deny, build up from real usage
The wrong way to write a tight policy is from your imagination — you’ll forget half the permissions and break the app. The right way is from evidence.
On AWS, IAM Access Analyzer can generate a policy from your CloudTrail history — the actual actions an identity used over the last N days:
# Generate a policy from observed CloudTrail activity
aws accessanalyzer start-policy-generation \
--policy-generation-details '{"principalArn":"arn:aws:iam::123:role/app"}'
Now you’re scoping to what the workload did, not what you think it does. Apply the generated policy in a staging environment first, watch for AccessDenied, add back anything legitimately needed, and only then promote it.
Scope actions AND resources AND conditions
A tight policy constrains all three dimensions. Most teams remember actions and forget the other two:
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::app-bucket/uploads/*",
"Condition": {
"StringEquals": { "aws:RequestedRegion": "us-east-1" },
"Bool": { "aws:SecureTransport": "true" }
}
}
- Actions: specific verbs, never
s3:*. - Resources: specific ARNs, never
"*". - Conditions: pin region, require TLS, restrict source IP or VPC where it makes sense.
That "Resource": "*" on a write action is the line attackers love. Conditions are the layer almost nobody uses and that quietly closes a lot of doors.
Eliminate long-lived credentials entirely
The best access key is the one that doesn’t exist. Wherever possible:
- Use roles, not users. Workloads assume a role and get short-lived credentials; there’s no static key to leak.
- Use OIDC federation for CI/CD so pipelines get minute-lived credentials instead of stored secrets.
- Use instance/pod identity (IRSA on EKS, workload identity on GKE) so applications never handle a key at all.
A leaked credential that expires in 15 minutes is a fundamentally different problem than one that’s valid forever.
Catch the privilege-escalation traps
Some permissions are dangerous not for what they do directly, but for what they let an identity grant itself. Watch especially for:
iam:PassRolecombined with a service that runs code (an attacker passes a powerful role to a Lambda they control).iam:CreatePolicyVersion/iam:PutUserPolicy— the identity can rewrite its own permissions.sts:AssumeRolewith overly broad trust policies.
These are the combinations that turn a modest foothold into full account compromise. They deserve specific scrutiny in every policy review.
Using AI to draft and audit policies
IAM policy JSON is fiddly, and the implications of a given statement aren’t obvious from reading it. This is a strong fit for AI — both for drafting a tight first version and for auditing an existing one.
For drafting, I describe the workload precisely:
“Draft a least-privilege AWS IAM policy for a service that reads objects from the s3://app-bucket/uploads/ prefix and writes processed results to s3://app-bucket/processed/, both in us-east-1 only, over TLS. No other access. Scope actions, resources, and conditions tightly.”
For auditing, I paste an existing policy:
“Review this IAM policy for over-permissioning. Flag every wildcard action or resource, every privilege-escalation risk (especially iam:PassRole, policy-editing actions, broad AssumeRole), and any missing conditions like region or TLS. Give the tightened version. Explain why each change is safe.”
The model is excellent at spotting the "*" you stopped seeing and the iam:PassRole you didn’t realize was dangerous. But it does not have your CloudTrail data — so the workflow is: AI proposes a tighter policy, you validate against real usage in staging, you watch for AccessDenied, and a human approves the change. Keep these prompts with your other security hardening prompts, and run policy diffs through our Code Review tool for a structured second pass.
Audit continuously, not once
Permissions drift. Someone adds an action for a one-off task and never removes it. Schedule a recurring review:
- Run Access Analyzer to find unused permissions and external-access findings.
- Reconcile granted permissions against recent actual usage.
- Remove anything an identity hasn’t used in 90 days — unused permission is pure risk with no upside.
The short version
Least privilege is your blast-radius limiter, so treat over-permissioning as a real vulnerability, not a convenience. Build policies from observed usage, not imagination. Scope actions, resources, and conditions — all three. Kill long-lived credentials in favor of roles and federation. Watch hard for privilege-escalation combinations. Use AI to draft tight policies and audit existing ones, then validate against real usage in staging before a human approves. The wildcard you remove today is the incident you don’t have next quarter.
AI-generated IAM policies are assistive, not authoritative. Always test scoped policies against real usage in a non-production environment before deploying them.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.