Back to Checks
aws

IAM user has no inline or attached policies

iam_policy_attached_only_to_group_or_roles
Service: iamSeverity: low
OfficialBuilt-inVerified
Categories:identity-access
Resource Type:AwsIamUser

IAM users have identity-based policies attached directly (managed or inline) instead of inheriting permissions via groups or roles.

Risk

Directly attached user policies hinder centralized control and cause privilege creep. If a user is compromised, excessive rights enable data exposure, resource tampering, and lateral movement, harming confidentiality and integrity. Revocation is error-prone, weakening separation of duties and auditability.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks iam_policy_attached_only_to_group_or_roles

Recommendation

Assign permissions to groups (humans) and roles (workloads); avoid user-attached policies. Enforce least privilege, prefer federation and temporary credentials, and use tags or permissions boundaries to constrain scope. Review regularly to remove direct user policies and right-size access.

Remediation

NativeIaC

# CloudFormation: ensure IAM user has no policies
Resources:
  <example_resource_name>:
    Type: AWS::IAM::User
    Properties:
      UserName: <example_resource_name>
      ManagedPolicyArns: []  # CRITICAL: empty list detaches all managed (attached) policies
      Policies: []           # CRITICAL: empty list removes all inline policies

Other

1In AWS Console, go to IAM > Users and select the target user
2Open the Permissions tab
3Under Permissions policies, remove each attached policy
4Under Inline policies, delete each inline policy
5Confirm changes; the user should show no inline or attached policies

Check Code

from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.iam_client import iam_client


class iam_policy_attached_only_to_group_or_roles(Check):
    def execute(self) -> Check_Report_AWS:
        findings = []
        if iam_client.users:
            for user in iam_client.users:
                if user.attached_policies or user.inline_policies:
                    if user.attached_policies:
                        for policy in user.attached_policies:
                            report = Check_Report_AWS(
                                metadata=self.metadata(), resource=user
                            )
                            report.region = iam_client.region
                            report.status = "FAIL"
                            report.status_extended = f"User {user.name} has the policy {policy['PolicyName']} attached."
                            report.resource_id = f"{user.name}/{policy['PolicyName']}"
                            findings.append(report)
                    if user.inline_policies:
                        for policy in user.inline_policies:
                            report = Check_Report_AWS(
                                metadata=self.metadata(), resource=user
                            )
                            report.region = iam_client.region
                            report.status = "FAIL"
                            report.status_extended = f"User {user.name} has the inline policy {policy} attached."
                            report.resource_id = f"{user.name}/{policy}"
                            findings.append(report)

                else:
                    report = Check_Report_AWS(metadata=self.metadata(), resource=user)
                    report.region = iam_client.region
                    report.status = "PASS"
                    report.status_extended = (
                        f"User {user.name} has no inline or attached policies."
                    )
                    findings.append(report)
        return findings