diff --git a/moto/cloudformation/models.py b/moto/cloudformation/models.py index 2b3dfee47..00cbf781d 100644 --- a/moto/cloudformation/models.py +++ b/moto/cloudformation/models.py @@ -150,6 +150,7 @@ class CloudFormationBackend(BaseBackend): role_arn=role_arn, ) self.stacks[stack_id] = new_stack + self._validate_export_uniqueness(new_stack) for export in new_stack.exports: self.exports[export.name] = export return new_stack @@ -217,6 +218,12 @@ class CloudFormationBackend(BaseBackend): next_token = str(token + 100) if len(all_exports) > token + 100 else None return exports, next_token + def _validate_export_uniqueness(self, stack): + new_stack_export_names = [x.name for x in stack.exports] + export_names = self.exports.keys() + if not set(export_names).isdisjoint(new_stack_export_names): + raise ValidationError(stack.stack_id, message='Export names must be unique across a given region') + cloudformation_backends = {} for region in boto.cloudformation.regions(): diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py index 8b4d72ad3..ba324985f 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py @@ -12,6 +12,7 @@ import sure # noqa # Ensure 'assert_raises' context manager support for Python 2.6 import tests.backport_assert_raises # noqa from nose.tools import assert_raises +import random dummy_template = { "AWSTemplateFormatVersion": "2010-09-09", @@ -457,9 +458,11 @@ def test_list_exports(): def test_list_exports_with_token(): cf = boto3.client('cloudformation', region_name='us-east-1') for i in range(101): + # Add index to ensure name is unique + dummy_output_template['Outputs']['StackVPC']['Export']['Name'] += str(i) cf.create_stack( StackName="test_stack", - TemplateBody=dummy_output_template_json, + TemplateBody=json.dumps(dummy_output_template), ) exports = cf.list_exports() exports['Exports'].should.have.length_of(100) @@ -484,3 +487,17 @@ def test_delete_stack_with_export(): cf.delete_stack(StackName=stack_id) cf.list_exports()['Exports'].should.have.length_of(0) + + +@mock_cloudformation +def test_export_names_must_be_unique(): + cf = boto3.resource('cloudformation', region_name='us-east-1') + first_stack = cf.create_stack( + StackName="test_stack", + TemplateBody=dummy_output_template_json, + ) + with assert_raises(ClientError): + cf.create_stack( + StackName="test_stack", + TemplateBody=dummy_output_template_json, + )