diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/cdk.out index 2313ab5436501..1f0068d32659a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"34.0.0"} \ No newline at end of file +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/integ.json index 4c852f34a8d7b..cdd86e13c9071 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "testCases": { "integ.permissions": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/lambda-permissions.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/lambda-permissions.assets.json index 0fd8578c0072d..abdbf49cb5e70 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/lambda-permissions.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/lambda-permissions.assets.json @@ -1,7 +1,7 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { - "4a7337dac9cfd962c6206c10044011cbcfe4c604459c5eada3df438eb578f341": { + "1a743b710c7ecd428918f2c36c87ccee4561ad5e3b7ea92c07269338e7baaddf": { "source": { "path": "lambda-permissions.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "4a7337dac9cfd962c6206c10044011cbcfe4c604459c5eada3df438eb578f341.json", + "objectKey": "1a743b710c7ecd428918f2c36c87ccee4561ad5e3b7ea92c07269338e7baaddf.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/lambda-permissions.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/lambda-permissions.template.json index 7ac3e75fde374..2cee179bb72dd 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/lambda-permissions.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/lambda-permissions.template.json @@ -117,6 +117,20 @@ "Principal": "apigateway.amazonaws.com" } }, + "MyLambdaInvokeZQDUzUWqDuiGYFrGB6ik9uIPJSgF8GSLIg6NTO8jHPIBE1EB3A4": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + "Principal": "*", + "PrincipalOrgID": "o-xxxxxxxxxx2" + } + }, "MyRoleF48FFE04": { "Type": "AWS::IAM::Role", "Properties": { @@ -148,6 +162,52 @@ "Arn" ] } + }, + { + "Action": "lambda:InvokeFunction", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + ":", + { + "Fn::GetAtt": [ + "v192FF9954", + "Version" + ] + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + ":$LATEST" + ] + ] + } + ] } ], "Version": "2012-10-17" @@ -159,6 +219,14 @@ } ] } + }, + "v192FF9954": { + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MyLambdaCCE802FB" + } + } } }, "Parameters": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/manifest.json index da8aab0b10e64..84a86c8091c44 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "artifacts": { "lambda-permissions.assets": { "type": "cdk:asset-manifest", @@ -18,7 +18,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/4a7337dac9cfd962c6206c10044011cbcfe4c604459c5eada3df438eb578f341.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/1a743b710c7ecd428918f2c36c87ccee4561ad5e3b7ea92c07269338e7baaddf.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -76,6 +76,12 @@ "data": "MyLambdaInvokeFcyXBRX02EWa52GlFECQiCzDt0fdRUDi4mo4foC5aU41318F58" } ], + "/lambda-permissions/MyLambda/InvokeZQDUzUWqDuiGYFrGB6ik9uIPJSgF8GSLIg6NTO8jHPI=": [ + { + "type": "aws:cdk:logicalId", + "data": "MyLambdaInvokeZQDUzUWqDuiGYFrGB6ik9uIPJSgF8GSLIg6NTO8jHPIBE1EB3A4" + } + ], "/lambda-permissions/MyRole/Resource": [ { "type": "aws:cdk:logicalId", @@ -88,25 +94,22 @@ "data": "MyRoleDefaultPolicyA36BE1DD" } ], - "/lambda-permissions/BootstrapVersion": [ + "/lambda-permissions/v1/Resource": [ { "type": "aws:cdk:logicalId", - "data": "BootstrapVersion" + "data": "v192FF9954" } ], - "/lambda-permissions/CheckBootstrapVersion": [ + "/lambda-permissions/BootstrapVersion": [ { "type": "aws:cdk:logicalId", - "data": "CheckBootstrapVersion" + "data": "BootstrapVersion" } ], - "MyLambdaInvokemcfVL7pJA0SB0Bm8yGhELN3cZ1c8fYqVoNxjNP4pYCE95D85164": [ + "/lambda-permissions/CheckBootstrapVersion": [ { "type": "aws:cdk:logicalId", - "data": "MyLambdaInvokemcfVL7pJA0SB0Bm8yGhELN3cZ1c8fYqVoNxjNP4pYCE95D85164", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] + "data": "CheckBootstrapVersion" } ] }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/tree.json index 2fdf6450ac79c..7687019e6dfa4 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.js.snapshot/tree.json @@ -20,8 +20,8 @@ "id": "ImportServiceRole", "path": "lambda-permissions/MyLambda/ServiceRole/ImportServiceRole", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" } }, "Resource": { @@ -59,14 +59,14 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" } }, "Resource": { @@ -89,8 +89,8 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" } }, "Invokehl--ab6+Vr41INt1IUX--IhhCesB4gzNedP5IURKNgciw=": { @@ -111,8 +111,8 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_lambda.CfnPermission", + "version": "0.0.0" } }, "Invoke138AF9IJcZORjZ--NKCKShZMMuVQwCnUkbFqMoQf5of0=": { @@ -133,8 +133,8 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_lambda.CfnPermission", + "version": "0.0.0" } }, "FunctionUrl": { @@ -157,14 +157,14 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_lambda.CfnUrl", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_lambda.FunctionUrl", + "version": "0.0.0" } }, "InvokeSz2P2C4jO--iX4AmIs1ANCq2qfq8PhgVeKtRAVyAkFmM=": { @@ -185,8 +185,8 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_lambda.CfnPermission", + "version": "0.0.0" } }, "InvokeFcyXBRX02EWa52GlF+ECQiCzDt0fdRUDi4mo4foC5aU=": { @@ -206,14 +206,44 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_lambda.CfnPermission", + "version": "0.0.0" + } + }, + "$LATEST": { + "id": "$LATEST", + "path": "lambda-permissions/MyLambda/$LATEST", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.FunctionBase", + "version": "0.0.0" + } + }, + "InvokeZQDUzUWqDuiGYFrGB6ik9uIPJSgF8GSLIg6NTO8jHPI=": { + "id": "InvokeZQDUzUWqDuiGYFrGB6ik9uIPJSgF8GSLIg6NTO8jHPI=", + "path": "lambda-permissions/MyLambda/InvokeZQDUzUWqDuiGYFrGB6ik9uIPJSgF8GSLIg6NTO8jHPI=", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Permission", + "aws:cdk:cloudformation:props": { + "action": "lambda:InvokeFunction", + "functionName": { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + "principal": "*", + "principalOrgId": "o-xxxxxxxxxx2" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnPermission", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" } }, "MyRole": { @@ -224,8 +254,8 @@ "id": "ImportMyRole", "path": "lambda-permissions/MyRole/ImportMyRole", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" } }, "Resource": { @@ -249,8 +279,8 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" } }, "DefaultPolicy": { @@ -274,6 +304,52 @@ "Arn" ] } + }, + { + "Action": "lambda:InvokeFunction", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + ":", + { + "Fn::GetAtt": [ + "v192FF9954", + "Version" + ] + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + ":$LATEST" + ] + ] + } + ] } ], "Version": "2012-10-17" @@ -287,42 +363,68 @@ } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "v1": { + "id": "v1", + "path": "lambda-permissions/v1", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-permissions/v1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Version", + "aws:cdk:cloudformation:props": { + "functionName": { + "Ref": "MyLambdaCCE802FB" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_lambda.CfnVersion", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.aws_lambda.Version", + "version": "0.0.0" } }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "lambda-permissions/BootstrapVersion", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" } }, "CheckBootstrapVersion": { "id": "CheckBootstrapVersion", "path": "lambda-permissions/CheckBootstrapVersion", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" } }, "Tree": { @@ -330,13 +432,13 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.2.70" + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.ts index ce56cd25c0f8a..c987a5f12d07e 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.permissions.ts @@ -27,3 +27,13 @@ fn.grantInvokeCompositePrincipal(new iam.CompositePrincipal( new iam.OrganizationPrincipal('o-mmmmmmmmmm'), new iam.ServicePrincipal('apigateway.amazonaws.com'), )); + +fn.grantInvokeLatestVersion(role); + +fn.grantInvokeLatestVersion(new iam.OrganizationPrincipal('o-xxxxxxxxxx2')); + +const version1 = new lambda.Version(stack, 'v1', { + lambda: fn, +}); + +fn.grantInvokeVersion(role, version1); \ No newline at end of file diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts index 0dcf4c581598c..86861e8a1ca10 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/experimental/edge-function.ts @@ -119,6 +119,12 @@ export class EdgeFunction extends Resource implements lambda.IVersion { public grantInvoke(identity: iam.IGrantable): iam.Grant { return this.lambda.grantInvoke(identity); } + public grantInvokeLatestVersion(identity: iam.IGrantable): iam.Grant { + return this.lambda.grantInvokeLatestVersion(identity); + } + public grantInvokeVersion(identity: iam.IGrantable, version: lambda.IVersion): iam.Grant { + return this.lambda.grantInvokeVersion(identity, version); + } public grantInvokeUrl(identity: iam.IGrantable): iam.Grant { return this.lambda.grantInvokeUrl(identity); } diff --git a/packages/aws-cdk-lib/aws-lambda/README.md b/packages/aws-cdk-lib/aws-lambda/README.md index 6cb5a4244dede..83384b195e8c5 100644 --- a/packages/aws-cdk-lib/aws-lambda/README.md +++ b/packages/aws-cdk-lib/aws-lambda/README.md @@ -205,6 +205,27 @@ You can also restrict permissions given to AWS services by providing a source account or ARN (representing the account and identifier of the resource that accesses the function or layer). +**Important**: +> By default `fn.grantInvoke()` grants permission to the principal to invoke any version of the function, including all past ones. If you only want the principal to be granted permission to invoke the latest version or the unqualified Lambda ARN, use `grantInvokeLatestVersion(grantee)`. + +```ts +declare const fn: lambda.Function; +const principal = new iam.ServicePrincipal('my-service'); +// Grant invoke only to latest version and unqualified lambda arn +fn.grantInvokeLatestVersion(principal); + +``` + +If you want to grant access for invoking a specific version of Lambda function, you can use `fn.grantInvokeVersion(grantee, version)` + +```ts +declare const fn: lambda.Function; +const principal = new iam.ServicePrincipal('my-service'); +declare const version: lambda.IVersion; +// Grant invoke only to the specific version +fn.grantInvokeVersion(principal, version); +``` + For more information, see [Granting function access to AWS services](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html#permissions-resource-serviceinvoke) in the AWS Lambda Developer Guide. diff --git a/packages/aws-cdk-lib/aws-lambda/lib/function-base.ts b/packages/aws-cdk-lib/aws-lambda/lib/function-base.ts index 22d20a8202fbf..c7d0bf5b4c543 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/function-base.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/function-base.ts @@ -97,6 +97,17 @@ export interface IFunction extends IResource, ec2.IConnectable, iam.IGrantable { */ grantInvoke(identity: iam.IGrantable): iam.Grant; + /** + * Grant the given identity permissions to invoke the $LATEST version or + * unqualified version of this Lambda + */ + grantInvokeLatestVersion(identity: iam.IGrantable): iam.Grant; + + /** + * Grant the given identity permissions to invoke the given version of this Lambda + */ + grantInvokeVersion(identity: iam.IGrantable, version: IVersion): iam.Grant; + /** * Grant the given identity permissions to invoke this Lambda Function URL */ @@ -439,6 +450,40 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC return grant; } + /** + * Grant the given identity permissions to invoke the $LATEST version or + * unqualified version of this Lambda + */ + public grantInvokeLatestVersion(grantee: iam.IGrantable): iam.Grant { + return this.grantInvokeVersion(grantee, this.latestVersion); + } + + /** + * Grant the given identity permissions to invoke the given version of this Lambda + */ + public grantInvokeVersion(grantee: iam.IGrantable, version: IVersion): iam.Grant { + const hash = createHash('sha256') + .update(JSON.stringify({ + principal: grantee.grantPrincipal.toString(), + conditions: grantee.grantPrincipal.policyFragment.conditions, + version: version.version, + }), 'utf8') + .digest('base64'); + const identifier = `Invoke${hash}`; + + // Memoize the result so subsequent grantInvoke() calls are idempotent + let grant = this._invocationGrants[identifier]; + if (!grant) { + let resouceArns = [`${this.functionArn}:${version.version}`]; + if (version == this.latestVersion) { + resouceArns.push(this.functionArn); + } + grant = this.grant(grantee, identifier, 'lambda:InvokeFunction', resouceArns); + this._invocationGrants[identifier] = grant; + } + return grant; + } + /** * Grant the given identity permissions to invoke this Lambda Function URL */ diff --git a/packages/aws-cdk-lib/aws-lambda/test/function.test.ts b/packages/aws-cdk-lib/aws-lambda/test/function.test.ts index b656f02af036d..f3ad8836102e5 100644 --- a/packages/aws-cdk-lib/aws-lambda/test/function.test.ts +++ b/packages/aws-cdk-lib/aws-lambda/test/function.test.ts @@ -1309,6 +1309,72 @@ describe('function', () => { }); }); + test('adds grantInvokeLatestVersion ', () => { + // GIVEN + const stack = new cdk.Stack(); + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.AccountPrincipal('1234'), + }); + const fn = new lambda.Function(stack, 'Function', { + code: lambda.Code.fromInline('xxx'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_LATEST, + }); + + // WHEN + fn.grantInvokeLatestVersion(role); + + // THEN function should have allow on both unqualified arn and arn:$LATEST + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: 'lambda:InvokeFunction', + Effect: 'Allow', + Resource: [ + { 'Fn::Join': ['', [{ 'Fn::GetAtt': ['Function76856677', 'Arn'] }, ':$LATEST']] }, + { 'Fn::GetAtt': ['Function76856677', 'Arn'] }, + ], + }, + ], + }, + }); + }); + + test('adds grantInvokeVersion ', () => { + // GIVEN + const stack = new cdk.Stack(); + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.AccountPrincipal('1234'), + }); + const fn = new lambda.Function(stack, 'Function', { + code: lambda.Code.fromInline('xxx'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_LATEST, + }); + + const lv2 = new lambda.Version(stack, 'v2', { + lambda: fn, + }); + // WHEN + fn.grantInvokeVersion(role, lv2); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: 'lambda:InvokeFunction', + Effect: 'Allow', + Resource: { 'Fn::Join': ['', [{ 'Fn::GetAtt': ['Function76856677', 'Arn'] }, ':', { 'Fn::GetAtt': ['v248F3DDCC', 'Version'] }]] }, + }, + ], + }, + }); + }); + test('with a service principal', () => { // GIVEN const stack = new cdk.Stack();