From bdd72526b9fb313f57dfe37066c0104da1ce1387 Mon Sep 17 00:00:00 2001 From: Guido Nebiolo Date: Tue, 14 Jul 2026 14:15:01 +0200 Subject: [PATCH 1/5] feat(function): add AWS::Logs::LogGroup resource model Add a LogGroup resource model backing the AWS::Logs::LogGroup CloudFormation type, with runtime attributes for Ref (name) and Fn::GetAtt Arn. This is the building block for a stack-managed CloudWatch log group on AWS::Serverless::Function. Refs #2665, #3791, #1216, #2057 --- samtranslator/model/log.py | 14 ++++++++++ tests/model/test_log.py | 52 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 tests/model/test_log.py diff --git a/samtranslator/model/log.py b/samtranslator/model/log.py index e4b3ed4f9b..8f647f64ea 100644 --- a/samtranslator/model/log.py +++ b/samtranslator/model/log.py @@ -11,3 +11,17 @@ class SubscriptionFilter(Resource): } runtime_attrs = {"name": lambda self: ref(self.logical_id), "arn": lambda self: fnGetAtt(self.logical_id, "Arn")} + + +class LogGroup(Resource): + resource_type = "AWS::Logs::LogGroup" + property_types = { + "LogGroupName": GeneratedProperty(), + "RetentionInDays": GeneratedProperty(), + "KmsKeyId": GeneratedProperty(), + "DataProtectionPolicy": GeneratedProperty(), + "LogGroupClass": GeneratedProperty(), + "Tags": GeneratedProperty(), + } + + runtime_attrs = {"name": lambda self: ref(self.logical_id), "arn": lambda self: fnGetAtt(self.logical_id, "Arn")} diff --git a/tests/model/test_log.py b/tests/model/test_log.py new file mode 100644 index 0000000000..8897468ccd --- /dev/null +++ b/tests/model/test_log.py @@ -0,0 +1,52 @@ +from unittest import TestCase + +from samtranslator.model.log import LogGroup, SubscriptionFilter + + +class TestLogGroup(TestCase): + def test_resource_type(self): + log_group = LogGroup("MyFunctionLogGroup") + self.assertEqual(log_group.resource_type, "AWS::Logs::LogGroup") + + def test_to_dict_with_properties(self): + log_group = LogGroup( + "MyFunctionLogGroup", + attributes={"DeletionPolicy": "Retain", "UpdateReplacePolicy": "Retain"}, + ) + log_group.LogGroupName = {"Fn::Sub": "/aws/lambda/${MyFunction}"} + log_group.RetentionInDays = 7 + + self.assertEqual( + log_group.to_dict(), + { + "MyFunctionLogGroup": { + "Type": "AWS::Logs::LogGroup", + "DeletionPolicy": "Retain", + "UpdateReplacePolicy": "Retain", + "Properties": { + "LogGroupName": {"Fn::Sub": "/aws/lambda/${MyFunction}"}, + "RetentionInDays": 7, + }, + } + }, + ) + + def test_runtime_attrs(self): + log_group = LogGroup("MyFunctionLogGroup") + self.assertEqual(log_group.get_runtime_attr("name"), {"Ref": "MyFunctionLogGroup"}) + self.assertEqual( + log_group.get_runtime_attr("arn"), + {"Fn::GetAtt": ["MyFunctionLogGroup", "Arn"]}, + ) + + def test_supported_properties(self): + # LogGroup and SubscriptionFilter live in the same module; make sure the + # LogGroup exposes the CloudFormation properties we rely on. + self.assertIn("LogGroupName", LogGroup.property_types) + self.assertIn("RetentionInDays", LogGroup.property_types) + self.assertIn("Tags", LogGroup.property_types) + self.assertIn("KmsKeyId", LogGroup.property_types) + self.assertIn("DataProtectionPolicy", LogGroup.property_types) + self.assertIn("LogGroupClass", LogGroup.property_types) + # Sanity check the neighbouring resource is untouched. + self.assertEqual(SubscriptionFilter.resource_type, "AWS::Logs::SubscriptionFilter") From 2792c0f97528f414be2213dc882ecc65a028fa6c Mon Sep 17 00:00:00 2001 From: Guido Nebiolo Date: Tue, 14 Jul 2026 14:16:08 +0200 Subject: [PATCH 2/5] feat(function): generate stack-managed CloudWatch log group via LogGroup property Add a LogGroup property to AWS::Serverless::Function that generates a stack-managed AWS::Logs::LogGroup. By default the group uses the conventional /aws/lambda/ name (no LoggingConfig or IAM change needed, since the group coincides with Lambda's default destination and AWSLambdaBasicExecutionRole already grants log writes). A custom LogGroupName binds the function via its native LoggingConfig.LogGroup. RetentionInDays is passed through and DeletionPolicy (Delete by default, or Retain) is applied to both DeletionPolicy and UpdateReplacePolicy so the group is deleted with the stack, avoiding orphaned log groups. When absent, output is unchanged for backward compatibility. Refs #2665, #3791, #1216, #2057 --- samtranslator/model/sam_resources.py | 61 +++++++++++++++ tests/model/test_sam_resources.py | 85 +++++++++++++++++++++ tests/translator/test_function_resources.py | 3 +- 3 files changed, 148 insertions(+), 1 deletion(-) diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index 14a9434bc0..7ce2492fe0 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -116,6 +116,7 @@ LambdaUrl, LambdaVersion, ) +from samtranslator.model.log import LogGroup as LogGroupResource from samtranslator.model.preferences.deployment_preference_collection import DeploymentPreferenceCollection from samtranslator.model.resource_policies import ResourcePolicies from samtranslator.model.role_utils import construct_role_for_resource @@ -201,6 +202,7 @@ class SamFunction(SamResourceMacro): "FunctionUrlConfig": PropertyType(False, IS_DICT), "RuntimeManagementConfig": PassThroughProperty(False), "LoggingConfig": PassThroughProperty(False), + "LogGroup": Property(False, IS_DICT), "RecursiveLoop": PassThroughProperty(False), "SourceKMSKeyArn": PassThroughProperty(False), "CapacityProviderConfig": PropertyType(False, IS_DICT), @@ -251,6 +253,7 @@ class SamFunction(SamResourceMacro): SnapStart: dict[str, Any] | None FunctionUrlConfig: dict[str, Any] | None LoggingConfig: dict[str, Any] | None + LogGroup: dict[str, Any] | None RecursiveLoop: str | None SourceKMSKeyArn: str | None CapacityProviderConfig: dict[str, Any] | None @@ -281,6 +284,8 @@ class SamFunction(SamResourceMacro): # EventConfig auto created SQS and SNS "DestinationTopic": SNSTopic.resource_type, "DestinationQueue": SQSQueue.resource_type, + # Stack-managed CloudWatch log group + "LogGroup": LogGroupResource.resource_type, } # Validation rules @@ -331,6 +336,11 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # noqa: P lambda_function = self._construct_lambda_function(intrinsics_resolver) resources.append(lambda_function) + # A stack-managed log group must be constructed before the version, because a custom + # log group name mutates the function's LoggingConfig which feeds into the version hash. + if self.LogGroup is not None: + resources.append(self._construct_log_group(lambda_function)) + if self.ProvisionedConcurrencyConfig and not self.AutoPublishAlias: raise InvalidResourceException( self.logical_id, @@ -795,6 +805,57 @@ def _construct_lambda_function(self, intrinsics_resolver: IntrinsicsResolver) -> self._validate_package_type(lambda_function) return lambda_function + def _construct_log_group(self, lambda_function: LambdaFunction) -> LogGroupResource: + """Construct a stack-managed ``AWS::Logs::LogGroup`` for the function. + + By default (no ``LogGroupName``) the log group uses the conventional name + ``/aws/lambda/`` so that it coincides with the function's default + logging destination. In this case the function's ``LoggingConfig`` is left untouched + and no additional IAM permissions are required (the AWS managed policy + ``AWSLambdaBasicExecutionRole`` already grants log writes on all log groups). + + When a custom ``LogGroupName`` is provided, the function is explicitly bound to the + log group through its native ``LoggingConfig.LogGroup`` property. A managed execution + role can still write to it via ``AWSLambdaBasicExecutionRole``; a user-provided role + must include ``logs:CreateLogStream``/``logs:PutLogEvents`` for the custom log group. + + Making the log group part of the stack lets customers control its retention and + ensures it is deleted together with the stack (see GitHub issue #1216). + """ + config = sam_expect(self.LogGroup, self.logical_id, "LogGroup").to_be_a_map() + + deletion_policy = config.get("DeletionPolicy", "Delete") + if deletion_policy not in ("Delete", "Retain"): + raise InvalidResourceException( + self.logical_id, + "'LogGroup.DeletionPolicy' must be one of 'Delete' or 'Retain'.", + ) + + attributes = self.get_passthrough_resource_attributes() or {} + attributes["DeletionPolicy"] = deletion_policy + attributes["UpdateReplacePolicy"] = deletion_policy + + log_group = LogGroupResource(logical_id=f"{self.logical_id}LogGroup", attributes=attributes) + + custom_name = config.get("LogGroupName") + if custom_name is not None: + # Mechanism 2: custom log group name. Bind the function to the log group via its + # native LoggingConfig so Lambda writes to the stack-managed group. + log_group.LogGroupName = custom_name + logging_config = dict(lambda_function.LoggingConfig or {}) + logging_config["LogGroup"] = custom_name + lambda_function.LoggingConfig = logging_config + else: + # Mechanism 1: conventional /aws/lambda/. Fn::Sub resolves the + # function's Ref (its physical name, even when auto-generated), matching Lambda's + # default logging destination without touching LoggingConfig or IAM. + log_group.LogGroupName = fnSub(f"/aws/lambda/${{{lambda_function.logical_id}}}") + + if "RetentionInDays" in config: + log_group.RetentionInDays = config["RetentionInDays"] + + return log_group + def _transform_capacity_provider_config(self) -> dict[str, Any]: """ Transform SAM CapacityProviderConfig to CloudFormation format. diff --git a/tests/model/test_sam_resources.py b/tests/model/test_sam_resources.py index a51d9c5f99..fb8c4d058e 100644 --- a/tests/model/test_sam_resources.py +++ b/tests/model/test_sam_resources.py @@ -9,6 +9,7 @@ from samtranslator.model.apigatewayv2 import ApiGatewayV2HttpApi from samtranslator.model.iam import IAMRole from samtranslator.model.lambda_ import LambdaFunction, LambdaLayerVersion, LambdaPermission, LambdaUrl, LambdaVersion +from samtranslator.model.log import LogGroup from samtranslator.model.packagetype import IMAGE, ZIP from samtranslator.model.sam_resources import ( SamApi, @@ -1023,3 +1024,87 @@ def test_managed_policy_name_within_intrinsic_if_else(self): self.assertEqual( iamRoles[0].ManagedPolicyArns[1]["Fn::If"][2], self.kwargs["managed_policy_map"][managedPolicyName] ) + + +class TestLogGroup(TestCase): + kwargs = { + "intrinsics_resolver": IntrinsicsResolver({}), + "event_resources": [], + "managed_policy_map": {"foo": "bar"}, + "resource_resolver": ResourceResolver({}), + } + + def _make_function(self): + function = SamFunction("MyFunction") + function.InlineCode = "hello world" + function.Runtime = "python3.12" + function.Handler = "index.handler" + return function + + def _transform(self, function): + resources = function.to_cloudformation(**self.kwargs) + functions = [r for r in resources if isinstance(r, LambdaFunction)] + log_groups = [r for r in resources if isinstance(r, LogGroup)] + return functions, log_groups + + @patch("boto3.session.Session.region_name", "ap-southeast-1") + def test_no_log_group_by_default(self): + # Backward compatibility: no LogGroup property means no log group generated. + function = self._make_function() + _, log_groups = self._transform(function) + self.assertEqual(log_groups, []) + + @patch("boto3.session.Session.region_name", "ap-southeast-1") + def test_default_log_group_name_and_deletion_policy(self): + function = self._make_function() + function.LogGroup = {"RetentionInDays": 7} + functions, log_groups = self._transform(function) + + self.assertEqual(len(log_groups), 1) + log_group = log_groups[0] + self.assertEqual(log_group.logical_id, "MyFunctionLogGroup") + self.assertEqual(log_group.LogGroupName, {"Fn::Sub": "/aws/lambda/${MyFunction}"}) + self.assertEqual(log_group.RetentionInDays, 7) + + log_group_dict = log_group.to_dict()["MyFunctionLogGroup"] + self.assertEqual(log_group_dict["DeletionPolicy"], "Delete") + self.assertEqual(log_group_dict["UpdateReplacePolicy"], "Delete") + + # Mechanism 1 must not touch the function's LoggingConfig. + self.assertIsNone(functions[0].LoggingConfig) + + @patch("boto3.session.Session.region_name", "ap-southeast-1") + def test_deletion_policy_retain(self): + function = self._make_function() + function.LogGroup = {"DeletionPolicy": "Retain"} + _, log_groups = self._transform(function) + + log_group_dict = log_groups[0].to_dict()["MyFunctionLogGroup"] + self.assertEqual(log_group_dict["DeletionPolicy"], "Retain") + self.assertEqual(log_group_dict["UpdateReplacePolicy"], "Retain") + + @patch("boto3.session.Session.region_name", "ap-southeast-1") + def test_custom_log_group_name_wires_logging_config(self): + function = self._make_function() + function.LoggingConfig = {"LogFormat": "JSON"} + function.LogGroup = {"LogGroupName": "my-custom-group", "RetentionInDays": 14} + functions, log_groups = self._transform(function) + + log_group = log_groups[0] + self.assertEqual(log_group.LogGroupName, "my-custom-group") + self.assertEqual(log_group.RetentionInDays, 14) + + # Mechanism 2 binds the function to the managed log group without dropping + # existing LoggingConfig settings. + self.assertEqual( + functions[0].LoggingConfig, + {"LogFormat": "JSON", "LogGroup": "my-custom-group"}, + ) + + @patch("boto3.session.Session.region_name", "ap-southeast-1") + def test_invalid_deletion_policy_raises(self): + function = self._make_function() + function.LogGroup = {"DeletionPolicy": "Snapshot"} + with pytest.raises(InvalidResourceException) as ctx: + function.to_cloudformation(**self.kwargs) + self.assertIn("LogGroup.DeletionPolicy", str(ctx.value)) diff --git a/tests/translator/test_function_resources.py b/tests/translator/test_function_resources.py index 73afe8b177..f98a4760c3 100644 --- a/tests/translator/test_function_resources.py +++ b/tests/translator/test_function_resources.py @@ -888,8 +888,9 @@ def _make_deployment_preference_collection(self): class TestSupportedResourceReferences(TestCase): def test_must_not_break_support(self): func = SamFunction("LogicalId") - self.assertEqual(4, len(func.referable_properties)) + self.assertEqual(5, len(func.referable_properties)) self.assertEqual(func.referable_properties["Alias"], "AWS::Lambda::Alias") self.assertEqual(func.referable_properties["Version"], "AWS::Lambda::Version") self.assertEqual(func.referable_properties["DestinationTopic"], "AWS::SNS::Topic") self.assertEqual(func.referable_properties["DestinationQueue"], "AWS::SQS::Queue") + self.assertEqual(func.referable_properties["LogGroup"], "AWS::Logs::LogGroup") From a76f10b7162dcd99b5bbc64af93df17ea003e0ba Mon Sep 17 00:00:00 2001 From: Guido Nebiolo Date: Tue, 14 Jul 2026 14:16:21 +0200 Subject: [PATCH 3/5] chore(schema): add LogGroup property to AWS::Serverless::Function Add the FunctionLogGroup model (RetentionInDays, DeletionPolicy, LogGroupName) to the Function Properties and Globals schema, document the new properties in sam-docs, and regenerate the SAM and unified JSON schemas. Refs #2665, #3791 --- .../schema_source/aws_serverless_function.py | 17 +++++ .../internal/schema_source/sam-docs.json | 10 ++- samtranslator/schema/schema.json | 40 +++++++++++ schema_source/sam.schema.json | 68 +++++++++++++++++++ 4 files changed, 133 insertions(+), 2 deletions(-) diff --git a/samtranslator/internal/schema_source/aws_serverless_function.py b/samtranslator/internal/schema_source/aws_serverless_function.py index 811f37d1bf..328100023e 100644 --- a/samtranslator/internal/schema_source/aws_serverless_function.py +++ b/samtranslator/internal/schema_source/aws_serverless_function.py @@ -44,6 +44,7 @@ httpapieventproperties = get_prop("sam-property-function-httpapi") iotruleeventproperties = get_prop("sam-property-function-iotrule") kinesiseventproperties = get_prop("sam-property-function-kinesis") +loggroup = get_prop("sam-property-function-loggroup") mqeventproperties = get_prop("sam-property-function-mq") mskeventproperties = get_prop("sam-property-function-msk") prop = get_prop(PROPERTIES_STEM) @@ -576,6 +577,20 @@ class ScheduleV2Event(BaseModel): TenancyConfig = PassThroughProp | None +class FunctionLogGroup(BaseModel): + RetentionInDays: PassThroughProp | None = passthrough_prop( + "sam-property-function-loggroup", + "RetentionInDays", + ["AWS::Logs::LogGroup", "Properties", "RetentionInDays"], + ) + DeletionPolicy: str | None = loggroup("DeletionPolicy") + LogGroupName: PassThroughProp | None = passthrough_prop( + "sam-property-function-loggroup", + "LogGroupName", + ["AWS::Logs::LogGroup", "Properties", "LogGroupName"], + ) + + class CapacityProviderConfig(BaseModel): Arn: SamIntrinsicable[str] = capacityproviderconfig("Arn") PerExecutionEnvironmentMaxConcurrency: SamIntrinsicable[int] | None = capacityproviderconfig( @@ -715,6 +730,7 @@ class Properties(BaseModel): "LoggingConfig", ["AWS::Lambda::Function", "Properties", "LoggingConfig"], ) + LogGroup: FunctionLogGroup | None = prop("LogGroup") RecursiveLoop: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "RecursiveLoop", @@ -809,6 +825,7 @@ class Globals(BaseModel): "LoggingConfig", ["AWS::Lambda::Function", "Properties", "LoggingConfig"], ) + LogGroup: FunctionLogGroup | None = prop("LogGroup") RecursiveLoop: PassThroughProp | None = passthrough_prop( PROPERTIES_STEM, "RecursiveLoop", diff --git a/samtranslator/internal/schema_source/sam-docs.json b/samtranslator/internal/schema_source/sam-docs.json index 1242ad857a..e40bd5048e 100644 --- a/samtranslator/internal/schema_source/sam-docs.json +++ b/samtranslator/internal/schema_source/sam-docs.json @@ -890,7 +890,8 @@ "Tracing": "The string that specifies the function's X-Ray tracing mode. \n+ `Active` \u2013 Activates X-Ray tracing for the function.\n+ `Disabled` \u2013 Deactivates X-Ray for the function.\n+ `PassThrough` \u2013 Activates X-Ray tracing for the function. Sampling decision is delegated to the downstream services.\nIf specified as `Active` or `PassThrough` and the `Role` property is not set, AWS SAM adds the `arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess` policy to the Lambda execution role that it creates for you. \nFor more information about X-Ray, see [Using AWS Lambda with AWS X-Ray](https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html) in the *AWS Lambda Developer Guide*. \n*Valid values*: [`Active`\\$1`Disabled`\\$1`PassThrough`] \n*Type*: String \n*Required*: No \n*CloudFormation compatibility*: This property is similar to the [`TracingConfig`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-tracingconfig) property of an `AWS::Lambda::Function` resource.", "VersionDeletionPolicy": "Specifies the deletion policy for the Lambda version resource that is created when `AutoPublishAlias` is set. This controls whether the version resource is retained or deleted when the stack is deleted. \n*Valid values*: `Delete`, `Retain`, or `Snapshot` \n*Type*: String \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an CloudFormation equivalent. It sets the `DeletionPolicy` attribute on the generated `AWS::Lambda::Version` resource.", "VersionDescription": "Specifies the `Description` field that is added on the new Lambda version resource. \n*Type*: String \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [`Description`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html#cfn-lambda-version-description) property of an `AWS::Lambda::Version` resource.", - "VpcConfig": "The configuration that enables this function to access private resources within your virtual private cloud (https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/VPC.html). \n*Type*: [VpcConfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-vpcconfig.html) \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [`VpcConfig`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-vpcconfig.html) property of an `AWS::Lambda::Function` resource." + "VpcConfig": "The configuration that enables this function to access private resources within your virtual private cloud (https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/VPC.html). \n*Type*: [VpcConfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-vpcconfig.html) \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [`VpcConfig`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-vpcconfig.html) property of an `AWS::Lambda::Function` resource.", + "LogGroup": "The stack-managed Amazon CloudWatch Logs log group for the function. Specifying this property creates an `AWS::Logs::LogGroup` resource that is managed as part of the stack, so you can control its retention and have it deleted together with the stack. \nBy default, the log group uses the conventional `/aws/lambda/` name and no changes are made to the function's `LoggingConfig`. If `LogGroupName` is provided, the function's `LoggingConfig.LogGroup` is set so the function writes to the managed log group. \n*Type*: [LogGroup](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent." }, "sam-resource-graphqlapi": { "ApiKeys": "Create a unique key that can be used to perform GraphQL operations requiring an API key. \n*Type*: [ApiKeys](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-graphqlapi-apikeys.html) \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn\u2019t have an CloudFormation equivalent.", @@ -994,6 +995,11 @@ "SubnetIds": "A list of subnet IDs where the service provisions ENIs for VPC egress connectivity. \n*Type*: List \n*Required*: Yes \n*CloudFormation compatibility*: This property is passed directly to the [`SubnetIds`](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-lambda-networkconnector-vpcegressconfiguration.html#cfn-lambda-networkconnector-vpcegressconfiguration-subnetids) property of `Configuration.VpcEgressConfiguration` of an `AWS::Lambda::NetworkConnector` resource.", "SecurityGroupIds": "A list of security group IDs to associate with the elastic network interfaces (ENIs). \n*Type*: List \n*Required*: Yes \n*CloudFormation compatibility*: This property is passed directly to the [`SecurityGroupIds`](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-lambda-networkconnector-vpcegressconfiguration.html#cfn-lambda-networkconnector-vpcegressconfiguration-securitygroupids) property of `Configuration.VpcEgressConfiguration` of an `AWS::Lambda::NetworkConnector` resource.", "NetworkProtocol": "The network protocol for the VPC egress connection. \n*Valid values*: `IPv4`, `DualStack` \n*Type*: String \n*Required*: Yes \n*CloudFormation compatibility*: This property is passed directly to the [`NetworkProtocol`](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-lambda-networkconnector-vpcegressconfiguration.html#cfn-lambda-networkconnector-vpcegressconfiguration-networkprotocol) property of `Configuration.VpcEgressConfiguration` of an `AWS::Lambda::NetworkConnector` resource." + }, + "sam-property-function-loggroup": { + "RetentionInDays": "The number of days to retain the log events in the managed log group. See [RetentionInDays](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html#cfn-logs-loggroup-retentionindays). \n*Type*: Integer \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [RetentionInDays](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html#cfn-logs-loggroup-retentionindays) property of an `AWS::Logs::LogGroup` resource.", + "DeletionPolicy": "Whether the managed log group is deleted or retained when it is removed from the stack. \n*Valid values*: `Delete`, `Retain` \n*Type*: String \n*Required*: No \n*Default*: `Delete` \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent. It sets the `DeletionPolicy` and `UpdateReplacePolicy` attributes of the generated `AWS::Logs::LogGroup` resource.", + "LogGroupName": "A custom name for the managed log group. When provided, the function's `LoggingConfig.LogGroup` is set to this value so the function writes to the managed log group. The name must not begin with `aws/`. \n*Type*: String \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [LogGroupName](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html#cfn-logs-loggroup-loggroupname) property of an `AWS::Logs::LogGroup` resource." } } -} \ No newline at end of file +} diff --git a/samtranslator/schema/schema.json b/samtranslator/schema/schema.json index 98f3347813..efcd257f34 100644 --- a/samtranslator/schema/schema.json +++ b/samtranslator/schema/schema.json @@ -366282,6 +366282,28 @@ "title": "Function", "type": "object" }, + "FunctionLogGroup": { + "additionalProperties": false, + "properties": { + "DeletionPolicy": { + "markdownDescription": "Whether the managed log group is deleted or retained when it is removed from the stack. \n*Valid values*: `Delete`, `Retain` \n*Type*: String \n*Required*: No \n*Default*: `Delete` \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent. It sets the `DeletionPolicy` and `UpdateReplacePolicy` attributes of the generated `AWS::Logs::LogGroup` resource.", + "title": "DeletionPolicy", + "type": "string" + }, + "LogGroupName": { + "markdownDescription": "A custom name for the managed log group. When provided, the function's `LoggingConfig.LogGroup` is set to this value so the function writes to the managed log group. The name must not begin with `aws/`. \n*Type*: String \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [LogGroupName](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html#cfn-logs-loggroup-loggroupname) property of an `AWS::Logs::LogGroup` resource.", + "title": "LogGroupName", + "type": "string" + }, + "RetentionInDays": { + "markdownDescription": "The number of days to retain the log events in the managed log group. See [RetentionInDays](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html#cfn-logs-loggroup-retentionindays). \n*Type*: Integer \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [RetentionInDays](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html#cfn-logs-loggroup-retentionindays) property of an `AWS::Logs::LogGroup` resource.", + "title": "RetentionInDays", + "type": "number" + } + }, + "title": "FunctionLogGroup", + "type": "object" + }, "FunctionUrlConfig": { "additionalProperties": false, "properties": { @@ -370823,6 +370845,15 @@ "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. The version is either a full ARN including the version or a reference to a LayerVersion resource. For example, a reference to a `LayerVersion` will be `!Ref MyLayer` while a full ARN including the version will be `arn:aws:lambda:region:account-id:layer:layer-name:version`. \n*Type*: List \n*Required*: No \n*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" }, + "LogGroup": { + "allOf": [ + { + "$ref": "#/definitions/FunctionLogGroup" + } + ], + "markdownDescription": "The stack-managed Amazon CloudWatch Logs log group for the function. Specifying this property creates an `AWS::Logs::LogGroup` resource that is managed as part of the stack, so you can control its retention and have it deleted together with the stack. \nBy default, the log group uses the conventional `/aws/lambda/` name and no changes are made to the function's `LoggingConfig`. If `LogGroupName` is provided, the function's `LoggingConfig.LogGroup` is set so the function writes to the managed log group. \n*Type*: [LogGroup](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent.", + "title": "LogGroup" + }, "LoggingConfig": { "$ref": "#/definitions/AWS::Lambda::Function.LoggingConfig", "markdownDescription": "The function's Amazon CloudWatch Logs configuration settings. \n*Type*: [LoggingConfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-loggingconfig.html) \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-loggingconfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-loggingconfig) property of an `AWS::Lambda::Function` resource.", @@ -371259,6 +371290,15 @@ "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. The version is either a full ARN including the version or a reference to a LayerVersion resource. For example, a reference to a `LayerVersion` will be `!Ref MyLayer` while a full ARN including the version will be `arn:aws:lambda:region:account-id:layer:layer-name:version`. \n*Type*: List \n*Required*: No \n*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" }, + "LogGroup": { + "allOf": [ + { + "$ref": "#/definitions/FunctionLogGroup" + } + ], + "markdownDescription": "The stack-managed Amazon CloudWatch Logs log group for the function. Specifying this property creates an `AWS::Logs::LogGroup` resource that is managed as part of the stack, so you can control its retention and have it deleted together with the stack. \nBy default, the log group uses the conventional `/aws/lambda/` name and no changes are made to the function's `LoggingConfig`. If `LogGroupName` is provided, the function's `LoggingConfig.LogGroup` is set so the function writes to the managed log group. \n*Type*: [LogGroup](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent.", + "title": "LogGroup" + }, "LoggingConfig": { "$ref": "#/definitions/AWS::Lambda::Function.LoggingConfig", "markdownDescription": "The function's Amazon CloudWatch Logs configuration settings. \n*Type*: [LoggingConfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-loggingconfig.html) \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-loggingconfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-loggingconfig) property of an `AWS::Lambda::Function` resource.", diff --git a/schema_source/sam.schema.json b/schema_source/sam.schema.json index 11cb241d90..06c9832ea4 100644 --- a/schema_source/sam.schema.json +++ b/schema_source/sam.schema.json @@ -1774,6 +1774,56 @@ "title": "Function", "type": "object" }, + "FunctionLogGroup": { + "additionalProperties": false, + "properties": { + "DeletionPolicy": { + "markdownDescription": "Whether the managed log group is deleted or retained when it is removed from the stack. \n*Valid values*: `Delete`, `Retain` \n*Type*: String \n*Required*: No \n*Default*: `Delete` \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent. It sets the `DeletionPolicy` and `UpdateReplacePolicy` attributes of the generated `AWS::Logs::LogGroup` resource.", + "title": "DeletionPolicy", + "type": "string" + }, + "LogGroupName": { + "__samPassThrough": { + "markdownDescriptionOverride": "A custom name for the managed log group. When provided, the function's `LoggingConfig.LogGroup` is set to this value so the function writes to the managed log group. The name must not begin with `aws/`. \n*Type*: String \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [LogGroupName](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html#cfn-logs-loggroup-loggroupname) property of an `AWS::Logs::LogGroup` resource.", + "schemaPath": [ + "definitions", + "AWS::Logs::LogGroup", + "properties", + "Properties", + "properties", + "LogGroupName" + ] + }, + "allOf": [ + { + "$ref": "#/definitions/PassThroughProp" + } + ], + "title": "LogGroupName" + }, + "RetentionInDays": { + "__samPassThrough": { + "markdownDescriptionOverride": "The number of days to retain the log events in the managed log group. See [RetentionInDays](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html#cfn-logs-loggroup-retentionindays). \n*Type*: Integer \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [RetentionInDays](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html#cfn-logs-loggroup-retentionindays) property of an `AWS::Logs::LogGroup` resource.", + "schemaPath": [ + "definitions", + "AWS::Logs::LogGroup", + "properties", + "Properties", + "properties", + "RetentionInDays" + ] + }, + "allOf": [ + { + "$ref": "#/definitions/PassThroughProp" + } + ], + "title": "RetentionInDays" + } + }, + "title": "FunctionLogGroup", + "type": "object" + }, "FunctionUrlConfig": { "additionalProperties": false, "properties": { @@ -7073,6 +7123,15 @@ "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. The version is either a full ARN including the version or a reference to a LayerVersion resource. For example, a reference to a `LayerVersion` will be `!Ref MyLayer` while a full ARN including the version will be `arn:aws:lambda:region:account-id:layer:layer-name:version`. \n*Type*: List \n*Required*: No \n*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" }, + "LogGroup": { + "allOf": [ + { + "$ref": "#/definitions/FunctionLogGroup" + } + ], + "markdownDescription": "The stack-managed Amazon CloudWatch Logs log group for the function. Specifying this property creates an `AWS::Logs::LogGroup` resource that is managed as part of the stack, so you can control its retention and have it deleted together with the stack. \nBy default, the log group uses the conventional `/aws/lambda/` name and no changes are made to the function's `LoggingConfig`. If `LogGroupName` is provided, the function's `LoggingConfig.LogGroup` is set so the function writes to the managed log group. \n*Type*: [LogGroup](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent.", + "title": "LogGroup" + }, "LoggingConfig": { "__samPassThrough": { "markdownDescriptionOverride": "The function's Amazon CloudWatch Logs configuration settings. \n*Type*: [LoggingConfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-loggingconfig.html) \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-loggingconfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-loggingconfig) property of an `AWS::Lambda::Function` resource.", @@ -7782,6 +7841,15 @@ "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. The version is either a full ARN including the version or a reference to a LayerVersion resource. For example, a reference to a `LayerVersion` will be `!Ref MyLayer` while a full ARN including the version will be `arn:aws:lambda:region:account-id:layer:layer-name:version`. \n*Type*: List \n*Required*: No \n*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" }, + "LogGroup": { + "allOf": [ + { + "$ref": "#/definitions/FunctionLogGroup" + } + ], + "markdownDescription": "The stack-managed Amazon CloudWatch Logs log group for the function. Specifying this property creates an `AWS::Logs::LogGroup` resource that is managed as part of the stack, so you can control its retention and have it deleted together with the stack. \nBy default, the log group uses the conventional `/aws/lambda/` name and no changes are made to the function's `LoggingConfig`. If `LogGroupName` is provided, the function's `LoggingConfig.LogGroup` is set so the function writes to the managed log group. \n*Type*: [LogGroup](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) \n*Required*: No \n*CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent.", + "title": "LogGroup" + }, "LoggingConfig": { "__samPassThrough": { "markdownDescriptionOverride": "The function's Amazon CloudWatch Logs configuration settings. \n*Type*: [LoggingConfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-loggingconfig.html) \n*Required*: No \n*CloudFormation compatibility*: This property is passed directly to the [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-loggingconfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-loggingconfig) property of an `AWS::Lambda::Function` resource.", From 6c0514f888ba219acba5b60efc223128b74ed302 Mon Sep 17 00:00:00 2001 From: Guido Nebiolo Date: Tue, 14 Jul 2026 14:16:32 +0200 Subject: [PATCH 4/5] feat(function): inherit LogGroup from Globals Add LogGroup to the Function properties supported in the Globals section so a common log group configuration (for example retention or deletion policy) can be defined once and inherited by all functions, with resource-level overrides merged on top. Refs #2665, #3791 --- samtranslator/plugins/globals/globals.py | 1 + .../translator/output/error_globals_unsupported_property.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/samtranslator/plugins/globals/globals.py b/samtranslator/plugins/globals/globals.py index e1603e5471..16e261edc6 100644 --- a/samtranslator/plugins/globals/globals.py +++ b/samtranslator/plugins/globals/globals.py @@ -54,6 +54,7 @@ class Globals: "FunctionUrlConfig", "RuntimeManagementConfig", "LoggingConfig", + "LogGroup", "RecursiveLoop", "SourceKMSKeyArn", "TenancyConfig", diff --git a/tests/translator/output/error_globals_unsupported_property.json b/tests/translator/output/error_globals_unsupported_property.json index b7aeb3c836..cd0118165c 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', 'RuntimeManagementConfig', 'LoggingConfig', 'RecursiveLoop', 'SourceKMSKeyArn', 'TenancyConfig', 'DurableConfig', 'CapacityProviderConfig', 'FunctionScalingConfig', 'PublishToLatestPublished', 'VersionDeletionPolicy']" + "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', 'RuntimeManagementConfig', 'LoggingConfig', 'LogGroup', 'RecursiveLoop', 'SourceKMSKeyArn', 'TenancyConfig', 'DurableConfig', 'CapacityProviderConfig', 'FunctionScalingConfig', 'PublishToLatestPublished', 'VersionDeletionPolicy']" ], - "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', 'RuntimeManagementConfig', 'LoggingConfig', 'RecursiveLoop', 'SourceKMSKeyArn', 'TenancyConfig', 'DurableConfig', 'CapacityProviderConfig', 'FunctionScalingConfig', 'PublishToLatestPublished', 'VersionDeletionPolicy']" + "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', 'RuntimeManagementConfig', 'LoggingConfig', 'LogGroup', 'RecursiveLoop', 'SourceKMSKeyArn', 'TenancyConfig', 'DurableConfig', 'CapacityProviderConfig', 'FunctionScalingConfig', 'PublishToLatestPublished', 'VersionDeletionPolicy']" } From 5447d9c372b9cc2ded0e25c720302c8b99d56dc7 Mon Sep 17 00:00:00 2001 From: Guido Nebiolo Date: Tue, 14 Jul 2026 14:16:44 +0200 Subject: [PATCH 5/5] test(function): add transform tests for managed log group generation Cover the default log group name (Mechanism 1), custom log group name with LoggingConfig wiring (Mechanism 2), DeletionPolicy Retain, Condition propagation to the log group, Globals inheritance/override, and the invalid DeletionPolicy error case, across all partitions. Refs #2665, #3791, #1216, #2057 --- ...ith_invalid_log_group_deletion_policy.yaml | 10 + .../input/function_with_log_group.yaml | 24 +++ .../function_with_log_group_condition.yaml | 21 ++ .../function_with_log_group_custom_name.yaml | 12 ++ .../function_with_log_group_globals.yaml | 22 +++ .../input/function_with_log_group_retain.yaml | 10 + .../aws-cn/function_with_log_group.json | 184 ++++++++++++++++++ .../function_with_log_group_condition.json | 87 +++++++++ .../function_with_log_group_custom_name.json | 70 +++++++ .../function_with_log_group_globals.json | 132 +++++++++++++ .../function_with_log_group_retain.json | 68 +++++++ .../aws-us-gov/function_with_log_group.json | 184 ++++++++++++++++++ .../function_with_log_group_condition.json | 87 +++++++++ .../function_with_log_group_custom_name.json | 70 +++++++ .../function_with_log_group_globals.json | 132 +++++++++++++ .../function_with_log_group_retain.json | 68 +++++++ ...ith_invalid_log_group_deletion_policy.json | 9 + .../output/function_with_log_group.json | 184 ++++++++++++++++++ .../function_with_log_group_condition.json | 87 +++++++++ .../function_with_log_group_custom_name.json | 70 +++++++ .../function_with_log_group_globals.json | 132 +++++++++++++ .../function_with_log_group_retain.json | 68 +++++++ 22 files changed, 1731 insertions(+) create mode 100644 tests/translator/input/error_function_with_invalid_log_group_deletion_policy.yaml create mode 100644 tests/translator/input/function_with_log_group.yaml create mode 100644 tests/translator/input/function_with_log_group_condition.yaml create mode 100644 tests/translator/input/function_with_log_group_custom_name.yaml create mode 100644 tests/translator/input/function_with_log_group_globals.yaml create mode 100644 tests/translator/input/function_with_log_group_retain.yaml create mode 100644 tests/translator/output/aws-cn/function_with_log_group.json create mode 100644 tests/translator/output/aws-cn/function_with_log_group_condition.json create mode 100644 tests/translator/output/aws-cn/function_with_log_group_custom_name.json create mode 100644 tests/translator/output/aws-cn/function_with_log_group_globals.json create mode 100644 tests/translator/output/aws-cn/function_with_log_group_retain.json create mode 100644 tests/translator/output/aws-us-gov/function_with_log_group.json create mode 100644 tests/translator/output/aws-us-gov/function_with_log_group_condition.json create mode 100644 tests/translator/output/aws-us-gov/function_with_log_group_custom_name.json create mode 100644 tests/translator/output/aws-us-gov/function_with_log_group_globals.json create mode 100644 tests/translator/output/aws-us-gov/function_with_log_group_retain.json create mode 100644 tests/translator/output/error_function_with_invalid_log_group_deletion_policy.json create mode 100644 tests/translator/output/function_with_log_group.json create mode 100644 tests/translator/output/function_with_log_group_condition.json create mode 100644 tests/translator/output/function_with_log_group_custom_name.json create mode 100644 tests/translator/output/function_with_log_group_globals.json create mode 100644 tests/translator/output/function_with_log_group_retain.json diff --git a/tests/translator/input/error_function_with_invalid_log_group_deletion_policy.yaml b/tests/translator/input/error_function_with_invalid_log_group_deletion_policy.yaml new file mode 100644 index 0000000000..2cc52fb5ad --- /dev/null +++ b/tests/translator/input/error_function_with_invalid_log_group_deletion_policy.yaml @@ -0,0 +1,10 @@ +Resources: + InvalidLogGroupFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python3.12 + LogGroup: + RetentionInDays: 7 + DeletionPolicy: Snapshot diff --git a/tests/translator/input/function_with_log_group.yaml b/tests/translator/input/function_with_log_group.yaml new file mode 100644 index 0000000000..1680c58a0c --- /dev/null +++ b/tests/translator/input/function_with_log_group.yaml @@ -0,0 +1,24 @@ +Resources: + RetentionFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python3.12 + LogGroup: + RetentionInDays: 7 + + DefaultLogGroupFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python3.12 + LogGroup: {} + + NoLogGroupFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python3.12 diff --git a/tests/translator/input/function_with_log_group_condition.yaml b/tests/translator/input/function_with_log_group_condition.yaml new file mode 100644 index 0000000000..9790a9dbba --- /dev/null +++ b/tests/translator/input/function_with_log_group_condition.yaml @@ -0,0 +1,21 @@ +Parameters: + CreateResources: + Type: String + Default: 'true' + +Conditions: + ShouldCreate: + Fn::Equals: + - !Ref CreateResources + - 'true' + +Resources: + ConditionalLogGroupFunction: + Type: AWS::Serverless::Function + Condition: ShouldCreate + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python3.12 + LogGroup: + RetentionInDays: 30 diff --git a/tests/translator/input/function_with_log_group_custom_name.yaml b/tests/translator/input/function_with_log_group_custom_name.yaml new file mode 100644 index 0000000000..e4d149fcc9 --- /dev/null +++ b/tests/translator/input/function_with_log_group_custom_name.yaml @@ -0,0 +1,12 @@ +Resources: + CustomLogGroupFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python3.12 + LoggingConfig: + LogFormat: JSON + LogGroup: + RetentionInDays: 14 + LogGroupName: my-custom-log-group diff --git a/tests/translator/input/function_with_log_group_globals.yaml b/tests/translator/input/function_with_log_group_globals.yaml new file mode 100644 index 0000000000..55dc83b728 --- /dev/null +++ b/tests/translator/input/function_with_log_group_globals.yaml @@ -0,0 +1,22 @@ +Globals: + Function: + LogGroup: + RetentionInDays: 30 + DeletionPolicy: Retain + +Resources: + InheritLogGroupFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python3.12 + + OverrideLogGroupFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python3.12 + LogGroup: + RetentionInDays: 7 diff --git a/tests/translator/input/function_with_log_group_retain.yaml b/tests/translator/input/function_with_log_group_retain.yaml new file mode 100644 index 0000000000..187be6aab9 --- /dev/null +++ b/tests/translator/input/function_with_log_group_retain.yaml @@ -0,0 +1,10 @@ +Resources: + RetainLogGroupFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python3.12 + LogGroup: + RetentionInDays: 90 + DeletionPolicy: Retain diff --git a/tests/translator/output/aws-cn/function_with_log_group.json b/tests/translator/output/aws-cn/function_with_log_group.json new file mode 100644 index 0000000000..3f8dcf9c58 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_log_group.json @@ -0,0 +1,184 @@ +{ + "Resources": { + "DefaultLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "DefaultLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DefaultLogGroupFunctionLogGroup": { + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${DefaultLogGroupFunction}" + } + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "DefaultLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "NoLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "NoLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "NoLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "RetentionFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "RetentionFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "RetentionFunctionLogGroup": { + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${RetentionFunction}" + }, + "RetentionInDays": 7 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "RetentionFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-cn/function_with_log_group_condition.json b/tests/translator/output/aws-cn/function_with_log_group_condition.json new file mode 100644 index 0000000000..a591b1a228 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_log_group_condition.json @@ -0,0 +1,87 @@ +{ + "Conditions": { + "ShouldCreate": { + "Fn::Equals": [ + { + "Ref": "CreateResources" + }, + "true" + ] + } + }, + "Parameters": { + "CreateResources": { + "Default": "true", + "Type": "String" + } + }, + "Resources": { + "ConditionalLogGroupFunction": { + "Condition": "ShouldCreate", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "ConditionalLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "ConditionalLogGroupFunctionLogGroup": { + "Condition": "ShouldCreate", + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${ConditionalLogGroupFunction}" + }, + "RetentionInDays": 30 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "ConditionalLogGroupFunctionRole": { + "Condition": "ShouldCreate", + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-cn/function_with_log_group_custom_name.json b/tests/translator/output/aws-cn/function_with_log_group_custom_name.json new file mode 100644 index 0000000000..3010b426ec --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_log_group_custom_name.json @@ -0,0 +1,70 @@ +{ + "Resources": { + "CustomLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "LoggingConfig": { + "LogFormat": "JSON", + "LogGroup": "my-custom-log-group" + }, + "Role": { + "Fn::GetAtt": [ + "CustomLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "CustomLogGroupFunctionLogGroup": { + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": "my-custom-log-group", + "RetentionInDays": 14 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "CustomLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-cn/function_with_log_group_globals.json b/tests/translator/output/aws-cn/function_with_log_group_globals.json new file mode 100644 index 0000000000..d06d00b021 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_log_group_globals.json @@ -0,0 +1,132 @@ +{ + "Resources": { + "InheritLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "InheritLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "InheritLogGroupFunctionLogGroup": { + "DeletionPolicy": "Retain", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${InheritLogGroupFunction}" + }, + "RetentionInDays": 30 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain" + }, + "InheritLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "OverrideLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "OverrideLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "OverrideLogGroupFunctionLogGroup": { + "DeletionPolicy": "Retain", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${OverrideLogGroupFunction}" + }, + "RetentionInDays": 7 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain" + }, + "OverrideLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-cn/function_with_log_group_retain.json b/tests/translator/output/aws-cn/function_with_log_group_retain.json new file mode 100644 index 0000000000..1d29065210 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_log_group_retain.json @@ -0,0 +1,68 @@ +{ + "Resources": { + "RetainLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "RetainLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "RetainLogGroupFunctionLogGroup": { + "DeletionPolicy": "Retain", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${RetainLogGroupFunction}" + }, + "RetentionInDays": 90 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain" + }, + "RetainLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_log_group.json b/tests/translator/output/aws-us-gov/function_with_log_group.json new file mode 100644 index 0000000000..f2c3fb6304 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_log_group.json @@ -0,0 +1,184 @@ +{ + "Resources": { + "DefaultLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "DefaultLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DefaultLogGroupFunctionLogGroup": { + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${DefaultLogGroupFunction}" + } + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "DefaultLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "NoLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "NoLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "NoLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "RetentionFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "RetentionFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "RetentionFunctionLogGroup": { + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${RetentionFunction}" + }, + "RetentionInDays": 7 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "RetentionFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_log_group_condition.json b/tests/translator/output/aws-us-gov/function_with_log_group_condition.json new file mode 100644 index 0000000000..a6b3bdfe47 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_log_group_condition.json @@ -0,0 +1,87 @@ +{ + "Conditions": { + "ShouldCreate": { + "Fn::Equals": [ + { + "Ref": "CreateResources" + }, + "true" + ] + } + }, + "Parameters": { + "CreateResources": { + "Default": "true", + "Type": "String" + } + }, + "Resources": { + "ConditionalLogGroupFunction": { + "Condition": "ShouldCreate", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "ConditionalLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "ConditionalLogGroupFunctionLogGroup": { + "Condition": "ShouldCreate", + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${ConditionalLogGroupFunction}" + }, + "RetentionInDays": 30 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "ConditionalLogGroupFunctionRole": { + "Condition": "ShouldCreate", + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_log_group_custom_name.json b/tests/translator/output/aws-us-gov/function_with_log_group_custom_name.json new file mode 100644 index 0000000000..ed5d6ca944 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_log_group_custom_name.json @@ -0,0 +1,70 @@ +{ + "Resources": { + "CustomLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "LoggingConfig": { + "LogFormat": "JSON", + "LogGroup": "my-custom-log-group" + }, + "Role": { + "Fn::GetAtt": [ + "CustomLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "CustomLogGroupFunctionLogGroup": { + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": "my-custom-log-group", + "RetentionInDays": 14 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "CustomLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_log_group_globals.json b/tests/translator/output/aws-us-gov/function_with_log_group_globals.json new file mode 100644 index 0000000000..cc3bce02f0 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_log_group_globals.json @@ -0,0 +1,132 @@ +{ + "Resources": { + "InheritLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "InheritLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "InheritLogGroupFunctionLogGroup": { + "DeletionPolicy": "Retain", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${InheritLogGroupFunction}" + }, + "RetentionInDays": 30 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain" + }, + "InheritLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "OverrideLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "OverrideLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "OverrideLogGroupFunctionLogGroup": { + "DeletionPolicy": "Retain", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${OverrideLogGroupFunction}" + }, + "RetentionInDays": 7 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain" + }, + "OverrideLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_log_group_retain.json b/tests/translator/output/aws-us-gov/function_with_log_group_retain.json new file mode 100644 index 0000000000..b34635f360 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_log_group_retain.json @@ -0,0 +1,68 @@ +{ + "Resources": { + "RetainLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "RetainLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "RetainLogGroupFunctionLogGroup": { + "DeletionPolicy": "Retain", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${RetainLogGroupFunction}" + }, + "RetentionInDays": 90 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain" + }, + "RetainLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/error_function_with_invalid_log_group_deletion_policy.json b/tests/translator/output/error_function_with_invalid_log_group_deletion_policy.json new file mode 100644 index 0000000000..223f326ec2 --- /dev/null +++ b/tests/translator/output/error_function_with_invalid_log_group_deletion_policy.json @@ -0,0 +1,9 @@ +{ + "_autoGeneratedBreakdownErrorMessage": [ + "Invalid Serverless Application Specification document. ", + "Number of errors found: 1. ", + "Resource with id [InvalidLogGroupFunction] is invalid. ", + "'LogGroup.DeletionPolicy' must be one of 'Delete' or 'Retain'." + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [InvalidLogGroupFunction] is invalid. 'LogGroup.DeletionPolicy' must be one of 'Delete' or 'Retain'." +} diff --git a/tests/translator/output/function_with_log_group.json b/tests/translator/output/function_with_log_group.json new file mode 100644 index 0000000000..160d972112 --- /dev/null +++ b/tests/translator/output/function_with_log_group.json @@ -0,0 +1,184 @@ +{ + "Resources": { + "DefaultLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "DefaultLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DefaultLogGroupFunctionLogGroup": { + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${DefaultLogGroupFunction}" + } + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "DefaultLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "NoLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "NoLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "NoLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "RetentionFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "RetentionFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "RetentionFunctionLogGroup": { + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${RetentionFunction}" + }, + "RetentionInDays": 7 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "RetentionFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/function_with_log_group_condition.json b/tests/translator/output/function_with_log_group_condition.json new file mode 100644 index 0000000000..1f013ea01b --- /dev/null +++ b/tests/translator/output/function_with_log_group_condition.json @@ -0,0 +1,87 @@ +{ + "Conditions": { + "ShouldCreate": { + "Fn::Equals": [ + { + "Ref": "CreateResources" + }, + "true" + ] + } + }, + "Parameters": { + "CreateResources": { + "Default": "true", + "Type": "String" + } + }, + "Resources": { + "ConditionalLogGroupFunction": { + "Condition": "ShouldCreate", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "ConditionalLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "ConditionalLogGroupFunctionLogGroup": { + "Condition": "ShouldCreate", + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${ConditionalLogGroupFunction}" + }, + "RetentionInDays": 30 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "ConditionalLogGroupFunctionRole": { + "Condition": "ShouldCreate", + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/function_with_log_group_custom_name.json b/tests/translator/output/function_with_log_group_custom_name.json new file mode 100644 index 0000000000..da82645607 --- /dev/null +++ b/tests/translator/output/function_with_log_group_custom_name.json @@ -0,0 +1,70 @@ +{ + "Resources": { + "CustomLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "LoggingConfig": { + "LogFormat": "JSON", + "LogGroup": "my-custom-log-group" + }, + "Role": { + "Fn::GetAtt": [ + "CustomLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "CustomLogGroupFunctionLogGroup": { + "DeletionPolicy": "Delete", + "Properties": { + "LogGroupName": "my-custom-log-group", + "RetentionInDays": 14 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Delete" + }, + "CustomLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/function_with_log_group_globals.json b/tests/translator/output/function_with_log_group_globals.json new file mode 100644 index 0000000000..4a087dd016 --- /dev/null +++ b/tests/translator/output/function_with_log_group_globals.json @@ -0,0 +1,132 @@ +{ + "Resources": { + "InheritLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "InheritLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "InheritLogGroupFunctionLogGroup": { + "DeletionPolicy": "Retain", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${InheritLogGroupFunction}" + }, + "RetentionInDays": 30 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain" + }, + "InheritLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "OverrideLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "OverrideLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "OverrideLogGroupFunctionLogGroup": { + "DeletionPolicy": "Retain", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${OverrideLogGroupFunction}" + }, + "RetentionInDays": 7 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain" + }, + "OverrideLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/function_with_log_group_retain.json b/tests/translator/output/function_with_log_group_retain.json new file mode 100644 index 0000000000..1775666ed1 --- /dev/null +++ b/tests/translator/output/function_with_log_group_retain.json @@ -0,0 +1,68 @@ +{ + "Resources": { + "RetainLogGroupFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "RetainLogGroupFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.12", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "RetainLogGroupFunctionLogGroup": { + "DeletionPolicy": "Retain", + "Properties": { + "LogGroupName": { + "Fn::Sub": "/aws/lambda/${RetainLogGroupFunction}" + }, + "RetentionInDays": 90 + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain" + }, + "RetainLogGroupFunctionRole": { + "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" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +}