diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index 76944e3fe..e2a3b7af0 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -329,7 +329,7 @@
- [ ] update_schema
- [ ] update_typed_link_facet
-## cloudformation - 20% implemented
+## cloudformation - 23% implemented
- [ ] cancel_update_stack
- [ ] continue_update_rollback
- [X] create_change_set
@@ -350,7 +350,7 @@
- [ ] describe_stack_set_operation
- [X] describe_stacks
- [ ] estimate_template_cost
-- [ ] execute_change_set
+- [X] execute_change_set
- [ ] get_stack_policy
- [ ] get_template
- [ ] get_template_summary
diff --git a/moto/cloudformation/models.py b/moto/cloudformation/models.py
index 42809608b..70c15d697 100644
--- a/moto/cloudformation/models.py
+++ b/moto/cloudformation/models.py
@@ -188,6 +188,24 @@ class CloudFormationBackend(BaseBackend):
self.change_sets[change_set_id] = stack
return change_set_id, stack.stack_id
+ def execute_change_set(self, change_set_name, stack_name=None):
+ stack = None
+ if change_set_name in self.change_sets:
+ # This means arn was passed in
+ stack = self.change_sets[change_set_name]
+ else:
+ for cs in self.change_sets:
+ if self.change_sets[cs].name == change_set_name:
+ stack = self.change_sets[cs]
+ if stack is None:
+ raise ValidationError(stack_name)
+ if stack.events[-1].resource_status == 'REVIEW_IN_PROGRESS':
+ stack._add_stack_event('CREATE_COMPLETE')
+ else:
+ stack._add_stack_event('UPDATE_IN_PROGRESS')
+ stack._add_stack_event('UPDATE_COMPLETE')
+ return True
+
def describe_stacks(self, name_or_stack_id):
stacks = self.stacks.values()
if name_or_stack_id:
diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py
index 93d59f686..07d263652 100644
--- a/moto/cloudformation/responses.py
+++ b/moto/cloudformation/responses.py
@@ -118,6 +118,24 @@ class CloudFormationResponse(BaseResponse):
template = self.response_template(CREATE_CHANGE_SET_RESPONSE_TEMPLATE)
return template.render(stack_id=stack_id, change_set_id=change_set_id)
+ @amzn_request_id
+ def execute_change_set(self):
+ stack_name = self._get_param('StackName')
+ change_set_name = self._get_param('ChangeSetName')
+ self.cloudformation_backend.execute_change_set(
+ stack_name=stack_name,
+ change_set_name=change_set_name,
+ )
+ if self.request_json:
+ return json.dumps({
+ 'ExecuteChangeSetResponse': {
+ 'ExecuteChangeSetResult': {},
+ }
+ })
+ else:
+ template = self.response_template(EXECUTE_CHANGE_SET_RESPONSE_TEMPLATE)
+ return template.render()
+
def describe_stacks(self):
stack_name_or_id = None
if self._get_param('StackName'):
@@ -302,6 +320,16 @@ CREATE_CHANGE_SET_RESPONSE_TEMPLATE = """
"""
+EXECUTE_CHANGE_SET_RESPONSE_TEMPLATE = """
+
+
+
+
+ {{ request_id }}
+
+
+"""
+
DESCRIBE_STACKS_TEMPLATE = """
diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
index d8b8cf142..1f3bfdec7 100644
--- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
@@ -337,6 +337,30 @@ def test_create_change_set_from_s3_url():
assert 'arn:aws:cloudformation:us-east-1:123456789:stack/NewStack' in response['StackId']
+@mock_cloudformation
+def test_execute_change_set_w_arn():
+ cf_conn = boto3.client('cloudformation', region_name='us-east-1')
+ change_set = cf_conn.create_change_set(
+ StackName='NewStack',
+ TemplateBody=dummy_template_json,
+ ChangeSetName='NewChangeSet',
+ ChangeSetType='CREATE',
+ )
+ cf_conn.execute_change_set(ChangeSetName=change_set['Id'])
+
+
+@mock_cloudformation
+def test_execute_change_set_w_name():
+ cf_conn = boto3.client('cloudformation', region_name='us-east-1')
+ change_set = cf_conn.create_change_set(
+ StackName='NewStack',
+ TemplateBody=dummy_template_json,
+ ChangeSetName='NewChangeSet',
+ ChangeSetType='CREATE',
+ )
+ cf_conn.execute_change_set(ChangeSetName='NewStack', StackName='NewStack')
+
+
@mock_cloudformation
def test_describe_stack_pagination():
conn = boto3.client('cloudformation', region_name='us-east-1')