diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.assets.json new file mode 100644 index 0000000000000..8480768d7c39b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.assets.json @@ -0,0 +1,20 @@ +{ + "version": "44.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "displayName": "SchemaRegistryIntegDefaultTestDeployAssertC190A9FD Template", + "source": { + "path": "SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/cdk.out new file mode 100644 index 0000000000000..b3a26d44a5f73 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"44.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-schema-registry.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/integ.json new file mode 100644 index 0000000000000..e0e447200cbb7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/integ.json @@ -0,0 +1,14 @@ +{ + "version": "44.0.0", + "testCases": { + "SchemaRegistryInteg/DefaultTest": { + "stacks": [ + "lambda-event-source-glue-schema-registry", + "lambda-event-source-confluent-schema-registry" + ], + "assertionStack": "SchemaRegistryInteg/DefaultTest/DeployAssert", + "assertionStackName": "SchemaRegistryIntegDefaultTestDeployAssertC190A9FD" + } + }, + "minimumCliVersion": "2.1018.0" +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-confluent-schema-registry.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-confluent-schema-registry.assets.json new file mode 100644 index 0000000000000..161fa2422eb49 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-confluent-schema-registry.assets.json @@ -0,0 +1,20 @@ +{ + "version": "44.0.0", + "files": { + "da9f0babd9a7dd86b158b5175c2006fd78e7a2f41f78006949658b482a4b865f": { + "displayName": "lambda-event-source-confluent-schema-registry Template", + "source": { + "path": "lambda-event-source-confluent-schema-registry.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "da9f0babd9a7dd86b158b5175c2006fd78e7a2f41f78006949658b482a4b865f.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-confluent-schema-registry.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-confluent-schema-registry.template.json new file mode 100644 index 0000000000000..ae7883d2c30a0 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-confluent-schema-registry.template.json @@ -0,0 +1,196 @@ +{ + "Resources": { + "ConfluentFunctionServiceRole116DBB1D": { + "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" + ] + ] + } + ] + } + }, + "ConfluentFunctionServiceRoleDefaultPolicy7BF57A6E": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": [ + { + "Ref": "ConfluentClientCertSecret23688D94" + }, + { + "Ref": "ConfluentRootCASecret99CAE53B" + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ConfluentFunctionServiceRoleDefaultPolicy7BF57A6E", + "Roles": [ + { + "Ref": "ConfluentFunctionServiceRole116DBB1D" + } + ] + } + }, + "ConfluentFunction5D29A9A6": { + "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": [ + "ConfluentFunctionServiceRole116DBB1D", + "Arn" + ] + }, + "Runtime": "nodejs18.x" + }, + "DependsOn": [ + "ConfluentFunctionServiceRoleDefaultPolicy7BF57A6E", + "ConfluentFunctionServiceRole116DBB1D" + ] + }, + "ConfluentFunctionKafkaEventSource46eb851822d1a9f28d63dc4ec210952etesttopicsmkconfluent90EB60AA": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 100, + "FunctionName": { + "Ref": "ConfluentFunction5D29A9A6" + }, + "ProvisionedPollerConfig": { + "MaximumPollers": 3, + "MinimumPollers": 1 + }, + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "kafka-broker-1:9092", + "kafka-broker-2:9092", + "kafka-broker-3:9092" + ] + } + }, + "SelfManagedKafkaEventSourceConfig": { + "ConsumerGroupId": "test-consumer-group-smk-confluent", + "SchemaRegistryConfig": { + "AccessConfigs": [ + { + "Type": "BASIC_AUTH", + "URI": { + "Ref": "ConfluentClientCertSecret23688D94" + } + } + ], + "EventRecordFormat": "JSON", + "SchemaRegistryURI": "https://schema-registry.example.com", + "SchemaValidationConfigs": [ + { + "Attribute": "KEY" + } + ] + } + }, + "SourceAccessConfigurations": [ + { + "Type": "CLIENT_CERTIFICATE_TLS_AUTH", + "URI": { + "Ref": "ConfluentClientCertSecret23688D94" + } + }, + { + "Type": "SERVER_ROOT_CA_CERTIFICATE", + "URI": { + "Ref": "ConfluentRootCASecret99CAE53B" + } + } + ], + "StartingPosition": "TRIM_HORIZON", + "Topics": [ + "test-topic-smk-confluent" + ] + } + }, + "ConfluentRootCASecret99CAE53B": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "SecretString": "{\"certificate\":\"-----BEGIN CERTIFICATE-----\\n MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw\\n cmUuiAii9R0=\\n -----END CERTIFICATE-----\\n -----BEGIN CERTIFICATE-----\\n MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb\\n c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==\\n -----END CERTIFICATE-----\\\"\\n \"}" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ConfluentClientCertSecret23688D94": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "SecretString": "{\"certificate\":\"-----BEGIN CERTIFICATE-----\\n MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw\\n cmUuiAii9R0=\\n -----END CERTIFICATE-----\\n -----BEGIN CERTIFICATE-----\\n MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb\\n c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==\\n -----END CERTIFICATE-----\\\"\\n \",\"privateKey\":\"-----BEGIN ENCRYPTED PRIVATE KEY-----\\n zp2mwJn2NYB7AZ7+imp0azDZb+8YG2aUCiyqb6PnnA==\\n -----END ENCRYPTED PRIVATE KEY-----\"}" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-glue-schema-registry.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-glue-schema-registry.assets.json new file mode 100644 index 0000000000000..3657c70bf17c4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-glue-schema-registry.assets.json @@ -0,0 +1,20 @@ +{ + "version": "44.0.0", + "files": { + "e8e95828b9fd0a1a052a7f37cffa2928e779a1d0b0f1c70b51c4ffd5cb261db9": { + "displayName": "lambda-event-source-glue-schema-registry Template", + "source": { + "path": "lambda-event-source-glue-schema-registry.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "e8e95828b9fd0a1a052a7f37cffa2928e779a1d0b0f1c70b51c4ffd5cb261db9.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-glue-schema-registry.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-glue-schema-registry.template.json new file mode 100644 index 0000000000000..8ea299ec53ace --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/lambda-event-source-glue-schema-registry.template.json @@ -0,0 +1,230 @@ +{ + "Resources": { + "GlueFunctionServiceRoleD5EDDC4C": { + "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" + ] + ] + } + ] + } + }, + "GlueFunctionServiceRoleDefaultPolicy16E5B38F": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": [ + { + "Ref": "GlueClientCertSecret3C32AE8A" + }, + { + "Ref": "GlueRootCASecretBD1C20BD" + } + ] + }, + { + "Action": "glue:GetRegistry", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "SchemaRegistry", + "Arn" + ] + } + }, + { + "Action": "glue:GetSchemaVersion", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SchemaRegistry", + "Arn" + ] + }, + { + "Fn::Sub": [ + "arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:schema/${registryName}/*", + { + "registryName": "smk-glue-test-schema-registry" + } + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "GlueFunctionServiceRoleDefaultPolicy16E5B38F", + "Roles": [ + { + "Ref": "GlueFunctionServiceRoleD5EDDC4C" + } + ] + } + }, + "GlueFunctionEB1182FC": { + "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": [ + "GlueFunctionServiceRoleD5EDDC4C", + "Arn" + ] + }, + "Runtime": "nodejs18.x" + }, + "DependsOn": [ + "GlueFunctionServiceRoleDefaultPolicy16E5B38F", + "GlueFunctionServiceRoleD5EDDC4C" + ] + }, + "GlueFunctionKafkaEventSource46eb851822d1a9f28d63dc4ec210952etesttopicsmkglueBB4DBE04": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 100, + "FunctionName": { + "Ref": "GlueFunctionEB1182FC" + }, + "ProvisionedPollerConfig": { + "MaximumPollers": 3, + "MinimumPollers": 1 + }, + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "kafka-broker-1:9092", + "kafka-broker-2:9092", + "kafka-broker-3:9092" + ] + } + }, + "SelfManagedKafkaEventSourceConfig": { + "ConsumerGroupId": "test-consumer-group-smk-glue", + "SchemaRegistryConfig": { + "EventRecordFormat": "JSON", + "SchemaRegistryURI": { + "Fn::GetAtt": [ + "SchemaRegistry", + "Arn" + ] + }, + "SchemaValidationConfigs": [ + { + "Attribute": "KEY" + } + ] + } + }, + "SourceAccessConfigurations": [ + { + "Type": "CLIENT_CERTIFICATE_TLS_AUTH", + "URI": { + "Ref": "GlueClientCertSecret3C32AE8A" + } + }, + { + "Type": "SERVER_ROOT_CA_CERTIFICATE", + "URI": { + "Ref": "GlueRootCASecretBD1C20BD" + } + } + ], + "StartingPosition": "TRIM_HORIZON", + "Topics": [ + "test-topic-smk-glue" + ] + } + }, + "GlueRootCASecretBD1C20BD": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "SecretString": "{\"certificate\":\"-----BEGIN CERTIFICATE-----\\n MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw\\n cmUuiAii9R0=\\n -----END CERTIFICATE-----\\n -----BEGIN CERTIFICATE-----\\n MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb\\n c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==\\n -----END CERTIFICATE-----\\\"\\n \"}" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "GlueClientCertSecret3C32AE8A": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "SecretString": "{\"certificate\":\"-----BEGIN CERTIFICATE-----\\n MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw\\n cmUuiAii9R0=\\n -----END CERTIFICATE-----\\n -----BEGIN CERTIFICATE-----\\n MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb\\n c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==\\n -----END CERTIFICATE-----\\\"\\n \",\"privateKey\":\"-----BEGIN ENCRYPTED PRIVATE KEY-----\\n zp2mwJn2NYB7AZ7+imp0azDZb+8YG2aUCiyqb6PnnA==\\n -----END ENCRYPTED PRIVATE KEY-----\"}" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SchemaRegistry": { + "Type": "AWS::Glue::Registry", + "Properties": { + "Description": "Schema registry for SMK integration tests", + "Name": "smk-glue-test-schema-registry" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/manifest.json new file mode 100644 index 0000000000000..f5496be7dba82 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/manifest.json @@ -0,0 +1,648 @@ +{ + "version": "44.0.0", + "artifacts": { + "lambda-event-source-glue-schema-registry.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "lambda-event-source-glue-schema-registry.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "lambda-event-source-glue-schema-registry": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "lambda-event-source-glue-schema-registry.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}/e8e95828b9fd0a1a052a7f37cffa2928e779a1d0b0f1c70b51c4ffd5cb261db9.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "lambda-event-source-glue-schema-registry.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "lambda-event-source-glue-schema-registry.assets" + ], + "metadata": { + "/lambda-event-source-glue-schema-registry/GlueFunction": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "handler": "*", + "code": "*", + "runtime": "*" + } + } + ], + "/lambda-event-source-glue-schema-registry/GlueFunction/ServiceRole": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "assumedBy": { + "principalAccount": "*", + "assumeRoleAction": "*" + }, + "managedPolicies": [ + { + "managedPolicyArn": "*" + } + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachInlinePolicy": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachInlinePolicy": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + } + ], + "/lambda-event-source-glue-schema-registry/GlueFunction/ServiceRole/ImportServiceRole": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/lambda-event-source-glue-schema-registry/GlueFunction/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "GlueFunctionServiceRoleD5EDDC4C" + } + ], + "/lambda-event-source-glue-schema-registry/GlueFunction/ServiceRole/DefaultPolicy": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachToRole": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachToRole": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + } + ], + "/lambda-event-source-glue-schema-registry/GlueFunction/ServiceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "GlueFunctionServiceRoleDefaultPolicy16E5B38F" + } + ], + "/lambda-event-source-glue-schema-registry/GlueFunction/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "GlueFunctionEB1182FC" + } + ], + "/lambda-event-source-glue-schema-registry/GlueFunction/KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-glue": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "target": "*", + "filters": "*", + "filterEncryption": "*", + "kafkaBootstrapServers": "*", + "kafkaTopic": "*", + "kafkaConsumerGroupId": "*", + "startingPosition": "TRIM_HORIZON", + "startingPositionTimestamp": "*", + "sourceAccessConfigurations": [ + { + "type": "*", + "uri": "*" + }, + { + "type": "*", + "uri": "*" + } + ], + "onFailure": "*", + "supportS3OnFailureDestination": true, + "provisionedPollerConfig": { + "minimumPollers": "*", + "maximumPollers": "*" + }, + "batchSize": "*", + "bisectBatchOnError": "*", + "reportBatchItemFailures": "*", + "maxBatchingWindow": "*", + "maxRecordAge": "*", + "retryAttempts": "*", + "parallelizationFactor": "*", + "tumblingWindow": "*", + "enabled": "*" + } + } + ], + "/lambda-event-source-glue-schema-registry/GlueFunction/KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-glue/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "GlueFunctionKafkaEventSource46eb851822d1a9f28d63dc4ec210952etesttopicsmkglueBB4DBE04" + } + ], + "/lambda-event-source-glue-schema-registry/GlueRootCASecret": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "secretObjectValue": "*" + } + } + ], + "/lambda-event-source-glue-schema-registry/GlueRootCASecret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "GlueRootCASecretBD1C20BD" + } + ], + "/lambda-event-source-glue-schema-registry/GlueClientCertSecret": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "secretObjectValue": "*" + } + } + ], + "/lambda-event-source-glue-schema-registry/GlueClientCertSecret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "GlueClientCertSecret3C32AE8A" + } + ], + "/lambda-event-source-glue-schema-registry/SchemaRegistry": [ + { + "type": "aws:cdk:logicalId", + "data": "SchemaRegistry" + } + ], + "/lambda-event-source-glue-schema-registry/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/lambda-event-source-glue-schema-registry/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ], + "GlueFunctioninlinePolicyAddedToExecutionRole043C5A5B3": [ + { + "type": "aws:cdk:logicalId", + "data": "GlueFunctioninlinePolicyAddedToExecutionRole043C5A5B3", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "GlueFunctioninlinePolicyAddedToExecutionRole14519DF66": [ + { + "type": "aws:cdk:logicalId", + "data": "GlueFunctioninlinePolicyAddedToExecutionRole14519DF66", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ] + }, + "displayName": "lambda-event-source-glue-schema-registry" + }, + "lambda-event-source-confluent-schema-registry.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "lambda-event-source-confluent-schema-registry.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "lambda-event-source-confluent-schema-registry": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "lambda-event-source-confluent-schema-registry.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}/da9f0babd9a7dd86b158b5175c2006fd78e7a2f41f78006949658b482a4b865f.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "lambda-event-source-confluent-schema-registry.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "lambda-event-source-confluent-schema-registry.assets" + ], + "metadata": { + "/lambda-event-source-confluent-schema-registry/ConfluentFunction": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "handler": "*", + "code": "*", + "runtime": "*" + } + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentFunction/ServiceRole": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "assumedBy": { + "principalAccount": "*", + "assumeRoleAction": "*" + }, + "managedPolicies": [ + { + "managedPolicyArn": "*" + } + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachInlinePolicy": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachInlinePolicy": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentFunction/ServiceRole/ImportServiceRole": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentFunction/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ConfluentFunctionServiceRole116DBB1D" + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentFunction/ServiceRole/DefaultPolicy": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachToRole": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachToRole": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentFunction/ServiceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ConfluentFunctionServiceRoleDefaultPolicy7BF57A6E" + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentFunction/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ConfluentFunction5D29A9A6" + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentFunction/KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-confluent": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "target": "*", + "filters": "*", + "filterEncryption": "*", + "kafkaBootstrapServers": "*", + "kafkaTopic": "*", + "kafkaConsumerGroupId": "*", + "startingPosition": "TRIM_HORIZON", + "startingPositionTimestamp": "*", + "sourceAccessConfigurations": [ + { + "type": "*", + "uri": "*" + }, + { + "type": "*", + "uri": "*" + } + ], + "onFailure": "*", + "supportS3OnFailureDestination": true, + "provisionedPollerConfig": { + "minimumPollers": "*", + "maximumPollers": "*" + }, + "batchSize": "*", + "bisectBatchOnError": "*", + "reportBatchItemFailures": "*", + "maxBatchingWindow": "*", + "maxRecordAge": "*", + "retryAttempts": "*", + "parallelizationFactor": "*", + "tumblingWindow": "*", + "enabled": "*" + } + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentFunction/KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-confluent/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ConfluentFunctionKafkaEventSource46eb851822d1a9f28d63dc4ec210952etesttopicsmkconfluent90EB60AA" + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentRootCASecret": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "secretObjectValue": "*" + } + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentRootCASecret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ConfluentRootCASecret99CAE53B" + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentClientCertSecret": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "secretObjectValue": "*" + } + } + ], + "/lambda-event-source-confluent-schema-registry/ConfluentClientCertSecret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ConfluentClientCertSecret23688D94" + } + ], + "/lambda-event-source-confluent-schema-registry/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/lambda-event-source-confluent-schema-registry/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "lambda-event-source-confluent-schema-registry" + }, + "SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "SchemaRegistryIntegDefaultTestDeployAssertC190A9FD": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.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}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "SchemaRegistryIntegDefaultTestDeployAssertC190A9FD.assets" + ], + "metadata": { + "/SchemaRegistryInteg/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/SchemaRegistryInteg/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "SchemaRegistryInteg/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + }, + "minimumCliVersion": "2.1018.0" +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/tree.json new file mode 100644 index 0000000000000..6c7fe68473f9a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.js.snapshot/tree.json @@ -0,0 +1 @@ +{"version":"tree-0.1","tree":{"id":"App","path":"","constructInfo":{"fqn":"aws-cdk-lib.App","version":"0.0.0"},"children":{"lambda-event-source-glue-schema-registry":{"id":"lambda-event-source-glue-schema-registry","path":"lambda-event-source-glue-schema-registry","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"GlueFunction":{"id":"GlueFunction","path":"lambda-event-source-glue-schema-registry/GlueFunction","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"0.0.0","metadata":[{"handler":"*","code":"*","runtime":"*"}]},"children":{"ServiceRole":{"id":"ServiceRole","path":"lambda-event-source-glue-schema-registry/GlueFunction/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"0.0.0","metadata":[{"assumedBy":{"principalAccount":"*","assumeRoleAction":"*"},"managedPolicies":[{"managedPolicyArn":"*"}]},{"addToPrincipalPolicy":[{}]},{"attachInlinePolicy":["*"]},{"attachInlinePolicy":["*"]},{"addToPrincipalPolicy":[{}]},{"addToPrincipalPolicy":[{}]},{"addToPrincipalPolicy":[{}]},{"addToPrincipalPolicy":[{}]}]},"children":{"ImportServiceRole":{"id":"ImportServiceRole","path":"lambda-event-source-glue-schema-registry/GlueFunction/ServiceRole/ImportServiceRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"0.0.0","metadata":["*"]}},"Resource":{"id":"Resource","path":"lambda-event-source-glue-schema-registry/GlueFunction/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"0.0.0"},"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"lambda-event-source-glue-schema-registry/GlueFunction/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"0.0.0","metadata":["*",{"attachToRole":["*"]},{"attachToRole":["*"]},{"addStatements":[{}]},{"addStatements":[{}]},{"addStatements":[{}]},{"addStatements":[{}]},{"addStatements":[{}]}]},"children":{"Resource":{"id":"Resource","path":"lambda-event-source-glue-schema-registry/GlueFunction/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["secretsmanager:DescribeSecret","secretsmanager:GetSecretValue"],"Effect":"Allow","Resource":[{"Ref":"GlueClientCertSecret3C32AE8A"},{"Ref":"GlueRootCASecretBD1C20BD"}]},{"Action":"glue:GetRegistry","Effect":"Allow","Resource":{"Fn::GetAtt":["SchemaRegistry","Arn"]}},{"Action":"glue:GetSchemaVersion","Effect":"Allow","Resource":[{"Fn::GetAtt":["SchemaRegistry","Arn"]},{"Fn::Sub":["arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:schema/${registryName}/*",{"registryName":"smk-glue-test-schema-registry"}]}]}],"Version":"2012-10-17"},"policyName":"GlueFunctionServiceRoleDefaultPolicy16E5B38F","roles":[{"Ref":"GlueFunctionServiceRoleD5EDDC4C"}]}}}}}}},"Resource":{"id":"Resource","path":"lambda-event-source-glue-schema-registry/GlueFunction/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"0.0.0"},"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":["GlueFunctionServiceRoleD5EDDC4C","Arn"]},"runtime":"nodejs18.x"}}},"KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-glue":{"id":"KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-glue","path":"lambda-event-source-glue-schema-registry/GlueFunction/KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-glue","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.EventSourceMapping","version":"0.0.0","metadata":[{"target":"*","filters":"*","filterEncryption":"*","kafkaBootstrapServers":"*","kafkaTopic":"*","kafkaConsumerGroupId":"*","startingPosition":"TRIM_HORIZON","startingPositionTimestamp":"*","sourceAccessConfigurations":[{"type":"*","uri":"*"},{"type":"*","uri":"*"}],"onFailure":"*","supportS3OnFailureDestination":true,"provisionedPollerConfig":{"minimumPollers":"*","maximumPollers":"*"},"batchSize":"*","bisectBatchOnError":"*","reportBatchItemFailures":"*","maxBatchingWindow":"*","maxRecordAge":"*","retryAttempts":"*","parallelizationFactor":"*","tumblingWindow":"*","enabled":"*"}]},"children":{"Resource":{"id":"Resource","path":"lambda-event-source-glue-schema-registry/GlueFunction/KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-glue/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnEventSourceMapping","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::EventSourceMapping","aws:cdk:cloudformation:props":{"batchSize":100,"functionName":{"Ref":"GlueFunctionEB1182FC"},"provisionedPollerConfig":{"minimumPollers":1,"maximumPollers":3},"selfManagedEventSource":{"endpoints":{"kafkaBootstrapServers":["kafka-broker-1:9092","kafka-broker-2:9092","kafka-broker-3:9092"]}},"selfManagedKafkaEventSourceConfig":{"consumerGroupId":"test-consumer-group-smk-glue","schemaRegistryConfig":{"schemaRegistryUri":{"Fn::GetAtt":["SchemaRegistry","Arn"]},"eventRecordFormat":"JSON","schemaValidationConfigs":[{"attribute":"KEY"}]}},"sourceAccessConfigurations":[{"type":"CLIENT_CERTIFICATE_TLS_AUTH","uri":{"Ref":"GlueClientCertSecret3C32AE8A"}},{"type":"SERVER_ROOT_CA_CERTIFICATE","uri":{"Ref":"GlueRootCASecretBD1C20BD"}}],"startingPosition":"TRIM_HORIZON","topics":["test-topic-smk-glue"]}}}}}}},"GlueRootCASecret":{"id":"GlueRootCASecret","path":"lambda-event-source-glue-schema-registry/GlueRootCASecret","constructInfo":{"fqn":"aws-cdk-lib.aws_secretsmanager.Secret","version":"0.0.0","metadata":[{"secretObjectValue":"*"}]},"children":{"Resource":{"id":"Resource","path":"lambda-event-source-glue-schema-registry/GlueRootCASecret/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_secretsmanager.CfnSecret","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::SecretsManager::Secret","aws:cdk:cloudformation:props":{"secretString":"{\"certificate\":\"-----BEGIN CERTIFICATE-----\\n MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw\\n cmUuiAii9R0=\\n -----END CERTIFICATE-----\\n -----BEGIN CERTIFICATE-----\\n MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb\\n c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==\\n -----END CERTIFICATE-----\\\"\\n \"}"}}}}},"GlueClientCertSecret":{"id":"GlueClientCertSecret","path":"lambda-event-source-glue-schema-registry/GlueClientCertSecret","constructInfo":{"fqn":"aws-cdk-lib.aws_secretsmanager.Secret","version":"0.0.0","metadata":[{"secretObjectValue":"*"}]},"children":{"Resource":{"id":"Resource","path":"lambda-event-source-glue-schema-registry/GlueClientCertSecret/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_secretsmanager.CfnSecret","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::SecretsManager::Secret","aws:cdk:cloudformation:props":{"secretString":"{\"certificate\":\"-----BEGIN CERTIFICATE-----\\n MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw\\n cmUuiAii9R0=\\n -----END CERTIFICATE-----\\n -----BEGIN CERTIFICATE-----\\n MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb\\n c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==\\n -----END CERTIFICATE-----\\\"\\n \",\"privateKey\":\"-----BEGIN ENCRYPTED PRIVATE KEY-----\\n zp2mwJn2NYB7AZ7+imp0azDZb+8YG2aUCiyqb6PnnA==\\n -----END ENCRYPTED PRIVATE KEY-----\"}"}}}}},"SchemaRegistry":{"id":"SchemaRegistry","path":"lambda-event-source-glue-schema-registry/SchemaRegistry","constructInfo":{"fqn":"aws-cdk-lib.aws_glue.CfnRegistry","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Glue::Registry","aws:cdk:cloudformation:props":{"description":"Schema registry for SMK integration tests","name":"smk-glue-test-schema-registry"}}},"BootstrapVersion":{"id":"BootstrapVersion","path":"lambda-event-source-glue-schema-registry/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"lambda-event-source-glue-schema-registry/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}}},"lambda-event-source-confluent-schema-registry":{"id":"lambda-event-source-confluent-schema-registry","path":"lambda-event-source-confluent-schema-registry","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"ConfluentFunction":{"id":"ConfluentFunction","path":"lambda-event-source-confluent-schema-registry/ConfluentFunction","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.Function","version":"0.0.0","metadata":[{"handler":"*","code":"*","runtime":"*"}]},"children":{"ServiceRole":{"id":"ServiceRole","path":"lambda-event-source-confluent-schema-registry/ConfluentFunction/ServiceRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"0.0.0","metadata":[{"assumedBy":{"principalAccount":"*","assumeRoleAction":"*"},"managedPolicies":[{"managedPolicyArn":"*"}]},{"addToPrincipalPolicy":[{}]},{"attachInlinePolicy":["*"]},{"attachInlinePolicy":["*"]},{"addToPrincipalPolicy":[{}]},{"addToPrincipalPolicy":[{}]},{"addToPrincipalPolicy":[{}]}]},"children":{"ImportServiceRole":{"id":"ImportServiceRole","path":"lambda-event-source-confluent-schema-registry/ConfluentFunction/ServiceRole/ImportServiceRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"0.0.0","metadata":["*"]}},"Resource":{"id":"Resource","path":"lambda-event-source-confluent-schema-registry/ConfluentFunction/ServiceRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"0.0.0"},"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"]]}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"lambda-event-source-confluent-schema-registry/ConfluentFunction/ServiceRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"0.0.0","metadata":["*",{"attachToRole":["*"]},{"attachToRole":["*"]},{"addStatements":[{}]},{"addStatements":[{}]},{"addStatements":[{}]},{"addStatements":[{}]}]},"children":{"Resource":{"id":"Resource","path":"lambda-event-source-confluent-schema-registry/ConfluentFunction/ServiceRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":["secretsmanager:DescribeSecret","secretsmanager:GetSecretValue"],"Effect":"Allow","Resource":[{"Ref":"ConfluentClientCertSecret23688D94"},{"Ref":"ConfluentRootCASecret99CAE53B"}]}],"Version":"2012-10-17"},"policyName":"ConfluentFunctionServiceRoleDefaultPolicy7BF57A6E","roles":[{"Ref":"ConfluentFunctionServiceRole116DBB1D"}]}}}}}}},"Resource":{"id":"Resource","path":"lambda-event-source-confluent-schema-registry/ConfluentFunction/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnFunction","version":"0.0.0"},"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":["ConfluentFunctionServiceRole116DBB1D","Arn"]},"runtime":"nodejs18.x"}}},"KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-confluent":{"id":"KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-confluent","path":"lambda-event-source-confluent-schema-registry/ConfluentFunction/KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-confluent","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.EventSourceMapping","version":"0.0.0","metadata":[{"target":"*","filters":"*","filterEncryption":"*","kafkaBootstrapServers":"*","kafkaTopic":"*","kafkaConsumerGroupId":"*","startingPosition":"TRIM_HORIZON","startingPositionTimestamp":"*","sourceAccessConfigurations":[{"type":"*","uri":"*"},{"type":"*","uri":"*"}],"onFailure":"*","supportS3OnFailureDestination":true,"provisionedPollerConfig":{"minimumPollers":"*","maximumPollers":"*"},"batchSize":"*","bisectBatchOnError":"*","reportBatchItemFailures":"*","maxBatchingWindow":"*","maxRecordAge":"*","retryAttempts":"*","parallelizationFactor":"*","tumblingWindow":"*","enabled":"*"}]},"children":{"Resource":{"id":"Resource","path":"lambda-event-source-confluent-schema-registry/ConfluentFunction/KafkaEventSource:46eb851822d1a9f28d63dc4ec210952e:test-topic-smk-confluent/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_lambda.CfnEventSourceMapping","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::Lambda::EventSourceMapping","aws:cdk:cloudformation:props":{"batchSize":100,"functionName":{"Ref":"ConfluentFunction5D29A9A6"},"provisionedPollerConfig":{"minimumPollers":1,"maximumPollers":3},"selfManagedEventSource":{"endpoints":{"kafkaBootstrapServers":["kafka-broker-1:9092","kafka-broker-2:9092","kafka-broker-3:9092"]}},"selfManagedKafkaEventSourceConfig":{"consumerGroupId":"test-consumer-group-smk-confluent","schemaRegistryConfig":{"schemaRegistryUri":"https://schema-registry.example.com","eventRecordFormat":"JSON","accessConfigs":[{"type":"BASIC_AUTH","uri":{"Ref":"ConfluentClientCertSecret23688D94"}}],"schemaValidationConfigs":[{"attribute":"KEY"}]}},"sourceAccessConfigurations":[{"type":"CLIENT_CERTIFICATE_TLS_AUTH","uri":{"Ref":"ConfluentClientCertSecret23688D94"}},{"type":"SERVER_ROOT_CA_CERTIFICATE","uri":{"Ref":"ConfluentRootCASecret99CAE53B"}}],"startingPosition":"TRIM_HORIZON","topics":["test-topic-smk-confluent"]}}}}}}},"ConfluentRootCASecret":{"id":"ConfluentRootCASecret","path":"lambda-event-source-confluent-schema-registry/ConfluentRootCASecret","constructInfo":{"fqn":"aws-cdk-lib.aws_secretsmanager.Secret","version":"0.0.0","metadata":[{"secretObjectValue":"*"}]},"children":{"Resource":{"id":"Resource","path":"lambda-event-source-confluent-schema-registry/ConfluentRootCASecret/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_secretsmanager.CfnSecret","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::SecretsManager::Secret","aws:cdk:cloudformation:props":{"secretString":"{\"certificate\":\"-----BEGIN CERTIFICATE-----\\n MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw\\n cmUuiAii9R0=\\n -----END CERTIFICATE-----\\n -----BEGIN CERTIFICATE-----\\n MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb\\n c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==\\n -----END CERTIFICATE-----\\\"\\n \"}"}}}}},"ConfluentClientCertSecret":{"id":"ConfluentClientCertSecret","path":"lambda-event-source-confluent-schema-registry/ConfluentClientCertSecret","constructInfo":{"fqn":"aws-cdk-lib.aws_secretsmanager.Secret","version":"0.0.0","metadata":[{"secretObjectValue":"*"}]},"children":{"Resource":{"id":"Resource","path":"lambda-event-source-confluent-schema-registry/ConfluentClientCertSecret/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_secretsmanager.CfnSecret","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::SecretsManager::Secret","aws:cdk:cloudformation:props":{"secretString":"{\"certificate\":\"-----BEGIN CERTIFICATE-----\\n MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw\\n cmUuiAii9R0=\\n -----END CERTIFICATE-----\\n -----BEGIN CERTIFICATE-----\\n MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb\\n c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg==\\n -----END CERTIFICATE-----\\\"\\n \",\"privateKey\":\"-----BEGIN ENCRYPTED PRIVATE KEY-----\\n zp2mwJn2NYB7AZ7+imp0azDZb+8YG2aUCiyqb6PnnA==\\n -----END ENCRYPTED PRIVATE KEY-----\"}"}}}}},"BootstrapVersion":{"id":"BootstrapVersion","path":"lambda-event-source-confluent-schema-registry/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"lambda-event-source-confluent-schema-registry/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}}},"SchemaRegistryInteg":{"id":"SchemaRegistryInteg","path":"SchemaRegistryInteg","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTest","version":"0.0.0"},"children":{"DefaultTest":{"id":"DefaultTest","path":"SchemaRegistryInteg/DefaultTest","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTestCase","version":"0.0.0"},"children":{"Default":{"id":"Default","path":"SchemaRegistryInteg/DefaultTest/Default","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"DeployAssert":{"id":"DeployAssert","path":"SchemaRegistryInteg/DefaultTest/DeployAssert","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"BootstrapVersion":{"id":"BootstrapVersion","path":"SchemaRegistryInteg/DefaultTest/DeployAssert/BootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnParameter","version":"0.0.0"}},"CheckBootstrapVersion":{"id":"CheckBootstrapVersion","path":"SchemaRegistryInteg/DefaultTest/DeployAssert/CheckBootstrapVersion","constructInfo":{"fqn":"aws-cdk-lib.CfnRule","version":"0.0.0"}}}}}}}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}}}}} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.ts new file mode 100644 index 0000000000000..5262159d5fb14 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.kafka-schema-registry.ts @@ -0,0 +1,186 @@ +import { TestFunction } from './test-function'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import { + SelfManagedKafkaEventSource, + AuthenticationMethod, + GlueSchemaRegistry, + ConfluentSchemaRegistry, +} from 'aws-cdk-lib/aws-lambda-event-sources'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { App, StackProps, Stack, SecretValue } from 'aws-cdk-lib'; +import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager'; +import { CfnRegistry } from 'aws-cdk-lib/aws-glue'; + +// Self-Managed Kafka Stack with Schema Registry +export class SmkGlueSchemaRegistryStack extends Stack { + constructor(scope: App, id: string, props?: StackProps) { + super(scope, id, props); + + // Create a Lambda function + const testLambdaFunction = new TestFunction(this, 'GlueFunction'); + + // Create dummy certificates for authentication + const dummyCertString = `-----BEGIN CERTIFICATE----- + MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw + cmUuiAii9R0= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb + c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg== + -----END CERTIFICATE-----" + `; + + const dummyPrivateKey = `-----BEGIN ENCRYPTED PRIVATE KEY----- + zp2mwJn2NYB7AZ7+imp0azDZb+8YG2aUCiyqb6PnnA== + -----END ENCRYPTED PRIVATE KEY-----`; + + // Create secrets for Kafka authentication + const rootCASecret = new secretsmanager.Secret(this, 'GlueRootCASecret', { + secretObjectValue: { + certificate: SecretValue.unsafePlainText(dummyCertString), + }, + }); + + const clientCertificatesSecret = new secretsmanager.Secret(this, 'GlueClientCertSecret', { + secretObjectValue: { + certificate: SecretValue.unsafePlainText(dummyCertString), + privateKey: SecretValue.unsafePlainText(dummyPrivateKey), + }, + }); + + // Grant read permissions to the Lambda function + rootCASecret.grantRead(testLambdaFunction); + clientCertificatesSecret.grantRead(testLambdaFunction); + + // Create a Glue Schema Registry + const glueRegistry = new CfnRegistry(this, 'SchemaRegistry', { + name: 'smk-glue-test-schema-registry', + description: 'Schema registry for SMK integration tests', + }); + + // Define Kafka bootstrap servers + const bootstrapServers = [ + 'kafka-broker-1:9092', + 'kafka-broker-2:9092', + 'kafka-broker-3:9092', + ]; + + // Common configuration for SMK event sources + const commonConfig = { + bootstrapServers, + secret: clientCertificatesSecret, + authenticationMethod: AuthenticationMethod.CLIENT_CERTIFICATE_TLS_AUTH, + rootCACertificate: rootCASecret, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + }; + + // SMK with Glue Schema Registry + testLambdaFunction.addEventSource(new SelfManagedKafkaEventSource({ + ...commonConfig, + topic: 'test-topic-smk-glue', + consumerGroupId: 'test-consumer-group-smk-glue', + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new GlueSchemaRegistry({ + schemaRegistry: glueRegistry, + eventRecordFormat: lambda.EventRecordFormat.JSON, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + })); + } +} + +// Self-Managed Kafka Stack with Schema Registry +export class SmkConfluentSchemaRegistryStack extends Stack { + constructor(scope: App, id: string, props?: StackProps) { + super(scope, id, props); + + // Create a Lambda function + const testLambdaFunction = new TestFunction(this, 'ConfluentFunction'); + + // Create dummy certificates for authentication + const dummyCertString = `-----BEGIN CERTIFICATE----- + MIIE5DCCAsygAwIBAgIRAPJdwaFaNRrytHBto0j5BA0wDQYJKoZIhvcNAQELBQAw + cmUuiAii9R0= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIFgjCCA2qgAwIBAgIQdjNZd6uFf9hbNC5RdfmHrzANBgkqhkiG9w0BAQsFADBb + c8PH3PSoAaRwMMgOSA2ALJvbRz8mpg== + -----END CERTIFICATE-----" + `; + + const dummyPrivateKey = `-----BEGIN ENCRYPTED PRIVATE KEY----- + zp2mwJn2NYB7AZ7+imp0azDZb+8YG2aUCiyqb6PnnA== + -----END ENCRYPTED PRIVATE KEY-----`; + + // Create secrets for Kafka authentication + const rootCASecret = new secretsmanager.Secret(this, 'ConfluentRootCASecret', { + secretObjectValue: { + certificate: SecretValue.unsafePlainText(dummyCertString), + }, + }); + + const clientCertificatesSecret = new secretsmanager.Secret(this, 'ConfluentClientCertSecret', { + secretObjectValue: { + certificate: SecretValue.unsafePlainText(dummyCertString), + privateKey: SecretValue.unsafePlainText(dummyPrivateKey), + }, + }); + + // Grant read permissions to the Lambda function + rootCASecret.grantRead(testLambdaFunction); + clientCertificatesSecret.grantRead(testLambdaFunction); + + // Define Kafka bootstrap servers + const bootstrapServers = [ + 'kafka-broker-1:9092', + 'kafka-broker-2:9092', + 'kafka-broker-3:9092', + ]; + + // Common configuration for SMK event sources + const commonConfig = { + bootstrapServers, + secret: clientCertificatesSecret, + authenticationMethod: AuthenticationMethod.CLIENT_CERTIFICATE_TLS_AUTH, + rootCACertificate: rootCASecret, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + }; + + // SMK with Confluent Schema Registry + testLambdaFunction.addEventSource(new SelfManagedKafkaEventSource({ + ...commonConfig, + topic: 'test-topic-smk-confluent', + consumerGroupId: 'test-consumer-group-smk-confluent', + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new ConfluentSchemaRegistry({ + schemaRegistryUri: 'https://schema-registry.example.com', + eventRecordFormat: lambda.EventRecordFormat.JSON, + authenticationType: lambda.KafkaSchemaRegistryAccessConfigType.BASIC_AUTH, + secret: clientCertificatesSecret, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + })); + } +} + +// Create the app and stacks +const app = new App({ + postCliContext: { + '@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy': false, + }, +}); +const glueStack = new SmkGlueSchemaRegistryStack(app, 'lambda-event-source-glue-schema-registry'); +const confluentStack = new SmkConfluentSchemaRegistryStack(app, 'lambda-event-source-confluent-schema-registry'); + +// Create the integration tests +new IntegTest(app, 'SchemaRegistryInteg', { + testCases: [glueStack, confluentStack], +}); + +app.synth(); 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 d50df87945969..c3ec512e21880 100644 --- a/packages/aws-cdk-lib/aws-lambda-event-sources/README.md +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/README.md @@ -445,6 +445,74 @@ myFunction.addEventSource(new ManagedKafkaEventSource({ })); ``` +Set a confluent or self-managed schema registry to de-serialize events from the event source. Note, this will similarly work for `SelfManagedKafkaEventSource` but the example only shows setup for `ManagedKafkaEventSource`. + +```ts +import { ManagedKafkaEventSource, ConfluentSchemaRegistry } from 'aws-cdk-lib/aws-lambda-event-sources'; +import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; + +// Your MSK cluster arn +declare const clusterArn: string; + +// The Kafka topic you want to subscribe to +const topic = 'some-cool-topic'; + +const secret = new Secret(this, 'Secret', { secretName: 'AmazonMSK_KafkaSecret' }); + +declare const myFunction: lambda.Function; +myFunction.addEventSource(new ManagedKafkaEventSource({ + clusterArn, + topic, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new ConfluentSchemaRegistry({ + schemaRegistryUri: 'https://example.com', + eventRecordFormat: lambda.EventRecordFormat.JSON, + authenticationType: lambda.KafkaSchemaRegistryAccessConfigType.BASIC_AUTH, + secret: secret, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), +})); +``` + +Set Glue schema registry to de-serialize events from the event source. Note, this will similarly work for `SelfManagedKafkaEventSource` but the example only shows setup for `ManagedKafkaEventSource`. + +```ts +import { CfnRegistry } from 'aws-cdk-lib/aws-glue'; +import { ManagedKafkaEventSource, GlueSchemaRegistry } from 'aws-cdk-lib/aws-lambda-event-sources'; + +// Your MSK cluster arn +declare const clusterArn: string; + +// The Kafka topic you want to subscribe to +const topic = 'some-cool-topic'; + +// Your Glue Schema Registry +const glueRegistry = new CfnRegistry(this, 'Registry', { + name: 'schema-registry', + description: 'Schema registry for event source', +}); + +declare const myFunction: lambda.Function; +myFunction.addEventSource(new ManagedKafkaEventSource({ + clusterArn, + topic, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new GlueSchemaRegistry({ + schemaRegistry: glueRegistry, + eventRecordFormat: lambda.EventRecordFormat.JSON, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), +})); +``` + ## Roadmap Eventually, this module will support all the event sources described under diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/lib/confluent-schema-registry.ts b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/confluent-schema-registry.ts new file mode 100644 index 0000000000000..b54330aceb53c --- /dev/null +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/confluent-schema-registry.ts @@ -0,0 +1,52 @@ +import { IEventSourceMapping, IFunction } from '../../aws-lambda/lib'; +import { ISchemaRegistry, KafkaSchemaRegistryAccessConfigType, KafkaSchemaRegistryConfig, SchemaRegistryProps } from '../../aws-lambda/lib/schema-registry'; +import { ISecret } from '../../aws-secretsmanager'; + +/** + * Properties for confluent schema registry configuration. + */ +export interface ConfluentSchemaRegistryProps extends SchemaRegistryProps { + /** + * The URI for your schema registry. + * + * @default - none + */ + readonly schemaRegistryUri: string; + + /** + * The type of authentication for schema registry credentials. + * @default none + */ + readonly authenticationType: KafkaSchemaRegistryAccessConfigType; + + /** + * The secret with the schema registry credentials. + * @default none + */ + readonly secret: ISecret; +} + +/** + * Confluent schema registry configuration for a Lambda event source. + */ +export class ConfluentSchemaRegistry implements ISchemaRegistry { + constructor(private readonly props: ConfluentSchemaRegistryProps) { + } + + /** + * Returns a schema registry configuration. + */ + public bind(_target: IEventSourceMapping, targetHandler: IFunction): KafkaSchemaRegistryConfig { + this.props.secret.grantRead(targetHandler); + + return { + schemaRegistryUri: this.props.schemaRegistryUri, + eventRecordFormat: this.props.eventRecordFormat, + accessConfigs: [{ + type: this.props.authenticationType, + uri: this.props.secret.secretArn, + }], + schemaValidationConfigs: this.props.schemaValidationConfigs, + }; + } +} diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/lib/glue-schema-registry.ts b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/glue-schema-registry.ts new file mode 100644 index 0000000000000..02e7b707b4ac3 --- /dev/null +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/glue-schema-registry.ts @@ -0,0 +1,92 @@ +import { CfnRegistry } from '../../aws-glue'; +import * as iam from '../../aws-iam'; +import { IEventSourceMapping, IFunction } from '../../aws-lambda/lib'; +import { ISchemaRegistry, KafkaSchemaRegistryConfig, SchemaRegistryProps } from '../../aws-lambda/lib/schema-registry'; +import { Fn, ValidationError } from '../../core'; + +const GLUE_SCHEMA_REGISTRY_ARN_REGEX = /^arn:[^:]+:glue:[^:]+:[^:]+:registry\/([^\/]+)$/; + +/** + * Properties for glue schema registry configuration. + */ +export interface GlueSchemaRegistryProps extends SchemaRegistryProps { + /** + * The CfnRegistry reference of your glue schema registry. If used, schemaRegistryArn will be ignored. + * + * @default - none + */ + readonly schemaRegistry?: CfnRegistry; + /** + * The Arn of your glue schema registry. + * + * @default - none + */ + readonly schemaRegistryArn?: string; +} + +/** + * Glue schema registry configuration for a Lambda event source. + */ +export class GlueSchemaRegistry implements ISchemaRegistry { + constructor(private readonly props: GlueSchemaRegistryProps) { + } + + /** + * Returns a schema registry configuration. + */ + public bind(_target: IEventSourceMapping, targetHandler: IFunction): KafkaSchemaRegistryConfig { + const registryProps = this.getRegistryProps(this.props, _target); + this.getSchemaRegistryPolicies( + registryProps.arn, + registryProps.name, + ).forEach(i => targetHandler.addToRolePolicy(i)); + + return { + schemaRegistryUri: registryProps.arn, + eventRecordFormat: this.props.eventRecordFormat, + schemaValidationConfigs: this.props.schemaValidationConfigs, + }; + } + + private getRegistryProps(props: GlueSchemaRegistryProps, _target: IEventSourceMapping) { + if (props.schemaRegistry) { + return { + arn: props.schemaRegistry.attrArn, + name: props.schemaRegistry.name, + }; + } + if (props.schemaRegistryArn) { + const glueRegistryMatch = props.schemaRegistryArn?.match(GLUE_SCHEMA_REGISTRY_ARN_REGEX); + if (!glueRegistryMatch) { + throw new ValidationError(`schemaRegistryArn ${this.props.schemaRegistryArn} must match ${GLUE_SCHEMA_REGISTRY_ARN_REGEX}`, _target); + } + return { + arn: props.schemaRegistryArn, + name: glueRegistryMatch[1], + }; + } + throw new ValidationError('one of schemaRegistryArn or schemaRegistry must be passed', _target); + } + + private getSchemaRegistryPolicies(glueRegistryArn: string, glueRegistryName: string) { + return [ + new iam.PolicyStatement( + { + actions: ['glue:GetRegistry'], + resources: [glueRegistryArn], + }, + ), + new iam.PolicyStatement( + { + actions: ['glue:GetSchemaVersion'], + resources: [ + glueRegistryArn, + Fn.sub('arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:schema/${registryName}/*', { + registryName: glueRegistryName, + }), + ], + }, + ), + ]; + } +} diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/lib/index.ts b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/index.ts index fa4d02603997f..ea710d869e1af 100644 --- a/packages/aws-cdk-lib/aws-lambda-event-sources/lib/index.ts +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/lib/index.ts @@ -9,3 +9,5 @@ export * from './stream'; export * from './sqs'; export * from './sqs-dlq'; export * from './s3-onfailuire-destination'; +export * from './confluent-schema-registry'; +export * from './glue-schema-registry'; 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 403e9530099d6..e341be604c31e 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 @@ -4,6 +4,7 @@ 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 { ISchemaRegistry } from '../../aws-lambda/lib/schema-registry'; import * as secretsmanager from '../../aws-secretsmanager'; import { Stack, Names, Annotations, UnscopedValidationError, ValidationError } from '../../core'; import { md5hash } from '../../core/lib/helpers-internal'; @@ -62,6 +63,13 @@ export interface KafkaEventSourceProps extends BaseStreamEventSourceProps { * @default - no timestamp */ readonly startingPositionTimestamp?: number; + + /** + * Specific configuration settings for a Kafka schema registry. + * + * @default - none + */ + readonly schemaRegistryConfig?: ISchemaRegistry; } /** @@ -181,6 +189,7 @@ export class ManagedKafkaEventSource extends StreamEventSource { onFailure: this.innerProps.onFailure, supportS3OnFailureDestination: true, provisionedPollerConfig: this.innerProps.provisionedPollerConfig, + schemaRegistryConfig: this.innerProps.schemaRegistryConfig, }), ); @@ -284,6 +293,7 @@ export class SelfManagedKafkaEventSource extends StreamEventSource { onFailure: this.innerProps.onFailure, supportS3OnFailureDestination: true, provisionedPollerConfig: this.innerProps.provisionedPollerConfig, + schemaRegistryConfig: this.innerProps.schemaRegistryConfig, }), ); 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 d0e6f15b004bd..4c1ceb76d9d2b 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 { CfnRegistry } from '../../aws-glue'; import { Key } from '../../aws-kms'; import * as lambda from '../../aws-lambda'; import { Bucket } from '../../aws-s3'; @@ -1384,5 +1385,654 @@ describe('KafkaEventSource', () => { StartingPositionTimestamp: 1640995200, }); }); + + test('MSK with glue kafka schema registry with invalid arn', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const clusterArn = 'some-arn'; + const kafkaTopic = 'some-topic'; + + const mskEventMapping = new sources.ManagedKafkaEventSource( + { + clusterArn, + topic: kafkaTopic, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new sources.GlueSchemaRegistry({ + schemaRegistryArn: 'invalid-arn', + eventRecordFormat: lambda.EventRecordFormat.JSON, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + }); + + // WHEN + expect(() => fn.addEventSource(mskEventMapping)) + .toThrow(/schemaRegistryArn invalid-arn must match/); + }); + + test('MSK with glue kafka schema registry with no schema registry props', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const clusterArn = 'some-arn'; + const kafkaTopic = 'some-topic'; + + const mskEventMapping = new sources.ManagedKafkaEventSource( + { + clusterArn, + topic: kafkaTopic, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new sources.GlueSchemaRegistry({ + eventRecordFormat: lambda.EventRecordFormat.JSON, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + }); + + // WHEN + expect(() => fn.addEventSource(mskEventMapping)) + .toThrow(/one of schemaRegistryArn or schemaRegistry must be passed/); + }); + + test('MSK with confluent kafka schema registry with secret', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const clusterArn = 'some-arn'; + const kafkaTopic = 'some-topic'; + const secret = new Secret(stack, 'Secret', { secretName: 'AmazonMSK_KafkaSecret' }); + + const mskEventMapping = new sources.ManagedKafkaEventSource( + { + clusterArn, + topic: kafkaTopic, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new sources.ConfluentSchemaRegistry({ + schemaRegistryUri: 'https://example.com', + eventRecordFormat: lambda.EventRecordFormat.JSON, + authenticationType: lambda.KafkaSchemaRegistryAccessConfigType.BASIC_AUTH, + secret: secret, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + }); + + // WHEN + fn.addEventSource(mskEventMapping); + + // THEN + expect(mskEventMapping.eventSourceMappingId).toBeDefined(); + expect(mskEventMapping.eventSourceMappingArn).toBeDefined(); + + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Lambda::EventSourceMapping', { + ProvisionedPollerConfig: { + MinimumPollers: 1, + MaximumPollers: 3, + }, + AmazonManagedKafkaEventSourceConfig: { + SchemaRegistryConfig: { + SchemaRegistryURI: 'https://example.com', + EventRecordFormat: 'JSON', + AccessConfigs: [ + { + Type: 'BASIC_AUTH', + URI: { + Ref: 'SecretA720EF05', + }, + }, + ], + SchemaValidationConfigs: [{ Attribute: 'KEY' }], + }, + }, + }); + }); + + test('MSK with glue kafka schema registry', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const glueRegistry = new CfnRegistry(stack, 'SchemaRegistry', { + name: 'msk-test-schema-registry', + description: 'Schema registry for SMK integration tests', + }); + const clusterArn = 'some-arn'; + const kafkaTopic = 'some-topic'; + + const mskEventMapping = new sources.ManagedKafkaEventSource( + { + clusterArn, + topic: kafkaTopic, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new sources.GlueSchemaRegistry({ + schemaRegistry: glueRegistry, + eventRecordFormat: lambda.EventRecordFormat.JSON, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + }); + + // WHEN + fn.addEventSource(mskEventMapping); + + // THEN + expect(mskEventMapping.eventSourceMappingId).toBeDefined(); + expect(mskEventMapping.eventSourceMappingArn).toBeDefined(); + + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'glue:GetRegistry', + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': ['SchemaRegistry', 'Arn'], + }, + }, + { + Action: 'glue:GetSchemaVersion', + Effect: 'Allow', + Resource: [ + { + 'Fn::GetAtt': ['SchemaRegistry', 'Arn'], + }, + { + 'Fn::Sub': [ + 'arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:schema/${registryName}/*', + { registryName: 'msk-test-schema-registry' }, + ], + }, + ], + }, + { + Action: [ + 'kafka:DescribeCluster', + 'kafka:GetBootstrapBrokers', + 'kafka:ListScramSecrets', + ], + Effect: 'Allow', + Resource: 'some-arn', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'FnServiceRoleDefaultPolicyC6A839BF', + Roles: [ + { + Ref: 'FnServiceRoleB9001A96', + }, + ], + }); + + template.hasResourceProperties('AWS::Lambda::EventSourceMapping', { + ProvisionedPollerConfig: { + MinimumPollers: 1, + MaximumPollers: 3, + }, + AmazonManagedKafkaEventSourceConfig: { + SchemaRegistryConfig: { + SchemaRegistryURI: { + 'Fn::GetAtt': ['SchemaRegistry', 'Arn'], + }, + EventRecordFormat: 'JSON', + SchemaValidationConfigs: [{ Attribute: 'KEY' }], + }, + }, + }); + }); + + test('MSK with glue kafka schema registry arn', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const glueRegistry = 'arn:aws:glue:us-west-2:123456789012:registry/example'; + const clusterArn = 'some-arn'; + const kafkaTopic = 'some-topic'; + + const mskEventMapping = new sources.ManagedKafkaEventSource( + { + clusterArn, + topic: kafkaTopic, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new sources.GlueSchemaRegistry({ + schemaRegistryArn: glueRegistry, + eventRecordFormat: lambda.EventRecordFormat.JSON, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + }); + + // WHEN + fn.addEventSource(mskEventMapping); + + // THEN + expect(mskEventMapping.eventSourceMappingId).toBeDefined(); + expect(mskEventMapping.eventSourceMappingArn).toBeDefined(); + + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'glue:GetRegistry', + Effect: 'Allow', + Resource: 'arn:aws:glue:us-west-2:123456789012:registry/example', + }, + { + Action: 'glue:GetSchemaVersion', + Effect: 'Allow', + Resource: [ + 'arn:aws:glue:us-west-2:123456789012:registry/example', + { + 'Fn::Sub': [ + 'arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:schema/${registryName}/*', + { registryName: 'example' }, + ], + }, + ], + }, + { + Action: [ + 'kafka:DescribeCluster', + 'kafka:GetBootstrapBrokers', + 'kafka:ListScramSecrets', + ], + Effect: 'Allow', + Resource: 'some-arn', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'FnServiceRoleDefaultPolicyC6A839BF', + Roles: [ + { + Ref: 'FnServiceRoleB9001A96', + }, + ], + }); + + template.hasResourceProperties('AWS::Lambda::EventSourceMapping', { + ProvisionedPollerConfig: { + MinimumPollers: 1, + MaximumPollers: 3, + }, + AmazonManagedKafkaEventSourceConfig: { + SchemaRegistryConfig: { + SchemaRegistryURI: 'arn:aws:glue:us-west-2:123456789012:registry/example', + EventRecordFormat: 'JSON', + SchemaValidationConfigs: [{ Attribute: 'KEY' }], + }, + }, + }); + }); + + test('MSK with kafka schema registry and consumer group ID', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const secret = new Secret(stack, 'Secret', { secretName: 'AmazonMSK_KafkaSecret' }); + const clusterArn = 'some-arn'; + const kafkaTopic = 'some-topic'; + + const mskEventMapping = new sources.ManagedKafkaEventSource( + { + clusterArn, + topic: kafkaTopic, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + consumerGroupId: 'my-consumer-group-id', + schemaRegistryConfig: new sources.ConfluentSchemaRegistry({ + schemaRegistryUri: 'https://example.com', + eventRecordFormat: lambda.EventRecordFormat.JSON, + authenticationType: lambda.KafkaSchemaRegistryAccessConfigType.BASIC_AUTH, + secret: secret, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + }); + + // WHEN + fn.addEventSource(mskEventMapping); + + // THEN + expect(mskEventMapping.eventSourceMappingId).toBeDefined(); + expect(mskEventMapping.eventSourceMappingArn).toBeDefined(); + + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Lambda::EventSourceMapping', { + ProvisionedPollerConfig: { + MinimumPollers: 1, + MaximumPollers: 3, + }, + AmazonManagedKafkaEventSourceConfig: { + ConsumerGroupId: 'my-consumer-group-id', + SchemaRegistryConfig: { + SchemaRegistryURI: 'https://example.com', + EventRecordFormat: 'JSON', + AccessConfigs: [ + { + Type: 'BASIC_AUTH', + URI: { + Ref: 'SecretA720EF05', + }, + }, + ], + SchemaValidationConfigs: [{ Attribute: 'KEY' }], + }, + }, + }); + }); + + test('SMK with confluent kafka schema registry and secret', () => { + 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 smkEventMapping = new sources.SelfManagedKafkaEventSource( + { + bootstrapServers: bootstrapServers, + topic: kafkaTopic, + secret: secret, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new sources.ConfluentSchemaRegistry({ + schemaRegistryUri: 'https://example.com', + eventRecordFormat: lambda.EventRecordFormat.JSON, + authenticationType: lambda.KafkaSchemaRegistryAccessConfigType.BASIC_AUTH, + secret: secret, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + }); + + // WHEN + fn.addEventSource(smkEventMapping); + + // THEN + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Lambda::EventSourceMapping', { + ProvisionedPollerConfig: { + MinimumPollers: 1, + MaximumPollers: 3, + }, + SelfManagedKafkaEventSourceConfig: { + SchemaRegistryConfig: { + SchemaRegistryURI: 'https://example.com', + EventRecordFormat: 'JSON', + AccessConfigs: [ + { + Type: 'BASIC_AUTH', + URI: { + Ref: 'SecretA720EF05', + }, + }, + ], + SchemaValidationConfigs: [{ Attribute: 'KEY' }], + }, + }, + }); + }); + + test('SMK with glue kafka schema registry', () => { + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const glueRegistry = new CfnRegistry(stack, 'SchemaRegistry', { + name: 'smk-test-schema-registry', + description: 'Schema registry for SMK integration tests', + }); + const kafkaTopic = 'some-topic'; + const secret = new Secret(stack, 'Secret', { secretName: 'AmazonMSK_KafkaSecret' }); + const bootstrapServers = ['kafka-broker:9092']; + + const smkEventMapping = new sources.SelfManagedKafkaEventSource( + { + bootstrapServers: bootstrapServers, + topic: kafkaTopic, + secret: secret, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new sources.GlueSchemaRegistry({ + schemaRegistry: glueRegistry, + eventRecordFormat: lambda.EventRecordFormat.JSON, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + }); + + // WHEN + fn.addEventSource(smkEventMapping); + + // THEN + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'glue:GetRegistry', + Effect: 'Allow', + Resource: { + 'Fn::GetAtt': ['SchemaRegistry', 'Arn'], + }, + }, + { + Action: 'glue:GetSchemaVersion', + Effect: 'Allow', + Resource: [ + { + 'Fn::GetAtt': ['SchemaRegistry', 'Arn'], + }, + { + 'Fn::Sub': [ + 'arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:schema/${registryName}/*', + { registryName: 'smk-test-schema-registry' }, + ], + }, + ], + }, + { + Action: [ + 'secretsmanager:GetSecretValue', + 'secretsmanager:DescribeSecret', + ], + Effect: 'Allow', + Resource: { + Ref: 'SecretA720EF05', + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'FnServiceRoleDefaultPolicyC6A839BF', + Roles: [ + { + Ref: 'FnServiceRoleB9001A96', + }, + ], + }); + + template.hasResourceProperties('AWS::Lambda::EventSourceMapping', { + ProvisionedPollerConfig: { + MinimumPollers: 1, + MaximumPollers: 3, + }, + SelfManagedKafkaEventSourceConfig: { + SchemaRegistryConfig: { + SchemaRegistryURI: { + 'Fn::GetAtt': ['SchemaRegistry', 'Arn'], + }, + EventRecordFormat: 'JSON', + SchemaValidationConfigs: [{ Attribute: 'KEY' }], + }, + }, + }); + }); + + test('SMK with glue kafka schema registry arn', () => { + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const glueRegistry = 'arn:aws:glue:us-west-2:123456789012:registry/example'; + const kafkaTopic = 'some-topic'; + const secret = new Secret(stack, 'Secret', { secretName: 'AmazonMSK_KafkaSecret' }); + const bootstrapServers = ['kafka-broker:9092']; + + const smkEventMapping = new sources.SelfManagedKafkaEventSource( + { + bootstrapServers: bootstrapServers, + topic: kafkaTopic, + secret: secret, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + schemaRegistryConfig: new sources.GlueSchemaRegistry({ + schemaRegistryArn: glueRegistry, + eventRecordFormat: lambda.EventRecordFormat.JSON, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + }); + + // WHEN + fn.addEventSource(smkEventMapping); + + // THEN + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'glue:GetRegistry', + Effect: 'Allow', + Resource: 'arn:aws:glue:us-west-2:123456789012:registry/example', + }, + { + Action: 'glue:GetSchemaVersion', + Effect: 'Allow', + Resource: [ + 'arn:aws:glue:us-west-2:123456789012:registry/example', + { + 'Fn::Sub': [ + 'arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:schema/${registryName}/*', + { registryName: 'example' }, + ], + }, + ], + }, + { + Action: [ + 'secretsmanager:GetSecretValue', + 'secretsmanager:DescribeSecret', + ], + Effect: 'Allow', + Resource: { + Ref: 'SecretA720EF05', + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'FnServiceRoleDefaultPolicyC6A839BF', + Roles: [ + { + Ref: 'FnServiceRoleB9001A96', + }, + ], + }); + + template.hasResourceProperties('AWS::Lambda::EventSourceMapping', { + ProvisionedPollerConfig: { + MinimumPollers: 1, + MaximumPollers: 3, + }, + SelfManagedKafkaEventSourceConfig: { + SchemaRegistryConfig: { + SchemaRegistryURI: 'arn:aws:glue:us-west-2:123456789012:registry/example', + EventRecordFormat: 'JSON', + SchemaValidationConfigs: [{ Attribute: 'KEY' }], + }, + }, + }); + }); + + test('SMK with kafka schema registry and consumer group ID', () => { + 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 smkEventMapping = new sources.SelfManagedKafkaEventSource( + { + bootstrapServers: bootstrapServers, + topic: kafkaTopic, + secret: secret, + startingPosition: lambda.StartingPosition.TRIM_HORIZON, + provisionedPollerConfig: { + minimumPollers: 1, + maximumPollers: 3, + }, + consumerGroupId: 'my-consumer-group-id', + schemaRegistryConfig: new sources.ConfluentSchemaRegistry({ + schemaRegistryUri: 'https://example.com', + eventRecordFormat: lambda.EventRecordFormat.JSON, + authenticationType: lambda.KafkaSchemaRegistryAccessConfigType.BASIC_AUTH, + secret: secret, + schemaValidationConfigs: [{ attribute: lambda.KafkaSchemaValidationAttribute.KEY }], + }), + }); + + // WHEN + fn.addEventSource(smkEventMapping); + + // THEN + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Lambda::EventSourceMapping', { + ProvisionedPollerConfig: { + MinimumPollers: 1, + MaximumPollers: 3, + }, + SelfManagedKafkaEventSourceConfig: { + ConsumerGroupId: 'my-consumer-group-id', + SchemaRegistryConfig: { + SchemaRegistryURI: 'https://example.com', + EventRecordFormat: 'JSON', + AccessConfigs: [ + { + Type: 'BASIC_AUTH', + URI: { + Ref: 'SecretA720EF05', + }, + }, + ], + SchemaValidationConfigs: [{ Attribute: 'KEY' }], + }, + }, + }); + }); }); }); 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 8409c478ab6eb..df14694d7bbc7 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,7 @@ import { Construct } from 'constructs'; import { IEventSourceDlq } from './dlq'; import { IFunction } from './function-base'; import { CfnEventSourceMapping } from './lambda.generated'; +import { ISchemaRegistry } from './schema-registry'; import * as iam from '../../aws-iam'; import { IKey } from '../../aws-kms'; import * as cdk from '../../core'; @@ -309,6 +310,13 @@ export interface EventSourceMappingOptions { * @default - Enhanced monitoring is disabled */ readonly metricsConfig?: MetricsConfig; + + /** + * Specific configuration settings for a Kafka schema registry. + * + * @default - none + */ + readonly schemaRegistryConfig?: ISchemaRegistry; } export enum MetricType { @@ -504,7 +512,6 @@ export class EventSourceMapping extends cdk.Resource implements IEventSourceMapp } let destinationConfig; - if (props.onFailure) { destinationConfig = { onFailure: props.onFailure.bind(this, props.target), @@ -516,7 +523,22 @@ export class EventSourceMapping extends cdk.Resource implements IEventSourceMapp selfManagedEventSource = { endpoints: { kafkaBootstrapServers: props.kafkaBootstrapServers } }; } - let consumerGroupConfig = props.kafkaConsumerGroupId ? { consumerGroupId: props.kafkaConsumerGroupId } : undefined; + let kafkaConfig: any; + if (props.kafkaConsumerGroupId || props.schemaRegistryConfig) { + kafkaConfig = {}; + if (props.kafkaConsumerGroupId) { + kafkaConfig.consumerGroupId = props.kafkaConsumerGroupId; + } + if (props.schemaRegistryConfig) { + const schemaRegistry = props.schemaRegistryConfig.bind(this, props.target); + kafkaConfig.schemaRegistryConfig = { + schemaRegistryUri: schemaRegistry.schemaRegistryUri, + eventRecordFormat: schemaRegistry.eventRecordFormat.value, + accessConfigs: schemaRegistry?.accessConfigs?.map((o) => {return { type: o.type.type, uri: o.uri };}), + schemaValidationConfigs: schemaRegistry.schemaValidationConfigs?.map((o) => {return { attribute: o.attribute.value };}), + }; + } + } const cfnEventSourceMapping = new CfnEventSourceMapping(this, 'Resource', { batchSize: props.batchSize, @@ -539,8 +561,8 @@ export class EventSourceMapping extends cdk.Resource implements IEventSourceMapp selfManagedEventSource, filterCriteria: props.filters ? { filters: props.filters }: undefined, kmsKeyArn: props.filterEncryption?.keyArn, - selfManagedKafkaEventSourceConfig: props.kafkaBootstrapServers ? consumerGroupConfig : undefined, - amazonManagedKafkaEventSourceConfig: props.eventSourceArn ? consumerGroupConfig : undefined, + selfManagedKafkaEventSourceConfig: props.kafkaBootstrapServers ? kafkaConfig : undefined, + amazonManagedKafkaEventSourceConfig: props.eventSourceArn ? kafkaConfig : undefined, provisionedPollerConfig: props.provisionedPollerConfig, metricsConfig: props.metricsConfig, }); diff --git a/packages/aws-cdk-lib/aws-lambda/lib/index.ts b/packages/aws-cdk-lib/aws-lambda/lib/index.ts index 918c48ffe0ca5..e23339e1f243d 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/index.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/index.ts @@ -26,6 +26,7 @@ export * from './function-url'; export * from './runtime-management'; export * from './params-and-secrets-layers'; export * from './snapstart-config'; +export * from './schema-registry'; // AWS::Lambda CloudFormation Resources: export * from './lambda.generated'; diff --git a/packages/aws-cdk-lib/aws-lambda/lib/schema-registry.ts b/packages/aws-cdk-lib/aws-lambda/lib/schema-registry.ts new file mode 100644 index 0000000000000..92b44239efa43 --- /dev/null +++ b/packages/aws-cdk-lib/aws-lambda/lib/schema-registry.ts @@ -0,0 +1,187 @@ +import { IEventSourceMapping } from './event-source-mapping'; +import { IFunction } from './function-base'; + +/** + * The format target function should recieve record in. + */ +export class EventRecordFormat { + /** + * The target function will recieve records as json objects. + */ + public static readonly JSON = new EventRecordFormat('JSON'); + + /** + * The target function will recieve records in same format as the schema source. + */ + public static readonly SOURCE = new EventRecordFormat('SOURCE'); + + /** A custom event record format */ + public static of(name: string): EventRecordFormat { + return new EventRecordFormat(name); + } + + /** + * The enum to use in `SchemaRegistryConfig.EventRecordFormat` property in CloudFormation + */ + public readonly value: string; + + private constructor(value: string) { + this.value = value; + } +} + +/** + * The type of authentication protocol for your schema registry. + */ +export class KafkaSchemaRegistryAccessConfigType { + /** + * The Secrets Manager secret that stores your broker credentials. + */ + public static readonly BASIC_AUTH = new KafkaSchemaRegistryAccessConfigType('BASIC_AUTH'); + + /** + * The Secrets Manager ARN of your secret key containing the certificate chain (X.509 PEM), private key (PKCS#8 PEM), + * and private key password (optional) used for mutual TLS authentication of your schema registry. + */ + public static readonly CLIENT_CERTIFICATE_TLS_AUTH = new KafkaSchemaRegistryAccessConfigType('CLIENT_CERTIFICATE_TLS_AUTH'); + + /** + * The Secrets Manager ARN of your secret key containing the root CA certificate (X.509 PEM) used for TLS encryption of your schema registry. + */ + public static readonly SERVER_ROOT_CA_CERTIFICATE = new KafkaSchemaRegistryAccessConfigType('SERVER_ROOT_CA_CERTIFICATE'); + + /** A custom source access configuration property for schema registry */ + public static of(name: string): KafkaSchemaRegistryAccessConfigType { + return new KafkaSchemaRegistryAccessConfigType(name); + } + + /** + * The key to use in `SchemaRegistryConfig.AccessConfig.Type` property in CloudFormation + */ + public readonly type: string; + + private constructor(type: string) { + this.type = type; + } +} + +/** + * Specific access configuration settings that tell Lambda how to authenticate with your schema registry. + * + * If you're working with an AWS Glue schema registry, don't provide authentication details in this object. Instead, ensure that your execution role has the required permissions for Lambda to access your cluster. + * + * If you're working with a Confluent schema registry, choose the authentication method in the Type field, and provide the AWS Secrets Manager secret ARN in the URI field. + */ +export interface KafkaSchemaRegistryAccessConfig { + /** + * The type of authentication Lambda uses to access your schema registry. + */ + readonly type: KafkaSchemaRegistryAccessConfigType; + /** + * The URI of the secret (Secrets Manager secret ARN) to authenticate with your schema registry. + * @see KafkaSchemaRegistryAccessConfigType + */ + readonly uri: string; +} + +/** + * Specific schema validation configuration settings that tell Lambda the message attributes you want to validate and filter using your schema registry. + */ +export class KafkaSchemaValidationAttribute { + /** + * De-serialize the key field of the parload to target function. + */ + public static readonly KEY = new KafkaSchemaValidationAttribute('KEY'); + + /** + * De-serialize the value field of the parload to target function. + */ + public static readonly VALUE = new KafkaSchemaValidationAttribute('VALUE'); + + /** A custom schema validation attribute property */ + public static of(name: string): KafkaSchemaValidationAttribute { + return new KafkaSchemaValidationAttribute(name); + } + + /** + * The enum to use in `SchemaRegistryConfig.SchemaValidationConfigs.Attribute` property in CloudFormation + */ + public readonly value: string; + + private constructor(value: string) { + this.value = value; + } +} + +/** + * Specific schema validation configuration settings that tell Lambda the message attributes you want to validate and filter using your schema registry. + */ +export interface KafkaSchemaValidationConfig { + /** + * The attributes you want your schema registry to validate and filter for. If you selected JSON as the EventRecordFormat, Lambda also deserializes the selected message attributes. + */ + readonly attribute: KafkaSchemaValidationAttribute; +} + +/** + * (Amazon MSK and self-managed Apache Kafka only) Specific configuration settings for a Kafka schema registry. + */ +export interface KafkaSchemaRegistryConfig { + /** + * The URI for your schema registry. The correct URI format depends on the type of schema registry you're using. + * + * @default - none + */ + readonly schemaRegistryUri: string; + /** + * The record format that Lambda delivers to your function after schema validation. + * - Choose JSON to have Lambda deliver the record to your function as a standard JSON object. + * - Choose SOURCE to have Lambda deliver the record to your function in its original source format. Lambda removes all schema metadata, such as the schema ID, before sending the record to your function. + * + * @default - none + */ + readonly eventRecordFormat: EventRecordFormat; + /** + * An array of access configuration objects that tell Lambda how to authenticate with your schema registry. + * + * @default - none + */ + readonly accessConfigs?: KafkaSchemaRegistryAccessConfig[]; + /** + * An array of schema validation configuration objects, which tell Lambda the message attributes you want to validate and filter using your schema registry. + * + * @default - none + */ + readonly schemaValidationConfigs: KafkaSchemaValidationConfig[]; +} + +/** + * Properties for schema registry configuration. + */ +export interface SchemaRegistryProps { + /** + * The record format that Lambda delivers to your function after schema validation. + * - Choose JSON to have Lambda deliver the record to your function as a standard JSON object. + * - Choose SOURCE to have Lambda deliver the record to your function in its original source format. Lambda removes all schema metadata, such as the schema ID, before sending the record to your function. + * + * @default - none + */ + readonly eventRecordFormat: EventRecordFormat; + + /** + * An array of schema validation configuration objects, which tell Lambda the message attributes you want to validate and filter using your schema registry. + * + * @default - none + */ + readonly schemaValidationConfigs: KafkaSchemaValidationConfig[]; +} + +/** + * A schema registry for an event source + */ +export interface ISchemaRegistry { + /** + * Returns the schema registry config of the event source + */ + bind(target: IEventSourceMapping, targetHandler: IFunction): KafkaSchemaRegistryConfig; +}