cloudformation_facts: describe all stacks by default
* cloudformation_facts describe all stacks by default * cloudformation_facts jittered backoff / retries * cloudformation_facts stack_name use default arg_spec * cloudformation_facts bugfix broken notification_arns output * cloudformation_facts add simplified "stack_tags" output * CloudFormationServiceManager.describe_stacks default args
This commit is contained in:
parent
501fc7a248
commit
380c43de4e
1 changed files with 44 additions and 35 deletions
|
@ -33,8 +33,9 @@ author: Justin Menga (@jmenga)
|
|||
options:
|
||||
stack_name:
|
||||
description:
|
||||
- The name or id of the CloudFormation stack
|
||||
required: true
|
||||
- The name or id of the CloudFormation stack. Gathers facts for all stacks by default.
|
||||
required: false
|
||||
default: null
|
||||
all_facts:
|
||||
description:
|
||||
- Get all stack information for the stack
|
||||
|
@ -151,7 +152,8 @@ try:
|
|||
except ImportError:
|
||||
HAS_BOTO3 = False
|
||||
|
||||
from ansible.module_utils.ec2 import get_aws_connection_info, ec2_argument_spec, boto3_conn, camel_dict_to_snake_dict
|
||||
from ansible.module_utils.ec2 import get_aws_connection_info, ec2_argument_spec, boto3_conn, camel_dict_to_snake_dict, \
|
||||
AWSRetry, boto3_tag_list_to_ansible_dict
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from functools import partial
|
||||
import json
|
||||
|
@ -169,20 +171,27 @@ class CloudFormationServiceManager:
|
|||
self.client = boto3_conn(module, conn_type='client',
|
||||
resource='cloudformation', region=region,
|
||||
endpoint=ec2_url, **aws_connect_kwargs)
|
||||
backoff_wrapper = AWSRetry.jittered_backoff(retries=10, delay=3, max_delay=30)
|
||||
self.client.describe_stacks = backoff_wrapper(self.client.describe_stacks)
|
||||
self.client.list_stack_resources = backoff_wrapper(self.client.list_stack_resources)
|
||||
self.client.describe_stack_events = backoff_wrapper(self.client.describe_stack_events)
|
||||
self.client.get_stack_policy = backoff_wrapper(self.client.get_stack_policy)
|
||||
self.client.get_template = backoff_wrapper(self.client.get_template)
|
||||
except botocore.exceptions.NoRegionError:
|
||||
self.module.fail_json(msg="Region must be specified as a parameter, in AWS_DEFAULT_REGION environment variable or in boto configuration file")
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Can't establish connection - " + str(e), exception=traceback.format_exc())
|
||||
|
||||
def describe_stack(self, stack_name):
|
||||
def describe_stacks(self, stack_name=None):
|
||||
try:
|
||||
func = partial(self.client.describe_stacks,StackName=stack_name)
|
||||
kwargs = {'StackName': stack_name} if stack_name else {}
|
||||
func = partial(self.client.describe_stacks, **kwargs)
|
||||
response = self.paginated_response(func, 'Stacks')
|
||||
if response:
|
||||
return response[0]
|
||||
self.module.fail_json(msg="Error describing stack - an empty response was returned")
|
||||
return response
|
||||
self.module.fail_json(msg="Error describing stack(s) - an empty response was returned")
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Error describing stack - " + str(e), exception=traceback.format_exc())
|
||||
self.module.fail_json(msg="Error describing stack(s) - " + str(e), exception=traceback.format_exc())
|
||||
|
||||
def list_stack_resources(self, stack_name):
|
||||
try:
|
||||
|
@ -240,7 +249,7 @@ def to_dict(items, key, value):
|
|||
def main():
|
||||
argument_spec = ec2_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
stack_name=dict(required=True, type='str' ),
|
||||
stack_name=dict(),
|
||||
all_facts=dict(required=False, default=False, type='bool'),
|
||||
stack_policy=dict(required=False, default=False, type='bool'),
|
||||
stack_events=dict(required=False, default=False, type='bool'),
|
||||
|
@ -253,36 +262,36 @@ def main():
|
|||
if not HAS_BOTO3:
|
||||
module.fail_json(msg='boto3 is required.')
|
||||
|
||||
# Describe the stack
|
||||
service_mgr = CloudFormationServiceManager(module)
|
||||
stack_name = module.params.get('stack_name')
|
||||
result = {
|
||||
'ansible_facts': { 'cloudformation': { stack_name:{} } }
|
||||
}
|
||||
facts = result['ansible_facts']['cloudformation'][stack_name]
|
||||
facts['stack_description'] = service_mgr.describe_stack(stack_name)
|
||||
|
||||
# Create stack output and stack parameter dictionaries
|
||||
if facts['stack_description']:
|
||||
facts['stack_outputs'] = to_dict(facts['stack_description'].get('Outputs'), 'OutputKey', 'OutputValue')
|
||||
facts['stack_parameters'] = to_dict(facts['stack_description'].get('Parameters'), 'ParameterKey', 'ParameterValue')
|
||||
result = {'ansible_facts': {'cloudformation': {}}}
|
||||
|
||||
# normalize stack description API output
|
||||
facts['stack_description'] = camel_dict_to_snake_dict(facts['stack_description'])
|
||||
# camel2snake doesn't handle NotificationARNs properly, so let's fix that
|
||||
facts['stack_description']['notification_arns'] = facts['stack_description'].pop('notification_ar_ns', [])
|
||||
for stack_description in service_mgr.describe_stacks(module.params.get('stack_name')):
|
||||
facts = {'stack_description': stack_description}
|
||||
stack_name = stack_description.get('StackName')
|
||||
|
||||
# Create optional stack outputs
|
||||
all_facts = module.params.get('all_facts')
|
||||
if all_facts or module.params.get('stack_resources'):
|
||||
facts['stack_resource_list'] = service_mgr.list_stack_resources(stack_name)
|
||||
facts['stack_resources'] = to_dict(facts.get('stack_resource_list'), 'LogicalResourceId', 'PhysicalResourceId')
|
||||
if all_facts or module.params.get('stack_template'):
|
||||
facts['stack_template'] = service_mgr.get_template(stack_name)
|
||||
if all_facts or module.params.get('stack_policy'):
|
||||
facts['stack_policy'] = service_mgr.get_stack_policy(stack_name)
|
||||
if all_facts or module.params.get('stack_events'):
|
||||
facts['stack_events'] = service_mgr.describe_stack_events(stack_name)
|
||||
# Create stack output and stack parameter dictionaries
|
||||
if facts['stack_description']:
|
||||
facts['stack_outputs'] = to_dict(facts['stack_description'].get('Outputs'), 'OutputKey', 'OutputValue')
|
||||
facts['stack_parameters'] = to_dict(facts['stack_description'].get('Parameters'), 'ParameterKey', 'ParameterValue')
|
||||
facts['stack_tags'] = boto3_tag_list_to_ansible_dict(facts['stack_description'].get('Tags'))
|
||||
|
||||
# normalize stack description API output
|
||||
facts['stack_description'] = camel_dict_to_snake_dict(facts['stack_description'])
|
||||
|
||||
# Create optional stack outputs
|
||||
all_facts = module.params.get('all_facts')
|
||||
if all_facts or module.params.get('stack_resources'):
|
||||
facts['stack_resource_list'] = service_mgr.list_stack_resources(stack_name)
|
||||
facts['stack_resources'] = to_dict(facts.get('stack_resource_list'), 'LogicalResourceId', 'PhysicalResourceId')
|
||||
if all_facts or module.params.get('stack_template'):
|
||||
facts['stack_template'] = service_mgr.get_template(stack_name)
|
||||
if all_facts or module.params.get('stack_policy'):
|
||||
facts['stack_policy'] = service_mgr.get_stack_policy(stack_name)
|
||||
if all_facts or module.params.get('stack_events'):
|
||||
facts['stack_events'] = service_mgr.describe_stack_events(stack_name)
|
||||
|
||||
result['ansible_facts']['cloudformation'][stack_name] = facts
|
||||
|
||||
result['changed'] = False
|
||||
module.exit_json(**result)
|
||||
|
|
Loading…
Reference in a new issue