diff --git a/moto/cloudformation/models.py b/moto/cloudformation/models.py
index 4e3027f8b..618e736e0 100644
--- a/moto/cloudformation/models.py
+++ b/moto/cloudformation/models.py
@@ -91,6 +91,10 @@ class CloudFormationBackend(BaseBackend):
# stack.template = template
# return stack
+ def list_stack_resources(self, stack_name_or_id):
+ stack = self.get_stack(stack_name_or_id)
+ return stack.stack_resources
+
def delete_stack(self, name_or_stack_id):
if name_or_stack_id in self.stacks:
# Delete by stack id
diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py
index 18c1d23f3..d0ee90138 100644
--- a/moto/cloudformation/responses.py
+++ b/moto/cloudformation/responses.py
@@ -67,7 +67,7 @@ class CloudFormationResponse(BaseResponse):
stack_name = self._get_param('StackName')
stack = self.cloudformation_backend.get_stack(stack_name)
- template = self.response_template(LIST_STACKS_RESOURCES_RESPONSE)
+ template = self.response_template(DESCRIBE_STACKS_RESOURCES_RESPONSE)
return template.render(stack=stack)
def list_stacks(self):
@@ -75,6 +75,13 @@ class CloudFormationResponse(BaseResponse):
template = self.response_template(LIST_STACKS_RESPONSE)
return template.render(stacks=stacks)
+ def list_stack_resources(self):
+ stack_name_or_id = self._get_param('StackName')
+ resources = self.cloudformation_backend.list_stack_resources(stack_name_or_id)
+
+ template = self.response_template(LIST_STACKS_RESOURCES_RESPONSE)
+ return template.render(resources=resources)
+
def get_template(self):
name_or_stack_id = self.querystring.get('StackName')[0]
@@ -166,7 +173,7 @@ LIST_STACKS_RESPONSE = """
"""
-LIST_STACKS_RESOURCES_RESPONSE = """
+DESCRIBE_STACKS_RESOURCES_RESPONSE = """
{% for resource in stack.stack_resources %}
@@ -181,3 +188,23 @@ LIST_STACKS_RESOURCES_RESPONSE = """
{% endfor %}
"""
+
+
+LIST_STACKS_RESOURCES_RESPONSE = """
+
+
+ {% for resource in resources %}
+
+ CREATE_COMPLETE
+ {{ resource.logical_resource_id }}
+ 2011-06-21T20:15:58Z
+ {{ resource.physical_resource_id }}
+ {{ resource.type }}
+
+ {% endfor %}
+
+
+
+ 2d06e36c-ac1d-11e0-a958-f9382b6eb86b
+
+"""
diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py
index 7db6c22e4..f06acf30b 100644
--- a/tests/test_cloudformation/test_cloudformation_stack_integration.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py
@@ -68,6 +68,37 @@ def test_stack_sqs_integration():
queue.physical_resource_id.should.equal("my-queue")
+@mock_cloudformation()
+def test_stack_list_resources():
+ sqs_template = {
+ "AWSTemplateFormatVersion": "2010-09-09",
+ "Resources": {
+ "QueueGroup": {
+
+ "Type": "AWS::SQS::Queue",
+ "Properties": {
+ "QueueName": "my-queue",
+ "VisibilityTimeout": 60,
+ }
+ },
+ },
+ }
+ sqs_template_json = json.dumps(sqs_template)
+
+ conn = boto.cloudformation.connect_to_region("us-west-1")
+ conn.create_stack(
+ "test_stack",
+ template_body=sqs_template_json,
+ )
+
+ resources = conn.list_stack_resources("test_stack")
+ assert len(resources) == 1
+ queue = resources[0]
+ queue.resource_type.should.equal('AWS::SQS::Queue')
+ queue.logical_resource_id.should.equal("QueueGroup")
+ queue.physical_resource_id.should.equal("my-queue")
+
+
@mock_ec2()
@mock_cloudformation()
def test_stack_ec2_integration():
@@ -1198,17 +1229,17 @@ def test_subnets_should_be_created_with_availability_zone():
vpc = vpc_conn.create_vpc("10.0.0.0/16")
subnet_template = {
- "AWSTemplateFormatVersion" : "2010-09-09",
- "Resources" : {
- "testSubnet" : {
- "Type" : "AWS::EC2::Subnet",
- "Properties" : {
- "VpcId" : vpc.id,
- "CidrBlock" : "10.0.0.0/24",
- "AvailabilityZone" : "us-west-1b",
- }
- }
- }
+ "AWSTemplateFormatVersion": "2010-09-09",
+ "Resources": {
+ "testSubnet": {
+ "Type": "AWS::EC2::Subnet",
+ "Properties": {
+ "VpcId": vpc.id,
+ "CidrBlock": "10.0.0.0/24",
+ "AvailabilityZone": "us-west-1b",
+ }
+ }
+ }
}
cf_conn = boto.cloudformation.connect_to_region("us-west-1")
template_json = json.dumps(subnet_template)