From 8743f81e15c17f8483dd89bf3ac97fff4e66cfa4 Mon Sep 17 00:00:00 2001 From: Timothy Klopotoski Date: Wed, 18 Aug 2021 13:41:02 -0400 Subject: [PATCH] CF: Raise ValidationError on bad resource id (#4186) --- moto/cloudformation/responses.py | 7 ++++ .../test_cloudformation_stack_crud_boto3.py | 33 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py index d5a57a1f3..1daf83ab8 100644 --- a/moto/cloudformation/responses.py +++ b/moto/cloudformation/responses.py @@ -230,11 +230,18 @@ class CloudFormationResponse(BaseResponse): stack = self.cloudformation_backend.get_stack(stack_name) logical_resource_id = self._get_param("LogicalResourceId") + resource = None for stack_resource in stack.stack_resources: if stack_resource.logical_resource_id == logical_resource_id: resource = stack_resource break + if not resource: + message = "Resource {0} does not exist for stack {1}".format( + logical_resource_id, stack_name + ) + raise ValidationError(stack_name, message) + template = self.response_template(DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE) return template.render(stack=stack, resource=resource) diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py index def515be2..9db754ca3 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py @@ -6,7 +6,7 @@ from datetime import datetime, timedelta import pytz import boto3 -from botocore.exceptions import ClientError +from botocore.exceptions import ClientError, ValidationError import sure # noqa import pytest @@ -1139,6 +1139,37 @@ def test_describe_stack_pagination(): assert "NextToken" not in resp2.keys() +@mock_cloudformation +def test_describe_stack_resource(): + cf_conn = boto3.client("cloudformation", region_name="us-east-1") + cf_conn.create_stack(StackName="test_stack", TemplateBody=dummy_template_json) + + stack = cf_conn.describe_stacks(StackName="test_stack")["Stacks"][0] + + response = cf_conn.describe_stack_resource( + StackName=stack["StackName"], LogicalResourceId="EC2Instance1" + ) + + resource = response["StackResourceDetail"] + resource["LogicalResourceId"].should.equal("EC2Instance1") + resource["ResourceStatus"].should.equal("CREATE_COMPLETE") + resource["ResourceType"].should.equal("AWS::EC2::Instance") + resource["StackId"].should.equal(stack["StackId"]) + + +@mock_cloudformation +def test_describe_stack_resource_when_resource_does_not_exist(): + cf_conn = boto3.client("cloudformation", region_name="us-east-1") + cf_conn.create_stack(StackName="test_stack", TemplateBody=dummy_template_json) + + stack = cf_conn.describe_stacks(StackName="test_stack")["Stacks"][0] + + with pytest.raises(ClientError, match="does not exist for stack"): + cf_conn.describe_stack_resource( + StackName=stack["StackName"], LogicalResourceId="DoesNotExist" + ) + + @mock_cloudformation def test_describe_stack_resources(): cf_conn = boto3.client("cloudformation", region_name="us-east-1")