diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py
index 8672c706..c7ced018 100644
--- a/moto/cloudformation/responses.py
+++ b/moto/cloudformation/responses.py
@@ -10,6 +10,31 @@ from moto.s3 import s3_backend
from moto.core import ACCOUNT_ID
from .models import cloudformation_backends
from .exceptions import ValidationError
+from .utils import yaml_tag_constructor
+
+
+def get_template_summary_response_from_template(template_body):
+ def get_resource_types(template_dict):
+ resources = {}
+ for key, value in template_dict.items():
+ if key == "Resources":
+ resources = value
+
+ resource_types = []
+ for key, value in resources.items():
+ resource_types.append(value["Type"])
+ return resource_types
+
+ yaml.add_multi_constructor("", yaml_tag_constructor)
+
+ try:
+ template_dict = yaml.load(template_body, Loader=yaml.Loader)
+ except (yaml.parser.ParserError, yaml.scanner.ScannerError):
+ template_dict = json.loads(template_body)
+
+ resources_types = get_resource_types(template_dict)
+ template_dict["resourceTypes"] = resources_types
+ return template_dict
class CloudFormationResponse(BaseResponse):
@@ -269,6 +294,20 @@ class CloudFormationResponse(BaseResponse):
template = self.response_template(GET_TEMPLATE_RESPONSE_TEMPLATE)
return template.render(stack=stack)
+ def get_template_summary(self):
+ stack_name = self._get_param("StackName")
+ template_url = self._get_param("TemplateURL")
+ stack_body = self._get_param("TemplateBody")
+
+ if stack_name:
+ stack_body = self.cloudformation_backend.get_stack(stack_name).template
+ elif template_url:
+ stack_body = self._get_stack_from_s3_url(template_url)
+
+ template_summary = get_template_summary_response_from_template(stack_body)
+ template = self.response_template(GET_TEMPLATE_SUMMARY_TEMPLATE)
+ return template.render(template_summary=template_summary)
+
def update_stack(self):
stack_name = self._get_param("StackName")
role_arn = self._get_param("RoleARN")
@@ -743,7 +782,6 @@ DESCRIBE_STACKS_TEMPLATE = """
"""
-
DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE = """
@@ -758,7 +796,6 @@ DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE = """
"""
-
DESCRIBE_STACK_RESOURCES_RESPONSE = """
@@ -777,7 +814,6 @@ DESCRIBE_STACK_RESOURCES_RESPONSE = """
"""
-
DESCRIBE_STACK_EVENTS_RESPONSE = """
@@ -802,7 +838,6 @@ DESCRIBE_STACK_EVENTS_RESPONSE = """
@@ -823,7 +858,6 @@ LIST_CHANGE_SETS_RESPONSE = """
"""
-
LIST_STACKS_RESPONSE = """
@@ -840,7 +874,6 @@ LIST_STACKS_RESPONSE = """
"""
-
LIST_STACKS_RESOURCES_RESPONSE = """
@@ -860,7 +893,6 @@ LIST_STACKS_RESOURCES_RESPONSE = """
"""
-
GET_TEMPLATE_RESPONSE_TEMPLATE = """
{{ stack.template }}
@@ -870,7 +902,6 @@ GET_TEMPLATE_RESPONSE_TEMPLATE = """
"""
-
DELETE_STACK_RESPONSE_TEMPLATE = """
5ccc7dcd-744c-11e5-be70-example
@@ -878,7 +909,6 @@ DELETE_STACK_RESPONSE_TEMPLATE = """
"""
-
LIST_EXPORTS_RESPONSE = """
@@ -1139,3 +1169,19 @@ LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = (
"""
)
+
+GET_TEMPLATE_SUMMARY_TEMPLATE = """
+
+ {{ template_summary.Description }}
+ {% for resource in template_summary.resourceTypes %}
+
+ {{ resource }}
+
+ {% endfor %}
+ {{ template_summary.AWSTemplateFormatVersion }}
+
+
+ b9b4b068-3a41-11e5-94eb-example
+
+
+"""
diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
index 1ebce46d..0bfaf9f0 100644
--- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
@@ -35,6 +35,14 @@ dummy_template = {
},
}
+dummy_template3 = {
+ "AWSTemplateFormatVersion": "2010-09-09",
+ "Description": "Stack 3",
+ "Resources": {
+ "VPC": {"Properties": {"CidrBlock": "192.168.0.0/16"}, "Type": "AWS::EC2::VPC"}
+ },
+}
+
dummy_template_yaml = """---
AWSTemplateFormatVersion: 2010-09-09
Description: Stack1 with yaml template
@@ -668,6 +676,48 @@ def test_boto3_create_stack_with_short_form_func_yaml():
)
+@mock_s3
+@mock_cloudformation
+def test_get_template_summary():
+ s3 = boto3.client("s3")
+ s3_conn = boto3.resource("s3", region_name="us-east-1")
+
+ conn = boto3.client("cloudformation", region_name="us-east-1")
+ result = conn.get_template_summary(TemplateBody=json.dumps(dummy_template3))
+
+ result["ResourceTypes"].should.equal(["AWS::EC2::VPC"])
+ result["Version"].should.equal("2010-09-09")
+ result["Description"].should.equal("Stack 3")
+
+ conn.create_stack(StackName="test_stack", TemplateBody=json.dumps(dummy_template3))
+
+ result = conn.get_template_summary(StackName="test_stack")
+
+ result["ResourceTypes"].should.equal(["AWS::EC2::VPC"])
+ result["Version"].should.equal("2010-09-09")
+ result["Description"].should.equal("Stack 3")
+
+ s3_conn.create_bucket(Bucket="foobar")
+ s3_conn.Object("foobar", "template-key").put(Body=json.dumps(dummy_template3))
+
+ key_url = s3.generate_presigned_url(
+ ClientMethod="get_object", Params={"Bucket": "foobar", "Key": "template-key"}
+ )
+
+ conn.create_stack(StackName="stack_from_url", TemplateURL=key_url)
+ result = conn.get_template_summary(TemplateURL=key_url)
+ result["ResourceTypes"].should.equal(["AWS::EC2::VPC"])
+ result["Version"].should.equal("2010-09-09")
+ result["Description"].should.equal("Stack 3")
+
+ conn = boto3.client("cloudformation", region_name="us-east-1")
+ result = conn.get_template_summary(TemplateBody=dummy_template_yaml)
+
+ result["ResourceTypes"].should.equal(["AWS::EC2::Instance"])
+ result["Version"].should.equal("2010-09-09")
+ result["Description"].should.equal("Stack1 with yaml template")
+
+
@mock_cloudformation
def test_boto3_create_stack_with_ref_yaml():
cf_conn = boto3.client("cloudformation", region_name="us-east-1")