Cloud / SRE

Debugging an AWS Glue iam:PassRole AccessDenied Error

How an AWS Glue updateJob AccessDenied error was traced through IAM group policy, managed-policy scope, MFA guardrails, and IAM simulator results.

Published Jun 17, 2026Updated Sep 06, 2026
AWSGlueIAMPassRoletroubleshooting
On this page
  1. Terms used here
  2. What failed first
  3. Verify the investigation account
  4. Identify the user permission source
  5. Check the Glue role itself
  6. Confirm the job is using that role
  7. Simulate the exact denied action
  8. Find the missing allow
  9. Minimal permission change
  10. Check the change
  11. Checklist for the next Glue PassRole failure
  12. IAM boundary established by the investigation
  13. References

An AWS Glue console save failed because the human IAM user could not pass the job runtime role. The error named iam:PassRole, so the investigation followed the caller’s permission to delegate that exact role, separately from the permissions the job would use after starting.

This is a common Glue failure mode: the user may have broad Glue permissions, but updating or running a job that uses an IAM role also requires permission to pass that role to the Glue service.[1]

The examples below use anonymized names:

ItemExample
Human userarn:aws:iam::<account-id>:user/<human-user>
Glue job<glue-job-name>
Runtime rolearn:aws:iam::<account-id>:role/<glue-runtime-role>
User groupGlueAccess

Terms used here

TermMeaning
iam:PassRoleIAM permission that lets a user hand an IAM role to an AWS service. Glue needs this when a job runs with a runtime role.
Runtime roleThe IAM role Glue assumes when the job runs. It decides what the job can read, write, or call.
Trust policyThe role-side rule that says which AWS service or principal may assume the role.
explicitDenyA simulator decision where a deny statement matched. It overrides any allow.
implicitDenyA simulator decision where no allow matched. Nothing explicitly denied it, but it is still not allowed.

What failed first

The console showed a failure similar to this:

Failed to update job
updateJob: AccessDeniedException:
User: arn:aws:iam::<account-id>:user/<human-user>
is not authorized to perform: iam:PassRole
on resource: arn:aws:iam::<account-id>:role/<glue-runtime-role>
because no identity-based policy allows the iam:PassRole action

The wording matters. Glue itself was reachable, and the failed API was updateJob. The denied action was not glue:UpdateJob; it was iam:PassRole.

Verify the investigation account

The first check was to confirm that the CLI profile pointed to the same AWS account shown in the console error.

aws sts get-caller-identity --profile <production-profile>

The returned account matched the account ID in the error message, so the rest of the IAM checks were run against the right account.

Identify the user permission source

The user had no direct attached policies and no inline user policies.

aws iam list-attached-user-policies \
  --user-name <human-user> \
  --profile <production-profile>

aws iam list-user-policies \
  --user-name <human-user> \
  --profile <production-profile>

The effective permissions came from groups:

aws iam list-groups-for-user \
  --user-name <human-user> \
  --profile <production-profile>

The user belonged to GlueAccess and a general developer group. That made the next question narrower: which group policy should allow iam:PassRole?

Check the Glue role itself

The target role was not the problem. Its trust policy allowed Glue to assume it:

aws iam get-role \
  --role-name <glue-runtime-role> \
  --profile <production-profile>

The trust policy contained:

{
  "Effect": "Allow",
  "Principal": {
    "Service": "glue.amazonaws.com"
  },
  "Action": "sts:AssumeRole"
}

That ruled out the role trust relationship as the primary cause. If the user could pass the role, Glue would be allowed to assume it.

Confirm the job is using that role

The Glue job was then read directly:

aws glue get-job \
  --job-name <glue-job-name> \
  --region ap-northeast-1 \
  --profile <production-profile> \
  --query "Job.{Name:Name,Role:Role,LastModifiedOn:LastModifiedOn}"

The job role matched the role in the console error. That connected the UI failure to a concrete IAM resource.

Simulate the exact denied action

The IAM policy simulator was the fastest way to separate three possibilities:

HypothesisWhat would prove it
The role trust is wrongget-role trust policy does not include Glue
A guardrail explicitly denies the requestSimulator returns explicitDeny
No policy allows this role to be passedSimulator returns implicitDeny

The first simulation checked the exact principal, action, role, and target service:

aws iam simulate-principal-policy \
  --profile <production-profile> \
  --policy-source-arn arn:aws:iam::<account-id>:user/<human-user> \
  --action-names iam:PassRole \
  --resource-arns arn:aws:iam::<account-id>:role/<glue-runtime-role> \
  --context-entries \
    ContextKeyName=iam:PassedToService,ContextKeyValues=glue.amazonaws.com,ContextKeyType=string

The simulator first showed an explicit deny from an MFA guardrail. That was real: the account had a policy that denies most actions when MFA is not present.

However, that was not the whole story. A second simulation added the MFA context shown below. Any source-address conditions would need their own matching context entries:

aws iam simulate-principal-policy \
  --profile <production-profile> \
  --policy-source-arn arn:aws:iam::<account-id>:user/<human-user> \
  --action-names iam:PassRole \
  --resource-arns arn:aws:iam::<account-id>:role/<glue-runtime-role> \
  --context-entries \
    ContextKeyName=iam:PassedToService,ContextKeyValues=glue.amazonaws.com,ContextKeyType=string \
    ContextKeyName=aws:MultiFactorAuthPresent,ContextKeyValues=true,ContextKeyType=boolean

After the guardrail was satisfied, the decision became implicitDeny. That meant there was still no allow statement for passing this specific role.

Find the missing allow

The GlueAccess group had AWS managed policy AWSGlueConsoleFullAccess. Its documented PassRole statements are scoped by role name and destination service.[2]

That policy includes iam:PassRole, but only for role names that match AWS Glue service role naming conventions:

{
  "Effect": "Allow",
  "Action": "iam:PassRole",
  "Resource": "arn:aws:iam::*:role/AWSGlueServiceRole*",
  "Condition": {
    "StringLike": {
      "iam:PassedToService": "glue.amazonaws.com"
    }
  }
}

It also allows the service-role path variant:

arn:aws:iam::*:role/service-role/AWSGlueServiceRole*

The job was using a custom role name, not an AWSGlueServiceRole* name. That is why broad Glue console access still did not allow the update.

Minimal permission change

The narrow fix was to add an inline policy to the GlueAccess group:

aws iam put-group-policy \
  --profile <production-profile> \
  --group-name GlueAccess \
  --policy-name AllowPassGlueRuntimeRoleToGlue \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AllowPassGlueRuntimeRoleToGlue",
        "Effect": "Allow",
        "Action": "iam:PassRole",
        "Resource": "arn:aws:iam::<account-id>:role/<glue-runtime-role>",
        "Condition": {
          "StringEquals": {
            "iam:PassedToService": "glue.amazonaws.com"
          }
        }
      }
    ]
  }'

This keeps the permission scoped to one role and one destination service. AWS recommends restricting iam:PassRole to approved role ARNs and using iam:PassedToService to limit the destination service.[3]

Check the change

The policy was read back from the group:

aws iam get-group-policy \
  --profile <production-profile> \
  --group-name GlueAccess \
  --policy-name AllowPassGlueRuntimeRoleToGlue

Then the simulator was run again with the same principal, action, role, and Glue service context. The decision changed to:

allowed

The simulator now allowed the action with the supplied context. That establishes the policy result in the simulation, not a successful Glue update. AWS notes that simulator decisions can differ from the live environment.[4] The remaining verification is to retry the original console save in the intended session and confirm the job configuration.

Checklist for the next Glue PassRole failure

When a Glue job update fails with iam:PassRole, use this order:

  1. Confirm the CLI profile and error message are in the same AWS account.
  2. Read the human user’s direct policies.
  3. Read the user’s groups and group policies.
  4. Read the target role trust policy.
  5. Read the Glue job and confirm which role it uses.
  6. Simulate iam:PassRole with iam:PassedToService=glue.amazonaws.com.
  7. If the simulator returns explicitDeny, inspect guardrail policies first.
  8. If it returns implicitDeny, add a narrow allow for the exact role.
  9. Re-run the simulator before asking the user to retry the console.

IAM boundary established by the investigation

glue:* and AWSGlueConsoleFullAccess do not automatically mean “can pass any Glue role.” iam:PassRole is a separate IAM action, and AWS managed policies may only allow it for role names that follow AWS service-role conventions.

The change grants only the role delegation the job needs:

Allow iam:PassRole
only on the runtime role the job needs
only when iam:PassedToService is glue.amazonaws.com

Close the investigation only after the original update succeeds with the intended session controls. The recorded simulator result alone does not establish that final step.

References

[1] Amazon Web Services. “Grant a user permissions to pass a role to an AWS service.” IAM User Guide.

[2] Amazon Web Services. “AWSGlueConsoleFullAccess.”

[3] Amazon Web Services. “IAM policy validation check reference: PassRole security warnings.” IAM User Guide.

[4] Amazon Web Services. “IAM policy testing with the IAM policy simulator.”