diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py
index 17b76854a..cceedc86e 100644
--- a/moto/cloudformation/responses.py
+++ b/moto/cloudformation/responses.py
@@ -50,6 +50,12 @@ class CloudFormationResponse(BaseResponse):
for item in self._get_list_prefix("Tags.member")
)
+ if self.stack_name_exists(new_stack_name=stack_name):
+ template = self.response_template(
+ CREATE_STACK_NAME_EXISTS_RESPONSE_TEMPLATE
+ )
+ return 400, {"status": 400}, template.render(name=stack_name)
+
# Hack dict-comprehension
parameters = dict(
[
@@ -82,6 +88,12 @@ class CloudFormationResponse(BaseResponse):
template = self.response_template(CREATE_STACK_RESPONSE_TEMPLATE)
return template.render(stack=stack)
+ def stack_name_exists(self, new_stack_name):
+ for stack in self.cloudformation_backend.stacks.values():
+ if stack.name == new_stack_name:
+ return True
+ return False
+
@amzn_request_id
def create_change_set(self):
stack_name = self._get_param("StackName")
@@ -564,6 +576,15 @@ CREATE_STACK_RESPONSE_TEMPLATE = """
"""
+CREATE_STACK_NAME_EXISTS_RESPONSE_TEMPLATE = """
+
+ Sender
+ AlreadyExistsException
+ Stack [{{ name }}] already exists
+
+ 950ff8d7-812a-44b3-bb0c-9b271b954104
+"""
+
UPDATE_STACK_RESPONSE_TEMPLATE = """
{{ stack.stack_id }}
diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud.py b/tests/test_cloudformation/test_cloudformation_stack_crud.py
index 8a0a0b11c..8749d4cfb 100644
--- a/tests/test_cloudformation/test_cloudformation_stack_crud.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_crud.py
@@ -98,12 +98,12 @@ def test_create_stack_hosted_zone_by_id():
},
}
conn.create_stack(
- "test_stack", template_body=json.dumps(dummy_template), parameters={}.items()
+ "test_stack1", template_body=json.dumps(dummy_template), parameters={}.items()
)
r53_conn = boto.connect_route53()
zone_id = r53_conn.get_zones()[0].id
conn.create_stack(
- "test_stack",
+ "test_stack2",
template_body=json.dumps(dummy_template2),
parameters={"ZoneId": zone_id}.items(),
)
diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
index cd76743dd..43f63dca2 100644
--- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
@@ -919,7 +919,9 @@ def test_execute_change_set_w_name():
def test_describe_stack_pagination():
conn = boto3.client("cloudformation", region_name="us-east-1")
for i in range(100):
- conn.create_stack(StackName="test_stack", TemplateBody=dummy_template_json)
+ conn.create_stack(
+ StackName="test_stack_{}".format(i), TemplateBody=dummy_template_json
+ )
resp = conn.describe_stacks()
stacks = resp["Stacks"]
@@ -1211,7 +1213,8 @@ def test_list_exports_with_token():
# Add index to ensure name is unique
dummy_output_template["Outputs"]["StackVPC"]["Export"]["Name"] += str(i)
cf.create_stack(
- StackName="test_stack", TemplateBody=json.dumps(dummy_output_template)
+ StackName="test_stack_{}".format(i),
+ TemplateBody=json.dumps(dummy_output_template),
)
exports = cf.list_exports()
exports["Exports"].should.have.length_of(100)
@@ -1273,3 +1276,16 @@ def test_non_json_redrive_policy():
stack.Resource("MainQueue").resource_status.should.equal("CREATE_COMPLETE")
stack.Resource("DeadLetterQueue").resource_status.should.equal("CREATE_COMPLETE")
+
+
+@mock_cloudformation
+def test_boto3_create_duplicate_stack():
+ cf_conn = boto3.client("cloudformation", region_name="us-east-1")
+ cf_conn.create_stack(
+ StackName="test_stack", TemplateBody=dummy_template_json,
+ )
+
+ with assert_raises(ClientError):
+ cf_conn.create_stack(
+ StackName="test_stack", TemplateBody=dummy_template_json,
+ )