cloud-sre

Debugging an AWS Glue iam:PassRole AccessDenied Error

A field note on tracing a Glue updateJob failure from the console error to IAM group policy, managed policy scope, MFA guardrails, and final simulator validation.

Jun 17, 2026
AWSGlueIAMPassRoletroubleshooting

An AWS Glue job failed to save from the console with an AccessDeniedException. The message said that a human IAM user was not authorized to perform iam:PassRole on the Glue runtime role.

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.

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 relevant context values for an MFA-authenticated and allowed-source request:

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.

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.

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

That is stronger validation than only retrying the console save, because it proves the identity-based policy now covers the exact IAM action that failed.

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.

What I keep from this

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 practical fix is not to broaden IAM access across all roles. The durable pattern is:

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