custom-resources: multiple AwsCustomResource constructs ignores `role` property when set in one of them
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the bug
When we create multiple AwsCustomResource resources and provided `role` to only one of them, the role is ignored and we get a permission error on deployment. (see the code to reproduce)
### Regression Issue
- [ ] Select this option if this issue appears to be a regression.
### Last Known Working CDK Version
_No response_
### Expected Behavior
Deployment successes.
### Current Behavior
Deployment fails with a permission error like below:
> Received response status [FAILED] from custom resource. Message returned: User: arn:aws:sts::REDACTED:assumed-role/CrDupRoleReproStack-AWS679f53fac002430cb0da5b7982bd-219ls3LkG2Ki/CrDupRoleReproStack-AWS679f53fac002430cb0da5b7982b-CnUgdL71XPPf is not authorized to perform: sqs:sendmessage on resource: arn:aws:sqs:ap-northeast-1:REDACTED:CrDupRoleReproStack-Queue4A7E3555-ubUVQOF1lini because no identity-based policy allows the sqs:sendmessage action
### Reproduction Steps
Deploy the below stack:
```ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as cr from 'aws-cdk-lib/custom-resources';
import { PolicyStatement, Role } from 'aws-cdk-lib/aws-iam';
import { Queue } from 'aws-cdk-lib/aws-sqs';
export class CrDupRoleReproStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const queue = new Queue(this, 'Queue');
const role = new Role(this, 'Role', {
assumedBy: new cdk.aws_iam.ServicePrincipal('lambda.amazonaws.com'),
});
new cr.AwsCustomResource(this, 'API1', {
onCreate: {
service: 's3',
action: 'listBuckets',
parameters: {
MaxBuckets: 1,
},
physicalResourceId: cr.PhysicalResourceId.of('...'),
},
policy: cr.AwsCustomResourcePolicy.fromStatements([
new PolicyStatement({
actions: ['s3:ListAllMyBuckets'],
resources: ['*'],
}),
]),
// If we set role to all of the custom resources, it successfully deploys.
// role,
});
new cr.AwsCustomResource(this, 'API2', {
onCreate: {
service: 'sqs',
action: 'sendMessage',
parameters: {
QueueUrl: queue.queueUrl,
MessageBody: 'hello',
},
physicalResourceId: cr.PhysicalResourceId.of('...'),
},
role,
});
queue.grantSendMessages(role);
}
}
```
### Possible Solution
The root cause of this is we use a shared SingletonFunction for all the AwsCustomResource, and we can set only one IAM role for a Lambda function. So the possible solution can be:
1. create multiple SingletonFunction based on which role to use
2. Just throw an error when it got a `role` property which becomes invalid
### Additional Information/Context
It would be great if we could write something like this to avoid creating a new role:
```ts
const api2 = new cr.AwsCustomResource(this, 'API2', {
onCreate: {
service: 'sqs',
action: 'sendMessage',
parameters: {
QueueUrl: queue.queueUrl,
MessageBody: 'hello',
},
physicalResourceId: cr.PhysicalResourceId.of('...'),
},
});
queue.grantSendMessages(api2);
```
However, we cannot do this because we have to set at least one of `policy` or `role` property.
### CDK CLI Version
2.173.1
### Framework Version
2.173.1
### Node.js Version
22
### OS
macOS
### Language
TypeScript
### Language Version
_No response_
### Other information
_No response_
Contributor guide
Research direction
Start at the AwsCustomResource implementation and the shared SingletonFunction handling described in the issue, then deploy the provided TypeScript reproduction with two resources using different role settings. Check how roles are selected and covered by tests; done means the explicit role is honored without breaking shared-resource behavior, or invalid combinations are clearly rejected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, typescript
- Domain
- cloud, infrastructure
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100