diff --git a/docs/globals.rst b/docs/globals.rst index eb42c5a52..3fe3d2275 100644 --- a/docs/globals.rst +++ b/docs/globals.rst @@ -74,6 +74,7 @@ Currently, the following resources and properties are being supported: Architectures: EphemeralStorage: RuntimeManagementConfig: + LoggingConfig: Api: # Properties of AWS::Serverless::Api diff --git a/integration/combination/test_function_with_msk.py b/integration/combination/test_function_with_msk.py index 8fcb707be..c5be2d72a 100644 --- a/integration/combination/test_function_with_msk.py +++ b/integration/combination/test_function_with_msk.py @@ -28,6 +28,15 @@ def test_function_with_msk_trigger_using_manage_policy(self): parameters.append(self.generate_parameter("MskClusterName2", cluster_name)) self._common_validations_for_MSK("combination/function_with_msk_using_managed_policy", parameters) + def test_function_with_msk_trigger_and_s3_onfailure_events_destinations(self): + companion_stack_outputs = self.companion_stack_outputs + parameters = self.get_parameters(companion_stack_outputs) + cluster_name = "MskCluster3-" + generate_suffix() + parameters.append(self.generate_parameter("MskClusterName3", cluster_name)) + self._common_validations_for_MSK( + "combination/function_with_msk_trigger_and_s3_onfailure_events_destinations", parameters + ) + def _common_validations_for_MSK(self, file_name, parameters): self.create_and_verify_stack(file_name, parameters) diff --git a/integration/resources/expected/combination/function_with_msk_trigger_and_s3_onfailure_events_destinations.json b/integration/resources/expected/combination/function_with_msk_trigger_and_s3_onfailure_events_destinations.json new file mode 100644 index 000000000..e45f42ea5 --- /dev/null +++ b/integration/resources/expected/combination/function_with_msk_trigger_and_s3_onfailure_events_destinations.json @@ -0,0 +1,22 @@ +[ + { + "LogicalResourceId": "MyMskStreamProcessor", + "ResourceType": "AWS::Lambda::Function" + }, + { + "LogicalResourceId": "MyLambdaExecutionRole", + "ResourceType": "AWS::IAM::Role" + }, + { + "LogicalResourceId": "MyMskCluster", + "ResourceType": "AWS::MSK::Cluster" + }, + { + "LogicalResourceId": "MyMskStreamProcessorMyMskEvent", + "ResourceType": "AWS::Lambda::EventSourceMapping" + }, + { + "LogicalResourceId": "PreCreatedS3Bucket", + "ResourceType": "AWS::S3::Bucket" + } +] diff --git a/integration/resources/templates/combination/function_with_msk_trigger_and_s3_onfailure_events_destinations.yaml b/integration/resources/templates/combination/function_with_msk_trigger_and_s3_onfailure_events_destinations.yaml new file mode 100644 index 000000000..98f2178ae --- /dev/null +++ b/integration/resources/templates/combination/function_with_msk_trigger_and_s3_onfailure_events_destinations.yaml @@ -0,0 +1,80 @@ +Parameters: + PreCreatedSubnetOne: + Type: String + PreCreatedSubnetTwo: + Type: String + MskClusterName3: + Type: String + +Resources: + MyLambdaExecutionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Action: [sts:AssumeRole] + Effect: Allow + Principal: + Service: [lambda.amazonaws.com] + Policies: + - PolicyName: IntegrationTestExecution + PolicyDocument: + Statement: + - Action: [kafka:DescribeCluster, kafka:GetBootstrapBrokers, ec2:CreateNetworkInterface, + ec2:DescribeNetworkInterfaces, ec2:DescribeVpcs, ec2:DeleteNetworkInterface, + ec2:DescribeSubnets, ec2:DescribeSecurityGroups, logs:CreateLogGroup, + logs:CreateLogStream, logs:PutLogEvents] + Effect: Allow + Resource: '*' + ManagedPolicyArns: [arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole] + Tags: + - {Value: SAM, Key: lambda:createdBy} + + MyMskCluster: + Type: AWS::MSK::Cluster + Properties: + BrokerNodeGroupInfo: + ClientSubnets: + - Ref: PreCreatedSubnetOne + - Ref: PreCreatedSubnetTwo + InstanceType: kafka.t3.small + StorageInfo: + EBSStorageInfo: + VolumeSize: 1 + ClusterName: + Ref: MskClusterName3 + KafkaVersion: 2.4.1.1 + NumberOfBrokerNodes: 2 + + MyMskStreamProcessor: + Type: AWS::Serverless::Function + Properties: + Runtime: nodejs18.x + Handler: index.handler + CodeUri: ${codeuri} + Role: + Fn::GetAtt: [MyLambdaExecutionRole, Arn] + Events: + MyMskEvent: + Type: MSK + Properties: + StartingPosition: LATEST + Stream: + Ref: MyMskCluster + Topics: + - MyDummyTestTopic + DestinationConfig: + OnFailure: + Type: S3 + Destination: + Fn::GetAtt: + - PreCreatedS3Bucket + - Arn + + PreCreatedS3Bucket: + Type: AWS::S3::Bucket + DeletionPolicy: Delete + +Metadata: + SamTransformTest: true diff --git a/samtranslator/__init__.py b/samtranslator/__init__.py index d88191f46..bd41b85a3 100644 --- a/samtranslator/__init__.py +++ b/samtranslator/__init__.py @@ -1 +1 @@ -__version__ = "1.79.0" +__version__ = "1.80.0" diff --git a/samtranslator/internal/schema_source/aws_serverless_function.py b/samtranslator/internal/schema_source/aws_serverless_function.py index 7f541ca45..81cf160c5 100644 --- a/samtranslator/internal/schema_source/aws_serverless_function.py +++ b/samtranslator/internal/schema_source/aws_serverless_function.py @@ -414,6 +414,7 @@ class MSKEventProperties(BaseModel): Stream: PassThroughProp = mskeventproperties("Stream") Topics: PassThroughProp = mskeventproperties("Topics") SourceAccessConfigurations: Optional[PassThroughProp] = mskeventproperties("SourceAccessConfigurations") + DestinationConfig: Optional[PassThroughProp] # TODO: add documentation class MSKEvent(BaseModel): @@ -506,6 +507,7 @@ class ScheduleV2Event(BaseModel): EphemeralStorage = Optional[PassThroughProp] SnapStart = Optional[PassThroughProp] # TODO: check the type RuntimeManagementConfig = Optional[PassThroughProp] # TODO: check the type +LoggingConfig = Optional[PassThroughProp] # TODO: add documentation class Properties(BaseModel): @@ -631,6 +633,7 @@ class Properties(BaseModel): Tracing: Optional[Tracing] = prop("Tracing") VersionDescription: Optional[PassThroughProp] = prop("VersionDescription") VpcConfig: Optional[VpcConfig] = prop("VpcConfig") + LoggingConfig: Optional[PassThroughProp] # TODO: add documentation class Globals(BaseModel): @@ -688,6 +691,7 @@ class Globals(BaseModel): ) SnapStart: Optional[SnapStart] = prop("SnapStart") RuntimeManagementConfig: Optional[RuntimeManagementConfig] = prop("RuntimeManagementConfig") + LoggingConfig: Optional[PassThroughProp] # TODO: add documentation class Resource(ResourceAttributes): diff --git a/samtranslator/model/eventsources/pull.py b/samtranslator/model/eventsources/pull.py index 4c49c86b6..85d8aa2d8 100644 --- a/samtranslator/model/eventsources/pull.py +++ b/samtranslator/model/eventsources/pull.py @@ -172,13 +172,15 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # noqa: P # `Type` property is for sam to attach the right policies destination_type = on_failure.get("Type") - # SAM attaches the policies for SQS or SNS only if 'Type' is given + # SAM attaches the policies for SQS, SNS or S3 only if 'Type' is given if destination_type: # delete this field as its used internally for SAM to determine the policy del on_failure["Type"] - # the values 'SQS' and 'SNS' are allowed. No intrinsics are allowed - if destination_type not in ["SQS", "SNS"]: - raise InvalidEventException(self.logical_id, "The only valid values for 'Type' are 'SQS' and 'SNS'") + # the values 'SQS', 'SNS', and 'S3' are allowed. No intrinsics are allowed + if destination_type not in ["SQS", "SNS", "S3"]: + raise InvalidEventException( + self.logical_id, "The only valid values for 'Type' are 'SQS', 'SNS', and 'S3'" + ) if destination_type == "SQS": queue_arn = on_failure.get("Destination") destination_config_policy = IAMRolePolicies().sqs_send_message_role_policy( @@ -189,6 +191,11 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # noqa: P destination_config_policy = IAMRolePolicies().sns_publish_role_policy( sns_topic_arn, self.logical_id ) + elif destination_type == "S3": + s3_arn = on_failure.get("Destination") + destination_config_policy = IAMRolePolicies().s3_send_event_payload_role_policy( + s3_arn, self.logical_id + ) lambda_eventsourcemapping.DestinationConfig = self.DestinationConfig diff --git a/samtranslator/model/iam.py b/samtranslator/model/iam.py index 5d263096b..7588ef72d 100644 --- a/samtranslator/model/iam.py +++ b/samtranslator/model/iam.py @@ -126,6 +126,19 @@ def sns_publish_role_policy(cls, topic_arn: Any, logical_id: str) -> Dict[str, A "PolicyDocument": {"Statement": [{"Action": "sns:publish", "Effect": "Allow", "Resource": topic_arn}]}, } + @classmethod + def s3_send_event_payload_role_policy(cls, s3_arn: Any, logical_id: str) -> Dict[str, Any]: + s3_arn_with_wild_card = {"Fn::Join": ["/", [s3_arn, "*"]]} + return { + "PolicyName": logical_id + "S3Policy", + "PolicyDocument": { + "Statement": [ + {"Action": "s3:PutObject", "Effect": "Allow", "Resource": s3_arn_with_wild_card}, + {"Action": "s3:ListBucket", "Effect": "Allow", "Resource": s3_arn}, + ] + }, + } + @classmethod def event_bus_put_events_role_policy(cls, event_bus_arn: Any, logical_id: str) -> Dict[str, Any]: return { diff --git a/samtranslator/model/lambda_.py b/samtranslator/model/lambda_.py index a5cb20a86..b79df9f4c 100644 --- a/samtranslator/model/lambda_.py +++ b/samtranslator/model/lambda_.py @@ -34,6 +34,7 @@ class LambdaFunction(Resource): "SnapStart": GeneratedProperty(), "EphemeralStorage": GeneratedProperty(), "RuntimeManagementConfig": GeneratedProperty(), + "LoggingConfig": GeneratedProperty(), } Code: Dict[str, Any] @@ -60,6 +61,7 @@ class LambdaFunction(Resource): SnapStart: Optional[Dict[str, Any]] EphemeralStorage: Optional[Dict[str, Any]] RuntimeManagementConfig: Optional[Dict[str, Any]] + LoggingConfig: Optional[Dict[str, Any]] runtime_attrs = {"name": lambda self: ref(self.logical_id), "arn": lambda self: fnGetAtt(self.logical_id, "Arn")} diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index 68d9a9e4d..f1382ea25 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -179,6 +179,7 @@ class SamFunction(SamResourceMacro): "SnapStart": PropertyType(False, IS_DICT), "FunctionUrlConfig": PropertyType(False, IS_DICT), "RuntimeManagementConfig": PassThroughProperty(False), + "LoggingConfig": PassThroughProperty(False), } FunctionName: Optional[Intrinsicable[str]] @@ -220,6 +221,7 @@ class SamFunction(SamResourceMacro): Architectures: Optional[List[Any]] SnapStart: Optional[Dict[str, Any]] FunctionUrlConfig: Optional[Dict[str, Any]] + LoggingConfig: Optional[Dict[str, Any]] event_resolver = ResourceTypeResolver( samtranslator.model.eventsources, @@ -603,6 +605,7 @@ def _construct_lambda_function(self, intrinsics_resolver: IntrinsicsResolver) -> lambda_function.CodeSigningConfigArn = self.CodeSigningConfigArn lambda_function.RuntimeManagementConfig = self.RuntimeManagementConfig # type: ignore[attr-defined] + lambda_function.LoggingConfig = self.LoggingConfig self._validate_package_type(lambda_function) return lambda_function diff --git a/samtranslator/plugins/globals/globals.py b/samtranslator/plugins/globals/globals.py index 46c54ea32..2bea29ba6 100644 --- a/samtranslator/plugins/globals/globals.py +++ b/samtranslator/plugins/globals/globals.py @@ -52,6 +52,7 @@ class Globals: "EphemeralStorage", "FunctionUrlConfig", "RuntimeManagementConfig", + "LoggingConfig", ], # Everything except # DefinitionBody: because its hard to reason about merge of Swagger dictionaries diff --git a/samtranslator/schema/schema.json b/samtranslator/schema/schema.json index f4e0c1e5d..47549e27e 100644 --- a/samtranslator/schema/schema.json +++ b/samtranslator/schema/schema.json @@ -85251,8 +85251,7 @@ } }, "required": [ - "Database", - "Sql" + "Database" ], "type": "object" }, @@ -250544,6 +250543,9 @@ "markdownDescription": "A string that configures how events will be read from Kafka topics\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`AmazonManagedKafkaConfiguration`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html) property of an `AWS::Lambda::EventSourceMapping` resource\\.", "title": "ConsumerGroupId" }, + "DestinationConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "FilterCriteria": { "allOf": [ { @@ -253079,6 +253081,9 @@ "markdownDescription": "The list of `LayerVersion` ARNs that this function should use\\. The order specified here is the order in which they will be imported when running the Lambda function\\. \n*Type*: List \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`Layers`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-layers) property of an `AWS::Lambda::Function` resource\\.", "title": "Layers" }, + "LoggingConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "MemorySize": { "allOf": [ { @@ -253424,6 +253429,9 @@ "markdownDescription": "The list of `LayerVersion` ARNs that this function should use\\. The order specified here is the order in which they will be imported when running the Lambda function\\. \n*Type*: List \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`Layers`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-layers) property of an `AWS::Lambda::Function` resource\\.", "title": "Layers" }, + "LoggingConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "MemorySize": { "allOf": [ { diff --git a/schema_source/cloudformation.schema.json b/schema_source/cloudformation.schema.json index 9da4c54f4..6d16f000f 100644 --- a/schema_source/cloudformation.schema.json +++ b/schema_source/cloudformation.schema.json @@ -85209,8 +85209,7 @@ } }, "required": [ - "Database", - "Sql" + "Database" ], "type": "object" }, diff --git a/schema_source/sam.schema.json b/schema_source/sam.schema.json index b5e1ccbd6..8e63394c0 100644 --- a/schema_source/sam.schema.json +++ b/schema_source/sam.schema.json @@ -2366,6 +2366,9 @@ "markdownDescription": "A string that configures how events will be read from Kafka topics\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`AmazonManagedKafkaConfiguration`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html) property of an `AWS::Lambda::EventSourceMapping` resource\\.", "title": "ConsumerGroupId" }, + "DestinationConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "FilterCriteria": { "allOf": [ { @@ -5335,6 +5338,9 @@ "markdownDescription": "The list of `LayerVersion` ARNs that this function should use\\. The order specified here is the order in which they will be imported when running the Lambda function\\. \n*Type*: List \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`Layers`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-layers) property of an `AWS::Lambda::Function` resource\\.", "title": "Layers" }, + "LoggingConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "MemorySize": { "allOf": [ { @@ -5857,6 +5863,9 @@ "markdownDescription": "The list of `LayerVersion` ARNs that this function should use\\. The order specified here is the order in which they will be imported when running the Lambda function\\. \n*Type*: List \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`Layers`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-layers) property of an `AWS::Lambda::Function` resource\\.", "title": "Layers" }, + "LoggingConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "MemorySize": { "allOf": [ { diff --git a/tests/translator/input/function_with_msk_s3_onfailure_dest.yaml b/tests/translator/input/function_with_msk_s3_onfailure_dest.yaml new file mode 100644 index 000000000..91acbf600 --- /dev/null +++ b/tests/translator/input/function_with_msk_s3_onfailure_dest.yaml @@ -0,0 +1,20 @@ +Resources: + MyMskStreamProcessor: + Type: AWS::Serverless::Function + Properties: + Runtime: nodejs12.x + Handler: index.handler + CodeUri: s3://sam-demo-bucket/kafka.zip + Events: + MyMskEvent: + Type: MSK + Properties: + StartingPosition: LATEST + Stream: !Sub arn:${AWS::Partition}:kafka:${AWS::Region}:012345678901:cluster/mycluster/6cc0432b-8618-4f44-bccc-e1fbd8fb7c4d-2 + Topics: + - MyDummyTestTopic + ConsumerGroupId: consumergroup1 + DestinationConfig: + OnFailure: + Type: S3 + Destination: !Sub arn:${AWS::Partition}:s3:::my-s3-arn diff --git a/tests/translator/input/globals_for_function.yaml b/tests/translator/input/globals_for_function.yaml index 2b8343c9e..3cd3dbcfc 100644 --- a/tests/translator/input/globals_for_function.yaml +++ b/tests/translator/input/globals_for_function.yaml @@ -30,6 +30,10 @@ Globals: Size: 1024 RuntimeManagementConfig: UpdateRuntimeOn: Auto + LoggingConfig: + LogGroup: myJsonStructuredLogs + + Resources: MinimalFunction: diff --git a/tests/translator/output/aws-cn/function_with_msk_s3_onfailure_dest.json b/tests/translator/output/aws-cn/function_with_msk_s3_onfailure_dest.json new file mode 100644 index 000000000..b04313a5a --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_msk_s3_onfailure_dest.json @@ -0,0 +1,114 @@ +{ + "Resources": { + "MyMskStreamProcessor": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyMskStreamProcessorRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyMskStreamProcessorMyMskEvent": { + "Properties": { + "AmazonManagedKafkaEventSourceConfig": { + "ConsumerGroupId": "consumergroup1" + }, + "DestinationConfig": { + "OnFailure": { + "Destination": { + "Fn::Sub": "arn:${AWS::Partition}:s3:::my-s3-arn" + } + } + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:kafka:${AWS::Region}:012345678901:cluster/mycluster/6cc0432b-8618-4f44-bccc-e1fbd8fb7c4d-2" + }, + "FunctionName": { + "Ref": "MyMskStreamProcessor" + }, + "StartingPosition": "LATEST", + "Topics": [ + "MyDummyTestTopic" + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "MyMskStreamProcessorRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaMSKExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + { + "Fn::Sub": "arn:${AWS::Partition}:s3:::my-s3-arn" + }, + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:s3:::my-s3-arn" + } + } + ] + }, + "PolicyName": "MyMskStreamProcessorMyMskEventS3Policy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-cn/globals_for_function.json b/tests/translator/output/aws-cn/globals_for_function.json index 7bf633101..ab1407f8c 100644 --- a/tests/translator/output/aws-cn/globals_for_function.json +++ b/tests/translator/output/aws-cn/globals_for_function.json @@ -28,6 +28,9 @@ "Fn::Sub": "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:layer:MyLayer2:2" } ], + "LoggingConfig": { + "LogGroup": "myJsonStructuredLogs" + }, "MemorySize": 512, "ReservedConcurrentExecutions": 100, "Role": { @@ -162,6 +165,9 @@ "Fn::Sub": "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:layer:MyLayer:1" } ], + "LoggingConfig": { + "LogGroup": "myJsonStructuredLogs" + }, "MemorySize": 1024, "ReservedConcurrentExecutions": 50, "Role": { diff --git a/tests/translator/output/aws-us-gov/function_with_msk_s3_onfailure_dest.json b/tests/translator/output/aws-us-gov/function_with_msk_s3_onfailure_dest.json new file mode 100644 index 000000000..e9b5b458d --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_msk_s3_onfailure_dest.json @@ -0,0 +1,114 @@ +{ + "Resources": { + "MyMskStreamProcessor": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyMskStreamProcessorRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyMskStreamProcessorMyMskEvent": { + "Properties": { + "AmazonManagedKafkaEventSourceConfig": { + "ConsumerGroupId": "consumergroup1" + }, + "DestinationConfig": { + "OnFailure": { + "Destination": { + "Fn::Sub": "arn:${AWS::Partition}:s3:::my-s3-arn" + } + } + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:kafka:${AWS::Region}:012345678901:cluster/mycluster/6cc0432b-8618-4f44-bccc-e1fbd8fb7c4d-2" + }, + "FunctionName": { + "Ref": "MyMskStreamProcessor" + }, + "StartingPosition": "LATEST", + "Topics": [ + "MyDummyTestTopic" + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "MyMskStreamProcessorRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaMSKExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + { + "Fn::Sub": "arn:${AWS::Partition}:s3:::my-s3-arn" + }, + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:s3:::my-s3-arn" + } + } + ] + }, + "PolicyName": "MyMskStreamProcessorMyMskEventS3Policy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/globals_for_function.json b/tests/translator/output/aws-us-gov/globals_for_function.json index 8405aa369..34e2d5cff 100644 --- a/tests/translator/output/aws-us-gov/globals_for_function.json +++ b/tests/translator/output/aws-us-gov/globals_for_function.json @@ -28,6 +28,9 @@ "Fn::Sub": "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:layer:MyLayer2:2" } ], + "LoggingConfig": { + "LogGroup": "myJsonStructuredLogs" + }, "MemorySize": 512, "ReservedConcurrentExecutions": 100, "Role": { @@ -162,6 +165,9 @@ "Fn::Sub": "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:layer:MyLayer:1" } ], + "LoggingConfig": { + "LogGroup": "myJsonStructuredLogs" + }, "MemorySize": 1024, "ReservedConcurrentExecutions": 50, "Role": { diff --git a/tests/translator/output/error_function_with_invalid_stream_eventsource_dest_type.json b/tests/translator/output/error_function_with_invalid_stream_eventsource_dest_type.json index a9c911511..562cb3744 100644 --- a/tests/translator/output/error_function_with_invalid_stream_eventsource_dest_type.json +++ b/tests/translator/output/error_function_with_invalid_stream_eventsource_dest_type.json @@ -4,12 +4,12 @@ "Number of errors found: 1. ", "Resource with id [MyFunction] is invalid. ", "Event with id [MyFunctionStreamEvent] is invalid. ", - "The only valid values for 'Type' are 'SQS' and 'SNS'" + "The only valid values for 'Type' are 'SQS', 'SNS', and 'S3'" ], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MyFunction] is invalid. Event with id [MyFunctionStreamEvent] is invalid. The only valid values for 'Type' are 'SQS' and 'SNS'", + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MyFunction] is invalid. Event with id [MyFunctionStreamEvent] is invalid. The only valid values for 'Type' are 'SQS', 'SNS', and 'S3'", "errors": [ { - "errorMessage": "Resource with id [MyFunction] is invalid. Event with id [MyFunctionStreamEvent] is invalid. The only valid values for 'Type' are 'SQS' and 'SNS'" + "errorMessage": "Resource with id [MyFunction] is invalid. Event with id [MyFunctionStreamEvent] is invalid. The only valid values for 'Type' are 'SQS', 'SNS', and 'S3'" } ] } diff --git a/tests/translator/output/error_globals_unsupported_property.json b/tests/translator/output/error_globals_unsupported_property.json index 2ec0b0c96..b3e28e3c7 100644 --- a/tests/translator/output/error_globals_unsupported_property.json +++ b/tests/translator/output/error_globals_unsupported_property.json @@ -4,7 +4,7 @@ "Number of errors found: 1. ", "'Globals' section is invalid. ", "'SomeKey' is not a supported property of 'Function'. ", - "Must be one of the following values - ['Handler', 'Runtime', 'CodeUri', 'DeadLetterQueue', 'Description', 'MemorySize', 'Timeout', 'VpcConfig', 'Environment', 'Tags', 'PropagateTags', 'Tracing', 'KmsKeyArn', 'AutoPublishAlias', 'AutoPublishAliasAllProperties', 'Layers', 'DeploymentPreference', 'RolePath', 'PermissionsBoundary', 'ReservedConcurrentExecutions', 'ProvisionedConcurrencyConfig', 'AssumeRolePolicyDocument', 'EventInvokeConfig', 'FileSystemConfigs', 'CodeSigningConfigArn', 'Architectures', 'SnapStart', 'EphemeralStorage', 'FunctionUrlConfig']" + "Must be one of the following values - ['Handler', 'Runtime', 'CodeUri', 'DeadLetterQueue', 'Description', 'MemorySize', 'Timeout', 'VpcConfig', 'Environment', 'Tags', 'PropagateTags', 'Tracing', 'KmsKeyArn', 'AutoPublishAlias', 'AutoPublishAliasAllProperties', 'Layers', 'DeploymentPreference', 'RolePath', 'PermissionsBoundary', 'ReservedConcurrentExecutions', 'ProvisionedConcurrencyConfig', 'AssumeRolePolicyDocument', 'EventInvokeConfig', 'FileSystemConfigs', 'CodeSigningConfigArn', 'Architectures', 'SnapStart', 'EphemeralStorage', 'FunctionUrlConfig', 'LoggingConfig']" ], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. 'Globals' section is invalid. 'SomeKey' is not a supported property of 'Function'. Must be one of the following values - ['Handler', 'Runtime', 'CodeUri', 'DeadLetterQueue', 'Description', 'MemorySize', 'Timeout', 'VpcConfig', 'Environment', 'Tags', 'PropagateTags', 'Tracing', 'KmsKeyArn', 'AutoPublishAlias', 'AutoPublishAliasAllProperties', 'Layers', 'DeploymentPreference', 'RolePath', 'PermissionsBoundary', 'ReservedConcurrentExecutions', 'ProvisionedConcurrencyConfig', 'AssumeRolePolicyDocument', 'EventInvokeConfig', 'FileSystemConfigs', 'CodeSigningConfigArn', 'Architectures', 'SnapStart', 'EphemeralStorage', 'FunctionUrlConfig']" + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. 'Globals' section is invalid. 'SomeKey' is not a supported property of 'Function'. Must be one of the following values - ['Handler', 'Runtime', 'CodeUri', 'DeadLetterQueue', 'Description', 'MemorySize', 'Timeout', 'VpcConfig', 'Environment', 'Tags', 'PropagateTags', 'Tracing', 'KmsKeyArn', 'AutoPublishAlias', 'AutoPublishAliasAllProperties', 'Layers', 'DeploymentPreference', 'RolePath', 'PermissionsBoundary', 'ReservedConcurrentExecutions', 'ProvisionedConcurrencyConfig', 'AssumeRolePolicyDocument', 'EventInvokeConfig', 'FileSystemConfigs', 'CodeSigningConfigArn', 'Architectures', 'SnapStart', 'EphemeralStorage', 'FunctionUrlConfig', 'LoggingConfig']" } diff --git a/tests/translator/output/function_with_msk_s3_onfailure_dest.json b/tests/translator/output/function_with_msk_s3_onfailure_dest.json new file mode 100644 index 000000000..7c3ed5f19 --- /dev/null +++ b/tests/translator/output/function_with_msk_s3_onfailure_dest.json @@ -0,0 +1,114 @@ +{ + "Resources": { + "MyMskStreamProcessor": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyMskStreamProcessorRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyMskStreamProcessorMyMskEvent": { + "Properties": { + "AmazonManagedKafkaEventSourceConfig": { + "ConsumerGroupId": "consumergroup1" + }, + "DestinationConfig": { + "OnFailure": { + "Destination": { + "Fn::Sub": "arn:${AWS::Partition}:s3:::my-s3-arn" + } + } + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:kafka:${AWS::Region}:012345678901:cluster/mycluster/6cc0432b-8618-4f44-bccc-e1fbd8fb7c4d-2" + }, + "FunctionName": { + "Ref": "MyMskStreamProcessor" + }, + "StartingPosition": "LATEST", + "Topics": [ + "MyDummyTestTopic" + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "MyMskStreamProcessorRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaMSKExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + { + "Fn::Sub": "arn:${AWS::Partition}:s3:::my-s3-arn" + }, + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:s3:::my-s3-arn" + } + } + ] + }, + "PolicyName": "MyMskStreamProcessorMyMskEventS3Policy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/globals_for_function.json b/tests/translator/output/globals_for_function.json index d401d4568..ae9543ab6 100644 --- a/tests/translator/output/globals_for_function.json +++ b/tests/translator/output/globals_for_function.json @@ -28,6 +28,9 @@ "Fn::Sub": "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:layer:MyLayer2:2" } ], + "LoggingConfig": { + "LogGroup": "myJsonStructuredLogs" + }, "MemorySize": 512, "ReservedConcurrentExecutions": 100, "Role": { @@ -162,6 +165,9 @@ "Fn::Sub": "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:layer:MyLayer:1" } ], + "LoggingConfig": { + "LogGroup": "myJsonStructuredLogs" + }, "MemorySize": 1024, "ReservedConcurrentExecutions": 50, "Role": {