skip to content

A developer with permission to create Lambda functions is told they also need iam:PassRole before they can give a function its execution role. What does iam:PassRole actually authorise, why is it a separate permission, and how would you scope it?

level: seniorimportance: should knowfreq 44%

answer

  1. not an API call, a check
  2. who chooses the identity
  3. run instance, read its credentials
  4. narrow by path, then by service
  5. the trust policy fences the other end

basics

~20 s

iam:PassRole authorises handing an existing IAM role to an AWS service so that service can assume it. It is separate because creating a resource and choosing its identity are different powers: without it, anyone able to launch compute could attach an admin role and inherit its permissions.

solid answer

~50 s

`iam:PassRole` is not an API call you make — it is an authorisation check the service performs when you supply a role ARN to something that will run with it: creating a Lambda function with an execution role, launching an EC2 instance with an instance profile, registering an ECS task definition with a task role. The principal doing the creating must be allowed `iam:PassRole` on that role's ARN. It is separate from `sts:AssumeRole` because the creator never receives the credentials; the *service* assumes the role and the creator merely gets to choose which one. Without the check, `lambda:CreateFunction` or `ec2:RunInstances` would be a privilege-escalation primitive: attach an admin role, run code, read its credentials. Scope it by resource — a dedicated path such as `arn:aws:iam::111122223333:role/app/*` rather than `"Resource": "*"` — and add the `iam:PassedToService` condition key so a role meant for Lambda cannot be passed to EC2.

code

json · 18 lines
json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["lambda:CreateFunction", "lambda:UpdateFunctionConfiguration"],
      "Resource": "arn:aws:lambda:eu-west-1:111122223333:function:orders-*"
    },
    {
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "arn:aws:iam::111122223333:role/app/team-orders/*",
      "Condition": {
        "StringEquals": { "iam:PassedToService": "lambda.amazonaws.com" }
      }
    }
  ]
}

go deeper

for a junior

Recognise the error when creating a Lambda or launching an instance fails on a role you selected, and know that attaching a role to a service needs its own permission beyond the create permission.

for a middle

Explain that PassRole is an authorisation check performed by the service, that the creator never receives credentials, and name the operations where it fires.

for a senior

Demonstrate the escalation path it blocks and scope a real grant — role path narrowing plus iam:PassedToService — while checking that the role's trust policy fences the same intent from the other side.

for a principal

Own PassRole as an organisation-wide control: a role-naming and path convention teams build against, automated audit for PassRole on wildcards, and a delegation model where teams manage their own workload roles without touching anyone else's.

## PassRole is a check, not an operation There is no `PassRole` API. `iam:PassRole` is an authorisation check that AWS services perform whenever a request includes a role ARN that the service will later assume on the resource's behalf. It appears in CloudTrail as part of the calling operation, not as its own event. The mental model: creating the resource and *choosing its identity* are two distinct privileges, and AWS grades them separately. ## Where the check fires Anywhere a role is attached to something that runs code or acts autonomously: - `lambda:CreateFunction` / `lambda:UpdateFunctionConfiguration` with an execution role. - `ec2:RunInstances` with an instance profile (plus `iam:PassRole` on the role inside it), and `ec2:AssociateIamInstanceProfile` on a running instance. - `ecs:RegisterTaskDefinition` with `taskRoleArn` or `executionRoleArn`. - `codebuild:CreateProject`, `states:CreateStateMachine`, `glue:CreateJob`, `rds:CreateDBInstance` with an enhanced-monitoring role, and many more. A developer given only `lambda:CreateFunction` gets a clear failure the first time they select a role, which is exactly the intended behaviour. ## The escalation it prevents Suppose a principal has `ec2:RunInstances` and unrestricted `iam:PassRole`. They launch a t3.micro with an instance profile holding `AdministratorAccess`, then read the temporary credentials from the metadata service on that instance. They have just promoted themselves to administrator without ever holding an admin policy. The same trick works with Lambda: create a function with a powerful execution role and put `sts:GetCallerIdentity` plus arbitrary API calls in the handler. This is a top finding in every IAM audit tool, and `"Action": "iam:PassRole", "Resource": "*"` is the shape it looks for. ## Scoping it properly Two levers, used together: **Resource narrowing.** Put application roles under an IAM path and grant PassRole only on that path, so a developer can pass the roles their team owns and nothing else: ```json { "Effect": "Allow", "Action": "iam:PassRole", "Resource": "arn:aws:iam::111122223333:role/app/team-orders/*" } ``` **Service narrowing** with the `iam:PassedToService` condition key, which carries the service principal the role is being handed to: ```json { "Effect": "Allow", "Action": "iam:PassRole", "Resource": "arn:aws:iam::111122223333:role/app/team-orders/*", "Condition": { "StringEquals": { "iam:PassedToService": "lambda.amazonaws.com" } } } ``` Now a role designed for Lambda cannot be attached to an EC2 instance, closing the path where a workload role is repurposed into an interactive shell's credentials. The role's own **trust policy** is the second half of the fence: even with PassRole, a role whose trust policy only allows `lambda.amazonaws.com` cannot be usefully passed to EC2, because EC2 would fail to assume it. Trust policy and PassRole enforce the same intent from opposite ends, and a strong answer names both. ## Service-linked roles are the exception Some roles are created and managed by the service itself — service-linked roles, with predictable names under `/aws-service-role/`. They are created with `iam:CreateServiceLinkedRole`, their trust and permission policies are defined by AWS and cannot be arbitrarily edited, and you do not pass them; the service uses its own. They exist so a service can act on your behalf (registering targets, scaling a group) without you hand-rolling a role, and they refuse deletion while the service still has resources depending on them. ## What a good answer sounds like Name the check, name the escalation it blocks, and show that you would grant it narrowly by path and by `iam:PassedToService` — and add that you audit for `iam:PassRole` on `*` the same way you audit for `iam:*`.

  • How does iam:PassRole differ from sts:AssumeRole?
    `sts:AssumeRole` gives the caller the role's credentials directly — they become the role. `iam:PassRole` gives the caller no credentials at all; it only allows them to nominate a role that an AWS service will assume when running a resource they created. The distinction matters in review: a developer may legitimately be allowed to pass a powerful workload role while never being allowed to assume it themselves.
  • What is a service-linked role and how is it different from a service role you create yourself?
    A service-linked role is created and owned by the AWS service, lives under the /aws-service-role/ path with a name the service defines, and carries trust and permission policies AWS controls. You create it with iam:CreateServiceLinkedRole rather than passing it, you cannot freely rewrite its policies, and it refuses deletion while dependent resources still exist. A service role you create is an ordinary role you author, scope and pass.
  • Someone requests iam:PassRole on Resource "*" to unblock a deployment. How do you respond?
    Treat it as equivalent to requesting administrator. Combined with any create-compute permission it allows attaching a privileged role and reading its credentials. The fix is to enumerate which roles the pipeline actually passes, place them under a shared path, and grant PassRole on that path with an iam:PassedToService condition for the target service — usually a five-minute change that unblocks the deployment without the escalation.

saying these in an interview costs you the question

  • Believing PassRole is an API call you invoke directly
  • Confusing PassRole with sts:AssumeRole
  • Granting iam:PassRole on Resource "*" as routine
  • Thinking the role's trust policy alone makes PassRole unnecessary
  • Assuming service-linked roles must be passed like any other role

context