Skip to content

Commit 7f2fccc

Browse files
authored
feat(core): addToRolePolicy() for custom resource provider (#20449)
Since we only get a singleton we need a `addToRolePolicy()` method to add statements when the singleton is used for multiple custom resources. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 4a87d39 commit 7f2fccc

File tree

4 files changed

+110
-10
lines changed

4 files changed

+110
-10
lines changed

packages/@aws-cdk/core/README.md

+17
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,23 @@ const roleArn = provider.roleArn;
626626

627627
This role ARN can then be used in resource-based IAM policies.
628628

629+
To add IAM policy statements to this role, use `addToRolePolicy()`:
630+
631+
```ts
632+
const provider = CustomResourceProvider.getOrCreateProvider(this, 'Custom::MyCustomResourceType', {
633+
codeDirectory: `${__dirname}/my-handler`,
634+
runtime: CustomResourceProviderRuntime.NODEJS_12_X,
635+
});
636+
provider.addToRolePolicy({
637+
Effect: 'Allow',
638+
Action: 's3:GetObject',
639+
Resource: '*',
640+
})
641+
```
642+
643+
Note that `addToRolePolicy()` uses direct IAM JSON policy blobs, *not* a
644+
`iam.PolicyStatement` object like you will see in the rest of the CDK.
645+
629646
#### The Custom Resource Provider Framework
630647

631648
The [`@aws-cdk/custom-resources`] module includes an advanced framework for

packages/@aws-cdk/core/lib/custom-resource-provider/custom-resource-provider.ts

+49-10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { AssetStaging } from '../asset-staging';
66
import { FileAssetPackaging } from '../assets';
77
import { CfnResource } from '../cfn-resource';
88
import { Duration } from '../duration';
9+
import { Lazy } from '../lazy';
910
import { Size } from '../size';
1011
import { Stack } from '../stack';
1112
import { Token } from '../token';
@@ -187,6 +188,8 @@ export class CustomResourceProvider extends Construct {
187188
*/
188189
public readonly roleArn: string;
189190

191+
private policyStatements?: any[];
192+
190193
protected constructor(scope: Construct, id: string, props: CustomResourceProviderProps) {
191194
super(scope, id);
192195

@@ -212,15 +215,11 @@ export class CustomResourceProvider extends Construct {
212215
packaging: FileAssetPackaging.ZIP_DIRECTORY,
213216
});
214217

215-
const policies = !props.policyStatements ? undefined : [
216-
{
217-
PolicyName: 'Inline',
218-
PolicyDocument: {
219-
Version: '2012-10-17',
220-
Statement: props.policyStatements,
221-
},
222-
},
223-
];
218+
if (props.policyStatements) {
219+
for (const statement of props.policyStatements) {
220+
this.addToRolePolicy(statement);
221+
}
222+
}
224223

225224
const role = new CfnResource(this, 'Role', {
226225
type: 'AWS::IAM::Role',
@@ -232,7 +231,7 @@ export class CustomResourceProvider extends Construct {
232231
ManagedPolicyArns: [
233232
{ 'Fn::Sub': 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' },
234233
],
235-
Policies: policies,
234+
Policies: Lazy.any({ produce: () => this.renderPolicies() }),
236235
},
237236
});
238237
this.roleArn = Token.asString(role.getAtt('Arn'));
@@ -267,6 +266,46 @@ export class CustomResourceProvider extends Construct {
267266
this.serviceToken = Token.asString(handler.getAtt('Arn'));
268267
}
269268

269+
/**
270+
* Add an IAM policy statement to the inline policy of the
271+
* provider's lambda function's role.
272+
*
273+
* **Please note**: this is a direct IAM JSON policy blob, *not* a `iam.PolicyStatement`
274+
* object like you will see in the rest of the CDK.
275+
*
276+
*
277+
* @example
278+
* declare const myProvider: CustomResourceProvider;
279+
*
280+
* myProvider.addToRolePolicy({
281+
* Effect: 'Allow',
282+
* Action: 's3:GetObject',
283+
* Resources: '*',
284+
* });
285+
*/
286+
public addToRolePolicy(statement: any): void {
287+
if (!this.policyStatements) {
288+
this.policyStatements = [];
289+
}
290+
this.policyStatements.push(statement);
291+
}
292+
293+
private renderPolicies() {
294+
if (!this.policyStatements) {
295+
return undefined;
296+
}
297+
298+
const policies = [{
299+
PolicyName: 'Inline',
300+
PolicyDocument: {
301+
Version: '2012-10-17',
302+
Statement: this.policyStatements,
303+
},
304+
}];
305+
306+
return policies;
307+
}
308+
270309
private renderEnvironmentVariables(env?: { [key: string]: string }) {
271310
if (!env || Object.keys(env).length === 0) {
272311
return undefined;

packages/@aws-cdk/core/test/custom-resource-provider/custom-resource-provider.test.ts

+27
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,33 @@ describe('custom resource provider', () => {
210210

211211
});
212212

213+
test('addToRolePolicy() can be used to add statements to the inline policy', () => {
214+
// GIVEN
215+
const stack = new Stack();
216+
217+
// WHEN
218+
const provider = CustomResourceProvider.getOrCreateProvider(stack, 'Custom:MyResourceType', {
219+
codeDirectory: TEST_HANDLER,
220+
runtime: CustomResourceProviderRuntime.NODEJS_12_X,
221+
policyStatements: [
222+
{ statement1: 123 },
223+
{ statement2: { foo: 111 } },
224+
],
225+
});
226+
provider.addToRolePolicy({ statement3: 456 });
227+
228+
// THEN
229+
const template = toCloudFormation(stack);
230+
const role = template.Resources.CustomMyResourceTypeCustomResourceProviderRoleBD5E655F;
231+
expect(role.Properties.Policies).toEqual([{
232+
PolicyName: 'Inline',
233+
PolicyDocument: {
234+
Version: '2012-10-17',
235+
Statement: [{ statement1: 123 }, { statement2: { foo: 111 } }, { statement3: 456 }],
236+
},
237+
}]);
238+
});
239+
213240
test('memorySize, timeout and description', () => {
214241
// GIVEN
215242
const stack = new Stack();

packages/aws-cdk-lib/README.md

+17
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,23 @@ const roleArn = provider.roleArn;
657657

658658
This role ARN can then be used in resource-based IAM policies.
659659

660+
To add IAM policy statements to this role, use `addToRolePolicy()`:
661+
662+
```ts
663+
const provider = CustomResourceProvider.getOrCreateProvider(this, 'Custom::MyCustomResourceType', {
664+
codeDirectory: `${__dirname}/my-handler`,
665+
runtime: CustomResourceProviderRuntime.NODEJS_12_X,
666+
});
667+
provider.addToRolePolicy({
668+
Effect: 'Allow',
669+
Action: 's3:GetObject',
670+
Resource: '*',
671+
})
672+
```
673+
674+
Note that `addToRolePolicy()` uses direct IAM JSON policy blobs, *not* a
675+
`iam.PolicyStatement` object like you will see in the rest of the CDK.
676+
660677
#### The Custom Resource Provider Framework
661678

662679
The [`@aws-cdk/custom-resources`] module includes an advanced framework for

0 commit comments

Comments
 (0)