DEV Community

Cover image for Fix an Over-Permissive IAM Policy with Access Analyzer Validation
miruky
miruky

Posted on

Fix an Over-Permissive IAM Policy with Access Analyzer Validation

Introduction

Hi, I'm miruky.

The IAM JSON policy editor can return IAM Access Analyzer findings before a policy is attached to a user or role. The basic validation checks policy grammar and AWS best practices, then groups its findings into Security, Errors, Warnings, and Suggestions.

This Console run starts with iam:PassRole on Resource: "*". Access Analyzer reports that the statement can pass multiple roles, so I replace the star-only resource with one generated role name and limit the destination to AWS Lambda. The result clears all four basic finding categories.

Basic policy validation has no additional charge. I did not run the custom Check for new access operation, which is a separate, charged check. The run creates one customer managed policy, leaves it unattached, and does not create the role named in the example ARN.

The identity used for this procedure needs access-analyzer:ValidatePolicy for the findings and the appropriate IAM permissions to list, create, inspect, and delete a customer managed policy. Use a validation identity that is separate from production administration, and do not attach the deliberately broad starting policy.

1. Open IAM from the target Console Region

I started with the AWS Console Region set to N. Virginia. IAM is a global service, so its own header changes to Global after the IAM page opens even though the Console URL retains region=us-east-1.

The AWS Console Region selector shows United States (N. Virginia) before opening global IAM.

The selector visibly reads United States (N. Virginia), so the regional entry point is fixed before IAM changes its header to Global. I then opened IAM, chose Policies, and searched for the generated customer managed policy name.

The IAM Policies page has no exact match for miruky-eqsbrcjhgbxzxwcg.

The filtered list reports no match for miruky-eqsbrcjhgbxzxwcg, so the validation policy will not collide with an existing policy. The exact-name filter also keeps unrelated AWS managed policies out of this check.

2. Reproduce the PassRole security warning

Choose Create policy, switch the policy editor to JSON, and replace the starter document with this policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PassAnyRole",
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "*"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

This statement is valid JSON and uses a valid IAM action, so a syntax-only check would not reject it. Its problem is scope: the star-only resource permits passing any role that the surrounding policy evaluation allows.

The IAM JSON editor contains iam:PassRole with a star-only Resource.

The editor visibly contains iam:PassRole with Resource: "*", which is the intentionally broad starting point. I waited for the validation pane below the editor to refresh before reading its category counts.

IAM Access Analyzer reports one Security finding for the broad policy.

The validation row now shows Security: 1; Errors, Warnings, and Suggestions remain at zero. I opened the Security tab to read the finding before changing the policy.

The expanded finding recommends a role ARN or the iam:PassedToService condition key.

That recommendation supplies two useful narrowing dimensions. The Resource element can identify which role may be passed, and iam:PassedToService can identify the AWS service that may receive it.

3. Restrict the role and destination service

Replace the policy with the following document:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PassOneRoleToLambda",
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "arn:aws:iam::*:role/miruky-ctprxglgmiihvzks",
      "Condition": {
        "StringEquals": {
          "iam:PassedToService": "lambda.amazonaws.com"
        }
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The role name is exact, and the condition limits the request to the Lambda service principal. For privacy, this demonstration uses a wildcard in the account field; a real deployment should use the full account-specific ARN of an existing role and verify that its trust policy allows Lambda.

The corrected policy names one generated role and limits passing it to Lambda.

The saved editor text now names miruky-ctprxglgmiihvzks and lambda.amazonaws.com together. The role is not created in this run, and the policy remains unattached so the exercise stays focused on validation output.

After the editor refreshes, inspect every basic finding tab again. All four counts should read zero before continuing.

Security, Errors, Warnings, and Suggestions all show zero findings.

A zero count means that these IAM Access Analyzer checks did not report a finding for the current document. It does not prove that the policy matches your business intent, that the referenced role exists, or that another policy type will not deny the eventual request.

4. Review and create the unattached policy

Choose Next, enter miruky-eqsbrcjhgbxzxwcg as the policy name, and add an optional validation-only description. Leave the tag list empty for this short-lived exercise.

The review page shows the generated policy name and the narrowed IAM permission.

The permissions summary shows IAM with limited write access and displays iam:PassedToService = lambda.amazonaws.com. Review that condition and the generated role name rather than relying only on the service-level summary.

Choose Create policy and wait for the Policies page to return. A success notification should name only the generated policy.

The IAM Console confirms that miruky-eqsbrcjhgbxzxwcg was created.

The success banner states Policy miruky-eqsbrcjhgbxzxwcg created. and returns to the Policies page. The object is a customer managed policy, and no user, group, role, or permissions boundary receives it during this run.

5. Verify the saved policy without attaching it

Open the policy and choose Entities attached. Both the permissions-policy count and the permissions-boundary count should remain zero.

The policy shows zero permissions-policy and zero permissions-boundary attachments.

The page shows Attached as a permissions policy (0) and Attached as a permissions boundary (0). This separates creating a policy document from granting its permissions to an identity.

Return to Permissions, switch the display to JSON, and compare the saved document with the corrected editor content. The exact generated role name and the Lambda condition should both remain present.

The saved policy JSON matches the validated role and Lambda restriction.

The saved JSON still contains miruky-ctprxglgmiihvzks, lambda.amazonaws.com, and the wildcard account segment *. This confirms that review did not restructure the validated permission, although a deployed policy should use the real account-specific role ARN.

Wrap-up

This run reproduced a specific Access Analyzer result rather than treating policy validation as a generic green check. iam:PassRole with a star-only Resource produced PassRole With Star In Resource, and narrowing the role name plus the destination service cleared the four basic finding categories.

Basic validation is a strong pre-save review, but it is not an authorization simulator or proof of least privilege. Test the full policy set, the role trust policy, permissions boundaries, session policies, service control policies, and the actual workflow before using a similar permission in production.

Thanks for reading this far.

See you in the next one.

Disclosure: This article was written with AI assistance and independently verified against the linked primary sources and observed results.

References

Top comments (0)