diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index 2b94b810d0131..47daad06b72ef 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.152.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.151.1-alpha.0...v2.152.0-alpha.0) (2024-08-14) + ## [2.151.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.151.0-alpha.0...v2.151.1-alpha.0) (2024-08-14) ## [2.151.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.150.0-alpha.0...v2.151.0-alpha.0) (2024-08-01) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index 01e2f4e4d8e71..072c1c8adad4b 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.152.0](https://github.com/aws/aws-cdk/compare/v2.151.1...v2.152.0) (2024-08-14) + + +### Features + +* **lambda:** support filter criteria encryption ([6aa72a2](https://github.com/aws/aws-cdk/commit/6aa72a215859ab96e9fd8b4ccee0d40bda753200)) + ## [2.151.1](https://github.com/aws/aws-cdk/compare/v2.151.0...v2.151.1) (2024-08-14) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/DynamoDBFilterCriteriaDefaultTestDeployAssert448231D5.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/DynamoDBFilterCriteriaDefaultTestDeployAssert448231D5.assets.json index a9f8c01bbffc7..042d7b3416bbf 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/DynamoDBFilterCriteriaDefaultTestDeployAssert448231D5.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/DynamoDBFilterCriteriaDefaultTestDeployAssert448231D5.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/cdk.out index 2313ab5436501..1f0068d32659a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.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-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/integ.json index 9e170621e743b..f4a60fafc3e3e 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "testCases": { "DynamoDBFilterCriteria/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-dynamodb.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-dynamodb.assets.json index f67fb7e688555..60c69bab59474 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-dynamodb.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-dynamodb.assets.json @@ -1,7 +1,7 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { - "12820430413ecb3acc272c29391ccb7d4852423d6630831ad3a1816e5ba6a66b": { + "635e0224ecca17d5512ed1bef8cfa79b63a3d53803e96c4382c47fe9408eb0c7": { "source": { "path": "lambda-event-source-filter-criteria-dynamodb.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "12820430413ecb3acc272c29391ccb7d4852423d6630831ad3a1816e5ba6a66b.json", + "objectKey": "635e0224ecca17d5512ed1bef8cfa79b63a3d53803e96c4382c47fe9408eb0c7.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-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-dynamodb.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-dynamodb.template.json index 51ecc3d8a5a7f..08858d9a49aef 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-dynamodb.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-dynamodb.template.json @@ -134,6 +134,166 @@ }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" + }, + "fctestkeyname524AF060": { + "Type": "AWS::KMS::Key", + "Properties": { + "Description": "KMS key for test fc encryption", + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": "kms:Decrypt", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PendingWindowInDays": 7 + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "F5ServiceRole2E897519": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "F5ServiceRoleDefaultPolicyF3745DE6": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "dynamodb:ListStreams", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "dynamodb:DescribeStream", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "TD925BC7E", + "StreamArn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "F5ServiceRoleDefaultPolicyF3745DE6", + "Roles": [ + { + "Ref": "F5ServiceRole2E897519" + } + ] + } + }, + "F5B560B5F9": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async function handler(event) {\n console.log('event:', JSON.stringify(event, undefined, 2));\n return { event };\n}" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "F5ServiceRole2E897519", + "Arn" + ] + }, + "Runtime": "nodejs18.x" + }, + "DependsOn": [ + "F5ServiceRoleDefaultPolicyF3745DE6", + "F5ServiceRole2E897519" + ] + }, + "F5DynamoDBEventSourcelambdaeventsourcefiltercriteriadynamodbT9CFE7D0688700B50": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 5, + "EventSourceArn": { + "Fn::GetAtt": [ + "TD925BC7E", + "StreamArn" + ] + }, + "FilterCriteria": { + "Filters": [ + { + "Pattern": "{\"eventName\":[\"INSERT\"],\"dynamodb\":{\"Keys\":{\"id\":{\"S\":[{\"exists\":true}]}}}}" + } + ] + }, + "FunctionName": { + "Ref": "F5B560B5F9" + }, + "KmsKeyArn": { + "Fn::GetAtt": [ + "fctestkeyname524AF060", + "Arn" + ] + }, + "StartingPosition": "LATEST" + } } }, "Parameters": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/manifest.json index 4bb3cd7d7c817..0b300d7ca1f56 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "artifacts": { "lambda-event-source-filter-criteria-dynamodb.assets": { "type": "cdk:asset-manifest", @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "lambda-event-source-filter-criteria-dynamodb.template.json", + "terminationProtection": false, "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}/12820430413ecb3acc272c29391ccb7d4852423d6630831ad3a1816e5ba6a66b.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/635e0224ecca17d5512ed1bef8cfa79b63a3d53803e96c4382c47fe9408eb0c7.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -63,6 +64,36 @@ "data": "TD925BC7E" } ], + "/lambda-event-source-filter-criteria-dynamodb/fc-test-key-name/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "fctestkeyname524AF060" + } + ], + "/lambda-event-source-filter-criteria-dynamodb/F5/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F5ServiceRole2E897519" + } + ], + "/lambda-event-source-filter-criteria-dynamodb/F5/ServiceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F5ServiceRoleDefaultPolicyF3745DE6" + } + ], + "/lambda-event-source-filter-criteria-dynamodb/F5/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F5B560B5F9" + } + ], + "/lambda-event-source-filter-criteria-dynamodb/F5/DynamoDBEventSource:lambdaeventsourcefiltercriteriadynamodbT9CFE7D06/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F5DynamoDBEventSourcelambdaeventsourcefiltercriteriadynamodbT9CFE7D0688700B50" + } + ], "/lambda-event-source-filter-criteria-dynamodb/BootstrapVersion": [ { "type": "aws:cdk:logicalId", @@ -91,6 +122,7 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "DynamoDBFilterCriteriaDefaultTestDeployAssert448231D5.template.json", + "terminationProtection": false, "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}", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/tree.json index 4674ff49a526f..757ace67fce4e 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.js.snapshot/tree.json @@ -183,7 +183,7 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_lambda.EventSourceMapping", + "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" } } @@ -243,6 +243,258 @@ "version": "0.0.0" } }, + "fc-test-key-name": { + "id": "fc-test-key-name", + "path": "lambda-event-source-filter-criteria-dynamodb/fc-test-key-name", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-criteria-dynamodb/fc-test-key-name/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Key", + "aws:cdk:cloudformation:props": { + "description": "KMS key for test fc encryption", + "keyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": "kms:Decrypt", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "pendingWindowInDays": 7 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.CfnKey", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.Key", + "version": "0.0.0" + } + }, + "F5": { + "id": "F5", + "path": "lambda-event-source-filter-criteria-dynamodb/F5", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "lambda-event-source-filter-criteria-dynamodb/F5/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "lambda-event-source-filter-criteria-dynamodb/F5/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-criteria-dynamodb/F5/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "lambda-event-source-filter-criteria-dynamodb/F5/ServiceRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-criteria-dynamodb/F5/ServiceRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "dynamodb:ListStreams", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "dynamodb:DescribeStream", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "TD925BC7E", + "StreamArn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "F5ServiceRoleDefaultPolicyF3745DE6", + "roles": [ + { + "Ref": "F5ServiceRole2E897519" + } + ] + } + }, + "constructInfo": { + "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" + } + }, + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-criteria-dynamodb/F5/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "zipFile": "exports.handler = async function handler(event) {\n console.log('event:', JSON.stringify(event, undefined, 2));\n return { event };\n}" + }, + "handler": "index.handler", + "role": { + "Fn::GetAtt": [ + "F5ServiceRole2E897519", + "Arn" + ] + }, + "runtime": "nodejs18.x" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + }, + "DynamoDBEventSource:lambdaeventsourcefiltercriteriadynamodbT9CFE7D06": { + "id": "DynamoDBEventSource:lambdaeventsourcefiltercriteriadynamodbT9CFE7D06", + "path": "lambda-event-source-filter-criteria-dynamodb/F5/DynamoDBEventSource:lambdaeventsourcefiltercriteriadynamodbT9CFE7D06", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-criteria-dynamodb/F5/DynamoDBEventSource:lambdaeventsourcefiltercriteriadynamodbT9CFE7D06/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::EventSourceMapping", + "aws:cdk:cloudformation:props": { + "batchSize": 5, + "eventSourceArn": { + "Fn::GetAtt": [ + "TD925BC7E", + "StreamArn" + ] + }, + "filterCriteria": { + "filters": [ + { + "pattern": "{\"eventName\":[\"INSERT\"],\"dynamodb\":{\"Keys\":{\"id\":{\"S\":[{\"exists\":true}]}}}}" + } + ] + }, + "functionName": { + "Ref": "F5B560B5F9" + }, + "kmsKeyArn": { + "Fn::GetAtt": [ + "fctestkeyname524AF060", + "Arn" + ] + }, + "startingPosition": "LATEST" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnEventSourceMapping", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "lambda-event-source-filter-criteria-dynamodb/BootstrapVersion", @@ -278,7 +530,7 @@ "path": "DynamoDBFilterCriteria/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } }, "DeployAssert": { @@ -324,7 +576,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.ts index 544290074e7f6..fcc180a696a81 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-filter-criteria.ts @@ -4,6 +4,7 @@ import * as cdk from 'aws-cdk-lib'; import * as integ from '@aws-cdk/integ-tests-alpha'; import { TestFunction } from './test-function'; import { DynamoEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; +import { Key } from 'aws-cdk-lib/aws-kms'; const app = new cdk.App(); @@ -36,6 +37,32 @@ fn.addEventSource(new DynamoEventSource(table, { ], })); +const myKey = new Key(stack, 'fc-test-key-name', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + pendingWindow: cdk.Duration.days(7), + description: 'KMS key for test fc encryption', +}); + +const fn2 = new TestFunction(stack, 'F5'); + +fn2.addEventSource(new DynamoEventSource(table, { + batchSize: 5, + startingPosition: lambda.StartingPosition.LATEST, + filters: [ + lambda.FilterCriteria.filter({ + eventName: lambda.FilterRule.isEqual('INSERT'), + dynamodb: { + Keys: { + id: { + S: lambda.FilterRule.exists(), + }, + }, + }, + }), + ], + filterEncryption: myKey, +})); + new integ.IntegTest(app, 'DynamoDBFilterCriteria', { testCases: [stack], }); diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/LambdaEventSourceKafkaSelfManagedTestDefaultTestDeployAssertAF78BD0F.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/LambdaEventSourceKafkaSelfManagedTestDefaultTestDeployAssertAF78BD0F.assets.json index 2f132e5215478..a01ffb4d5f4c8 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/LambdaEventSourceKafkaSelfManagedTestDefaultTestDeployAssertAF78BD0F.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/LambdaEventSourceKafkaSelfManagedTestDefaultTestDeployAssertAF78BD0F.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/cdk.out index 2313ab5436501..1f0068d32659a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.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-event-sources/test/integ.kafka-selfmanaged.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/integ.json index be64da9864e1b..eb53722c5afaf 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "testCases": { "LambdaEventSourceKafkaSelfManagedTest/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/lambda-event-source-kafka-self-managed.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/lambda-event-source-kafka-self-managed.assets.json index d67485e3551c0..cb4ec6e990114 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/lambda-event-source-kafka-self-managed.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/lambda-event-source-kafka-self-managed.assets.json @@ -1,7 +1,7 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { - "138a217a9e2d3bad4739ea506408a27aca8886a97c0fbacffcba80f39d2d26b0": { + "4bf07b5cad381e52a796b0a42748934cce430e155ffe31f0366eef200d40356f": { "source": { "path": "lambda-event-source-kafka-self-managed.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "138a217a9e2d3bad4739ea506408a27aca8886a97c0fbacffcba80f39d2d26b0.json", + "objectKey": "4bf07b5cad381e52a796b0a42748934cce430e155ffe31f0366eef200d40356f.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-event-sources/test/integ.kafka-selfmanaged.js.snapshot/lambda-event-source-kafka-self-managed.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/lambda-event-source-kafka-self-managed.template.json index 88cf96bcf5d69..dd921a80f1344 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/lambda-event-source-kafka-self-managed.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/lambda-event-source-kafka-self-managed.template.json @@ -143,6 +143,185 @@ }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" + }, + "fctestkeyname524AF060": { + "Type": "AWS::KMS::Key", + "Properties": { + "Description": "KMS key for test fc encryption", + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": "kms:Decrypt", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PendingWindowInDays": 7 + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "F2ServiceRole7F7C6006": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "F2ServiceRoleDefaultPolicy999D30A8": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": [ + { + "Ref": "S509448A1" + }, + { + "Ref": "SC0855C491" + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "F2ServiceRoleDefaultPolicy999D30A8", + "Roles": [ + { + "Ref": "F2ServiceRole7F7C6006" + } + ] + } + }, + "F23BAC7B9C": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async function handler(event) {\n console.log('event:', JSON.stringify(event, undefined, 2));\n return { event };\n}" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "F2ServiceRole7F7C6006", + "Arn" + ] + }, + "Runtime": "nodejs18.x" + }, + "DependsOn": [ + "F2ServiceRoleDefaultPolicy999D30A8", + "F2ServiceRole7F7C6006" + ] + }, + "F2KafkaEventSource838c4d5ff3c99c1a617120adfca83e5bmytesttopic20A678189": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 100, + "FilterCriteria": { + "Filters": [ + { + "Pattern": "{\"numericEquals\":[{\"numeric\":[\"=\",2]}]}" + } + ] + }, + "FunctionName": { + "Ref": "F23BAC7B9C" + }, + "KmsKeyArn": { + "Fn::GetAtt": [ + "fctestkeyname524AF060", + "Arn" + ] + }, + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "my-self-hosted-kafka-broker-1:9092", + "my-self-hosted-kafka-broker-2:9092", + "my-self-hosted-kafka-broker-3:9092" + ] + } + }, + "SelfManagedKafkaEventSourceConfig": { + "ConsumerGroupId": "myTestConsumerGroup2" + }, + "SourceAccessConfigurations": [ + { + "Type": "CLIENT_CERTIFICATE_TLS_AUTH", + "URI": { + "Ref": "SC0855C491" + } + }, + { + "Type": "SERVER_ROOT_CA_CERTIFICATE", + "URI": { + "Ref": "S509448A1" + } + } + ], + "StartingPosition": "TRIM_HORIZON", + "Topics": [ + "my-test-topic2" + ] + } } }, "Parameters": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/manifest.json index 87b62f798b18b..06655a65cd8cb 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "artifacts": { "lambda-event-source-kafka-self-managed.assets": { "type": "cdk:asset-manifest", @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "lambda-event-source-kafka-self-managed.template.json", + "terminationProtection": false, "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}/138a217a9e2d3bad4739ea506408a27aca8886a97c0fbacffcba80f39d2d26b0.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/4bf07b5cad381e52a796b0a42748934cce430e155ffe31f0366eef200d40356f.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -69,6 +70,36 @@ "data": "SC0855C491" } ], + "/lambda-event-source-kafka-self-managed/fc-test-key-name/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "fctestkeyname524AF060" + } + ], + "/lambda-event-source-kafka-self-managed/F2/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F2ServiceRole7F7C6006" + } + ], + "/lambda-event-source-kafka-self-managed/F2/ServiceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F2ServiceRoleDefaultPolicy999D30A8" + } + ], + "/lambda-event-source-kafka-self-managed/F2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F23BAC7B9C" + } + ], + "/lambda-event-source-kafka-self-managed/F2/KafkaEventSource:838c4d5ff3c99c1a617120adfca83e5b:my-test-topic2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F2KafkaEventSource838c4d5ff3c99c1a617120adfca83e5bmytesttopic20A678189" + } + ], "/lambda-event-source-kafka-self-managed/BootstrapVersion": [ { "type": "aws:cdk:logicalId", @@ -97,6 +128,7 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "LambdaEventSourceKafkaSelfManagedTestDefaultTestDeployAssertAF78BD0F.template.json", + "terminationProtection": false, "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}", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/tree.json index 9bdb89aea6878..54543d8610b3a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.js.snapshot/tree.json @@ -260,6 +260,277 @@ "version": "0.0.0" } }, + "fc-test-key-name": { + "id": "fc-test-key-name", + "path": "lambda-event-source-kafka-self-managed/fc-test-key-name", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-kafka-self-managed/fc-test-key-name/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Key", + "aws:cdk:cloudformation:props": { + "description": "KMS key for test fc encryption", + "keyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": "kms:Decrypt", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "pendingWindowInDays": 7 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.CfnKey", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.Key", + "version": "0.0.0" + } + }, + "F2": { + "id": "F2", + "path": "lambda-event-source-kafka-self-managed/F2", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "lambda-event-source-kafka-self-managed/F2/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "lambda-event-source-kafka-self-managed/F2/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "lambda-event-source-kafka-self-managed/F2/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "lambda-event-source-kafka-self-managed/F2/ServiceRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-kafka-self-managed/F2/ServiceRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": [ + { + "Ref": "S509448A1" + }, + { + "Ref": "SC0855C491" + } + ] + } + ], + "Version": "2012-10-17" + }, + "policyName": "F2ServiceRoleDefaultPolicy999D30A8", + "roles": [ + { + "Ref": "F2ServiceRole7F7C6006" + } + ] + } + }, + "constructInfo": { + "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" + } + }, + "Resource": { + "id": "Resource", + "path": "lambda-event-source-kafka-self-managed/F2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "zipFile": "exports.handler = async function handler(event) {\n console.log('event:', JSON.stringify(event, undefined, 2));\n return { event };\n}" + }, + "handler": "index.handler", + "role": { + "Fn::GetAtt": [ + "F2ServiceRole7F7C6006", + "Arn" + ] + }, + "runtime": "nodejs18.x" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + }, + "KafkaEventSource:838c4d5ff3c99c1a617120adfca83e5b:my-test-topic2": { + "id": "KafkaEventSource:838c4d5ff3c99c1a617120adfca83e5b:my-test-topic2", + "path": "lambda-event-source-kafka-self-managed/F2/KafkaEventSource:838c4d5ff3c99c1a617120adfca83e5b:my-test-topic2", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-kafka-self-managed/F2/KafkaEventSource:838c4d5ff3c99c1a617120adfca83e5b:my-test-topic2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::EventSourceMapping", + "aws:cdk:cloudformation:props": { + "batchSize": 100, + "filterCriteria": { + "filters": [ + { + "pattern": "{\"numericEquals\":[{\"numeric\":[\"=\",2]}]}" + } + ] + }, + "functionName": { + "Ref": "F23BAC7B9C" + }, + "kmsKeyArn": { + "Fn::GetAtt": [ + "fctestkeyname524AF060", + "Arn" + ] + }, + "selfManagedEventSource": { + "endpoints": { + "kafkaBootstrapServers": [ + "my-self-hosted-kafka-broker-1:9092", + "my-self-hosted-kafka-broker-2:9092", + "my-self-hosted-kafka-broker-3:9092" + ] + } + }, + "selfManagedKafkaEventSourceConfig": { + "consumerGroupId": "myTestConsumerGroup2" + }, + "sourceAccessConfigurations": [ + { + "type": "CLIENT_CERTIFICATE_TLS_AUTH", + "uri": { + "Ref": "SC0855C491" + } + }, + { + "type": "SERVER_ROOT_CA_CERTIFICATE", + "uri": { + "Ref": "S509448A1" + } + } + ], + "startingPosition": "TRIM_HORIZON", + "topics": [ + "my-test-topic2" + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnEventSourceMapping", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.EventSourceMapping", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "lambda-event-source-kafka-self-managed/BootstrapVersion", @@ -295,7 +566,7 @@ "path": "LambdaEventSourceKafkaSelfManagedTest/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } }, "DeployAssert": { @@ -341,7 +612,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.ts index 2ddbece1eee21..3af619c6f8bc2 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-selfmanaged.ts @@ -4,6 +4,7 @@ import * as cdk from 'aws-cdk-lib'; import * as integ from '@aws-cdk/integ-tests-alpha'; import { TestFunction } from './test-function'; import { AuthenticationMethod, SelfManagedKafkaEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; +import { Key } from 'aws-cdk-lib/aws-kms'; class KafkaSelfManagedEventSourceTest extends cdk.Stack { constructor(scope: cdk.App, id: string) { @@ -60,6 +61,32 @@ zp2mwJn2NYB7AZ7+imp0azDZb+8YG2aUCiyqb6PnnA== ], }), ); + + const myKey = new Key(this, 'fc-test-key-name', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + pendingWindow: cdk.Duration.days(7), + description: 'KMS key for test fc encryption', + }); + + const fn2 = new TestFunction(this, 'F2'); + rootCASecret.grantRead(fn2); + clientCertificatesSecret.grantRead(fn2); + + fn2.addEventSource(new SelfManagedKafkaEventSource({ + bootstrapServers, + topic: 'my-test-topic2', + consumerGroupId: 'myTestConsumerGroup2', + secret: clientCertificatesSecret, + authenticationMethod: AuthenticationMethod.CLIENT_CERTIFICATE_TLS_AUTH, + rootCACertificate: rootCASecret, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + filters: [ + lambda.FilterCriteria.filter({ + numericEquals: lambda.FilterRule.isEqual(2), + }), + ], + filterEncryption: myKey, + })); } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/SQSFilterCriteriaDefaultTestDeployAssert70A9A808.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/SQSFilterCriteriaDefaultTestDeployAssert70A9A808.assets.json index 29409e5062689..316f67625c262 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/SQSFilterCriteriaDefaultTestDeployAssert70A9A808.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/SQSFilterCriteriaDefaultTestDeployAssert70A9A808.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/cdk.out index 2313ab5436501..1f0068d32659a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.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-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/integ.json index cc01f8e83d5e0..4d9d3a1966196 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "testCases": { "SQSFilterCriteria/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-sqs.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-sqs.assets.json index 0a297a763dd0f..0491d75417637 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-sqs.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-sqs.assets.json @@ -1,7 +1,7 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { - "68d6842ea4469781d0a31e238982e6e384917b532b01a55bda819955e7d3beda": { + "b13ae7c9fc3e3d5e6921b00ac79b3ec76c7b8006a622b9b14fc849181dbce1e7": { "source": { "path": "lambda-event-source-filter-criteria-sqs.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "68d6842ea4469781d0a31e238982e6e384917b532b01a55bda819955e7d3beda.json", + "objectKey": "b13ae7c9fc3e3d5e6921b00ac79b3ec76c7b8006a622b9b14fc849181dbce1e7.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-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-sqs.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-sqs.template.json index f34c9cf70ac42..bbe7eda0090d2 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-sqs.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/lambda-event-source-filter-criteria-sqs.template.json @@ -109,6 +109,162 @@ "Type": "AWS::SQS::Queue", "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" + }, + "fctestkeyname524AF060": { + "Type": "AWS::KMS::Key", + "Properties": { + "Description": "KMS key for test fc encryption", + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": "kms:Decrypt", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PendingWindowInDays": 7 + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "F2ServiceRole7F7C6006": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "F2ServiceRoleDefaultPolicy999D30A8": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "sqs:ChangeMessageVisibility", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + "sqs:GetQueueUrl", + "sqs:ReceiveMessage" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "Q63C6E3AB", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "F2ServiceRoleDefaultPolicy999D30A8", + "Roles": [ + { + "Ref": "F2ServiceRole7F7C6006" + } + ] + } + }, + "F23BAC7B9C": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async function handler(event) {\n console.log('event:', JSON.stringify(event, undefined, 2));\n return { event };\n}" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "F2ServiceRole7F7C6006", + "Arn" + ] + }, + "Runtime": "nodejs18.x" + }, + "DependsOn": [ + "F2ServiceRoleDefaultPolicy999D30A8", + "F2ServiceRole7F7C6006" + ] + }, + "F2SqsEventSourcelambdaeventsourcefiltercriteriasqsQA0FC5C9369D735ED": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 5, + "EventSourceArn": { + "Fn::GetAtt": [ + "Q63C6E3AB", + "Arn" + ] + }, + "FilterCriteria": { + "Filters": [ + { + "Pattern": "{\"body\":{\"id\":[{\"exists\":true}]}}" + } + ] + }, + "FunctionName": { + "Ref": "F23BAC7B9C" + }, + "KmsKeyArn": { + "Fn::GetAtt": [ + "fctestkeyname524AF060", + "Arn" + ] + } + } } }, "Parameters": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/manifest.json index 80e8bb91a4194..ab4927fa81244 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "artifacts": { "lambda-event-source-filter-criteria-sqs.assets": { "type": "cdk:asset-manifest", @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "lambda-event-source-filter-criteria-sqs.template.json", + "terminationProtection": false, "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}/68d6842ea4469781d0a31e238982e6e384917b532b01a55bda819955e7d3beda.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/b13ae7c9fc3e3d5e6921b00ac79b3ec76c7b8006a622b9b14fc849181dbce1e7.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -63,6 +64,36 @@ "data": "Q63C6E3AB" } ], + "/lambda-event-source-filter-criteria-sqs/fc-test-key-name/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "fctestkeyname524AF060" + } + ], + "/lambda-event-source-filter-criteria-sqs/F2/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F2ServiceRole7F7C6006" + } + ], + "/lambda-event-source-filter-criteria-sqs/F2/ServiceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F2ServiceRoleDefaultPolicy999D30A8" + } + ], + "/lambda-event-source-filter-criteria-sqs/F2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F23BAC7B9C" + } + ], + "/lambda-event-source-filter-criteria-sqs/F2/SqsEventSource:lambdaeventsourcefiltercriteriasqsQA0FC5C93/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "F2SqsEventSourcelambdaeventsourcefiltercriteriasqsQA0FC5C9369D735ED" + } + ], "/lambda-event-source-filter-criteria-sqs/BootstrapVersion": [ { "type": "aws:cdk:logicalId", @@ -74,6 +105,15 @@ "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } + ], + "fctestkeynameAliasEF6099A0": [ + { + "type": "aws:cdk:logicalId", + "data": "fctestkeynameAliasEF6099A0", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } ] }, "displayName": "lambda-event-source-filter-criteria-sqs" @@ -91,6 +131,7 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "SQSFilterCriteriaDefaultTestDeployAssert70A9A808.template.json", + "terminationProtection": false, "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}", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/tree.json index b0b978a2f835c..df8b30717c630 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.js.snapshot/tree.json @@ -179,7 +179,7 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_lambda.EventSourceMapping", + "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" } } @@ -211,6 +211,254 @@ "version": "0.0.0" } }, + "fc-test-key-name": { + "id": "fc-test-key-name", + "path": "lambda-event-source-filter-criteria-sqs/fc-test-key-name", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-criteria-sqs/fc-test-key-name/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Key", + "aws:cdk:cloudformation:props": { + "description": "KMS key for test fc encryption", + "keyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": "kms:Decrypt", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "pendingWindowInDays": 7 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.CfnKey", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.Key", + "version": "0.0.0" + } + }, + "F2": { + "id": "F2", + "path": "lambda-event-source-filter-criteria-sqs/F2", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "lambda-event-source-filter-criteria-sqs/F2/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "lambda-event-source-filter-criteria-sqs/F2/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-criteria-sqs/F2/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "lambda-event-source-filter-criteria-sqs/F2/ServiceRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-criteria-sqs/F2/ServiceRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "sqs:ChangeMessageVisibility", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + "sqs:GetQueueUrl", + "sqs:ReceiveMessage" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "Q63C6E3AB", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "F2ServiceRoleDefaultPolicy999D30A8", + "roles": [ + { + "Ref": "F2ServiceRole7F7C6006" + } + ] + } + }, + "constructInfo": { + "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" + } + }, + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-criteria-sqs/F2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "zipFile": "exports.handler = async function handler(event) {\n console.log('event:', JSON.stringify(event, undefined, 2));\n return { event };\n}" + }, + "handler": "index.handler", + "role": { + "Fn::GetAtt": [ + "F2ServiceRole7F7C6006", + "Arn" + ] + }, + "runtime": "nodejs18.x" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + }, + "SqsEventSource:lambdaeventsourcefiltercriteriasqsQA0FC5C93": { + "id": "SqsEventSource:lambdaeventsourcefiltercriteriasqsQA0FC5C93", + "path": "lambda-event-source-filter-criteria-sqs/F2/SqsEventSource:lambdaeventsourcefiltercriteriasqsQA0FC5C93", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-criteria-sqs/F2/SqsEventSource:lambdaeventsourcefiltercriteriasqsQA0FC5C93/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::EventSourceMapping", + "aws:cdk:cloudformation:props": { + "batchSize": 5, + "eventSourceArn": { + "Fn::GetAtt": [ + "Q63C6E3AB", + "Arn" + ] + }, + "filterCriteria": { + "filters": [ + { + "pattern": "{\"body\":{\"id\":[{\"exists\":true}]}}" + } + ] + }, + "functionName": { + "Ref": "F23BAC7B9C" + }, + "kmsKeyArn": { + "Fn::GetAtt": [ + "fctestkeyname524AF060", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnEventSourceMapping", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "lambda-event-source-filter-criteria-sqs/BootstrapVersion", @@ -246,7 +494,7 @@ "path": "SQSFilterCriteria/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } }, "DeployAssert": { @@ -292,7 +540,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.ts index 3890428a70ff8..98d536b52e49f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.sqs-with-filter-criteria.ts @@ -4,6 +4,7 @@ import * as cdk from 'aws-cdk-lib'; import * as integ from '@aws-cdk/integ-tests-alpha'; import { TestFunction } from './test-function'; import { SqsEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; +import { Key } from 'aws-cdk-lib/aws-kms'; const app = new cdk.App(); @@ -23,6 +24,26 @@ fn.addEventSource(new SqsEventSource(queue, { ], })); +const myKey = new Key(stack, 'fc-test-key-name', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + pendingWindow: cdk.Duration.days(7), + description: 'KMS key for test fc encryption', +}); + +const fn2 = new TestFunction(stack, 'F2'); + +fn2.addEventSource(new SqsEventSource(queue, { + batchSize: 5, + filters: [ + lambda.FilterCriteria.filter({ + body: { + id: lambda.FilterRule.exists(), + }, + }), + ], + filterEncryption: myKey, +})); + new integ.IntegTest(app, 'SQSFilterCriteria', { testCases: [stack], }); diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/README.md b/packages/aws-cdk-lib/aws-lambda-event-sources/README.md index c7fe6791f4df6..8c88ae99bc8b8 100644 --- a/packages/aws-cdk-lib/aws-lambda-event-sources/README.md +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/README.md @@ -313,6 +313,39 @@ myFunction.addEventSource(new ManagedKafkaEventSource({ })); ``` +By default, Lambda will encrypt Filter Criteria using AWS managed keys. But if you want to use a self managed KMS key to encrypt the filters, You can specify the self managed key using the `filterEncryption` property. + +```ts +import { ManagedKafkaEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; +import { Key } from 'aws-cdk-lib/aws-kms'; + +// Your MSK cluster arn +const clusterArn = 'arn:aws:kafka:us-east-1:0123456789019:cluster/SalesCluster/abcd1234-abcd-cafe-abab-9876543210ab-4'; + +// The Kafka topic you want to subscribe to +const topic = 'some-cool-topic'; + +// Your self managed KMS key +const myKey = Key.fromKeyArn( + this, + 'SourceBucketEncryptionKey', + 'arn:aws:kms:us-east-1:123456789012:key/', +); + +declare const myFunction: lambda.Function; +myFunction.addEventSource(new ManagedKafkaEventSource({ + clusterArn, + topic, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + filters: [ + lambda.FilterCriteria.filter({ + stringEquals: lambda.FilterRule.isEqual('test'), + }), + ], + filterEncryption: myKey, +})); +``` + You can also specify an S3 bucket as an "on failure" destination: ```ts diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/lib/kafka.ts b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/kafka.ts index d9d03988a19a3..3c75a45a51447 100644 --- a/packages/aws-cdk-lib/aws-lambda-event-sources/lib/kafka.ts +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/kafka.ts @@ -2,6 +2,7 @@ import { Construct } from 'constructs'; import { StreamEventSource, BaseStreamEventSourceProps } from './stream'; import { ISecurityGroup, IVpc, SubnetSelection } from '../../aws-ec2'; import * as iam from '../../aws-iam'; +import { IKey } from '../../aws-kms'; import * as lambda from '../../aws-lambda'; import * as secretsmanager from '../../aws-secretsmanager'; import { Stack, Names } from '../../core'; @@ -38,6 +39,16 @@ export interface KafkaEventSourceProps extends BaseStreamEventSourceProps { */ readonly filters?: Array<{[key: string]: any}>; + /** + * Add Customer managed KMS key to encrypt Filter Criteria. + * @see https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html + * By default, Lambda will encrypt Filter Criteria using AWS managed keys + * @see https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#aws-managed-cmk + * + * @default - none + */ + readonly filterEncryption?: IKey; + /** * Add an on Failure Destination for this Kafka event. SNS/SQS/S3 are supported * @@ -146,6 +157,7 @@ export class ManagedKafkaEventSource extends StreamEventSource { this.enrichMappingOptions({ eventSourceArn: this.innerProps.clusterArn, filters: this.innerProps.filters, + filterEncryption: this.innerProps.filterEncryption, startingPosition: this.innerProps.startingPosition, sourceAccessConfigurations: this.sourceAccessConfigurations(), kafkaTopic: this.innerProps.topic, @@ -236,6 +248,7 @@ export class SelfManagedKafkaEventSource extends StreamEventSource { this.mappingId(target), this.enrichMappingOptions({ filters: this.innerProps.filters, + filterEncryption: this.innerProps.filterEncryption, kafkaBootstrapServers: this.innerProps.bootstrapServers, kafkaTopic: this.innerProps.topic, kafkaConsumerGroupId: this.innerProps.consumerGroupId, diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/lib/sqs.ts b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/sqs.ts index 6b175bc18cd25..bcf6f4adf573d 100644 --- a/packages/aws-cdk-lib/aws-lambda-event-sources/lib/sqs.ts +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/sqs.ts @@ -1,3 +1,4 @@ +import { IKey } from '../../aws-kms'; import * as lambda from '../../aws-lambda'; import * as sqs from '../../aws-sqs'; import { Duration, Names, Token, Annotations } from '../../core'; @@ -47,6 +48,16 @@ export interface SqsEventSourceProps { */ readonly filters?: Array<{[key: string]: any}>; + /** + * Add Customer managed KMS key to encrypt Filter Criteria. + * @see https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html + * By default, Lambda will encrypt Filter Criteria using AWS managed keys + * @see https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#aws-managed-cmk + * + * @default - none + */ + readonly filterEncryption?: IKey; + /** * The maximum concurrency setting limits the number of concurrent instances of the function that an Amazon SQS event source can invoke. * @@ -94,6 +105,7 @@ export class SqsEventSource implements lambda.IEventSource { enabled: this.props.enabled, eventSourceArn: this.queue.queueArn, filters: this.props.filters, + filterEncryption: this.props.filterEncryption, }); this._eventSourceMappingId = eventSourceMapping.eventSourceMappingId; this._eventSourceMappingArn = eventSourceMapping.eventSourceMappingArn; diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/lib/stream.ts b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/stream.ts index 46b20d01e1787..414de7472f0e5 100644 --- a/packages/aws-cdk-lib/aws-lambda-event-sources/lib/stream.ts +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/stream.ts @@ -1,4 +1,5 @@ import { S3OnFailureDestination } from './s3-onfailuire-destination'; +import { IKey } from '../../aws-kms'; import * as lambda from '../../aws-lambda'; import { Duration } from '../../core'; @@ -125,6 +126,17 @@ export interface StreamEventSourceProps extends BaseStreamEventSourceProps { * @default - None */ readonly filters?: Array<{[key: string]: any}>; + + /** + * Add Customer managed KMS key to encrypt Filter Criteria. + * @see https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html + * By default, Lambda will encrypt Filter Criteria using AWS managed keys + * @see https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#aws-managed-cmk + * + * @default - none + */ + readonly filterEncryption?: IKey; + } /** @@ -155,6 +167,7 @@ export abstract class StreamEventSource implements lambda.IEventSource { tumblingWindow: this.props.tumblingWindow, enabled: this.props.enabled, filters: this.props.filters, + filterEncryption: this.props.filterEncryption, }; } } diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/test/dynamo.test.ts b/packages/aws-cdk-lib/aws-lambda-event-sources/test/dynamo.test.ts index f93e002170452..eb331b0de8972 100644 --- a/packages/aws-cdk-lib/aws-lambda-event-sources/test/dynamo.test.ts +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/test/dynamo.test.ts @@ -1,6 +1,7 @@ import { TestFunction } from './test-function'; import { Template } from '../../assertions'; import * as dynamodb from '../../aws-dynamodb'; +import { Key } from '../../aws-kms'; import * as lambda from '../../aws-lambda'; import { Bucket } from '../../aws-s3'; import * as sqs from '../../aws-sqs'; @@ -288,6 +289,128 @@ describe('DynamoEventSource', () => { }); }); + test('adding filter criteria encryption', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const table = new dynamodb.Table(stack, 'T', { + partitionKey: { + name: 'id', + type: dynamodb.AttributeType.STRING, + }, + stream: dynamodb.StreamViewType.NEW_IMAGE, + }); + + const myKey = Key.fromKeyArn( + stack, + 'SourceBucketEncryptionKey', + 'arn:aws:kms:us-east-1:123456789012:key/', + ); + + // WHEN + fn.addEventSource(new sources.DynamoEventSource(table, { + startingPosition: lambda.StartingPosition.LATEST, + filters: [ + lambda.FilterCriteria.filter({ + eventName: lambda.FilterRule.isEqual('INSERT'), + dynamodb: { + Keys: { + id: { + S: lambda.FilterRule.exists(), + }, + }, + }, + }), + ], + filterEncryption: myKey, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::EventSourceMapping', { + 'EventSourceArn': { + 'Fn::GetAtt': [ + 'TD925BC7E', + 'StreamArn', + ], + }, + 'FunctionName': { + 'Ref': 'Fn9270CBC0', + }, + 'FilterCriteria': { + 'Filters': [ + { + 'Pattern': '{"eventName":["INSERT"],"dynamodb":{"Keys":{"id":{"S":[{"exists":true}]}}}}', + }, + ], + }, + KmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/', + 'StartingPosition': 'LATEST', + }); + }); + + test('adding filter criteria encryption with stack key', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const table = new dynamodb.Table(stack, 'T', { + partitionKey: { + name: 'id', + type: dynamodb.AttributeType.STRING, + }, + stream: dynamodb.StreamViewType.NEW_IMAGE, + }); + + const myKey = new Key(stack, 'fc-test-key-name', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + pendingWindow: cdk.Duration.days(7), + description: 'KMS key for test fc encryption', + }); + + // WHEN + fn.addEventSource(new sources.DynamoEventSource(table, { + startingPosition: lambda.StartingPosition.LATEST, + filters: [ + lambda.FilterCriteria.filter({ + eventName: lambda.FilterRule.isEqual('INSERT'), + dynamodb: { + Keys: { + id: { + S: lambda.FilterRule.exists(), + }, + }, + }, + }), + ], + filterEncryption: myKey, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::', { Ref: 'AWS::AccountId' }, ':root']], + }, + }, + Resource: '*', + }, + { + Action: 'kms:Decrypt', + Effect: 'Allow', + Principal: { + Service: 'lambda.amazonaws.com', + }, + Resource: '*', + }, + ], + }, + }); + }); + test('specific maxBatchingWindow', () => { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/test/kafka.test.ts b/packages/aws-cdk-lib/aws-lambda-event-sources/test/kafka.test.ts index cee9ca715e0f2..d5b1df2c9a657 100644 --- a/packages/aws-cdk-lib/aws-lambda-event-sources/test/kafka.test.ts +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/test/kafka.test.ts @@ -1,6 +1,7 @@ import { TestFunction } from './test-function'; import { Template, Match } from '../../assertions'; import { SecurityGroup, SubnetType, Vpc } from '../../aws-ec2'; +import { Key } from '../../aws-kms'; import * as lambda from '../../aws-lambda'; import { Bucket } from '../../aws-s3'; import { Secret } from '../../aws-secretsmanager'; @@ -173,6 +174,110 @@ describe('KafkaEventSource', () => { }); }); + test('adding filter criteria encryption', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const clusterArn = 'some-arn'; + const kafkaTopic = 'some-topic'; + const myKey = Key.fromKeyArn( + stack, + 'SourceBucketEncryptionKey', + 'arn:aws:kms:us-east-1:123456789012:key/', + ); + + // WHEN + fn.addEventSource(new sources.ManagedKafkaEventSource( + { + clusterArn, + topic: kafkaTopic, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + filters: [ + lambda.FilterCriteria.filter({ + orFilter: lambda.FilterRule.or('one', 'two'), + stringEquals: lambda.FilterRule.isEqual('test'), + }), + lambda.FilterCriteria.filter({ + numericEquals: lambda.FilterRule.isEqual(1), + }), + ], + filterEncryption: myKey, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::EventSourceMapping', { + FilterCriteria: { + Filters: [ + { + Pattern: '{"orFilter":["one","two"],"stringEquals":["test"]}', + }, + { + Pattern: '{"numericEquals":[{"numeric":["=",1]}]}', + }, + ], + }, + KmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/', + }); + }); + + test('adding filter criteria encryption with stack key', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const clusterArn = 'some-arn'; + const kafkaTopic = 'some-topic'; + + const myKey = new Key(stack, 'fc-test-key-name', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + pendingWindow: cdk.Duration.days(7), + description: 'KMS key for test fc encryption', + }); + + // WHEN + fn.addEventSource(new sources.ManagedKafkaEventSource( + { + clusterArn, + topic: kafkaTopic, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + filters: [ + lambda.FilterCriteria.filter({ + orFilter: lambda.FilterRule.or('one', 'two'), + stringEquals: lambda.FilterRule.isEqual('test'), + }), + lambda.FilterCriteria.filter({ + numericEquals: lambda.FilterRule.isEqual(1), + }), + ], + filterEncryption: myKey, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::', { Ref: 'AWS::AccountId' }, ':root']], + }, + }, + Resource: '*', + }, + { + Action: 'kms:Decrypt', + Effect: 'Allow', + Principal: { + Service: 'lambda.amazonaws.com', + }, + Resource: '*', + }, + ], + }, + }); + }); + test('with s3 onfailure destination', () => { // GIVEN const stack = new cdk.Stack(); @@ -315,6 +420,54 @@ describe('KafkaEventSource', () => { }); }); + test('adding filter criteria encryption', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const kafkaTopic = 'some-topic'; + const secret = new Secret(stack, 'Secret', { secretName: 'AmazonMSK_KafkaSecret' }); + const bootstrapServers = ['kafka-broker:9092']; + const myKey = Key.fromKeyArn( + stack, + 'SourceBucketEncryptionKey', + 'arn:aws:kms:us-east-1:123456789012:key/', + ); + + // WHEN + fn.addEventSource(new sources.SelfManagedKafkaEventSource( + { + bootstrapServers: bootstrapServers, + topic: kafkaTopic, + secret: secret, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + filters: [ + lambda.FilterCriteria.filter({ + orFilter: lambda.FilterRule.or('one', 'two'), + stringEquals: lambda.FilterRule.isEqual('test'), + }), + lambda.FilterCriteria.filter({ + numericEquals: lambda.FilterRule.isEqual(1), + }), + ], + filterEncryption: myKey, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::EventSourceMapping', { + FilterCriteria: { + Filters: [ + { + Pattern: '{"orFilter":["one","two"],"stringEquals":["test"]}', + }, + { + Pattern: '{"numericEquals":[{"numeric":["=",1]}]}', + }, + ], + }, + KmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/', + }); + }); + test('without vpc, secret must be set', () => { const stack = new cdk.Stack(); const fn = new TestFunction(stack, 'Fn'); diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/test/sqs.test.ts b/packages/aws-cdk-lib/aws-lambda-event-sources/test/sqs.test.ts index 48e09e24bbf8b..3865e02f63039 100644 --- a/packages/aws-cdk-lib/aws-lambda-event-sources/test/sqs.test.ts +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/test/sqs.test.ts @@ -1,6 +1,7 @@ import { TestFunction } from './test-function'; import { Template } from '../../assertions'; import * as iam from '../../aws-iam'; +import { Key } from '../../aws-kms'; import * as lambda from '../../aws-lambda'; import * as sqs from '../../aws-sqs'; import * as cdk from '../../core'; @@ -439,6 +440,92 @@ describe('SQSEventSource', () => { }); }); + test('adding filter criteria encryption', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const q = new sqs.Queue(stack, 'Q'); + const myKey = Key.fromKeyArn( + stack, + 'SourceBucketEncryptionKey', + 'arn:aws:kms:us-east-1:123456789012:key/', + ); + + // WHEN + fn.addEventSource(new sources.SqsEventSource(q, { + filters: [ + lambda.FilterCriteria.filter({ + body: { + id: lambda.FilterRule.exists(), + }, + }), + ], + filterEncryption: myKey, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::EventSourceMapping', { + 'FilterCriteria': { + 'Filters': [ + { + 'Pattern': '{"body":{"id":[{"exists":true}]}}', + }, + ], + }, + KmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/', + }); + }); + + test('adding filter criteria encryption with stack key', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const q = new sqs.Queue(stack, 'Q'); + const myKey = new Key(stack, 'fc-test-key-name', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + pendingWindow: cdk.Duration.days(7), + description: 'KMS key for test fc encryption', + }); + + // WHEN + fn.addEventSource(new sources.SqsEventSource(q, { + filters: [ + lambda.FilterCriteria.filter({ + body: { + id: lambda.FilterRule.exists(), + }, + }), + ], + filterEncryption: myKey, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + Action: 'kms:*', + Effect: 'Allow', + Principal: { + AWS: { + 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::', { Ref: 'AWS::AccountId' }, ':root']], + }, + }, + Resource: '*', + }, + { + Action: 'kms:Decrypt', + Effect: 'Allow', + Principal: { + Service: 'lambda.amazonaws.com', + }, + Resource: '*', + }, + ], + }, + }); + }); + test('fails if maxConcurrency < 2', () => { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/aws-cdk-lib/aws-lambda/README.md b/packages/aws-cdk-lib/aws-lambda/README.md index 58dc34ef74d53..160f9296c6242 100644 --- a/packages/aws-cdk-lib/aws-lambda/README.md +++ b/packages/aws-cdk-lib/aws-lambda/README.md @@ -780,6 +780,53 @@ fn.addEventSource(new eventsources.DynamoEventSource(table, { })); ``` +By default, Lambda will encrypt Filter Criteria using AWS managed keys. But if you want to use a self managed KMS key to encrypt the filters, You can specify the self managed key using the `filterEncryption` property. + +```ts +import * as eventsources from 'aws-cdk-lib/aws-lambda-event-sources'; +import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; +import { Key } from 'aws-cdk-lib/aws-kms'; + +declare const fn: lambda.Function; +const table = new dynamodb.Table(this, 'Table', { + partitionKey: { + name: 'id', + type: dynamodb.AttributeType.STRING, + }, + stream: dynamodb.StreamViewType.NEW_IMAGE, +}); +// Your self managed KMS key +const myKey = Key.fromKeyArn( + this, + 'SourceBucketEncryptionKey', + 'arn:aws:kms:us-east-1:123456789012:key/', +); + +fn.addEventSource(new eventsources.DynamoEventSource(table, { + startingPosition: lambda.StartingPosition.LATEST, + filters: [lambda.FilterCriteria.filter({ eventName: lambda.FilterRule.isEqual('INSERT') })], + filterEncryption: myKey, +})); +``` + +> Lambda requires allow `kms:Decrypt` on Lambda principal `lambda.amazonaws.com` to use the key for Filter Criteria Encryption. If you create the KMS key in the stack, CDK will automatically add this permission to the Key when you creates eventSourceMapping. However, if you import the key using function like `Key.fromKeyArn` then you need to add the following permission to the KMS key before using it to encrypt Filter Criteria + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Action": "kms:Decrypt", + "Resource": "*" + } + ] +} +``` + See the documentation for the __@aws-cdk/aws-lambda-event-sources__ module for more details. ## Imported Lambdas diff --git a/packages/aws-cdk-lib/aws-lambda/lib/event-source-mapping.ts b/packages/aws-cdk-lib/aws-lambda/lib/event-source-mapping.ts index 1f49011ca1bd5..a441ce148788a 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/event-source-mapping.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/event-source-mapping.ts @@ -2,6 +2,8 @@ import { Construct } from 'constructs'; import { IEventSourceDlq } from './dlq'; import { IFunction } from './function-base'; import { CfnEventSourceMapping } from './lambda.generated'; +import * as iam from '../../aws-iam'; +import { IKey } from '../../aws-kms'; import * as cdk from '../../core'; /** @@ -251,6 +253,16 @@ export interface EventSourceMappingOptions { */ readonly filters?: Array<{[key: string]: any}>; + /** + * Add Customer managed KMS key to encrypt Filter Criteria. + * @see https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html + * By default, Lambda will encrypt Filter Criteria using AWS managed keys + * @see https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#aws-managed-cmk + * + * @default - none + */ + readonly filterEncryption?: IKey; + /** * Check if support S3 onfailure destination(ODF). Currently only MSK and self managed kafka event support S3 ODF * @@ -388,6 +400,23 @@ export class EventSourceMapping extends cdk.Resource implements IEventSourceMapp this.validateKafkaConsumerGroupIdOrThrow(props.kafkaConsumerGroupId); } + if (props.filterEncryption !== undefined && props.filters == undefined) { + throw new Error('filter criteria must be provided to enable setting filter criteria encryption'); + } + + /** + * Grants the Lambda function permission to decrypt data using the specified KMS key. + * This step is necessary for setting up encrypted filter criteria. + * + * If the KMS key was created within this CloudFormation stack (via `new Key`), a Key policy + * will be attached to the key to allow the Lambda function to access it. However, if the + * Key is imported from an existing ARN (`Key.fromKeyArn`), no action will be taken. + */ + if (props.filterEncryption !== undefined) { + const lambdaPrincipal = new iam.ServicePrincipal('lambda.amazonaws.com'); + props.filterEncryption.grantDecrypt(lambdaPrincipal); + } + let destinationConfig; if (props.onFailure) { @@ -423,6 +452,7 @@ export class EventSourceMapping extends cdk.Resource implements IEventSourceMapp sourceAccessConfigurations: props.sourceAccessConfigurations?.map((o) => {return { type: o.type.type, uri: o.uri };}), selfManagedEventSource, filterCriteria: props.filters ? { filters: props.filters }: undefined, + kmsKeyArn: props.filterEncryption?.keyArn, selfManagedKafkaEventSourceConfig: props.kafkaBootstrapServers ? consumerGroupConfig : undefined, amazonManagedKafkaEventSourceConfig: props.eventSourceArn ? consumerGroupConfig : undefined, }); diff --git a/packages/aws-cdk-lib/aws-lambda/test/event-source-mapping.test.ts b/packages/aws-cdk-lib/aws-lambda/test/event-source-mapping.test.ts index 8182a15406db7..6eafc9267471d 100644 --- a/packages/aws-cdk-lib/aws-lambda/test/event-source-mapping.test.ts +++ b/packages/aws-cdk-lib/aws-lambda/test/event-source-mapping.test.ts @@ -1,4 +1,5 @@ import { Match, Template } from '../../assertions'; +import { Key } from '../../aws-kms'; import * as cdk from '../../core'; import * as lambda from '../lib'; import { Code, EventSourceMapping, Function, Runtime, Alias, StartingPosition, FilterRule, FilterCriteria } from '../lib'; @@ -323,6 +324,74 @@ describe('event source mapping', () => { }); }); + test('adding filter criteria encryption', () => { + const topicNameParam = new cdk.CfnParameter(stack, 'TopicNameParam', { + type: 'String', + }); + + let eventSourceArn = 'some-arn'; + + const myKey = Key.fromKeyArn( + stack, + 'SourceBucketEncryptionKey', + 'arn:aws:kms:us-east-1:123456789012:key/', + ); + + // WHEN + new EventSourceMapping(stack, 'test', { + target: fn, + eventSourceArn: eventSourceArn, + kafkaTopic: topicNameParam.valueAsString, + filters: [ + FilterCriteria.filter({ + orFilter: FilterRule.or('one', 'two'), + stringEquals: FilterRule.isEqual('test'), + }), + FilterCriteria.filter({ + numericEquals: FilterRule.isEqual(1), + }), + ], + filterEncryption: myKey, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::EventSourceMapping', { + FilterCriteria: { + Filters: [ + { + Pattern: '{"orFilter":["one","two"],"stringEquals":["test"]}', + }, + { + Pattern: '{"numericEquals":[{"numeric":["=",1]}]}', + }, + ], + }, + KmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/', + }); + + }); + + test('adding filter criteria encryption without filter criteria', () => { + const topicNameParam = new cdk.CfnParameter(stack, 'TopicNameParam', { + type: 'String', + }); + + let eventSourceArn = 'some-arn'; + + const myKey = Key.fromKeyArn( + stack, + 'SourceBucketEncryptionKey', + 'arn:aws:kms:us-east-1:123456789012:key/', + ); + + expect(() => new EventSourceMapping(stack, 'test', { + target: fn, + eventSourceArn: eventSourceArn, + kafkaTopic: topicNameParam.valueAsString, + filterEncryption: myKey, + })).toThrow(/filter criteria must be provided to enable setting filter criteria encryption/); + }); + test('kafkaBootstrapServers appears in stack', () => { const topicNameParam = new cdk.CfnParameter(stack, 'TopicNameParam', { type: 'String', diff --git a/tools/@aws-cdk/spec2cdk/temporary-schemas/us-east-1/aws-lambda-eventsourcemapping.json b/tools/@aws-cdk/spec2cdk/temporary-schemas/us-east-1/aws-lambda-eventsourcemapping.json new file mode 100644 index 0000000000000..965ef4c491c1a --- /dev/null +++ b/tools/@aws-cdk/spec2cdk/temporary-schemas/us-east-1/aws-lambda-eventsourcemapping.json @@ -0,0 +1,409 @@ +{ + "typeName" : "AWS::Lambda::EventSourceMapping", + "description" : "Resource Type definition for AWS::Lambda::EventSourceMapping", + "nonPublicProperties": ["/properties/KmsKeyArn"], + "additionalProperties" : false, + "properties" : { + "Id": { + "description": "Event Source Mapping Identifier UUID.", + "type": "string", + "pattern": "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", + "minLength": 36, + "maxLength": 36 + }, + "BatchSize": { + "description": "The maximum number of items to retrieve in a single batch.", + "type": "integer", + "minimum": 1, + "maximum": 10000 + }, + "BisectBatchOnFunctionError": { + "description": "(Streams) If the function returns an error, split the batch in two and retry.", + "type": "boolean" + }, + "DestinationConfig": { + "description": "(Kinesis, DynamoDB, Amazon MSK, and self-managed Kafka event sources only) A configuration object that specifies the destination of an event after Lambda processes it.", + "$ref": "#/definitions/DestinationConfig" + }, + "Enabled": { + "description": "Disables the event source mapping to pause polling and invocation.", + "type": "boolean" + }, + "EventSourceArn": { + "description": "The Amazon Resource Name (ARN) of the event source.", + "type": "string", + "pattern": "arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\\-])+:([a-z]{2}(-gov)?(-iso([a-z])?)?-[a-z]+-\\d{1})?:(\\d{12})?:(.*)", + "minLength": 12, + "maxLength": 1024 + }, + "FilterCriteria": { + "description": "The filter criteria to control event filtering.", + "$ref": "#/definitions/FilterCriteria" + }, + "KmsKeyArn": { + "description": "The Amazon Resource Name (ARN) of the KMS key.", + "type": "string", + "pattern": "(arn:(aws[a-zA-Z-]*)?:[a-z0-9-.]+:.*)|()", + "minLength": 12, + "maxLength": 2048 + }, + "FunctionName": { + "description": "The name of the Lambda function.", + "type": "string", + "pattern": "(arn:(aws[a-zA-Z-]*)?:lambda:)?([a-z]{2}(-gov)?(-iso([a-z])?)?-[a-z]+-\\d{1}:)?(\\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\\$LATEST|[a-zA-Z0-9-_]+))?", + "minLength": 1, + "maxLength": 140 + }, + "MaximumBatchingWindowInSeconds": { + "description": "(Streams) The maximum amount of time to gather records before invoking the function, in seconds.", + "type": "integer", + "minimum": 0, + "maximum": 300 + }, + "MaximumRecordAgeInSeconds": { + "description": "(Streams) The maximum age of a record that Lambda sends to a function for processing.", + "type": "integer", + "minimum": -1, + "maximum": 604800 + }, + "MaximumRetryAttempts": { + "description": "(Streams) The maximum number of times to retry when the function returns an error.", + "type": "integer", + "minimum": -1, + "maximum": 10000 + }, + "ParallelizationFactor": { + "description": "(Streams) The number of batches to process from each shard concurrently.", + "type": "integer", + "minimum": 1, + "maximum": 10 + }, + "StartingPosition": { + "description": "The position in a stream from which to start reading. Required for Amazon Kinesis and Amazon DynamoDB Streams sources.", + "type": "string", + "pattern": "(LATEST|TRIM_HORIZON|AT_TIMESTAMP)+", + "minLength": 6, + "maxLength": 12 + }, + "StartingPositionTimestamp": { + "description": "With StartingPosition set to AT_TIMESTAMP, the time from which to start reading, in Unix time seconds.", + "type": "number" + }, + "Topics": { + "description": "(Kafka) A list of Kafka topics.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "pattern": "^[^.]([a-zA-Z0-9\\-_.]+)", + "minLength": 1, + "maxLength": 249 + }, + "minItems": 1, + "maxItems": 1 + }, + "Queues": { + "description": "(ActiveMQ) A list of ActiveMQ queues.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "pattern": "[\\s\\S]*", + "minLength": 1, + "maxLength": 1000 + }, + "minItems": 1, + "maxItems": 1 + }, + "SourceAccessConfigurations": { + "description": "A list of SourceAccessConfiguration.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/SourceAccessConfiguration" + }, + "minItems": 1, + "maxItems": 22 + }, + "TumblingWindowInSeconds": { + "description": "(Streams) Tumbling window (non-overlapping time window) duration to perform aggregations.", + "type": "integer", + "minimum": 0, + "maximum": 900 + }, + "FunctionResponseTypes": { + "description": "(Streams) A list of response types supported by the function.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "ReportBatchItemFailures" + ] + }, + "minLength": 0, + "maxLength": 1 + }, + "SelfManagedEventSource": { + "description": "Self-managed event source endpoints.", + "$ref": "#/definitions/SelfManagedEventSource" + }, + "AmazonManagedKafkaEventSourceConfig": { + "description": "Specific configuration settings for an MSK event source.", + "$ref": "#/definitions/AmazonManagedKafkaEventSourceConfig" + }, + "SelfManagedKafkaEventSourceConfig": { + "description": "Specific configuration settings for a Self-Managed Apache Kafka event source.", + "$ref": "#/definitions/SelfManagedKafkaEventSourceConfig" + }, + "ScalingConfig": { + "description": "The scaling configuration for the event source.", + "$ref": "#/definitions/ScalingConfig" + }, + "DocumentDBEventSourceConfig": { + "description": "Document db event source config.", + "$ref": "#/definitions/DocumentDBEventSourceConfig" + } + }, + "definitions" : { + "DestinationConfig" : { + "type" : "object", + "additionalProperties" : false, + "description": "A configuration object that specifies the destination of an event after Lambda processes it.", + "properties" : { + "OnFailure": { + "description": "A destination for records of invocations that failed processing.", + "$ref": "#/definitions/OnFailure" + } + } + }, + "FilterCriteria": { + "type": "object", + "description": "The filter criteria to control event filtering.", + "additionalProperties" : false, + "properties": { + "Filters": { + "description": "List of filters of this FilterCriteria", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/Filter" + }, + "minItems": 1, + "maxItems": 20 + } + } + }, + "Filter": { + "type": "object", + "description": "The filter object that defines parameters for ESM filtering.", + "additionalProperties" : false, + "properties": { + "Pattern": { + "type": "string", + "description": "The filter pattern that defines which events should be passed for invocations.", + "pattern": ".*", + "minLength": 0, + "maxLength": 4096 + } + } + }, + "OnFailure": { + "type" : "object", + "description" : "A destination for records of invocations that failed processing.", + "additionalProperties" : false, + "properties" : { + "Destination": { + "description": "The Amazon Resource Name (ARN) of the destination resource.", + "type": "string", + "pattern": "arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\\-])+:([a-z]{2}(-gov)?(-iso([a-z])?)?-[a-z]+-\\d{1})?:(\\d{12})?:(.*)", + "minLength": 12, + "maxLength": 1024 + } + } + }, + "SourceAccessConfiguration" : { + "type" : "object", + "additionalProperties" : false, + "description": "The configuration used by AWS Lambda to access event source", + "properties" : { + "Type" : { + "description": "The type of source access configuration.", + "enum": [ + "BASIC_AUTH", + "VPC_SUBNET", + "VPC_SECURITY_GROUP", + "SASL_SCRAM_512_AUTH", + "SASL_SCRAM_256_AUTH", + "VIRTUAL_HOST", + "CLIENT_CERTIFICATE_TLS_AUTH", + "SERVER_ROOT_CA_CERTIFICATE" + ], + "type": "string" + }, + "URI" : { + "description": "The URI for the source access configuration resource.", + "type": "string", + "pattern": "[a-zA-Z0-9-\\/*:_+=.@-]*", + "minLength": 1, + "maxLength": 200 + } + } + }, + "SelfManagedEventSource" : { + "type": "object", + "additionalProperties": false, + "description": "The configuration used by AWS Lambda to access a self-managed event source.", + "properties": { + "Endpoints": { + "description": "The endpoints for a self-managed event source.", + "$ref": "#/definitions/Endpoints" + } + } + }, + "Endpoints" : { + "type": "object", + "additionalProperties": false, + "description": "The endpoints used by AWS Lambda to access a self-managed event source.", + "properties": { + "KafkaBootstrapServers": { + "type": "array", + "description": "A list of Kafka server endpoints.", + "uniqueItems": true, + "items": { + "type": "string", + "description": "The URL of a Kafka server.", + "pattern": "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9]):[0-9]{1,5}", + "minLength": 1, + "maxLength": 300 + }, + "minItems": 1, + "maxItems": 10 + } + } + }, + "ConsumerGroupId": { + "description": "The identifier for the Kafka Consumer Group to join.", + "type": "string", + "pattern": "[a-zA-Z0-9-\\/*:_+=.@-]*", + "minLength": 1, + "maxLength": 200 + }, + "AmazonManagedKafkaEventSourceConfig": { + "description": "Specific configuration settings for an MSK event source.", + "type": "object", + "additionalProperties": false, + "properties": { + "ConsumerGroupId": { + "description": "The identifier for the Kafka Consumer Group to join.", + "$ref": "#/definitions/ConsumerGroupId" + } + } + }, + "SelfManagedKafkaEventSourceConfig": { + "description": "Specific configuration settings for a Self-Managed Apache Kafka event source.", + "type": "object", + "additionalProperties": false, + "properties": { + "ConsumerGroupId": { + "description": "The identifier for the Kafka Consumer Group to join.", + "$ref": "#/definitions/ConsumerGroupId" + } + } + }, + "MaximumConcurrency": { + "description": "The maximum number of concurrent functions that an event source can invoke.", + "type": "integer", + "minimum": 2, + "maximum": 1000 + }, + "ScalingConfig": { + "description": "The scaling configuration for the event source.", + "type": "object", + "additionalProperties": false, + "properties": { + "MaximumConcurrency": { + "description": "The maximum number of concurrent functions that the event source can invoke.", + "$ref": "#/definitions/MaximumConcurrency" + } + } + }, + "DocumentDBEventSourceConfig": { + "description": "Document db event source config.", + "type": "object", + "additionalProperties": false, + "properties": { + "DatabaseName": { + "description": "The database name to connect to.", + "type": "string", + "minLength": 1, + "maxLength": 63 + }, + "CollectionName": { + "description": "The collection name to connect to.", + "type": "string", + "minLength": 1, + "maxLength": 57 + }, + "FullDocument": { + "description": "Include full document in change stream response. The default option will only send the changes made to documents to Lambda. If you want the complete document sent to Lambda, set this to UpdateLookup.", + "type": "string", + "enum": [ + "UpdateLookup", + "Default" + ] + } + } + } + }, + "required" : [ "FunctionName" ], + "createOnlyProperties" : [ + "/properties/EventSourceArn", + "/properties/StartingPosition", + "/properties/StartingPositionTimestamp", + "/properties/SelfManagedEventSource", + "/properties/AmazonManagedKafkaEventSourceConfig", + "/properties/SelfManagedKafkaEventSourceConfig" + ], + "readOnlyProperties" : [ "/properties/Id" ], + "primaryIdentifier" : [ "/properties/Id" ], + "propertyTransform" : { + "/properties/StartingPositionTimestamp": "StartingPositionTimestamp * 1000" + }, + "handlers": { + "create": { + "permissions": [ + "lambda:CreateEventSourceMapping", + "lambda:GetEventSourceMapping" + ] + }, + "delete": { + "permissions": [ + "lambda:DeleteEventSourceMapping", + "lambda:GetEventSourceMapping" + ] + }, + "list": { + "permissions": [ + "lambda:ListEventSourceMappings" + ] + }, + "read": { + "permissions": [ + "lambda:GetEventSourceMapping" + ] + }, + "update": { + "permissions": [ + "lambda:UpdateEventSourceMapping", + "lambda:GetEventSourceMapping" + ] + } + }, + "tagging": { + "taggable": false, + "tagOnCreate": false, + "tagUpdatable": false, + "cloudFormationSystemTags": false + } +} \ No newline at end of file diff --git a/version.v2.json b/version.v2.json index 443e45a11be1d..21304aecb929a 100644 --- a/version.v2.json +++ b/version.v2.json @@ -1,4 +1,4 @@ { - "version": "2.151.1", - "alphaVersion": "2.151.1-alpha.0" + "version": "2.152.0", + "alphaVersion": "2.152.0-alpha.0" } \ No newline at end of file