Skip to main content
emnode / learn
Compliance Low severity

AWS Security Hub · IAM

IAM.2: Policies attached directly to users do not scale or audit cleanly

Written and reviewed by Emnode · Last reviewed

What does AWS Security Hub IAM.2 check?

IAM.2 fails when an IAM user has any managed or inline policy attached directly to the user rather than inherited through a group. The control inspects every user and reports the ones carrying their own permissions.

Why does IAM.2 matter?

Per-user policies don't scale and they obscure intent. When permissions accrete one user at a time, nobody can answer "who has admin and why", least-privilege reviews become impossible, and a forgotten grant on a departed contractor lingers indefinitely. Anchoring access to groups gives every grant a documented, auditable reason to exist.

How do I fix IAM.2?

  1. List users with directly attached managed and inline policies to scope the work.
  2. Create groups that model real job functions and attach the policies there instead.
  3. Add each user to the right group, then detach the user-level policies once access is confirmed.
  4. For the longer term, move human identities to IAM Identity Center so AWS isn't where you manage people at all.

Remediation script · bash

# Generate a least-privilege policy from real usage, then promote it to default.
aws accessanalyzer start-policy-generation \
  --policy-generation-details principalArn=arn:aws:iam::123456789012:role/DataPipelineWorker
aws iam create-policy-version \
  --policy-arn arn:aws:iam::123456789012:policy/DataPipelineWorkerPolicy \
  --policy-document file://generated-policy.json --set-as-default

# Move a user's direct policy to a group: attach + add BEFORE detach, so there is no gap.
aws iam attach-group-policy --group-name developers \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
aws iam add-user-to-group --group-name developers --user-name nina
aws iam detach-user-policy --user-name nina \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

# Detach a broad CloudShell grant, then scope down anyone with a genuine need.
aws iam detach-group-policy --group-name developers \
  --policy-arn arn:aws:iam::aws:policy/AWSCloudShellFullAccess

Full walkthrough (console steps, edge cases and verification) in the lesson Enforce IAM least privilege.

Is IAM.2 a false positive?

A break-glass emergency user that must stay isolated from any group is a legitimate exception — document it and accept the finding rather than weakening the control.

Part of the learning path Lock down access
  • IAM.1 A policy grants full "*" administrative privileges
  • IAM.3 Long-lived access keys have not been rotated
  • IAM.4 The root user still has long-lived access keys
  • IAM.5 Console users without MFA are one phish from compromise
  • IAM.6 The root user is not protected by hardware MFA
  • IAM.7 The IAM password policy is too weak
  • IAM.8 Unused IAM keys and passwords are waiting to be leaked
  • IAM.9 The root user can sign in without MFA
  • IAM.10 IAM user password policies should be strong (PCI DSS)
  • IAM.19 MFA should be enabled for all IAM users
  • IAM.21 Wildcard permissions grant far more access than intended
  • IAM.22 IAM credentials unused for 45 days should be removed