From 362849ce3bbcdb2085c8dd37130098d93a175620 Mon Sep 17 00:00:00 2001 From: "localstack[bot]" Date: Thu, 2 Oct 2025 07:32:45 +0000 Subject: [PATCH 1/4] prepare next development iteration From ae41d258c27911075642d64dc5bc361f64d1cc48 Mon Sep 17 00:00:00 2001 From: Brian Rinaldi Date: Thu, 2 Oct 2025 10:25:30 -0400 Subject: [PATCH 2/4] Update README for 4.9 (#13216) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 50b28d01b25c5..2d3c9533e78f6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

-:zap: We are thrilled to announce the release of LocalStack 4.8 :zap: +:zap: We are thrilled to announce the release of LocalStack 4.9 :zap:

@@ -93,7 +93,7 @@ Start LocalStack inside a Docker container by running: / /___/ /_/ / /__/ /_/ / /___/ / /_/ /_/ / /__/ ,< /_____/\____/\___/\__,_/_//____/\__/\__,_/\___/_/|_| -- LocalStack CLI: 4.8.0 +- LocalStack CLI: 4.9.0 - Profile: default - App: https://app.localstack.cloud From 478dbcf1598fff6b41a3b8483bf1a720a2222ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristopher=20Pinz=C3=B3n?= <18080804+pinzon@users.noreply.github.com> Date: Thu, 2 Oct 2025 11:55:02 -0500 Subject: [PATCH 3/4] CFN: add validation in GetAtt for conditionally canceled resources (#13213) --- .../engine/v2/change_set_model_preproc.py | 15 +++++++++++ .../cloudformation/engine/test_conditions.py | 27 +++++++++++++++++++ .../engine/test_conditions.snapshot.json | 16 +++++++++++ .../engine/test_conditions.validation.json | 17 +++++++++++- .../conditions/get-att-condition.yml | 21 +++++++++++++++ 5 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 tests/aws/templates/conditions/get-att-condition.yml diff --git a/localstack-core/localstack/services/cloudformation/engine/v2/change_set_model_preproc.py b/localstack-core/localstack/services/cloudformation/engine/v2/change_set_model_preproc.py index 8cfc0993485f4..8533c98394559 100644 --- a/localstack-core/localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +++ b/localstack-core/localstack/services/cloudformation/engine/v2/change_set_model_preproc.py @@ -572,6 +572,21 @@ def _resolve_attribute(self, arguments: str | list[str], select_before: bool) -> resource_name=logical_name_of_resource, node_template=self._change_set.update_model.node_template, ) + + if not is_nothing(node_resource.condition_reference): + condition = self._get_node_condition_if_exists(node_resource.condition_reference.value) + evaluation_result = self._resolve_condition(condition.name) + + if select_before and not evaluation_result.before: + raise ValidationError( + f"Template format error: Unresolved resource dependencies [{logical_name_of_resource}] in the Resources block of the template" + ) + + if not select_before and not evaluation_result.after: + raise ValidationError( + f"Template format error: Unresolved resource dependencies [{logical_name_of_resource}] in the Resources block of the template" + ) + node_property: NodeProperty | None = self._get_node_property_for( property_name=attribute_name, node_resource=node_resource ) diff --git a/tests/aws/services/cloudformation/engine/test_conditions.py b/tests/aws/services/cloudformation/engine/test_conditions.py index ed2a4a4579556..229f5ca86d776 100644 --- a/tests/aws/services/cloudformation/engine/test_conditions.py +++ b/tests/aws/services/cloudformation/engine/test_conditions.py @@ -1,6 +1,7 @@ import os.path import pytest +from tests.aws.services.cloudformation.conftest import skip_if_legacy_engine from localstack.services.cloudformation.v2.utils import is_v2_engine from localstack.testing.aws.util import is_aws_cloud @@ -125,6 +126,32 @@ def test_dependent_ref(self, aws_client, snapshot): ) snapshot.match("dependent_ref_exc", e.value.response) + @markers.aws.validated + @skip_if_legacy_engine + def test_dependent_get_att(self, aws_client, snapshot): + """ + Tests behavior of a stack with 2 resources where one depends on the other. + The referenced resource won't be deployed due to its condition evaluating to false, so the GetAtt can't be resolved. + + This immediately leads to an error. + """ + + stack_name = f"test-condition-ref-stack-{short_uid()}" + changeset_name = "initial" + with pytest.raises(aws_client.cloudformation.exceptions.ClientError) as e: + aws_client.cloudformation.create_change_set( + StackName=stack_name, + ChangeSetName=changeset_name, + ChangeSetType="CREATE", + TemplateBody=load_file( + os.path.join(THIS_DIR, "../../../templates/conditions/get-att-condition.yml") + ), + Parameters=[ + {"ParameterKey": "OptionParameter", "ParameterValue": "option-b"}, + ], + ) + snapshot.match("dependent_ref_exc", e.value.response) + @markers.aws.validated @pytest.mark.skipif(condition=not is_aws_cloud(), reason="not supported yet") def test_dependent_ref_intrinsic_fn_condition(self, aws_client, deploy_cfn_template): diff --git a/tests/aws/services/cloudformation/engine/test_conditions.snapshot.json b/tests/aws/services/cloudformation/engine/test_conditions.snapshot.json index 4d8bc281ae75c..3ae8686edd44d 100644 --- a/tests/aws/services/cloudformation/engine/test_conditions.snapshot.json +++ b/tests/aws/services/cloudformation/engine/test_conditions.snapshot.json @@ -759,5 +759,21 @@ } } } + }, + "tests/aws/services/cloudformation/engine/test_conditions.py::TestCloudFormationConditions::test_dependent_get_att": { + "recorded-date": "30-09-2025, 19:25:10", + "recorded-content": { + "dependent_ref_exc": { + "Error": { + "Code": "ValidationError", + "Message": "Template format error: Unresolved resource dependencies [MyTopic] in the Resources block of the template", + "Type": "Sender" + }, + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 400 + } + } + } } } diff --git a/tests/aws/services/cloudformation/engine/test_conditions.validation.json b/tests/aws/services/cloudformation/engine/test_conditions.validation.json index a2579fb8517c8..33145a6055c7e 100644 --- a/tests/aws/services/cloudformation/engine/test_conditions.validation.json +++ b/tests/aws/services/cloudformation/engine/test_conditions.validation.json @@ -1,6 +1,21 @@ { + "tests/aws/services/cloudformation/engine/test_conditions.py::TestCloudFormationConditions::test_dependent_get_att": { + "last_validated_date": "2025-09-30T19:25:10+00:00", + "durations_in_seconds": { + "setup": 0.23, + "call": 0.36, + "teardown": 0.0, + "total": 0.59 + } + }, "tests/aws/services/cloudformation/engine/test_conditions.py::TestCloudFormationConditions::test_dependent_ref": { - "last_validated_date": "2023-06-26T12:18:26+00:00" + "last_validated_date": "2025-09-30T19:03:49+00:00", + "durations_in_seconds": { + "setup": 0.27, + "call": 0.34, + "teardown": 0.0, + "total": 0.61 + } }, "tests/aws/services/cloudformation/engine/test_conditions.py::TestCloudFormationConditions::test_nested_conditions[prod-bucket-policy]": { "last_validated_date": "2023-06-26T12:24:03+00:00" diff --git a/tests/aws/templates/conditions/get-att-condition.yml b/tests/aws/templates/conditions/get-att-condition.yml new file mode 100644 index 0000000000000..cd17bb4fb6bef --- /dev/null +++ b/tests/aws/templates/conditions/get-att-condition.yml @@ -0,0 +1,21 @@ +Parameters: + OptionParameter: + Type: String + AllowedValues: + - option-a + - option-b +Resources: + MyTopic: + Type: AWS::SNS::Topic + Condition: ShouldCreateTopic + + MySsmParam: + Type: AWS::SSM::Parameter + Properties: + Type: String + Value: !GetAtt MyTopic.TopicName + +Conditions: + ShouldCreateTopic: !Equals + - !Ref OptionParameter + - option-a \ No newline at end of file From 30111e0c98775044c465227aab476b9e4d403a45 Mon Sep 17 00:00:00 2001 From: "localstack[bot]" Date: Fri, 3 Oct 2025 11:11:26 +0000 Subject: [PATCH 4/4] release version 4.9.1