* * Implements Change Sets on updating a cloudformation stack when create_changeset=true (#23490) * * Silence test complaints ;) * * Added optional changeset_name parameter. * Check if changeset with the requested name already exist. * Documentation fix * * Added warning when cloudformation stack has pending changesets. * Fix documentation
This commit is contained in:
parent
7bed60ba5f
commit
dd07d11ae5
1 changed files with 64 additions and 0 deletions
|
@ -93,6 +93,23 @@ options:
|
|||
present, the stack does exist, and neither 'template' nor 'template_url' are specified, the previous template will be reused.
|
||||
required: false
|
||||
version_added: "2.0"
|
||||
create_changeset:
|
||||
description:
|
||||
- "If stack already exists create a changeset instead of directly applying changes.
|
||||
See the AWS Change Sets docs U(http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html).
|
||||
WARNING: if the stack does not exist, it will be created without changeset. If the state is absent, the stack will be deleted immediately with no
|
||||
changeset."
|
||||
required: false
|
||||
default: false
|
||||
version_added: "2.4"
|
||||
changeset_name:
|
||||
description:
|
||||
- Name given to the changeset when creating a changeset, only used when create_changeset is true. By default a name prefixed with Ansible-STACKNAME
|
||||
is generated based on input parameters.
|
||||
See the AWS Change Sets docs U(http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html)
|
||||
required: false
|
||||
default: null
|
||||
version_added: "2.4"
|
||||
template_format:
|
||||
description:
|
||||
- (deprecated) For local templates, allows specification of json or yaml format. Templates are now passed raw to CloudFormation regardless of format.
|
||||
|
@ -239,6 +256,7 @@ except ImportError:
|
|||
# import a class, otherwise we'll use a fully qualified path
|
||||
from ansible.module_utils.ec2 import AWSRetry
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.utils.hashing import secure_hash_s
|
||||
import ansible.module_utils.ec2
|
||||
|
||||
def boto_exception(err):
|
||||
|
@ -297,6 +315,45 @@ def create_stack(module, stack_params, cfn):
|
|||
return result
|
||||
|
||||
|
||||
def list_changesets(cfn, stack_name):
|
||||
res = cfn.list_change_sets(StackName=stack_name)
|
||||
changesets = []
|
||||
for cs in res['Summaries']:
|
||||
changesets.append(cs['ChangeSetName'])
|
||||
return changesets
|
||||
|
||||
def create_changeset(module, stack_params, cfn):
|
||||
if 'TemplateBody' not in stack_params and 'TemplateURL' not in stack_params:
|
||||
module.fail_json(msg="Either 'template' or 'template_url' is required.")
|
||||
|
||||
try:
|
||||
if not 'ChangeSetName' in stack_params:
|
||||
# Determine ChangeSetName using hash of parameters.
|
||||
changeset_name = 'Ansible-' + stack_params['StackName'] + '-' + secure_hash_s(json.dumps(stack_params, sort_keys=True))
|
||||
stack_params['ChangeSetName'] = changeset_name
|
||||
# Determine if this changeset already exists
|
||||
pending_changesets = list_changesets(cfn, stack_params['StackName'])
|
||||
if changeset_name in pending_changesets:
|
||||
warning = 'WARNING: '+str(len(pending_changesets))+' pending changeset(s) exist(s) for this stack!'
|
||||
result = dict(changed=False, output='ChangeSet ' + changeset_name + ' already exists.', warnings=[warning])
|
||||
else:
|
||||
cs = cfn.create_change_set(**stack_params)
|
||||
result = stack_operation(cfn, stack_params['StackName'], 'UPDATE')
|
||||
result['warnings'] = [('Created changeset named ' + changeset_name + ' for stack ' + stack_params['StackName']),
|
||||
('You can execute it using: aws cloudformation execute-change-set --change-set-name ' + cs['Id']),
|
||||
('NOTE that dependencies on this stack might fail due to pending changes!')]
|
||||
except Exception as err:
|
||||
error_msg = boto_exception(err)
|
||||
if 'No updates are to be performed.' in error_msg:
|
||||
result = dict(changed=False, output='Stack is already up-to-date.')
|
||||
else:
|
||||
module.fail_json(msg=error_msg)
|
||||
|
||||
if not result:
|
||||
module.fail_json(msg="empty result")
|
||||
return result
|
||||
|
||||
|
||||
def update_stack(module, stack_params, cfn):
|
||||
if 'TemplateBody' not in stack_params and 'TemplateURL' not in stack_params:
|
||||
stack_params['UsePreviousTemplate'] = True
|
||||
|
@ -402,6 +459,8 @@ def main():
|
|||
disable_rollback=dict(default=False, type='bool'),
|
||||
template_url=dict(default=None, required=False),
|
||||
template_format=dict(default=None, choices=['json', 'yaml'], required=False),
|
||||
create_changeset=dict(default=False, type='bool'),
|
||||
changeset_name=dict(default=None, required=False),
|
||||
role_arn=dict(default=None, required=False),
|
||||
tags=dict(default=None, type='dict')
|
||||
)
|
||||
|
@ -434,6 +493,9 @@ def main():
|
|||
if module.params['stack_policy'] is not None:
|
||||
stack_params['StackPolicyBody'] = open(module.params['stack_policy'], 'r').read()
|
||||
|
||||
if module.params['changeset_name'] is not None:
|
||||
stack_params['ChangeSetName'] = module.params['changeset_name']
|
||||
|
||||
template_parameters = module.params['template_parameters']
|
||||
stack_params['Parameters'] = [{'ParameterKey':k, 'ParameterValue':str(v)} for k, v in template_parameters.items()]
|
||||
|
||||
|
@ -456,6 +518,8 @@ def main():
|
|||
if state == 'present':
|
||||
if not stack_info:
|
||||
result = create_stack(module, stack_params, cfn)
|
||||
elif create_changeset:
|
||||
result = create_changeset(module, stack_params, cfn)
|
||||
else:
|
||||
result = update_stack(module, stack_params, cfn)
|
||||
|
||||
|
|
Loading…
Reference in a new issue