From 70a7a7e0a0bcb6bcfde3a1d2394caa28a37b92a8 Mon Sep 17 00:00:00 2001 From: Sahil Shah <53784576+sahilshah6196@users.noreply.github.com> Date: Tue, 29 Jun 2021 13:28:52 -0400 Subject: [PATCH] SSM parameters in cloudformation (AWS::SSM::Parameter::) are not recognized and resolved for default values (#4042) * Add ssm parsing support for cloudformation stacks --- moto/cloudformation/parsing.py | 34 +++++++++---------- .../test_cloudformation/test_stack_parsing.py | 20 ++++++++++- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py index 0b5f13aae..77175fd3e 100644 --- a/moto/cloudformation/parsing.py +++ b/moto/cloudformation/parsing.py @@ -504,11 +504,26 @@ class ResourceMap(collections_abc.Mapping): key = s3_backend.get_object(bucket_name, name) self._parsed_resources.update(json.loads(key.value)) + def parse_ssm_parameter(self, value, value_type): + + # The Value in SSM parameters is the SSM parameter path + # we need to use ssm_backend to retreive the + # actual value from parameter store + parameter = ssm_backends[self._region_name].get_parameter(value, False) + actual_value = parameter.value + if value_type.find("List") > 0: + return actual_value.split(",") + return actual_value + def load_parameters(self): parameter_slots = self._template.get("Parameters", {}) for parameter_name, parameter in parameter_slots.items(): # Set the default values. - self.resolved_parameters[parameter_name] = parameter.get("Default") + value = parameter.get("Default") + value_type = parameter.get("Type") + if value_type.startswith("AWS::SSM::Parameter::") and value: + value = self.parse_ssm_parameter(value, value_type) + self.resolved_parameters[parameter_name] = value # Set any input parameters that were passed self.no_echo_parameter_keys = [] @@ -517,23 +532,8 @@ class ResourceMap(collections_abc.Mapping): parameter_slot = parameter_slots[key] value_type = parameter_slot.get("Type", "String") - - def _parse_ssm_parameter(value, value_type): - # The Value in SSM parameters is the SSM parameter path - # we need to use ssm_backend to retreive the - # actual value from parameter store - parameter = ssm_backends[self._region_name].get_parameter( - value, False - ) - actual_value = parameter.value - - if value_type.find("List") > 0: - return actual_value.split(",") - - return actual_value - if value_type.startswith("AWS::SSM::Parameter::"): - value = _parse_ssm_parameter(value, value_type) + value = self.parse_ssm_parameter(value, value_type) if value_type == "CommaDelimitedList" or value_type.startswith("List"): value = value.split(",") diff --git a/tests/test_cloudformation/test_stack_parsing.py b/tests/test_cloudformation/test_stack_parsing.py index e1b1dc812..843b1b22c 100644 --- a/tests/test_cloudformation/test_stack_parsing.py +++ b/tests/test_cloudformation/test_stack_parsing.py @@ -78,7 +78,10 @@ parameters = { ssm_parameter = { "Parameters": { "SingleParamCfn": {"Type": "AWS::SSM::Parameter::Value"}, - "ListParamCfn": {"Type": "AWS::SSM::Parameter::Value>"}, + "ListParamCfn": { + "Type": "AWS::SSM::Parameter::Value>", + "Default": "/path/to/list/param", + }, } } @@ -537,3 +540,18 @@ def test_ssm_parameter_parsing(): stack.resource_map.resolved_parameters["ListParamCfn"].should.equal( ["comma", "separated", "string"] ) + + # Not passing in a value for ListParamCfn to test Default value + if not settings.TEST_SERVER_MODE: + stack = FakeStack( + stack_id="test_id", + name="test_stack", + template=ssm_parameter_template_json, + parameters={"SingleParamCfn": "/path/to/single/param",}, + region_name="us-west-1", + ) + + stack.resource_map.resolved_parameters["SingleParamCfn"].should.equal("string") + stack.resource_map.resolved_parameters["ListParamCfn"].should.equal( + ["comma", "separated", "string"] + )