AWS::IAM::Role
Creates an IAM role — a set of permissions that principals assume rather than hold, and the join between “who is asking” and “what they may do”.
Ref returns
Role nameacme-payments-task-roleFn::GetAtt
2
attributesMinimal template
Every required property, nothing elseResources:
TaskRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole{
"Resources": {
"TaskRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
}
}
}
}import { Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
new Role(this, 'TaskRole', {
assumedBy: new ServicePrincipal('ecs-tasks.amazonaws.com'),
});Overview #
A role is a set of permissions with no credentials attached. Principals do not have a role; they assume it, receiving temporary credentials that expire. That indirection is the whole point: it is what makes it possible to grant an EC2 instance, a Lambda function, or an engineer at another company access to something without ever creating a long-lived key.
Two documents govern every role, and confusing them is the most common source of “why is this denied”:
AssumeRolePolicyDocument— the trust policy. Answers who may become this role. Contains aPrincipal.PoliciesandManagedPolicyArns— the permission policies. Answer what the role may then do. Contain noPrincipal, because the principal is the role.
An AccessDenied on sts:AssumeRole is a trust policy problem. An
AccessDenied on anything else is a permission policy problem. The two are
never fixed in the same place.
How a request is actually evaluated #
Worth internalising, because it explains almost every surprising denial.
Deny at any stage ends the evaluation immediately; an Allow must survive every stage that applies.The consequence: a role whose policy plainly allows an action can still be denied by a service control policy, a permissions boundary, or a resource policy, and none of those appear in the role’s own definition. When a denial makes no sense, work outwards from the role rather than staring at it.
A role you would actually deploy #
Resources:
PaymentsTaskRole:
Type: AWS::IAM::Role
Properties:
Description: Runtime role for the payments API tasks.
# Who may become this role. Conditions scope it to *our* tasks,
# not to every ECS task in the account.
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
Condition:
ArnLike:
aws:SourceArn: !Sub 'arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:*'
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
# What it may then do. Inline, because it is meaningless elsewhere.
Policies:
- PolicyName: read-payments-config
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: ssm:GetParametersByPath
Resource: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/acme/payments/production'
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
Resource: !Sub '${AssetBucket.Arn}/receipts/*'
PermissionsBoundary: !Ref DeveloperBoundary
Tags:
- Key: System
Value: payments
{
"Resources": {
"PaymentsTaskRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"Description": "Runtime role for the payments API tasks.",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": {
"ArnLike": {
"aws:SourceArn": { "Fn::Sub": "arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:*" }
},
"StringEquals": { "aws:SourceAccount": { "Ref": "AWS::AccountId" } }
}
}
]
},
"Policies": [
{
"PolicyName": "read-payments-config",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ssm:GetParametersByPath",
"Resource": { "Fn::Sub": "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/acme/payments/production" }
},
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": { "Fn::Sub": "${AssetBucket.Arn}/receipts/*" }
}
]
}
}
],
"PermissionsBoundary": { "Ref": "DeveloperBoundary" },
"Tags": [{ "Key": "System", "Value": "payments" }]
}
}
}
}
Referencing a role #
!Ref PaymentsTaskRole
→
acme-payments-task-rolethe name, without the path
!GetAtt PaymentsTaskRole.Arn
→
arn:aws:iam::123456789012:role/service-roles/acme-payments-task-role
Use the ARN anywhere the value crosses a boundary — another account’s trust
policy, an sts:AssumeRole call, a PassRole condition. The bare name is only
safe inside the same account and only for roles at the default path.
Properties
Expand a row for the full reference; nested types open in placeAssumeRolePolicyDocument JSONThe trust policy — who is allowed to assume this role. Required No interruption — updates in place
The single most important property on the resource, and the one people confuse with the permission policies. This document answers who may become this role;PoliciesandManagedPolicyArnsanswer what the role may then do.- Type
JSON- Required
- Yes
- Update behaviour
- No interruption
RoleName StringThe role's name. Generated from the stack and logical ID when omitted. Replacement — CloudFormation creates a new resource and deletes the old one Create-only
Naming a role explicitly makes it referenceable from outside the stack and readable in CloudTrail. It also makes the stack non-repeatable within an account, and requiresCAPABILITY_NAMED_IAMon every operation.- Type
String- Required
- No
- Update behaviour
- Replacement
- Pattern
^[\w+=,.@-]+$Letters, digits, and `_ + = , . @ -`. No spaces, no slashes — a slash belongs in `Path`.- Length
1 – 64
Example values
acme-payments-task-role
Description StringFree-text description of the role's purpose. No interruption — updates in place
Worth filling in. It is displayed in the assume-role picker in the console and in Access Analyzer findings, which are exactly the moments when somebody is trying to work out what a role is for.- Type
String- Required
- No
- Update behaviour
- No interruption
- Length
0 – 1000
ManagedPolicyArns Array of StringARNs of managed policies to attach. No interruption — updates in place
The quota of 20 attached managed policies per role is a hard limit and not adjustable in the way most IAM quotas are. Large permission sets belong in customer-managed policies with several statements, not in many small attached policies.- Type
Array of String- Required
- No
- Update behaviour
- No interruption
- Items
0 – 20
Example values
["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]
MaxSessionDuration IntegerLongest session, in seconds, that AssumeRole may issue for this role. No interruption — updates in place
This is a ceiling, not a duration. The caller still asks for a duration and gets the smaller of the two.- Type
Integer- Required
- No
- Update behaviour
- No interruption
- Default
3600- Range
3600 – 43200
Example values
3600
Path StringA namespace for the role, used for organisation and for wildcard policies. Replacement — CloudFormation creates a new resource and deletes the old one Create-only
Paths are the closest thing IAM has to folders. Their practical value is in permission boundaries and delegation policies, wherearn:aws:iam::*:role/service-roles/payments/*scopes an administrator to one team’s roles.- Type
String- Required
- No
- Update behaviour
- Replacement
- Default
/- Pattern
^(/|/[\x21-\x7E]+/)$Must begin and end with a forward slash.- Length
0 – 512
Example values
/service-roles/payments/
PermissionsBoundary StringARN of a managed policy that caps what this role can ever be granted. No interruption — updates in place
A boundary grants nothing. Effective permissions are the intersection of the boundary and the role’s policies, so a boundary can only ever subtract.- Type
String- Required
- No
- Update behaviour
- No interruption
Example values
arn:aws:iam::123456789012:policy/acme-developer-boundary
Policies Array of InlinePolicyInline policies embedded in the role, deleted with it. No interruption — updates in place
Inline policies cannot be shared or reused, which is exactly why they are often the right choice: a policy that only makes sense for one role should not be attachable to another, and it should disappear when the role does.- Type
Array of InlinePolicy- Required
- No
- Update behaviour
- No interruption
InlinePolicy properties
PolicyName StringName of the inline policy, unique within the role. Required No interruption — updates in place
Name of the inline policy, unique within the role.
- Type
String- Required
- Yes
- Update behaviour
- No interruption
- Length
1 – 128
Example values
read-payments-config
PolicyDocument JSONThe permission policy document. Required No interruption — updates in place
The permission policy document.
- Type
JSON- Required
- Yes
- Update behaviour
- No interruption
Tags Array of TagKey/value tags on the role. No interruption — updates in place
Key/value tags on the role.
- Type
Array of Tag- Required
- No
- Update behaviour
- No interruption
- Items
0 – 50
Tag properties
Key StringTag key. Required No interruption — updates in place
Tag key.
- Type
String- Required
- Yes
- Update behaviour
- No interruption
- Length
1 – 128
Value StringTag value. Required No interruption — updates in place
Tag value.
- Type
String- Required
- Yes
- Update behaviour
- No interruption
- Length
0 – 256
| Property | Type | Required | Update | Description |
|---|---|---|---|---|
| AssumeRolePolicyDocument | JSON | Yes | None | The trust policy — who is allowed to assume this role. |
| RoleName | String | No | Replacement | The role's name. Generated from the stack and logical ID when omitted. |
| Description | String | No | None | Free-text description of the role's purpose. |
| ManagedPolicyArns | Array of String | No | None | ARNs of managed policies to attach. |
| MaxSessionDuration | Integer | No | None | Longest session, in seconds, that AssumeRole may issue for this role. |
| Path | String | No | Replacement | A namespace for the role, used for organisation and for wildcard policies. |
| PermissionsBoundary | String | No | None | ARN of a managed policy that caps what this role can ever be granted. |
| Policies | Array of InlinePolicy | No | None | Inline policies embedded in the role, deleted with it. |
| Policies.PolicyName | String | Yes | None | Name of the inline policy, unique within the role. |
| Policies.PolicyDocument | JSON | Yes | None | The permission policy document. |
| Tags | Array of Tag | No | None | Key/value tags on the role. |
| Tags.Key | String | Yes | None | Tag key. |
| Tags.Value | String | Yes | None | Tag value. |
Generated from the schema. The first pair shows only required and conditionally-required properties — a template you can paste and deploy. Property keys are ordered alphabetically here rather than required-first, because that is the order a template file conventionally uses.
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Key: Value{
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Key": "Value"
}
}
}Every property, three levels deep:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Key: Value
Description: String
ManagedPolicyArns:
- String
MaxSessionDuration: '3600'
Path: /service-roles/payments/
PermissionsBoundary: arn:aws:iam::123456789012:policy/acme-developer-boundary
Policies:
- PolicyDocument:
Key: Value
PolicyName: read-payments-config
RoleName: acme-payments-task-role
Tags:
- Key: String
Value: String{
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Key": "Value"
},
"Description": "String",
"ManagedPolicyArns": [
"String"
],
"MaxSessionDuration": "3600",
"Path": "/service-roles/payments/",
"PermissionsBoundary": "arn:aws:iam::123456789012:policy/acme-developer-boundary",
"Policies": [
{
"PolicyDocument": {
"Key": "Value"
},
"PolicyName": "read-payments-config"
}
],
"RoleName": "acme-payments-task-role",
"Tags": [
{
"Key": "String",
"Value": "String"
}
]
}
}Return values
What other resources can read from this oneRefFn::GetAtt Arn — reconstructing the ARN from this value silently drops
the path.!Ref MyResource
→
acme-payments-task-role
Fn::GetAtt attributes
| Attribute | Type | Description | Example value |
|---|---|---|---|
| Arn | String | The role’s ARN, including its path. This is what sts:AssumeRole takes and what other trust policies should reference. | arn:aws:iam::123456789012:role/service-roles/acme-payments-task-role |
| RoleId | String | The role’s unique ID. Stable for the life of the role and regenerated on replacement — which is what makes a deleted-and-recreated role fail policies that pinned the old ID. | AROA1EXAMPLEID234567 |
Required permissions
For the principal running the stack operationcreate
- iam:CreateRole
- iam:PutRolePolicy
- iam:AttachRolePolicy
- iam:TagRole
- iam:GetRole
read
- iam:GetRole
- iam:ListRolePolicies
- iam:GetRolePolicy
- iam:ListAttachedRolePolicies
- iam:ListRoleTags
update
- iam:UpdateRole
- iam:UpdateAssumeRolePolicy
- iam:PutRolePolicy
- iam:DeleteRolePolicy
- iam:AttachRolePolicy
- iam:DetachRolePolicy
- iam:TagRole
- iam:UntagRole
- iam:PutRolePermissionsBoundary
delete
- iam:DeleteRole
- iam:DeleteRolePolicy
- iam:DetachRolePolicy
list
- iam:ListRoles
{
"Statement": [
{
"Action": [
"iam:AttachRolePolicy",
"iam:CreateRole",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:GetRole",
"iam:GetRolePolicy",
"iam:ListAttachedRolePolicies",
"iam:ListRolePolicies",
"iam:ListRoles",
"iam:ListRoleTags",
"iam:PutRolePermissionsBoundary",
"iam:PutRolePolicy",
"iam:TagRole",
"iam:UntagRole",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole"
],
"Effect": "Allow",
"Resource": "*",
"Sid": "ManageResource"
}
],
"Version": "2012-10-17"
}Statement:
- Action:
- iam:AttachRolePolicy
- iam:CreateRole
- iam:DeleteRole
- iam:DeleteRolePolicy
- iam:DetachRolePolicy
- iam:GetRole
- iam:GetRolePolicy
- iam:ListAttachedRolePolicies
- iam:ListRolePolicies
- iam:ListRoles
- iam:ListRoleTags
- iam:PutRolePermissionsBoundary
- iam:PutRolePolicy
- iam:TagRole
- iam:UntagRole
- iam:UpdateAssumeRolePolicy
- iam:UpdateRole
Effect: Allow
Resource: '*'
Sid: ManageResource
Version: '2012-10-17'