← Back to blog

2026-06-25 · 8 min read

AWS IAM Least Privilege Without Losing Your Mind

A practical, repeatable approach to least-privilege IAM on AWS, start-deny, use Access Analyzer, scope with conditions, and avoid the wildcard traps that quietly grant too much.

#aws#iam#security#devsecops#cloud
AWS IAM Least Privilege Without Losing Your Mind

AWS IAM Least Privilege Without Losing Your Mind

Least privilege is the IAM principle everyone agrees with and few actually implement, because doing it by hand is tedious and "Action": "*" always works. Here's a practical approach that gets you most of the way without turning into a full-time job.

Why it matters

An over-permissive role is a standing risk: if the credentials leak or a service is compromised, the blast radius is everything that role can touch. Least privilege shrinks that blast radius to "only what this thing actually needs."

1. Start from deny, add what's used

Don't start from a broad policy and try to trim it, start from nothing and add what the workload actually calls. The trick is knowing what it calls:

  • CloudTrail + IAM Access Analyzer can generate a policy from observed activity. Run the workload with a broad policy in a non-prod account, let it exercise its paths, then generate a scoped policy from what it actually used.
  • For new workloads, add permissions iteratively as AccessDenied errors appear, annoying for a day, correct forever.

2. Scope resources, not just actions

s3:GetObject on "Resource": "*" means every object in every bucket. Scope it:

{
  "Effect": "Allow",
  "Action": ["s3:GetObject"],
  "Resource": "arn:aws:s3:::my-app-bucket/*"
}

Most managed policies are convenient but broad. Prefer customer-managed policies scoped to your actual ARNs.

3. Use conditions to tighten further

Conditions are the underused superpower:

"Condition": {
  "StringEquals": { "aws:RequestedRegion": "us-east-1" },
  "Bool": { "aws:SecureTransport": "true" }
}

Restrict by region, source VPC, MFA presence, tag, or time. A policy that only works from your VPC over TLS is far safer than one that works from anywhere.

4. Prefer roles over long-lived keys

  • Workloads on AWS: use IAM roles (instance profiles, IRSA for EKS, task roles for ECS), no stored keys.
  • CI/CD and external systems: use OIDC federation so there are no long-lived AKIA… keys to leak.
  • Humans: use IAM Identity Center (SSO) with short-lived sessions, not IAM users.

5. Guardrails above the role: SCPs

Even a perfect role can't protect you from a thousand imperfect ones. Service Control Policies at the AWS Organizations level set hard ceilings e.g. "deny disabling CloudTrail," "deny outside approved regions", that no account or role can exceed. (My aws-multi-account-organization repo shows this pattern.)

6. Find and fix what's already over-permissioned

  • IAM Access Analyzer flags resources shared externally and unused access.
  • Last accessed data (in the IAM console) shows permissions a role hasn't used in months, strong candidates to remove.
  • Alert on new iam:* and *:* policies in CI with tfsec/checkov.

The wildcard traps to avoid

  • "Action": "*" on "Resource": "*", admin in disguise.
  • iam:PassRole with "Resource": "*", lets a principal hand any role to a service (privilege escalation).
  • NotAction policies, easy to reason about wrong; prefer explicit Action allow-lists.

A workable routine

  1. New workload → start denied, generate a policy from observed use.
  2. Scope every statement to specific ARNs.
  3. Add region/transport/tag conditions where they fit.
  4. No long-lived keys, roles and OIDC only.
  5. SCPs as the org-wide ceiling.
  6. Quarterly: prune unused permissions via Access Analyzer + last-accessed.

Least privilege isn't a one-time project; it's a habit backed by the right tools. Done this way, it's maintainable, not the soul-crushing exercise it's reputed to be.


Securing your AWS estate? IAM hardening and guardrails are part of my consulting, reach out.

Share:LinkedInXWhatsApp

Related articles

Reactions & comments