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 argspull/4420/head
parent
501fc7a248
commit
380c43de4e
|
@ -33,8 +33,9 @@ author: Justin Menga (@jmenga)
|
||||||
options:
|
options:
|
||||||
stack_name:
|
stack_name:
|
||||||
description:
|
description:
|
||||||
- The name or id of the CloudFormation stack
|
- The name or id of the CloudFormation stack. Gathers facts for all stacks by default.
|
||||||
required: true
|
required: false
|
||||||
|
default: null
|
||||||
all_facts:
|
all_facts:
|
||||||
description:
|
description:
|
||||||
- Get all stack information for the stack
|
- Get all stack information for the stack
|
||||||
|
@ -151,7 +152,8 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_BOTO3 = False
|
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 ansible.module_utils.basic import AnsibleModule
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import json
|
import json
|
||||||
|
@ -169,20 +171,27 @@ class CloudFormationServiceManager:
|
||||||
self.client = boto3_conn(module, conn_type='client',
|
self.client = boto3_conn(module, conn_type='client',
|
||||||
resource='cloudformation', region=region,
|
resource='cloudformation', region=region,
|
||||||
endpoint=ec2_url, **aws_connect_kwargs)
|
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:
|
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")
|
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:
|
except Exception as e:
|
||||||
self.module.fail_json(msg="Can't establish connection - " + str(e), exception=traceback.format_exc())
|
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:
|
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')
|
response = self.paginated_response(func, 'Stacks')
|
||||||
if response:
|
if response:
|
||||||
return response[0]
|
return response
|
||||||
self.module.fail_json(msg="Error describing stack - an empty response was returned")
|
self.module.fail_json(msg="Error describing stack(s) - an empty response was returned")
|
||||||
except Exception as e:
|
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):
|
def list_stack_resources(self, stack_name):
|
||||||
try:
|
try:
|
||||||
|
@ -240,7 +249,7 @@ def to_dict(items, key, value):
|
||||||
def main():
|
def main():
|
||||||
argument_spec = ec2_argument_spec()
|
argument_spec = ec2_argument_spec()
|
||||||
argument_spec.update(dict(
|
argument_spec.update(dict(
|
||||||
stack_name=dict(required=True, type='str' ),
|
stack_name=dict(),
|
||||||
all_facts=dict(required=False, default=False, type='bool'),
|
all_facts=dict(required=False, default=False, type='bool'),
|
||||||
stack_policy=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'),
|
stack_events=dict(required=False, default=False, type='bool'),
|
||||||
|
@ -253,24 +262,22 @@ def main():
|
||||||
if not HAS_BOTO3:
|
if not HAS_BOTO3:
|
||||||
module.fail_json(msg='boto3 is required.')
|
module.fail_json(msg='boto3 is required.')
|
||||||
|
|
||||||
# Describe the stack
|
|
||||||
service_mgr = CloudFormationServiceManager(module)
|
service_mgr = CloudFormationServiceManager(module)
|
||||||
stack_name = module.params.get('stack_name')
|
|
||||||
result = {
|
result = {'ansible_facts': {'cloudformation': {}}}
|
||||||
'ansible_facts': { 'cloudformation': { stack_name:{} } }
|
|
||||||
}
|
for stack_description in service_mgr.describe_stacks(module.params.get('stack_name')):
|
||||||
facts = result['ansible_facts']['cloudformation'][stack_name]
|
facts = {'stack_description': stack_description}
|
||||||
facts['stack_description'] = service_mgr.describe_stack(stack_name)
|
stack_name = stack_description.get('StackName')
|
||||||
|
|
||||||
# Create stack output and stack parameter dictionaries
|
# Create stack output and stack parameter dictionaries
|
||||||
if facts['stack_description']:
|
if facts['stack_description']:
|
||||||
facts['stack_outputs'] = to_dict(facts['stack_description'].get('Outputs'), 'OutputKey', 'OutputValue')
|
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_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
|
# normalize stack description API output
|
||||||
facts['stack_description'] = camel_dict_to_snake_dict(facts['stack_description'])
|
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', [])
|
|
||||||
|
|
||||||
# Create optional stack outputs
|
# Create optional stack outputs
|
||||||
all_facts = module.params.get('all_facts')
|
all_facts = module.params.get('all_facts')
|
||||||
|
@ -284,6 +291,8 @@ def main():
|
||||||
if all_facts or module.params.get('stack_events'):
|
if all_facts or module.params.get('stack_events'):
|
||||||
facts['stack_events'] = service_mgr.describe_stack_events(stack_name)
|
facts['stack_events'] = service_mgr.describe_stack_events(stack_name)
|
||||||
|
|
||||||
|
result['ansible_facts']['cloudformation'][stack_name] = facts
|
||||||
|
|
||||||
result['changed'] = False
|
result['changed'] = False
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue