Give an AWS service a role it can assume

Six steps to create a service role (trust policy, permissions, instance profile for EC2), let the deployer pass it, protect any resource policy that trusts a service principal, and verify, without duplicating roles the console made for you.

markdownguidev4VJY9-SB7Fstage: buildtrust: officialservice: iaminterface: cliinterface: sdkinterface: iaccreate service roleexecution roletask rolecreate-roleassume-role-policy-documentiam:PassRoleinstance profileservice-linked rolerole manager

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

Give an AWS service a role

Create the role with a trust policy naming the service principal, attach only the permissions the service needs, and make sure whoever deploys the resource has iam:PassRole on that role. Missing PassRole is the usual reason a correct role still fails at deploy time.

1. Check whether the role already exists

  • A service-linked role? If the service uses one (its docs say so), you usually do not create it: the service makes it when you create the first resource. You need iam:CreateServiceLinkedRole for that to succeed, and you cannot edit its permissions.
  • Did a console make one? Since August 2026, IAM role manager makes or reuses a default role in supported consoles (Lambda and EventBridge among them). Ask your human, or list roles, before creating another: aws iam list-roles.

2. Write the trust policy

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Service": "<service>.amazonaws.com" },
    "Action": "sts:AssumeRole"
  }]
}

Take the service principal from the service's own documentation; it is case-sensitive and not always the obvious name. Known-good and known-bad variants: patterns/iam/trust-policies.md.

3. Create the role and attach permissions

aws iam create-role --role-name <role-name> \
  --assume-role-policy-document file://trust.json

aws iam attach-role-policy --role-name <role-name> \
  --policy-arn arn:aws:iam::<account-id>:policy/<customer-managed-policy>
# or an inline policy:
aws iam put-role-policy --role-name <role-name> \
  --policy-name <inline-name> --policy-document file://permissions.json

What this changes: creates a role (it counts toward the account's roles quota) and attaches policies. It costs nothing by itself (UNVERIFIED in the collected sources). Role names are up to 64 characters, unique per account ignoring case, and cannot be renamed later.

The caller needs iam:CreateRole and iam:AttachRolePolicy or iam:PutRolePolicy, scoped to the role ARN. AWS advises keeping broad role-creation rights with administrators: someone who can create roles and attach policies can grant themselves more access. To delegate it safely, give the delegate a permissions boundary they must apply (reference/iam/policy-evaluation.md).

Start from an AWS managed policy if you must, then narrow to a customer managed policy: guides/iam/least-privilege-policy.md.

EC2 only: an instance uses a role through an instance profile, which the CLI does not create for you:

aws iam create-instance-profile --instance-profile-name <name>
aws iam add-role-to-instance-profile --instance-profile-name <name> --role-name <role-name>

4. Let the deployer pass the role

Whoever creates or updates the resource (a person, a CI role, a CloudFormation service role) needs iam:PassRole on this role:

{
  "Effect": "Allow",
  "Action": "iam:PassRole",
  "Resource": "arn:aws:iam::<account-id>:role/<role-name>",
  "Condition": { "StringEquals": { "iam:PassedToService": "<service>.amazonaws.com" } }
}
  • Scope Resource to specific role ARNs, never *.
  • Do not control PassRole with role tags; AWS calls that unreliable.
  • PassRole leaves no CloudTrail event of its own; the passed role appears in the event of the call that used it.
  • The role and the service using it must be in the same account.

Without it, the deploy fails with an access denial naming iam:PassRole (exact text not collected; see reference/iam/errors.md).

5. Protect resource policies that trust a service principal

If the service will write to or read from a resource whose own policy names the service principal (a bucket receiving logs, a topic, a queue), add the narrowest confused-deputy condition so the service only acts for your work:

"Condition": { "ArnEquals": { "aws:SourceArn": "<ARN of the resource the service acts for>" } }

Or aws:SourceAccount with your account ID when the source resource ARN is not known in advance.

6. Verify

  • Create or update the resource that uses the role, and confirm it runs.
  • Check the role's effective decisions without running anything: aws iam simulate-principal-policy --policy-source-arn <role ARN> --action-names <action> --resource-arns <resource ARN>.
  • If anything fails, follow guides/iam/fix-access-denied.md. If a console step does not match what your human sees: guides/when-the-console-has-changed.md.

Read this document as markdown — the same bytes an agent receives.