diff --git a/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts b/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts index 260897073ea20..7eb0bce5f4765 100644 --- a/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts +++ b/packages/aws-cdk-lib/custom-resources/lib/provider-framework/provider.ts @@ -249,6 +249,20 @@ export class Provider extends Construct implements ICustomResourceProvider { }; } + private addPermissions(frameworkLambda: lambda.Function, userDefinedHandlerLambda: lambda.IFunction) { + userDefinedHandlerLambda.grantInvoke(frameworkLambda); + + /* + lambda:GetFunction is needed as the framework Lambda use it to poll the state of User Defined + Handler until it is ACTIVE state + */ + frameworkLambda.addToRolePolicy(new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: ['lambda:GetFunction'], + resources: [userDefinedHandlerLambda.functionArn], + })); + } + private createFunction(entrypoint: string, name?: string) { const fn = new lambda.Function(this, `framework-${entrypoint}`, { code: lambda.Code.fromAsset(RUNTIME_HANDLER_PATH, { @@ -271,11 +285,11 @@ export class Provider extends Construct implements ICustomResourceProvider { }); fn.addEnvironment(consts.USER_ON_EVENT_FUNCTION_ARN_ENV, this.onEventHandler.functionArn); - this.onEventHandler.grantInvoke(fn); + this.addPermissions(fn, this.onEventHandler); if (this.isCompleteHandler) { fn.addEnvironment(consts.USER_IS_COMPLETE_FUNCTION_ARN_ENV, this.isCompleteHandler.functionArn); - this.isCompleteHandler.grantInvoke(fn); + this.addPermissions(fn, this.isCompleteHandler); } return fn; diff --git a/packages/aws-cdk-lib/custom-resources/test/provider-framework/provider.test.ts b/packages/aws-cdk-lib/custom-resources/test/provider-framework/provider.test.ts index e7a6283d5a436..d056cbbd5f397 100644 --- a/packages/aws-cdk-lib/custom-resources/test/provider-framework/provider.test.ts +++ b/packages/aws-cdk-lib/custom-resources/test/provider-framework/provider.test.ts @@ -474,7 +474,8 @@ describe('role', () => { }); // THEN - Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Lambda::Function', { Role: { 'Fn::GetAtt': [ 'MyRoleF48FFE04', @@ -482,6 +483,49 @@ describe('role', () => { ], }, }); + template.hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'lambda:InvokeFunction', + Effect: 'Allow', + Resource: [ + { + 'Fn::GetAtt': [ + 'MyHandler6B74D312', + 'Arn', + ], + }, + { + 'Fn::Join': [ + '', + [ + { + 'Fn::GetAtt': [ + 'MyHandler6B74D312', + 'Arn', + ], + }, + ':*', + ], + ], + }, + ], + }, + { + Action: 'lambda:GetFunction', + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'MyHandler6B74D312', + 'Arn', + ], + }, + }, + ], + Version: '2012-10-17', + }, + }); }); it('uses default role otherwise', () => { @@ -498,7 +542,8 @@ describe('role', () => { }); // THEN - Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Lambda::Function', { Role: { 'Fn::GetAtt': [ 'MyProviderframeworkonEventServiceRole8761E48D', @@ -506,6 +551,49 @@ describe('role', () => { ], }, }); + template.hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'lambda:InvokeFunction', + Effect: 'Allow', + Resource: [ + { + 'Fn::GetAtt': [ + 'MyHandler6B74D312', + 'Arn', + ], + }, + { + 'Fn::Join': [ + '', + [ + { + 'Fn::GetAtt': [ + 'MyHandler6B74D312', + 'Arn', + ], + }, + ':*', + ], + ], + }, + ], + }, + { + Action: 'lambda:GetFunction', + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': [ + 'MyHandler6B74D312', + 'Arn', + ], + }, + }, + ], + Version: '2012-10-17', + }, + }); }); });