diff --git a/.cfnlintrc.yaml b/.cfnlintrc.yaml index e2e5f1f3c..b704fa915 100644 --- a/.cfnlintrc.yaml +++ b/.cfnlintrc.yaml @@ -119,6 +119,7 @@ ignore_templates: - tests/translator/output/**/webfunction_with_all_properties.json # New AWS::Lambda::WebFunction* types cfn-lint does not recognize; DependsOn is intentional (W3005) - tests/translator/output/**/webfunction_multiregion.json # New AWS::Lambda::WebFunction* types cfn-lint does not recognize; DependsOn is intentional (W3005) - tests/translator/output/**/webfunction_globals.json # New AWS::Lambda::WebFunction* types cfn-lint does not recognize; DependsOn is intentional (W3005) + - tests/translator/output/**/webfunction_update_replace_policy.json # New AWS::Lambda::WebFunction* types cfn-lint does not recognize; DependsOn is intentional (W3005) - tests/translator/output/**/network_connector_with_compute_types.json # cfn-lint's published AWS::Lambda::NetworkConnector schema enum for AssociatedComputeResourceTypes still lists only MicroVm; the preview WebFunction compute type trips E3030 - tests/translator/output/**/graphqlapi_api_cache.json # pre-existing: fixture uses placeholder ApiCachingBehavior/Type values ('somebehaviour'/'mytype') that a cfn-lint registry-schema update now rejects (E3030); unrelated to this change - tests/translator/output/**/basic_function.json # W1030: fixture uses Ref for VpcConfig SubnetIds, which cannot match the subnet-id pattern diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index 0630deefd..5d6774855 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -2132,9 +2132,11 @@ def _construct_lambda_webfunction(self) -> LambdaWebFunction: return function def _construct_revision(self, execution_role_arn: Any) -> LambdaWebFunctionRevision: - revision = LambdaWebFunctionRevision( - f"{self.logical_id}Revision", attributes=self.get_passthrough_resource_attributes() - ) + attributes = self.get_passthrough_resource_attributes() + # Keep a replaced revision: endpoints may still route to it, and deleting it would fail and be retried. + # Deleting the function deletes its revisions. Endpoints aren't retained. + attributes.setdefault("UpdateReplacePolicy", "Retain") + revision = LambdaWebFunctionRevision(f"{self.logical_id}Revision", attributes=attributes) # Ref the function (its ARN) instead of repeating the name, like AWS::Lambda::Version does, so a # FunctionName change only has to be made on the AWS::Lambda::WebFunction. revision.FunctionName = ref(self.logical_id) diff --git a/tests/translator/input/webfunction_update_replace_policy.yaml b/tests/translator/input/webfunction_update_replace_policy.yaml new file mode 100644 index 000000000..ecdd2abca --- /dev/null +++ b/tests/translator/input/webfunction_update_replace_policy.yaml @@ -0,0 +1,11 @@ +Resources: + # An explicit UpdateReplacePolicy replaces the revision's default (Retain) + MyWebFunction: + Type: AWS::Serverless::WebFunction + UpdateReplacePolicy: Delete + Properties: + Runtime: nodejs24.x + AuthType: IamAuth + CodeUri: s3://sam-demo-bucket/webfunc.zip +Metadata: + SamTransformTest: true diff --git a/tests/translator/output/aws-cn/webfunction_basic.json b/tests/translator/output/aws-cn/webfunction_basic.json index 7ef41a172..ec03a05b9 100644 --- a/tests/translator/output/aws-cn/webfunction_basic.json +++ b/tests/translator/output/aws-cn/webfunction_basic.json @@ -96,7 +96,8 @@ "TimeoutSeconds": 30 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "MyWebFunctionRole": { "Properties": { diff --git a/tests/translator/output/aws-cn/webfunction_globals.json b/tests/translator/output/aws-cn/webfunction_globals.json index d73e92783..382796b30 100644 --- a/tests/translator/output/aws-cn/webfunction_globals.json +++ b/tests/translator/output/aws-cn/webfunction_globals.json @@ -111,7 +111,8 @@ "TimeoutSeconds": 30 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "WebFunctionOneRole": { "Properties": { @@ -260,7 +261,8 @@ "TimeoutSeconds": 60 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "WebFunctionTwoRole": { "Properties": { diff --git a/tests/translator/output/aws-cn/webfunction_multiregion.json b/tests/translator/output/aws-cn/webfunction_multiregion.json index bc8aa73d0..032ce73fd 100644 --- a/tests/translator/output/aws-cn/webfunction_multiregion.json +++ b/tests/translator/output/aws-cn/webfunction_multiregion.json @@ -103,7 +103,8 @@ "TimeoutSeconds": 30 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "MyWebFunctionRole": { "Properties": { diff --git a/tests/translator/output/aws-cn/webfunction_update_replace_policy.json b/tests/translator/output/aws-cn/webfunction_update_replace_policy.json new file mode 100644 index 000000000..df835c00d --- /dev/null +++ b/tests/translator/output/aws-cn/webfunction_update_replace_policy.json @@ -0,0 +1,139 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Resources": { + "MyWebFunction": { + "Properties": { + "FunctionName": { + "Fn::Sub": [ + "MyWebFunction-${StackIdSuffix}", + { + "StackIdSuffix": { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + } + ] + }, + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::WebFunction", + "UpdateReplacePolicy": "Delete" + }, + "MyWebFunctionEndpoint": { + "DependsOn": [ + "MyWebFunction", + "MyWebFunctionRevision" + ], + "Properties": { + "AuthType": "IamAuth", + "EndpointName": "prod", + "EndpointType": "HomeRegion", + "FunctionName": { + "Ref": "MyWebFunction" + }, + "RevisionWeights": [ + { + "RevisionId": { + "Fn::GetAtt": [ + "MyWebFunctionRevision", + "RevisionId" + ] + }, + "Weight": 100 + } + ] + }, + "Type": "AWS::Lambda::WebFunctionEndpoint", + "UpdateReplacePolicy": "Delete" + }, + "MyWebFunctionRevision": { + "DependsOn": [ + "MyWebFunction" + ], + "Properties": { + "BuildConfig": { + "CodeConfig": { + "S3Object": { + "Bucket": "sam-demo-bucket", + "Key": "webfunc.zip" + } + }, + "RuntimeConfig": { + "Runtime": "nodejs24.x" + } + }, + "FunctionName": { + "Ref": "MyWebFunction" + }, + "ServiceConfig": { + "ExecutionRoleArn": { + "Fn::GetAtt": [ + "MyWebFunctionRole", + "Arn" + ] + }, + "TimeoutSeconds": 30 + } + }, + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Delete" + }, + "MyWebFunctionRole": { + "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", + "UpdateReplacePolicy": "Delete" + } + } +} diff --git a/tests/translator/output/aws-cn/webfunction_with_all_properties.json b/tests/translator/output/aws-cn/webfunction_with_all_properties.json index 97c0448ea..30bc87b85 100644 --- a/tests/translator/output/aws-cn/webfunction_with_all_properties.json +++ b/tests/translator/output/aws-cn/webfunction_with_all_properties.json @@ -122,7 +122,8 @@ "TimeoutSeconds": 120 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "MyWebFunctionRole": { "Properties": { diff --git a/tests/translator/output/aws-us-gov/webfunction_basic.json b/tests/translator/output/aws-us-gov/webfunction_basic.json index 7f8e0bebc..365dd15ea 100644 --- a/tests/translator/output/aws-us-gov/webfunction_basic.json +++ b/tests/translator/output/aws-us-gov/webfunction_basic.json @@ -96,7 +96,8 @@ "TimeoutSeconds": 30 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "MyWebFunctionRole": { "Properties": { diff --git a/tests/translator/output/aws-us-gov/webfunction_globals.json b/tests/translator/output/aws-us-gov/webfunction_globals.json index 9f7756fb1..bf148bcdf 100644 --- a/tests/translator/output/aws-us-gov/webfunction_globals.json +++ b/tests/translator/output/aws-us-gov/webfunction_globals.json @@ -111,7 +111,8 @@ "TimeoutSeconds": 30 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "WebFunctionOneRole": { "Properties": { @@ -260,7 +261,8 @@ "TimeoutSeconds": 60 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "WebFunctionTwoRole": { "Properties": { diff --git a/tests/translator/output/aws-us-gov/webfunction_multiregion.json b/tests/translator/output/aws-us-gov/webfunction_multiregion.json index 147866b93..68975963b 100644 --- a/tests/translator/output/aws-us-gov/webfunction_multiregion.json +++ b/tests/translator/output/aws-us-gov/webfunction_multiregion.json @@ -103,7 +103,8 @@ "TimeoutSeconds": 30 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "MyWebFunctionRole": { "Properties": { diff --git a/tests/translator/output/aws-us-gov/webfunction_update_replace_policy.json b/tests/translator/output/aws-us-gov/webfunction_update_replace_policy.json new file mode 100644 index 000000000..2226ae39b --- /dev/null +++ b/tests/translator/output/aws-us-gov/webfunction_update_replace_policy.json @@ -0,0 +1,139 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Resources": { + "MyWebFunction": { + "Properties": { + "FunctionName": { + "Fn::Sub": [ + "MyWebFunction-${StackIdSuffix}", + { + "StackIdSuffix": { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + } + ] + }, + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::WebFunction", + "UpdateReplacePolicy": "Delete" + }, + "MyWebFunctionEndpoint": { + "DependsOn": [ + "MyWebFunction", + "MyWebFunctionRevision" + ], + "Properties": { + "AuthType": "IamAuth", + "EndpointName": "prod", + "EndpointType": "HomeRegion", + "FunctionName": { + "Ref": "MyWebFunction" + }, + "RevisionWeights": [ + { + "RevisionId": { + "Fn::GetAtt": [ + "MyWebFunctionRevision", + "RevisionId" + ] + }, + "Weight": 100 + } + ] + }, + "Type": "AWS::Lambda::WebFunctionEndpoint", + "UpdateReplacePolicy": "Delete" + }, + "MyWebFunctionRevision": { + "DependsOn": [ + "MyWebFunction" + ], + "Properties": { + "BuildConfig": { + "CodeConfig": { + "S3Object": { + "Bucket": "sam-demo-bucket", + "Key": "webfunc.zip" + } + }, + "RuntimeConfig": { + "Runtime": "nodejs24.x" + } + }, + "FunctionName": { + "Ref": "MyWebFunction" + }, + "ServiceConfig": { + "ExecutionRoleArn": { + "Fn::GetAtt": [ + "MyWebFunctionRole", + "Arn" + ] + }, + "TimeoutSeconds": 30 + } + }, + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Delete" + }, + "MyWebFunctionRole": { + "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", + "UpdateReplacePolicy": "Delete" + } + } +} diff --git a/tests/translator/output/aws-us-gov/webfunction_with_all_properties.json b/tests/translator/output/aws-us-gov/webfunction_with_all_properties.json index 29aa41061..9ca4b9410 100644 --- a/tests/translator/output/aws-us-gov/webfunction_with_all_properties.json +++ b/tests/translator/output/aws-us-gov/webfunction_with_all_properties.json @@ -122,7 +122,8 @@ "TimeoutSeconds": 120 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "MyWebFunctionRole": { "Properties": { diff --git a/tests/translator/output/webfunction_basic.json b/tests/translator/output/webfunction_basic.json index d836688b9..b7f80a563 100644 --- a/tests/translator/output/webfunction_basic.json +++ b/tests/translator/output/webfunction_basic.json @@ -96,7 +96,8 @@ "TimeoutSeconds": 30 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "MyWebFunctionRole": { "Properties": { diff --git a/tests/translator/output/webfunction_globals.json b/tests/translator/output/webfunction_globals.json index 2cdbfdaca..1f06aee11 100644 --- a/tests/translator/output/webfunction_globals.json +++ b/tests/translator/output/webfunction_globals.json @@ -111,7 +111,8 @@ "TimeoutSeconds": 30 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "WebFunctionOneRole": { "Properties": { @@ -260,7 +261,8 @@ "TimeoutSeconds": 60 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "WebFunctionTwoRole": { "Properties": { diff --git a/tests/translator/output/webfunction_multiregion.json b/tests/translator/output/webfunction_multiregion.json index ecb5349c8..97af84542 100644 --- a/tests/translator/output/webfunction_multiregion.json +++ b/tests/translator/output/webfunction_multiregion.json @@ -103,7 +103,8 @@ "TimeoutSeconds": 30 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "MyWebFunctionRole": { "Properties": { diff --git a/tests/translator/output/webfunction_update_replace_policy.json b/tests/translator/output/webfunction_update_replace_policy.json new file mode 100644 index 000000000..8a4c0381d --- /dev/null +++ b/tests/translator/output/webfunction_update_replace_policy.json @@ -0,0 +1,139 @@ +{ + "Metadata": { + "SamTransformTest": true + }, + "Resources": { + "MyWebFunction": { + "Properties": { + "FunctionName": { + "Fn::Sub": [ + "MyWebFunction-${StackIdSuffix}", + { + "StackIdSuffix": { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + } + ] + } + } + ] + }, + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::WebFunction", + "UpdateReplacePolicy": "Delete" + }, + "MyWebFunctionEndpoint": { + "DependsOn": [ + "MyWebFunction", + "MyWebFunctionRevision" + ], + "Properties": { + "AuthType": "IamAuth", + "EndpointName": "prod", + "EndpointType": "HomeRegion", + "FunctionName": { + "Ref": "MyWebFunction" + }, + "RevisionWeights": [ + { + "RevisionId": { + "Fn::GetAtt": [ + "MyWebFunctionRevision", + "RevisionId" + ] + }, + "Weight": 100 + } + ] + }, + "Type": "AWS::Lambda::WebFunctionEndpoint", + "UpdateReplacePolicy": "Delete" + }, + "MyWebFunctionRevision": { + "DependsOn": [ + "MyWebFunction" + ], + "Properties": { + "BuildConfig": { + "CodeConfig": { + "S3Object": { + "Bucket": "sam-demo-bucket", + "Key": "webfunc.zip" + } + }, + "RuntimeConfig": { + "Runtime": "nodejs24.x" + } + }, + "FunctionName": { + "Ref": "MyWebFunction" + }, + "ServiceConfig": { + "ExecutionRoleArn": { + "Fn::GetAtt": [ + "MyWebFunctionRole", + "Arn" + ] + }, + "TimeoutSeconds": 30 + } + }, + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Delete" + }, + "MyWebFunctionRole": { + "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", + "UpdateReplacePolicy": "Delete" + } + } +} diff --git a/tests/translator/output/webfunction_with_all_properties.json b/tests/translator/output/webfunction_with_all_properties.json index f81521a47..e371c2bce 100644 --- a/tests/translator/output/webfunction_with_all_properties.json +++ b/tests/translator/output/webfunction_with_all_properties.json @@ -122,7 +122,8 @@ "TimeoutSeconds": 120 } }, - "Type": "AWS::Lambda::WebFunctionRevision" + "Type": "AWS::Lambda::WebFunctionRevision", + "UpdateReplacePolicy": "Retain" }, "MyWebFunctionRole": { "Properties": {