Back to Checks
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 CloudRun
prowler aws --checks iam_policy_attached_only_to_group_or_rolesRecommendation
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 policiesOther
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