IAM Groups vs. Direct Policy Attachment: Why Groups Win Every Time

When you first set up AWS IAM, attaching a policy directly to a user feels fast and simple — but that shortcut quietly becomes a maintenance nightmare as your team grows. Understanding how IAM Groups change the operational model is one of the most practical things a cloud engineer can internalize early.

TL;DR: IAM Groups vs. Direct Policy Attachment

Attach policies to IAM Groups, not individual users. A group like Developers centralizes permission management: add a user to the group and they inherit all attached policies instantly. Direct attachment scatters permissions across users, making audits painful and permission drift nearly inevitable.

DimensionDirect User AttachmentIAM Group
Policy updatesEdit every affected userEdit once on the group
OnboardingManually attach policies per userAdd user to group
Audit surfaceScattered across N usersCentralized on group
Least-privilege enforcementInconsistent, error-proneConsistent by design
Permission drift riskHigh — stale attachments accumulateLower — group membership is the control point

How IAM Groups Work: The Permission Inheritance Model

An IAM Group is not a principal in the AWS authorization model — it cannot assume roles or sign API requests. Its sole function is to act as a policy container. When a user is added to a group, IAM evaluates that user's effective permissions as the union of: policies attached directly to the user, policies attached to every group the user belongs to, and any session policies in effect.

This means a user in both the Developers group and the ReadOnlyAuditors group accumulates permissions from both. There is no group-level deny that overrides another group — explicit Deny statements in any attached policy still override all Allows, regardless of which group or user attachment they come from.

flowchart TD A["IAM User: alice"] -->|"member of"| B["IAM Group: Developers"] A -->|"member of"| C["IAM Group: ReadOnlyAuditors"] B -->|"attached policy"| D["ManagedPolicy: S3ReadOnly"] B -->|"attached policy"| E["ManagedPolicy: EC2Describe"] C -->|"attached policy"| F["ManagedPolicy: ReadOnlyAccess"] A -->|"direct attachment"| G["InlinePolicy: SelfInspect"] D & E & F & G --> H["Effective Permissions
union of all sources"] H --> I{"Explicit Deny
anywhere?"} I -->|"Yes"| J["DENY — overrides all Allows"] I -->|"No"| K["ALLOW — request proceeds"]
  1. User added to group: IAM links the user principal to the group's policy set — no policy copy occurs.
  2. API call made: IAM evaluates all policies attached to the user directly plus all policies on every group the user belongs to.
  3. Effective permissions: The union of all Allows, minus any explicit Denies from any source (user, group, SCP, resource policy).
  4. Group removal: Removing a user from the group immediately revokes all permissions sourced from that group's policies — no policy cleanup required on the user object.

Why Direct Attachment Fails at Scale

Imagine a team of 20 developers. Each has AmazonS3ReadOnlyAccess attached directly. Your security team decides to restrict S3 access to a specific bucket prefix. You now have 20 policy edits to make — and if one is missed, that user retains broader access indefinitely. This is permission drift: the gap between intended and actual permissions that widens silently over time.

Think of direct policy attachment like handing every employee a physical key to every room they need. IAM Groups are the access card system — change the card profile once, and every holder of that profile is updated instantly.

The operational cost compounds during offboarding. When a user is deleted, any directly attached managed policies are automatically detached. But inline policies are deleted with the user. The real risk is the period before deletion — a departing employee whose access was never reviewed still holds every policy that was attached over their tenure. With group-based access, revoking all permissions is a single group membership removal, auditable in CloudTrail.

Permission drift is the symptom. The actual cause is that direct attachment has no natural review forcing function — there is no single place to look and ask "what can a Developer do?"

Creating an IAM Group and Attaching a Policy

The following commands create a Developers group, attach a managed policy, and add a user — the complete onboarding sequence.

# 1. Create the group
aws iam create-group \
  --group-name Developers

# 2. Attach a managed policy to the group
aws iam attach-group-policy \
  --group-name Developers \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

# 3. Add a user to the group
aws iam add-user-to-group \
  --group-name Developers \
  --user-name alice

# 4. Verify group membership
aws iam get-group \
  --group-name Developers

# 5. List policies attached to the group
aws iam list-attached-group-policies \
  --group-name Developers

Delegated IAM Policy: Letting Users Inspect Their Own Permissions

A common operational need is allowing users to view their own group memberships and attached policies without granting broad IAM read access. The policy below implements this with careful resource scoping.

🔽 Click to expand: Self-inspection IAM policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSelfUserInspection",
      "Effect": "Allow",
      "Action": [
        "iam:GetUser"
      ],
      "Resource": "arn:aws:iam::123456789012:user/${aws:username}"
    },
    {
      "Sid": "AllowGroupMembershipLookup",
      "Effect": "Allow",
      "Action": [
        "iam:ListGroupsForUser"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowGroupPolicyInspection",
      "Effect": "Allow",
      "Action": [
        "iam:ListAttachedGroupPolicies",
        "iam:GetGroupPolicy"
      ],
      "Resource": "arn:aws:iam::123456789012:group/*"
    }
  ]
}

Resource-scoping notes:

  • iam:GetUser supports resource-level permissions and can be scoped to specific user ARNs (e.g., arn:aws:iam::123456789012:user/${aws:username}).
  • iam:ListGroupsForUser does not support resource-level permissions according to the AWS Service Authorization Reference — it must use "Resource": "*".
  • iam:ListUsers also does not support resource-level permissions and is intentionally excluded here to avoid granting broader enumeration rights.

Auditing Group Membership and Policy Attachment via CLI

Before a security review or access audit, these commands give you a complete picture of who belongs to which group and what policies are in effect.

# List all IAM groups in the account
aws iam list-groups

# List all users in a specific group
aws iam get-group \
  --group-name Developers

# List all groups a specific user belongs to
aws iam list-groups-for-user \
  --user-name alice

# List managed policies attached to a group
aws iam list-attached-group-policies \
  --group-name Developers

# List inline policies on a group
aws iam list-group-policies \
  --group-name Developers

For account-wide visibility, the IAM credential report and AWS IAM Access Analyzer provide broader coverage — but the CLI commands above are the fastest path to answering "what can this user do and why?" during an incident.

Common Pitfalls When Using IAM Groups

Pitfall 1: Leaving direct attachments in place after migrating to groups

Engineers often create a group, attach the correct policy, add users — and forget to remove the older directly-attached policies from those users. The user now has permissions from two sources, and the group-based model provides no isolation benefit. After migrating to groups, explicitly audit and remove redundant direct attachments.

# List policies directly attached to a user
aws iam list-attached-user-policies \
  --user-name alice

# Detach a directly-attached policy from a user
aws iam detach-user-policy \
  --user-name alice \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

Pitfall 2: Treating group membership as a substitute for role-based access

IAM Groups apply only to IAM users. They have no effect on IAM roles, federated identities, or service principals. If your team authenticates via AWS IAM Identity Center (SSO) or assumes roles through federation, group membership on the IAM user object is irrelevant to their effective permissions. The correct control plane in those architectures is permission sets in IAM Identity Center or trust policies on roles.

Pitfall 3: Inline policies on groups

IAM supports inline policies on groups, but they are harder to audit, cannot be reused across groups, and do not appear in the managed policy inventory. Prefer managed policies attached to groups. Reserve inline policies only for cases where a permission must be strictly bound to a single group's lifecycle and must not be reusable.

flowchart LR subgraph Onboarding N1["New User"] -->|"add-user-to-group"| G1["Group: Developers"] G1 -->|"inherits"| P1["S3ReadOnly Policy"] G1 -->|"inherits"| P2["EC2Describe Policy"] end subgraph PolicyUpdate Admin["Admin"] -->|"attach-group-policy"| G1 G1 -->|"propagates to all members"| U1["alice"] G1 -->|"propagates to all members"| U2["bob"] G1 -->|"propagates to all members"| U3["carol"] end subgraph Offboarding G1 -->|"remove-user-from-group"| OFF["alice removed
permissions revoked"] end
  1. Onboarding path (green): New user → add to group → inherits all group policies. No per-user policy work required.
  2. Permission update path: Policy change on the group propagates to all members simultaneously.
  3. Offboarding path: Remove user from group → all group-sourced permissions revoked. User object can then be deleted or disabled.
  4. Audit path: Single group object is the complete source of truth for what a role-class of users can do.

IAM Groups and AWS Organizations: The SCP Layer

One detail that surprises engineers: even if a user's group grants broad permissions, Service Control Policies (SCPs) applied at the AWS Organizations level act as a permission ceiling. An SCP cannot grant permissions — it can only restrict what the maximum effective permissions are for any principal in the account, including IAM users in groups.

This means your group-based model and your SCP guardrails are complementary, not redundant. Groups define what a user is intended to do within an account. SCPs define what any principal in that account is ever allowed to do, regardless of what IAM policies say.

Wrap-Up: IAM Groups Are the Minimum Viable Access Control Pattern

Direct policy attachment is not wrong for a single-user account or a quick proof of concept. At any scale beyond that, IAM Groups are the correct default. They centralize the permission model, make onboarding and offboarding deterministic, and give auditors a single object to inspect per role class.

The next step beyond groups is evaluating AWS IAM Identity Center for workforce access — it extends the group model to multi-account environments with permission sets and integrates with your existing identity provider. For service-to-service access, IAM roles remain the correct mechanism; groups do not apply there.

Review the AWS IAM Groups documentation and the Service Authorization Reference to verify resource-level support for any IAM action before scoping policies.

Glossary

TermDefinition
IAM GroupA collection of IAM users that shares attached policies. Not a principal — cannot make API calls or assume roles.
Managed PolicyA standalone IAM policy with its own ARN that can be attached to multiple users, groups, or roles.
Inline PolicyA policy embedded directly in a single user, group, or role. Deleted when the parent entity is deleted.
Permission DriftThe accumulation of unintended permissions over time due to incomplete access reviews or stale attachments.
SCP (Service Control Policy)An AWS Organizations policy that sets the maximum permissions boundary for all principals in an account or OU.

Related Posts

Comments

Popular posts from this blog

EC2 No Internet Access in Custom VPC: Fix Internet Gateway and Route Table

EC2 SSH Connection Timeout: Which Security Group Rules to Check

Difference Between IAM User and IAM Role: Which One Should Your EC2 Use?