---
cuppy_hub: aws
cuppy_path: guides/iam/create-service-role.md
cuppy_section: docs
cuppy_doc: VJY9-SB7F
cuppy_doc_uuid: 375009ea-b483-4529-8672-84648a3317b3
cuppy_permalink: https://cuppy.io/aws/d/VJY9-SB7F
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/guides/iam/create-service-role
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: b2c605ec95e8ad2c69addaee9af7cecc1a2989222daa851fbbe37d5f0c37a267
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# 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

```json
{
  "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:

```json
{
  "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:

```json
"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`.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 131 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/aws/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
