Amazon modules: rename _facts with ansible_facts result to _info (#60178)

* aws_s3_bucket_facts -> aws_s3_bucket_info

* cloudformation_facts -> cloudformation_info

* cloudfront_facts -> cloudfront_info

* ecs_service_facts -> ecs_service_info

* efs_facts -> efs_info

* Add changelog and porting guide entry.

* lambda_facts -> lambda_info

* Improve examples.

* Add subsection on renamed modules.

* Add sentence on registering variables.

* Fix ReST.

* Instead of renaming lambda_facts, deprecate it and replace with new module.

* Rename internal variable.

* Re-add sanity ignores for lambda_facts.
This commit is contained in:
Felix Fontein 2019-08-13 14:01:37 +02:00 committed by Sloane Hertel
parent dee13ea4f8
commit 2d98734ad5
26 changed files with 666 additions and 133 deletions

4
.github/BOTMETA.yml vendored
View file

@ -53,7 +53,7 @@ files:
$modules/cloud/amazon/cloudformation.py:
maintainers: ryansb
ignored: tedder
$modules/cloud/amazon/cloudfront_facts.py: willthames
$modules/cloud/amazon/cloudfront_info.py: willthames
$modules/cloud/amazon/cloudtrail.py: $team_ansible
$modules/cloud/amazon/ec2.py:
maintainers: $team_ansible
@ -82,7 +82,7 @@ files:
$modules/cloud/amazon/ecs_cluster.py: willthames
$modules/cloud/amazon/ecs_ecr.py: willthames
$modules/cloud/amazon/ecs_service.py: willthames
$modules/cloud/amazon/ecs_service_facts.py: willthames
$modules/cloud/amazon/ecs_service_info.py: willthames
$modules/cloud/amazon/ecs_task.py: willthames
$modules/cloud/amazon/ecs_taskdefinition.py: willthames
$modules/cloud/amazon/ecs_taskdefinition_info.py: willthames

View file

@ -0,0 +1,12 @@
minor_changes:
- The ``aws_s3_bucket_facts`` module has been renamed to ``aws_s3_bucket_info``.
When called with the new name, the module no longer returns ``ansible_facts``.
- The ``cloudformation_facts`` module has been renamed to ``cloudformation_info``.
When called with the new name, the module no longer returns ``ansible_facts``.
- The ``cloudfront_facts`` module has been renamed to ``cloudfront_info``.
When called with the new name, the module no longer returns ``ansible_facts``.
- The ``ecs_service_facts`` module has been renamed to ``ecs_service_info``.
When called with the new name, the module no longer returns ``ansible_facts``.
- The ``efs_facts`` module has been renamed to ``efs_info``.
When called with the new name, the module no longer returns ``ansible_facts``.
- The ``lambda_facts`` module has been deprecated. Use ``lambda_info`` instead.

View file

@ -62,6 +62,8 @@ Deprecation notices
The following modules will be removed in Ansible 2.13. Please update update your playbooks accordingly.
* lambda_facts use :ref:`lambda_info <lambda_info_module>` instead.
* nxos_linkagg use :ref:`nxos_lag_interfaces <nxos_lag_interfaces_module>` instead.
* vyos_interface use :ref:`vyos_interfaces <vyos_interfaces_module>` instead.
@ -73,6 +75,29 @@ The following functionality will be removed in Ansible 2.12. Please update updat
* ``vmware_cluster`` DRS, HA and VSAN configuration; use `vmware_cluster_drs <vmware_cluster_drs_module>`, `vmware_cluster_ha <vmware_cluster_ha_module>` and `vmware_cluster_vsan <vmware_cluster_vsan_module>` instead.
Renamed modules
^^^^^^^^^^^^^^^
The following modules have been renamed. The old name is deprecated and will
be removed in Ansible 2.13. Please update update your playbooks accordingly.
* The ``aws_s3_bucket_facts`` module was renamed to :ref:`aws_s3_bucket_info <aws_s3_bucket_info_module>`.
When called with the new name, the module no longer returns ``ansible_facts``.
To access return values, :ref:`register a variable <registered_variables>`.
* The ``cloudformation_facts`` module was renamed to :ref:`cloudformation_info <cloudformation_info_module>`.
When called with the new name, the module no longer returns ``ansible_facts``.
To access return values, :ref:`register a variable <registered_variables>`.
* The ``cloudfront_facts`` module was renamed to :ref:`cloudfront_info <cloudfront_info_module>`.
When called with the new name, the module no longer returns ``ansible_facts``.
To access return values, :ref:`register a variable <registered_variables>`.
* The ``ecs_service_facts`` module was renamed to :ref:`ecs_service_info <ecs_service_info_module>`.
When called with the new name, the module no longer returns ``ansible_facts``.
To access return values, :ref:`register a variable <registered_variables>`.
* The ``efs_facts`` module was renamed to :ref:`efs_info <efs_info_module>`.
When called with the new name, the module no longer returns ``ansible_facts``.
To access return values, :ref:`register a variable <registered_variables>`.
Noteworthy module changes
-------------------------

View file

@ -111,8 +111,8 @@ In a playbook, you can set module defaults for whole groups of modules, such as
group/aws:
region: us-west-2
tasks:
- aws_s3_bucket_facts:
# now the region is shared between both facts modules
- aws_s3_bucket_info:
# now the region is shared between both info modules
- ec2_ami_info:
filters:
name: 'RHEL*7.5*'

View file

@ -58,7 +58,7 @@ groupings:
- aws
aws_s3:
- aws
aws_s3_bucket_facts:
aws_s3_bucket_info:
- aws
aws_s3_cors:
- aws
@ -84,13 +84,13 @@ groupings:
- aws
cloudformation:
- aws
cloudformation_facts:
cloudformation_info:
- aws
cloudformation_stack_set:
- aws
cloudfront_distribution:
- aws
cloudfront_facts:
cloudfront_info:
- aws
cloudfront_invalidation:
- aws
@ -254,7 +254,7 @@ groupings:
- aws
ecs_service:
- aws
ecs_service_facts:
ecs_service_info:
- aws
ecs_task:
- aws
@ -264,7 +264,7 @@ groupings:
- aws
efs:
- aws
efs_facts:
efs_info:
- aws
elasticache:
- aws
@ -328,7 +328,7 @@ groupings:
- aws
lambda_event:
- aws
lambda_facts:
lambda_info:
- aws
lambda_policy:
- aws

View file

@ -0,0 +1 @@
aws_s3_bucket_info.py

View file

@ -0,0 +1 @@
cloudformation_info.py

View file

@ -0,0 +1 @@
cloudfront_info.py

View file

@ -0,0 +1 @@
ecs_service_info.py

View file

@ -0,0 +1 @@
efs_info.py

View file

@ -15,13 +15,17 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: lambda_facts
deprecated:
removed_in: '2.13'
why: Deprecated in favour of C(_info) module.
alternative: Use M(lambda_info) instead.
short_description: Gathers AWS Lambda function details as Ansible facts
description:
- Gathers various details related to Lambda functions, including aliases, versions and event source mappings.

View file

@ -12,13 +12,15 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
DOCUMENTATION = '''
---
module: aws_s3_bucket_facts
module: aws_s3_bucket_info
short_description: Lists S3 buckets in AWS
requirements:
- boto3 >= 1.4.4
- python >= 2.6
description:
- Lists S3 buckets in AWS
- This module was called C(aws_s3_bucket_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(aws_s3_bucket_info) module no longer returns C(ansible_facts)!
version_added: "2.4"
author: "Gerben Geijteman (@hyperized)"
extends_documentation_fragment:
@ -32,7 +34,12 @@ EXAMPLES = '''
# Note: Only AWS S3 is currently supported
# Lists all s3 buckets
- aws_s3_bucket_facts:
- aws_s3_bucket_info:
register: result
- name: List buckets
debug:
msg: "{{ result['buckets'] }}"
'''
RETURN = '''
@ -84,6 +91,10 @@ def main():
# Including ec2 argument spec
module = AnsibleModule(argument_spec=ec2_argument_spec(), supports_check_mode=True)
is_old_facts = module._name == 'aws_s3_bucket_facts'
if is_old_facts:
module.deprecate("The 'aws_s3_bucket_facts' module has been renamed to 'aws_s3_bucket_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
# Verify Boto3 is used
if not HAS_BOTO3:
@ -98,7 +109,10 @@ def main():
result['buckets'] = get_bucket_list(module, connection)
# Send exit
if is_old_facts:
module.exit_json(msg="Retrieved s3 facts.", ansible_facts=result)
else:
module.exit_json(msg="Retrieved s3 info.", **result)
if __name__ == '__main__':

View file

@ -13,10 +13,12 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
DOCUMENTATION = '''
---
module: cloudformation_facts
short_description: Obtain facts about an AWS CloudFormation stack
module: cloudformation_info
short_description: Obtain information about an AWS CloudFormation stack
description:
- Gets information about an AWS CloudFormation stack
- This module was called C(cloudformation_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(cloudformation_info) module no longer returns C(ansible_facts)!
requirements:
- boto3 >= 1.0.0
- python >= 2.6
@ -25,7 +27,7 @@ author: Justin Menga (@jmenga)
options:
stack_name:
description:
- The name or id of the CloudFormation stack. Gathers facts for all stacks by default.
- The name or id of the CloudFormation stack. Gathers information on all stacks by default.
all_facts:
description:
- Get all stack information for the stack
@ -60,10 +62,20 @@ EXAMPLES = '''
# Note: These examples do not set authentication details, see the AWS Guide for details.
# Get summary information about a stack
- cloudformation_info:
stack_name: my-cloudformation-stack
register: output
- debug:
msg: "{{ output['cloudformation']['my-cloudformation-stack'] }}"
# When the module is called as cloudformation_facts, return values are published
# in ansible_facts['cloudformation'][<stack_name>] and can be used as follows.
# Note that this is deprecated and will stop working in Ansible 2.13.
- cloudformation_facts:
stack_name: my-cloudformation-stack
# Facts are published in ansible_facts['cloudformation'][<stack_name>]
- debug:
msg: "{{ ansible_facts['cloudformation']['my-cloudformation-stack'] }}"
@ -71,27 +83,27 @@ EXAMPLES = '''
- set_fact:
stack_name: my-awesome-stack
- cloudformation_facts:
- cloudformation_info:
stack_name: "{{ stack_name }}"
register: my_stack
- debug:
msg: "{{ my_stack.ansible_facts.cloudformation[stack_name].stack_outputs }}"
msg: "{{ my_stack.cloudformation[stack_name].stack_outputs }}"
# Get all stack information about a stack
- cloudformation_facts:
- cloudformation_info:
stack_name: my-cloudformation-stack
all_facts: true
# Get stack resource and stack policy information about a stack
- cloudformation_facts:
- cloudformation_info:
stack_name: my-cloudformation-stack
stack_resources: true
stack_policy: true
# Fail if the stack doesn't exist
- name: try to get facts about a stack but fail if it doesn't exist
cloudformation_facts:
cloudformation_info:
stack_name: nonexistent-stack
all_facts: yes
failed_when: cloudformation['nonexistent-stack'] is undefined
@ -273,6 +285,10 @@ def main():
))
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
is_old_facts = module._name == 'cloudformation_facts'
if is_old_facts:
module.deprecate("The 'cloudformation_facts' module has been renamed to 'cloudformation_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
if not HAS_BOTO3:
module.fail_json(msg='boto3 is required.')
@ -306,7 +322,10 @@ def main():
if all_facts or module.params.get('stack_events'):
facts['stack_events'] = service_mgr.describe_stack_events(stack_name)
if is_old_facts:
result['ansible_facts']['cloudformation'][stack_name] = facts
else:
result['cloudformation'][stack_name] = facts
result['changed'] = False
module.exit_json(**result)

View file

@ -21,10 +21,12 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
DOCUMENTATION = '''
---
module: cloudfront_facts
module: cloudfront_info
short_description: Obtain facts about an AWS CloudFront distribution
description:
- Gets information about an AWS CloudFront distribution
- This module was called C(cloudfront_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(cloudfront_info) module no longer returns C(ansible_facts)!
requirements:
- boto3 >= 1.0.0
- python >= 2.6
@ -154,23 +156,38 @@ EXAMPLES = '''
# Note: These examples do not set authentication details, see the AWS Guide for details.
# Get a summary of distributions
- cloudfront_facts:
- cloudfront_info:
summary: true
register: result
# Get information about a distribution
- cloudfront_info:
distribution: true
distribution_id: my-cloudfront-distribution-id
register: result_did
- debug:
msg: "{{ result_did['cloudfront']['my-cloudfront-distribution-id'] }}"
# Get information about a distribution using the CNAME of the cloudfront distribution.
- cloudfront_info:
distribution: true
domain_name_alias: www.my-website.com
register: result_website
- debug:
msg: "{{ result_website['cloudfront']['www.my-website.com'] }}"
# When the module is called as cloudfront_facts, return values are published
# in ansible_facts['cloudfront'][<id>] and can be used as follows.
# Note that this is deprecated and will stop working in Ansible 2.13.
- cloudfront_facts:
distribution: true
distribution_id: my-cloudfront-distribution-id
# Get information about a distribution using the CNAME of the cloudfront distribution.
- cloudfront_facts:
distribution: true
domain_name_alias: www.my-website.com
# Facts are published in ansible_facts['cloudfront'][<distribution_name>]
- debug:
msg: "{{ ansible_facts['cloudfront']['my-cloudfront-distribution-id'] }}"
- cloudfront_facts:
distribution: true
domain_name_alias: www.my-website.com
- debug:
msg: "{{ ansible_facts['cloudfront']['www.my-website.com'] }}"
@ -584,6 +601,10 @@ def main():
))
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
is_old_facts = module._name == 'cloudfront_facts'
if is_old_facts:
module.deprecate("The 'cloudfront_facts' module has been renamed to 'cloudfront_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
if not HAS_BOTO3:
module.fail_json(msg='boto3 is required.')
@ -696,7 +717,10 @@ def main():
result['changed'] = False
result['cloudfront'].update(facts)
if is_old_facts:
module.exit_json(msg="Retrieved cloudfront facts.", ansible_facts=result)
else:
module.exit_json(msg="Retrieved cloudfront info.", **result)
if __name__ == '__main__':

View file

@ -12,10 +12,12 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
DOCUMENTATION = '''
---
module: ecs_service_facts
module: ecs_service_info
short_description: list or describe services in ecs
description:
- Lists or describes services in ecs.
- This module was called C(ecs_service_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(ecs_service_info) module no longer returns C(ansible_facts)!
version_added: "2.1"
author:
- "Mark Chance (@Java1Guy)"
@ -53,14 +55,16 @@ EXAMPLES = '''
# Note: These examples do not set authentication details, see the AWS Guide for details.
# Basic listing example
- ecs_service_facts:
- ecs_service_info:
cluster: test-cluster
service: console-test-service
details: true
register: output
# Basic listing example
- ecs_service_facts:
- ecs_service_info:
cluster: test-cluster
register: output
'''
RETURN = '''
@ -220,6 +224,10 @@ def main():
))
module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True)
is_old_facts = module._name == 'ecs_service_facts'
if is_old_facts:
module.deprecate("The 'ecs_service_facts' module has been renamed to 'ecs_service_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
show_details = module.params.get('details')
@ -229,15 +237,18 @@ def main():
services = module.params['service']
else:
services = task_mgr.list_services(module.params['cluster'])['services']
ecs_facts = dict(services=[], services_not_running=[])
ecs_info = dict(services=[], services_not_running=[])
for chunk in chunks(services, 10):
running_services, services_not_running = task_mgr.describe_services(module.params['cluster'], chunk)
ecs_facts['services'].extend(running_services)
ecs_facts['services_not_running'].extend(services_not_running)
ecs_info['services'].extend(running_services)
ecs_info['services_not_running'].extend(services_not_running)
else:
ecs_facts = task_mgr.list_services(module.params['cluster'])
ecs_info = task_mgr.list_services(module.params['cluster'])
module.exit_json(changed=False, ansible_facts=ecs_facts, **ecs_facts)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=ecs_info, **ecs_info)
else:
module.exit_json(changed=False, **ecs_info)
if __name__ == '__main__':

View file

@ -13,10 +13,12 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
DOCUMENTATION = '''
---
module: efs_facts
module: efs_info
short_description: Get information about Amazon EFS file systems
description:
- This module can be used to search Amazon EFS file systems.
- This module was called C(efs_facts) before Ansible 2.9, returning C(ansible_facts).
Note that the M(efs_info) module no longer returns C(ansible_facts)!
version_added: "2.2"
requirements: [ boto3 ]
author:
@ -43,20 +45,25 @@ extends_documentation_fragment:
EXAMPLES = '''
- name: Find all existing efs
efs_facts:
efs_info:
register: result
- name: Find efs using id
efs_facts:
efs_info:
id: fs-1234abcd
register: result
- name: Searching all EFS instances with tag Name = 'myTestNameTag', in subnet 'subnet-1a2b3c4d' and with security group 'sg-4d3c2b1a'
efs_facts:
efs_info:
tags:
name: myTestNameTag
targets:
- subnet-1a2b3c4d
- sg-4d3c2b1a
register: result
- debug:
msg: "{{ result['efs'] }}"
'''
RETURN = '''
@ -358,6 +365,10 @@ def main():
module = AnsibleAWSModule(argument_spec=argument_spec,
supports_check_mode=True)
is_old_facts = module._name == 'efs_facts'
if is_old_facts:
module.deprecate("The 'efs_facts' module has been renamed to 'efs_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
region, _, aws_connect_params = get_aws_connection_info(module, boto3=True)
connection = EFSConnection(module, region, **aws_connect_params)
@ -379,7 +390,10 @@ def main():
targets = [(item, prefix_to_attr(item)) for item in targets]
file_systems_info = [item for item in file_systems_info if has_targets(item['mount_targets'], targets)]
if is_old_facts:
module.exit_json(changed=False, ansible_facts={'efs': file_systems_info})
else:
module.exit_json(changed=False, efs=file_systems_info)
if __name__ == '__main__':

View file

@ -83,33 +83,37 @@ EXAMPLES = '''
memory_size: 128
role: "arn:aws:iam::{{ account }}:role/API2LambdaExecRole"
- name: Get information
lambda_info:
name: myLambdaFunction
register: lambda_info
- name: show results
debug:
var: lambda_facts
msg: "{{ lambda_info['lambda_facts'] }}"
# The following will set the Dev alias to the latest version ($LATEST) since version is omitted (or = 0)
- name: "alias 'Dev' for function {{ lambda_facts.FunctionName }} "
- name: "alias 'Dev' for function {{ lambda_info.lambda_facts.FunctionName }} "
lambda_alias:
state: "{{ state | default('present') }}"
function_name: "{{ lambda_facts.FunctionName }}"
function_name: "{{ lambda_info.lambda_facts.FunctionName }}"
name: Dev
description: Development is $LATEST version
# The QA alias will only be created when a new version is published (i.e. not = '$LATEST')
- name: "alias 'QA' for function {{ lambda_facts.FunctionName }} "
- name: "alias 'QA' for function {{ lambda_info.lambda_facts.FunctionName }} "
lambda_alias:
state: "{{ state | default('present') }}"
function_name: "{{ lambda_facts.FunctionName }}"
function_name: "{{ lambda_info.lambda_facts.FunctionName }}"
name: QA
version: "{{ lambda_facts.Version }}"
description: "QA is version {{ lambda_facts.Version }}"
when: lambda_facts.Version != "$LATEST"
version: "{{ lambda_info.lambda_facts.Version }}"
description: "QA is version {{ lambda_info.lambda_facts.Version }}"
when: lambda_info.lambda_facts.Version != "$LATEST"
# The Prod alias will have a fixed version based on a variable
- name: "alias 'Prod' for function {{ lambda_facts.FunctionName }} "
- name: "alias 'Prod' for function {{ lambda_info.lambda_facts.FunctionName }} "
lambda_alias:
state: "{{ state | default('present') }}"
function_name: "{{ lambda_facts.FunctionName }}"
function_name: "{{ lambda_info.lambda_facts.FunctionName }}"
name: Prod
version: "{{ production_version }}"
description: "Production is version {{ production_version }}"

View file

@ -0,0 +1,398 @@
#!/usr/bin/python
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: lambda_info
short_description: Gathers AWS Lambda function details
description:
- Gathers various details related to Lambda functions, including aliases, versions and event source mappings.
Use module M(lambda) to manage the lambda function itself, M(lambda_alias) to manage function aliases and
M(lambda_event) to manage lambda event source mappings.
version_added: "2.9"
options:
query:
description:
- Specifies the resource type for which to gather information. Leave blank to retrieve all information.
required: true
choices: [ "aliases", "all", "config", "mappings", "policy", "versions" ]
default: "all"
function_name:
description:
- The name of the lambda function for which information is requested.
aliases: [ "function", "name"]
event_source_arn:
description:
- For query type 'mappings', this is the Amazon Resource Name (ARN) of the Amazon Kinesis or DynamoDB stream.
author: Pierre Jodouin (@pjodouin)
requirements:
- boto3
extends_documentation_fragment:
- aws
- ec2
'''
EXAMPLES = '''
---
# Simple example of listing all info for a function
- name: List all for a specific function
lambda_info:
query: all
function_name: myFunction
register: my_function_details
# List all versions of a function
- name: List function versions
lambda_info:
query: versions
function_name: myFunction
register: my_function_versions
# List all lambda function versions
- name: List all function
lambda_info:
query: all
max_items: 20
register: output
- name: show Lambda information
debug:
msg: "{{ output['function'] }}"
'''
RETURN = '''
---
function:
description: lambda function list
returned: success
type: dict
function.TheName:
description: lambda function information, including event, mapping, and version information
returned: success
type: dict
'''
from ansible.module_utils.aws.core import AnsibleAWSModule
from ansible.module_utils.ec2 import camel_dict_to_snake_dict, get_aws_connection_info, boto3_conn
import json
import datetime
import sys
import re
try:
from botocore.exceptions import ClientError
except ImportError:
pass # protected by AnsibleAWSModule
def fix_return(node):
"""
fixup returned dictionary
:param node:
:return:
"""
if isinstance(node, datetime.datetime):
node_value = str(node)
elif isinstance(node, list):
node_value = [fix_return(item) for item in node]
elif isinstance(node, dict):
node_value = dict([(item, fix_return(node[item])) for item in node.keys()])
else:
node_value = node
return node_value
def alias_details(client, module):
"""
Returns list of aliases for a specified function.
:param client: AWS API client reference (boto3)
:param module: Ansible module reference
:return dict:
"""
lambda_info = dict()
function_name = module.params.get('function_name')
if function_name:
params = dict()
if module.params.get('max_items'):
params['MaxItems'] = module.params.get('max_items')
if module.params.get('next_marker'):
params['Marker'] = module.params.get('next_marker')
try:
lambda_info.update(aliases=client.list_aliases(FunctionName=function_name, **params)['Aliases'])
except ClientError as e:
if e.response['Error']['Code'] == 'ResourceNotFoundException':
lambda_info.update(aliases=[])
else:
module.fail_json_aws(e, msg="Trying to get aliases")
else:
module.fail_json(msg='Parameter function_name required for query=aliases.')
return {function_name: camel_dict_to_snake_dict(lambda_info)}
def all_details(client, module):
"""
Returns all lambda related facts.
:param client: AWS API client reference (boto3)
:param module: Ansible module reference
:return dict:
"""
if module.params.get('max_items') or module.params.get('next_marker'):
module.fail_json(msg='Cannot specify max_items nor next_marker for query=all.')
lambda_info = dict()
function_name = module.params.get('function_name')
if function_name:
lambda_info[function_name] = {}
lambda_info[function_name].update(config_details(client, module)[function_name])
lambda_info[function_name].update(alias_details(client, module)[function_name])
lambda_info[function_name].update(policy_details(client, module)[function_name])
lambda_info[function_name].update(version_details(client, module)[function_name])
lambda_info[function_name].update(mapping_details(client, module)[function_name])
else:
lambda_info.update(config_details(client, module))
return lambda_info
def config_details(client, module):
"""
Returns configuration details for one or all lambda functions.
:param client: AWS API client reference (boto3)
:param module: Ansible module reference
:return dict:
"""
lambda_info = dict()
function_name = module.params.get('function_name')
if function_name:
try:
lambda_info.update(client.get_function_configuration(FunctionName=function_name))
except ClientError as e:
if e.response['Error']['Code'] == 'ResourceNotFoundException':
lambda_info.update(function={})
else:
module.fail_json_aws(e, msg="Trying to get {0} configuration".format(function_name))
else:
params = dict()
if module.params.get('max_items'):
params['MaxItems'] = module.params.get('max_items')
if module.params.get('next_marker'):
params['Marker'] = module.params.get('next_marker')
try:
lambda_info.update(function_list=client.list_functions(**params)['Functions'])
except ClientError as e:
if e.response['Error']['Code'] == 'ResourceNotFoundException':
lambda_info.update(function_list=[])
else:
module.fail_json_aws(e, msg="Trying to get function list")
functions = dict()
for func in lambda_info.pop('function_list', []):
functions[func['FunctionName']] = camel_dict_to_snake_dict(func)
return functions
return {function_name: camel_dict_to_snake_dict(lambda_info)}
def mapping_details(client, module):
"""
Returns all lambda event source mappings.
:param client: AWS API client reference (boto3)
:param module: Ansible module reference
:return dict:
"""
lambda_info = dict()
params = dict()
function_name = module.params.get('function_name')
if function_name:
params['FunctionName'] = module.params.get('function_name')
if module.params.get('event_source_arn'):
params['EventSourceArn'] = module.params.get('event_source_arn')
if module.params.get('max_items'):
params['MaxItems'] = module.params.get('max_items')
if module.params.get('next_marker'):
params['Marker'] = module.params.get('next_marker')
try:
lambda_info.update(mappings=client.list_event_source_mappings(**params)['EventSourceMappings'])
except ClientError as e:
if e.response['Error']['Code'] == 'ResourceNotFoundException':
lambda_info.update(mappings=[])
else:
module.fail_json_aws(e, msg="Trying to get source event mappings")
if function_name:
return {function_name: camel_dict_to_snake_dict(lambda_info)}
return camel_dict_to_snake_dict(lambda_info)
def policy_details(client, module):
"""
Returns policy attached to a lambda function.
:param client: AWS API client reference (boto3)
:param module: Ansible module reference
:return dict:
"""
if module.params.get('max_items') or module.params.get('next_marker'):
module.fail_json(msg='Cannot specify max_items nor next_marker for query=policy.')
lambda_info = dict()
function_name = module.params.get('function_name')
if function_name:
try:
# get_policy returns a JSON string so must convert to dict before reassigning to its key
lambda_info.update(policy=json.loads(client.get_policy(FunctionName=function_name)['Policy']))
except ClientError as e:
if e.response['Error']['Code'] == 'ResourceNotFoundException':
lambda_info.update(policy={})
else:
module.fail_json_aws(e, msg="Trying to get {0} policy".format(function_name))
else:
module.fail_json(msg='Parameter function_name required for query=policy.')
return {function_name: camel_dict_to_snake_dict(lambda_info)}
def version_details(client, module):
"""
Returns all lambda function versions.
:param client: AWS API client reference (boto3)
:param module: Ansible module reference
:return dict:
"""
lambda_info = dict()
function_name = module.params.get('function_name')
if function_name:
params = dict()
if module.params.get('max_items'):
params['MaxItems'] = module.params.get('max_items')
if module.params.get('next_marker'):
params['Marker'] = module.params.get('next_marker')
try:
lambda_info.update(versions=client.list_versions_by_function(FunctionName=function_name, **params)['Versions'])
except ClientError as e:
if e.response['Error']['Code'] == 'ResourceNotFoundException':
lambda_info.update(versions=[])
else:
module.fail_json_aws(e, msg="Trying to get {0} versions".format(function_name))
else:
module.fail_json(msg='Parameter function_name required for query=versions.')
return {function_name: camel_dict_to_snake_dict(lambda_info)}
def main():
"""
Main entry point.
:return dict: ansible facts
"""
argument_spec = dict(
function_name=dict(required=False, default=None, aliases=['function', 'name']),
query=dict(required=False, choices=['aliases', 'all', 'config', 'mappings', 'policy', 'versions'], default='all'),
event_source_arn=dict(required=False, default=None)
)
module = AnsibleAWSModule(
argument_spec=argument_spec,
supports_check_mode=True,
mutually_exclusive=[],
required_together=[]
)
# validate function_name if present
function_name = module.params['function_name']
if function_name:
if not re.search(r"^[\w\-:]+$", function_name):
module.fail_json(
msg='Function name {0} is invalid. Names must contain only alphanumeric characters and hyphens.'.format(function_name)
)
if len(function_name) > 64:
module.fail_json(msg='Function name "{0}" exceeds 64 character limit'.format(function_name))
try:
region, endpoint, aws_connect_kwargs = get_aws_connection_info(module, boto3=True)
aws_connect_kwargs.update(dict(region=region,
endpoint=endpoint,
conn_type='client',
resource='lambda'
))
client = boto3_conn(module, **aws_connect_kwargs)
except ClientError as e:
module.fail_json_aws(e, "trying to set up boto connection")
this_module = sys.modules[__name__]
invocations = dict(
aliases='alias_details',
all='all_details',
config='config_details',
mappings='mapping_details',
policy='policy_details',
versions='version_details',
)
this_module_function = getattr(this_module, invocations[module.params['query']])
all_facts = fix_return(this_module_function(client, module))
results = dict(function=all_facts, changed=False)
if module.check_mode:
results['msg'] = 'Check mode set but ignored for fact gathering only.'
module.exit_json(**results)
if __name__ == '__main__':
main()

View file

@ -15,7 +15,7 @@ description:
- This module allows the management of AWS Lambda policy statements.
It is idempotent and supports "Check" mode. Use module M(lambda) to manage the lambda
function itself, M(lambda_alias) to manage function aliases, M(lambda_event) to manage event source mappings
such as Kinesis streams, M(execute_lambda) to execute a lambda function and M(lambda_facts) to gather facts
such as Kinesis streams, M(execute_lambda) to execute a lambda function and M(lambda_info) to gather information
relating to one or more lambda functions.
version_added: "2.4"

View file

@ -101,9 +101,9 @@ EXAMPLES = r'''
register: sls
# The cloudformation stack is always named the same as the full service, so the
# cloudformation_facts module can get a full list of the stack resources, as
# cloudformation_info module can get a full list of the stack resources, as
# well as stack events and outputs
- cloudformation_facts:
- cloudformation_info:
region: us-east-1
stack_name: '{{ sls.service_name }}'
stack_resources: true

View file

@ -1,5 +1,5 @@
cloud/aws
ecs_service_facts
ecs_service_info
ecs_task
ecs_taskdefinition
ecs_taskdefinition_info

View file

@ -569,61 +569,61 @@
register: ecs_service_creation_replica
- name: obtain facts for all ECS services in the cluster
ecs_service_facts:
ecs_service_info:
cluster: "{{ ecs_cluster_name }}"
details: yes
events: no
<<: *aws_connection_info
register: ecs_service_facts
register: ecs_service_info
- name: assert that facts are useful
assert:
that:
- "'services' in ecs_service_facts"
- ecs_service_facts.services | length > 0
- "'events' not in ecs_service_facts.services[0]"
- "'services' in ecs_service_info"
- ecs_service_info.services | length > 0
- "'events' not in ecs_service_info.services[0]"
- name: obtain facts for existing service in the cluster
ecs_service_facts:
ecs_service_info:
cluster: "{{ ecs_cluster_name }}"
service: "{{ ecs_service_name }}"
details: yes
events: no
<<: *aws_connection_info
register: ecs_service_facts
register: ecs_service_info
- name: assert that existing service is available and running
assert:
that:
- "ecs_service_facts.services|length == 1"
- "ecs_service_facts.services_not_running|length == 0"
- "ecs_service_info.services|length == 1"
- "ecs_service_info.services_not_running|length == 0"
- name: obtain facts for non-existent service in the cluster
ecs_service_facts:
ecs_service_info:
cluster: "{{ ecs_cluster_name }}"
service: madeup
details: yes
events: no
<<: *aws_connection_info
register: ecs_service_facts
register: ecs_service_info
- name: assert that non-existent service is missing
assert:
that:
- "ecs_service_facts.services_not_running[0].reason == 'MISSING'"
- "ecs_service_info.services_not_running[0].reason == 'MISSING'"
- name: obtain specific ECS service facts
ecs_service_facts:
ecs_service_info:
service: "{{ ecs_service_name }}2"
cluster: "{{ ecs_cluster_name }}"
details: yes
<<: *aws_connection_info
register: ecs_service_facts
register: ecs_service_info
- name: check that facts contain network configuration
assert:
that:
- "'networkConfiguration' in ecs_service_facts.ansible_facts.services[0]"
- "'networkConfiguration' in ecs_service_info.ansible_facts.services[0]"
- name: attempt to get facts from missing task definition
ecs_taskdefinition_info:
@ -783,24 +783,24 @@
msg: "***** TESTING COMPLETE. COMMENCE TEARDOWN *****"
- name: obtain ECS service facts
ecs_service_facts:
ecs_service_info:
service: "{{ ecs_service_name }}"
cluster: "{{ ecs_cluster_name }}"
details: yes
<<: *aws_connection_info
register: ecs_service_facts
register: ecs_service_info
- name: scale down ECS service
ecs_service:
state: present
name: "{{ ecs_service_name }}"
cluster: "{{ ecs_cluster_name }}"
task_definition: "{{ ecs_service_facts.ansible_facts.services[0].taskDefinition }}"
task_definition: "{{ ecs_service_info.ansible_facts.services[0].taskDefinition }}"
desired_count: 0
deployment_configuration: "{{ ecs_service_deployment_configuration }}"
placement_strategy: "{{ ecs_service_placement_strategy }}"
load_balancers:
- targetGroupArn: "{{ ecs_service_facts.ansible_facts.services[0].loadBalancers[0].targetGroupArn }}"
- targetGroupArn: "{{ ecs_service_info.ansible_facts.services[0].loadBalancers[0].targetGroupArn }}"
containerName: "{{ ecs_task_name }}"
containerPort: "{{ ecs_task_container_port }}"
<<: *aws_connection_info
@ -808,25 +808,25 @@
register: ecs_service_scale_down
- name: obtain second ECS service facts
ecs_service_facts:
ecs_service_info:
service: "{{ ecs_service_name }}2"
cluster: "{{ ecs_cluster_name }}"
details: yes
<<: *aws_connection_info
ignore_errors: yes
register: ecs_service_facts
register: ecs_service_info
- name: scale down second ECS service
ecs_service:
state: present
name: "{{ ecs_service_name }}2"
cluster: "{{ ecs_cluster_name }}"
task_definition: "{{ ecs_service_facts.ansible_facts.services[0].taskDefinition }}"
task_definition: "{{ ecs_service_info.ansible_facts.services[0].taskDefinition }}"
desired_count: 0
deployment_configuration: "{{ ecs_service_deployment_configuration }}"
placement_strategy: "{{ ecs_service_placement_strategy }}"
load_balancers:
- targetGroupArn: "{{ ecs_service_facts.ansible_facts.services[0].loadBalancers[0].targetGroupArn }}"
- targetGroupArn: "{{ ecs_service_info.ansible_facts.services[0].loadBalancers[0].targetGroupArn }}"
containerName: "{{ ecs_task_name }}"
containerPort: "{{ ecs_task_container_port }}"
<<: *aws_connection_info

View file

@ -1,3 +1,3 @@
cloud/aws
unsupported
efs_facts
efs_info

View file

@ -71,17 +71,17 @@
# ============================================================
- name: Get all EFS Facts
efs_facts:
efs_info:
<<: *aws_connection_info
register: efs_result
- assert:
that:
- (efs_result.ansible_facts.efs | length) >= 1
- (efs_result.efs | length) >= 1
# ============================================================
- name: Get EFS by creation token
efs_facts:
efs_info:
name: "{{ resource_prefix }}-test-efs"
<<: *aws_connection_info
register: efs_result
@ -89,27 +89,27 @@
- set_fact:
efs_result_assertions:
- efs_result is not changed
- (efs_result.ansible_facts.efs | length) == 1
- efs_result.ansible_facts.efs[0].creation_token == "{{ resource_prefix }}-test-efs"
- efs_result.ansible_facts.efs[0].file_system_id == created_efs.efs.file_system_id
- efs_result.ansible_facts.efs[0].number_of_mount_targets == 2
- (efs_result.ansible_facts.efs[0].mount_targets | length) == 2
- efs_result.ansible_facts.efs[0].name == "{{ resource_prefix }}-test-tag"
- efs_result.ansible_facts.efs[0].tags.Name == "{{ resource_prefix }}-test-tag"
- efs_result.ansible_facts.efs[0].tags.Purpose == "file-storage"
- efs_result.ansible_facts.efs[0].encrypted == false
- efs_result.ansible_facts.efs[0].life_cycle_state == "available"
- efs_result.ansible_facts.efs[0].performance_mode == "generalPurpose"
- efs_result.ansible_facts.efs[0].throughput_mode == "bursting"
- efs_result.ansible_facts.efs[0].mount_targets[0].security_groups[0] == vpc_default_sg_id
- efs_result.ansible_facts.efs[0].mount_targets[1].security_groups[0] == vpc_default_sg_id
- (efs_result.efs | length) == 1
- efs_result.efs[0].creation_token == "{{ resource_prefix }}-test-efs"
- efs_result.efs[0].file_system_id == created_efs.efs.file_system_id
- efs_result.efs[0].number_of_mount_targets == 2
- (efs_result.efs[0].mount_targets | length) == 2
- efs_result.efs[0].name == "{{ resource_prefix }}-test-tag"
- efs_result.efs[0].tags.Name == "{{ resource_prefix }}-test-tag"
- efs_result.efs[0].tags.Purpose == "file-storage"
- efs_result.efs[0].encrypted == false
- efs_result.efs[0].life_cycle_state == "available"
- efs_result.efs[0].performance_mode == "generalPurpose"
- efs_result.efs[0].throughput_mode == "bursting"
- efs_result.efs[0].mount_targets[0].security_groups[0] == vpc_default_sg_id
- efs_result.efs[0].mount_targets[1].security_groups[0] == vpc_default_sg_id
- assert:
that: "{{efs_result_assertions}}"
# ============================================================
- name: Get EFS by id
efs_facts:
efs_info:
id: "{{created_efs.efs.file_system_id}}"
<<: *aws_connection_info
register: efs_result
@ -119,7 +119,7 @@
# ============================================================
- name: Get EFS by tag
efs_facts:
efs_info:
tags:
Name: "{{ resource_prefix }}-test-tag"
<<: *aws_connection_info
@ -130,7 +130,7 @@
# ============================================================
- name: Get EFS by target (subnet_id)
efs_facts:
efs_info:
targets:
- "{{testing_subnet_a.subnet.id}}"
<<: *aws_connection_info
@ -141,7 +141,7 @@
# ============================================================
- name: Get EFS by target (security_group_id)
efs_facts:
efs_info:
targets:
- "{{vpc_default_sg_id}}"
<<: *aws_connection_info
@ -152,7 +152,7 @@
# ============================================================
- name: Get EFS by tag and target
efs_facts:
efs_info:
tags:
Name: "{{ resource_prefix }}-test-tag"
targets:
@ -230,7 +230,7 @@
# ============================================================
- name: Check new facts with provisioned mode
efs_facts:
efs_info:
name: "{{ resource_prefix }}-test-efs"
<<: *aws_connection_info
register: efs_result
@ -238,18 +238,18 @@
- set_fact:
efs_result_assertions:
- efs_result is not changed
- efs_result.ansible_facts.efs[0].throughput_mode == "provisioned"
- efs_result.ansible_facts.efs[0].provisioned_throughput_in_mibps == 8.0
- (efs_result.ansible_facts.efs | length) == 1
- efs_result.ansible_facts.efs[0].creation_token == "{{ resource_prefix }}-test-efs"
- efs_result.ansible_facts.efs[0].file_system_id == created_efs.efs.file_system_id
- efs_result.efs[0].throughput_mode == "provisioned"
- efs_result.efs[0].provisioned_throughput_in_mibps == 8.0
- (efs_result.efs | length) == 1
- efs_result.efs[0].creation_token == "{{ resource_prefix }}-test-efs"
- efs_result.efs[0].file_system_id == created_efs.efs.file_system_id
- assert:
that: "{{efs_result_assertions}}"
# ============================================================
- name: Query unknown EFS by tag
efs_facts:
efs_info:
tags:
Name: "{{ resource_prefix }}-unknown"
<<: *aws_connection_info
@ -258,10 +258,10 @@
- assert:
that:
- efs_result is not changed
- (efs_result.ansible_facts.efs | length) == 0
- (efs_result.efs | length) == 0
- name: Query unknown EFS by target
efs_facts:
efs_info:
targets:
- sg-00000000000
<<: *aws_connection_info
@ -270,7 +270,7 @@
- assert:
that:
- efs_result is not changed
- (efs_result.ansible_facts.efs | length) == 0
- (efs_result.efs | length) == 0
# ============================================================
always:

View file

@ -93,7 +93,7 @@
region: us-east-1
aws_secret_key: foobar
block:
- aws_s3_bucket_facts:
- aws_s3_bucket_info:
ignore_errors: true
register: s3
- assert:
@ -106,7 +106,7 @@
aws_secret_key: foobar
aws_access_key: foobar
block:
- aws_s3_bucket_facts:
- aws_s3_bucket_info:
ignore_errors: true
register: s3
- assert:

View file

@ -659,7 +659,7 @@ lib/ansible/modules/cloud/amazon/aws_waf_web_acl.py validate-modules:E338
lib/ansible/modules/cloud/amazon/cloudformation.py validate-modules:E324
lib/ansible/modules/cloud/amazon/cloudformation.py validate-modules:E337
lib/ansible/modules/cloud/amazon/cloudformation.py validate-modules:E338
lib/ansible/modules/cloud/amazon/cloudformation_facts.py validate-modules:E338
lib/ansible/modules/cloud/amazon/cloudformation_info.py validate-modules:E338
lib/ansible/modules/cloud/amazon/cloudformation_stack_set.py validate-modules:E322
lib/ansible/modules/cloud/amazon/cloudformation_stack_set.py validate-modules:E337
lib/ansible/modules/cloud/amazon/cloudformation_stack_set.py validate-modules:E338
@ -670,10 +670,10 @@ lib/ansible/modules/cloud/amazon/cloudfront_distribution.py validate-modules:E32
lib/ansible/modules/cloud/amazon/cloudfront_distribution.py validate-modules:E326
lib/ansible/modules/cloud/amazon/cloudfront_distribution.py validate-modules:E337
lib/ansible/modules/cloud/amazon/cloudfront_distribution.py validate-modules:E338
lib/ansible/modules/cloud/amazon/cloudfront_facts.py future-import-boilerplate
lib/ansible/modules/cloud/amazon/cloudfront_facts.py metaclass-boilerplate
lib/ansible/modules/cloud/amazon/cloudfront_facts.py validate-modules:E323
lib/ansible/modules/cloud/amazon/cloudfront_facts.py validate-modules:E337
lib/ansible/modules/cloud/amazon/cloudfront_info.py future-import-boilerplate
lib/ansible/modules/cloud/amazon/cloudfront_info.py metaclass-boilerplate
lib/ansible/modules/cloud/amazon/cloudfront_info.py validate-modules:E323
lib/ansible/modules/cloud/amazon/cloudfront_info.py validate-modules:E337
lib/ansible/modules/cloud/amazon/cloudfront_invalidation.py future-import-boilerplate
lib/ansible/modules/cloud/amazon/cloudfront_invalidation.py metaclass-boilerplate
lib/ansible/modules/cloud/amazon/cloudfront_invalidation.py validate-modules:E324
@ -881,9 +881,9 @@ lib/ansible/modules/cloud/amazon/ecs_service.py future-import-boilerplate
lib/ansible/modules/cloud/amazon/ecs_service.py metaclass-boilerplate
lib/ansible/modules/cloud/amazon/ecs_service.py validate-modules:E337
lib/ansible/modules/cloud/amazon/ecs_service.py validate-modules:E338
lib/ansible/modules/cloud/amazon/ecs_service_facts.py validate-modules:E324
lib/ansible/modules/cloud/amazon/ecs_service_facts.py validate-modules:E337
lib/ansible/modules/cloud/amazon/ecs_service_facts.py validate-modules:E338
lib/ansible/modules/cloud/amazon/ecs_service_info.py validate-modules:E324
lib/ansible/modules/cloud/amazon/ecs_service_info.py validate-modules:E337
lib/ansible/modules/cloud/amazon/ecs_service_info.py validate-modules:E338
lib/ansible/modules/cloud/amazon/ecs_task.py validate-modules:E337
lib/ansible/modules/cloud/amazon/ecs_task.py validate-modules:E338
lib/ansible/modules/cloud/amazon/ecs_taskdefinition.py future-import-boilerplate
@ -893,9 +893,9 @@ lib/ansible/modules/cloud/amazon/ecs_taskdefinition.py validate-modules:E338
lib/ansible/modules/cloud/amazon/ecs_taskdefinition_info.py validate-modules:E337
lib/ansible/modules/cloud/amazon/efs.py pylint:blacklisted-name
lib/ansible/modules/cloud/amazon/efs.py validate-modules:E337
lib/ansible/modules/cloud/amazon/efs_facts.py pylint:blacklisted-name
lib/ansible/modules/cloud/amazon/efs_facts.py validate-modules:E337
lib/ansible/modules/cloud/amazon/efs_facts.py validate-modules:E338
lib/ansible/modules/cloud/amazon/efs_info.py pylint:blacklisted-name
lib/ansible/modules/cloud/amazon/efs_info.py validate-modules:E337
lib/ansible/modules/cloud/amazon/efs_info.py validate-modules:E338
lib/ansible/modules/cloud/amazon/elasticache.py future-import-boilerplate
lib/ansible/modules/cloud/amazon/elasticache.py metaclass-boilerplate
lib/ansible/modules/cloud/amazon/elasticache.py validate-modules:E324
@ -1001,9 +1001,12 @@ lib/ansible/modules/cloud/amazon/lambda_alias.py validate-modules:E338
lib/ansible/modules/cloud/amazon/lambda_event.py validate-modules:E317
lib/ansible/modules/cloud/amazon/lambda_event.py validate-modules:E337
lib/ansible/modules/cloud/amazon/lambda_event.py validate-modules:E338
lib/ansible/modules/cloud/amazon/lambda_facts.py future-import-boilerplate
lib/ansible/modules/cloud/amazon/lambda_facts.py metaclass-boilerplate
lib/ansible/modules/cloud/amazon/lambda_facts.py validate-modules:E338
lib/ansible/modules/cloud/amazon/_lambda_facts.py future-import-boilerplate
lib/ansible/modules/cloud/amazon/_lambda_facts.py metaclass-boilerplate
lib/ansible/modules/cloud/amazon/_lambda_facts.py validate-modules:E338
lib/ansible/modules/cloud/amazon/lambda_info.py future-import-boilerplate
lib/ansible/modules/cloud/amazon/lambda_info.py metaclass-boilerplate
lib/ansible/modules/cloud/amazon/lambda_info.py validate-modules:E338
lib/ansible/modules/cloud/amazon/lambda_policy.py future-import-boilerplate
lib/ansible/modules/cloud/amazon/lambda_policy.py metaclass-boilerplate
lib/ansible/modules/cloud/amazon/lambda_policy.py validate-modules:E337