Skip to content

Commit

Permalink
cloudformation: add support for DisableRollback to upadte stack (ansi…
Browse files Browse the repository at this point in the history
…ble-collections#1681)

cloudformation: add support for DisableRollback to upadte stack

SUMMARY

Update stack operation supports DisableRollback.
Fixes ansible-collections#1655

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

cloudformation
ADDITIONAL INFORMATION



https://botocore.amazonaws.com/v1/documentation/api/latest/reference/services/cloudformation/client/update_stack.html

Reviewed-by: Alina Buzachis
Reviewed-by: Mandar Kulkarni <[email protected]>
Reviewed-by: Helen Bailey <[email protected]>
  • Loading branch information
mandar242 authored Sep 1, 2023
1 parent 1a077fb commit 46e9dd4
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- cloudformation - Add support for ``disable_rollback`` to update stack operation (https://github.com/ansible-collections/amazon.aws/issues/1681).
4 changes: 3 additions & 1 deletion plugins/modules/cloudformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def create_stack(module, stack_params, cfn, events_limit):
msg="Either 'template', 'template_body' or 'template_url' is required when the stack does not exist."
)

# 'DisableRollback', 'TimeoutInMinutes', 'EnableTerminationProtection' and
# 'TimeoutInMinutes', 'EnableTerminationProtection' and
# 'OnFailure' only apply on creation, not update.
if module.params.get("on_create_failure") is not None:
stack_params["OnFailure"] = module.params["on_create_failure"]
Expand Down Expand Up @@ -483,6 +483,8 @@ def update_stack(module, stack_params, cfn, events_limit):
if module.params["stack_policy_on_update_body"] is not None:
stack_params["StackPolicyDuringUpdateBody"] = module.params["stack_policy_on_update_body"]

stack_params["DisableRollback"] = module.params["disable_rollback"]

# if the state is present and the stack already exists, we try to update it.
# AWS will tell us if the stack template and parameters are the same and
# don't need to be updated.
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/targets/cloudformation/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
stack_name: "{{ resource_prefix }}"
stack_name_disable_rollback_true: "{{ resource_prefix }}-drb-true"
stack_name_disable_rollback_false: "{{ resource_prefix }}-drb-false"

availability_zone: '{{ ec2_availability_zone_names[0] }}'

Expand Down
4 changes: 4 additions & 0 deletions tests/integration/targets/cloudformation/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
az: "{{ availability_zone }}"
register: testing_subnet

# ==== Cloudformation tests with disable_rollback ====================

- import_tasks: test_disable_rollback.yml

# ==== Cloudformation tests ===============================================

# 1. Basic stack creation (check mode, actual run and idempotency)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
---
- name: Run cloudformation tests for `disable_rollback` parameter
block:

# disable rollback to true
- name: create a cloudformation stack (disable_rollback=true) (check mode)
amazon.aws.cloudformation:
stack_name: "{{ stack_name_disable_rollback_true }}"
state: present
disable_rollback: true
template_body: "{{ lookup('file','cf_template.json') }}"
template_parameters:
InstanceType: "t3.nano"
ImageId: "{{ ec2_ami_id }}"
SubnetId: "{{ testing_subnet.subnet.id }}"
register: cf_stack
check_mode: yes

- name: check task return attributes
assert:
that:
- cf_stack.changed
- "'msg' in cf_stack and 'New stack would be created' in cf_stack.msg"

- name: create a cloudformation stack (disable_rollback=true)
amazon.aws.cloudformation:
stack_name: "{{ stack_name_disable_rollback_true }}"
state: present
disable_rollback: true
template_body: "{{ lookup('file','cf_template.json') }}"
template_parameters:
InstanceType: "t3.nano"
ImageId: "{{ ec2_ami_id }}"
SubnetId: "{{ testing_subnet.subnet.id }}"
register: cf_stack

- name: get stack details
cloudformation_info:
stack_name: "{{ stack_name_disable_rollback_true }}"
register: stack_info

- name: assert stack info
assert:
that:
- "'cloudformation' in stack_info"
- stack_info.cloudformation | length == 1
- stack_info.cloudformation[stack_name_disable_rollback_true].stack_description.disable_rollback == true

# disable rollback to false
- name: create a cloudformation stack (disable_rollback=false) (check mode)
amazon.aws.cloudformation:
stack_name: "{{ stack_name_disable_rollback_false }}"
state: present
disable_rollback: false
template_body: "{{ lookup('file','cf_template.json') }}"
template_parameters:
InstanceType: "t3.nano"
ImageId: "{{ ec2_ami_id }}"
SubnetId: "{{ testing_subnet.subnet.id }}"
register: cf_stack
check_mode: yes

- name: check task return attributes
assert:
that:
- cf_stack.changed
- "'msg' in cf_stack and 'New stack would be created' in cf_stack.msg"

- name: create a cloudformation stack (disable_rollback=false)
amazon.aws.cloudformation:
stack_name: "{{ stack_name_disable_rollback_false }}"
state: present
disable_rollback: false
template_body: "{{ lookup('file','cf_template.json') }}"
template_parameters:
InstanceType: "t3.nano"
ImageId: "{{ ec2_ami_id }}"
SubnetId: "{{ testing_subnet.subnet.id }}"
register: cf_stack

- name: get stack details
cloudformation_info:
stack_name: "{{ stack_name_disable_rollback_false }}"
register: stack_info

- name: assert stack info
assert:
that:
- "'cloudformation' in stack_info"
- "stack_info.cloudformation | length == 1"
- "stack_info.cloudformation[stack_name_disable_rollback_false].stack_description.disable_rollback == false"

# disable rollback not set
- name: create a cloudformation stack (disable_rollback not set) (check mode)
amazon.aws.cloudformation:
stack_name: "{{ stack_name }}"
state: present
template_body: "{{ lookup('file','cf_template.json') }}"
template_parameters:
InstanceType: "t3.nano"
ImageId: "{{ ec2_ami_id }}"
SubnetId: "{{ testing_subnet.subnet.id }}"
register: cf_stack
check_mode: yes

- name: check task return attributes
assert:
that:
- cf_stack.changed
- "'msg' in cf_stack and 'New stack would be created' in cf_stack.msg"

- name: create a cloudformation stack (disable_rollback not set)
amazon.aws.cloudformation:
stack_name: "{{ stack_name }}"
state: present
template_body: "{{ lookup('file','cf_template.json') }}"
template_parameters:
InstanceType: "t3.nano"
ImageId: "{{ ec2_ami_id }}"
SubnetId: "{{ testing_subnet.subnet.id }}"
register: cf_stack

- name: get stack details
cloudformation_info:
stack_name: "{{ stack_name }}"
register: stack_info

- name: assert stack info
assert:
that:
- "'cloudformation' in stack_info"
- "stack_info.cloudformation | length == 1"
- "stack_info.cloudformation[stack_name].stack_description.disable_rollback == false"

# =============================================================================================
# Test Scenario
# 1. create a cloudformation stack
# 2. try update, FAILED by providing wrong ami id (disable_rollback=true, do not delete failed stack)
# 3. Fix the ami id, retry update, fails as disable_rollback=False
# 4. Try (3) with disable_rollback=true, update completes
# =============================================================================================

- name: Create a cloudformation stack
amazon.aws.cloudformation:
stack_name: "{{ stack_name }}-failtest"
state: present
template_body: "{{ lookup('file','cf_template.json') }}"
disable_rollback: false
template_parameters:
InstanceType: "t3.nano"
ImageId: "{{ ec2_ami_id }}"
SubnetId: "{{ testing_subnet.subnet.id }}"
register: cf_stack
ignore_errors: true

- name: Update the cloudformation stack with wrong ami (fails, does not delete failed as disable_rollback=true)
amazon.aws.cloudformation:
stack_name: "{{ stack_name }}-failtest"
state: present
template_body: "{{ lookup('file','cf_template.json') }}"
disable_rollback: true
template_parameters:
InstanceType: "t3.nano"
ImageId: "{{ ec2_ami_id }}1" # wrong ami provided
SubnetId: "{{ testing_subnet.subnet.id }}"
register: cf_stack
ignore_errors: true

# update stack by correcting AMI ID
- name: Fix the AMI ID and retry updating the cloudformation stack (fails with disable_rollback=false)
amazon.aws.cloudformation:
stack_name: "{{ stack_name }}-failtest"
state: present
template_body: "{{ lookup('file','cf_template.json') }}"
disable_rollback: false
template_parameters:
InstanceType: "t3.nano"
ImageId: "{{ ec2_ami_id }}"
SubnetId: "{{ testing_subnet.subnet.id }}"
register: cf_stack
ignore_errors: true

- name: Fix the AMI ID and retry updating the cloudformation stack (passes with disable_rollback=true)
amazon.aws.cloudformation:
stack_name: "{{ stack_name }}-failtest"
state: present
template_body: "{{ lookup('file','cf_template.json') }}"
disable_rollback: true
template_parameters:
InstanceType: "t3.nano"
ImageId: "{{ ec2_ami_id }}"
SubnetId: "{{ testing_subnet.subnet.id }}"
register: cf_stack

- name: get stack details
cloudformation_info:
stack_name: "{{ stack_name }}-failtest"
register: stack_info

- name: Assert that update was successful
assert:
that:
- cf_stack.changed
- cf_stack.output == "Stack UPDATE complete"
- stack_info.cloudformation["{{ stack_name }}-failtest"].stack_description.stack_status == "UPDATE_COMPLETE"

always:

- name: delete stack
cloudformation:
stack_name: "{{ item }}"
state: absent
ignore_errors: true
with_items:
- "{{ stack_name_disable_rollback_true }}"
- "{{ stack_name_disable_rollback_false }}"
- "{{ stack_name }}-failtest"
- "{{ stack_name }}"

0 comments on commit 46e9dd4

Please sign in to comment.