diff --git a/modules/cloud/amazon/README.md b/modules/cloud/amazon/README.md index 58d6c6f63..8921e50b1 100644 --- a/modules/cloud/amazon/README.md +++ b/modules/cloud/amazon/README.md @@ -1,6 +1,3 @@ # Modules pending upstream release. -@see https://github.com/ansible/ansible/pull/61575/files -@see https://github.com/ansible/ansible/pull/63168/files -@see https://github.com/ansible/ansible/pull/46624/files -@see https://github.com/ansible-collections/community.aws/pull/224 +None at the moment. diff --git a/modules/cloud/amazon/ec2_eip.py b/modules/cloud/amazon/ec2_eip.py deleted file mode 100644 index ff625d356..000000000 --- a/modules/cloud/amazon/ec2_eip.py +++ /dev/null @@ -1,638 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['stableinterface'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: ec2_eip -short_description: manages EC2 elastic IP (EIP) addresses. -description: - - This module can allocate or release an EIP. - - This module can associate/disassociate an EIP with instances or network interfaces. -version_added: "1.4" -options: - device_id: - description: - - The id of the device for the EIP. Can be an EC2 Instance id or Elastic Network Interface (ENI) id. - required: false - aliases: [ instance_id ] - version_added: "2.0" - public_ip: - description: - - The IP address of a previously allocated EIP. - - If present and device is specified, the EIP is associated with the device. - - If absent and device is specified, the EIP is disassociated from the device. - aliases: [ ip ] - state: - description: - - If present, allocate an EIP or associate an existing EIP with a device. - - If absent, disassociate the EIP from the device and optionally release it. - choices: ['present', 'absent'] - default: present - in_vpc: - description: - - Allocate an EIP inside a VPC or not. Required if specifying an ENI. - default: 'no' - type: bool - version_added: "1.4" - reuse_existing_ip_allowed: - description: - - Reuse an EIP that is not associated to a device (when available), instead of allocating a new one. - default: 'no' - type: bool - version_added: "1.6" - release_on_disassociation: - description: - - whether or not to automatically release the EIP when it is disassociated - default: 'no' - type: bool - version_added: "2.0" - private_ip_address: - description: - - The primary or secondary private IP address to associate with the Elastic IP address. - version_added: "2.3" - allow_reassociation: - description: - - Specify this option to allow an Elastic IP address that is already associated with another - network interface or instance to be re-associated with the specified instance or interface. - default: 'no' - type: bool - version_added: "2.5" - tag_name: - description: - - When reuse_existing_ip_allowed is true, suppplement with this option to only reuse - an Elastic IP if it is tagged with tag_name. - default: 'no' - version_added: "2.9" - tag_value: - description: - - Supplements tag_name but also checks that the value of the tag provided in tag_name matches tag_value. - matches the tag_value - default: 'no' - version_added: "2.9" - public_ipv4_pool: - description: - - Allocates the new Elastic IP from the provided public IPv4 pool (BYOIP) - only applies to newly allocated Elastic IPs, isn't validated when reuse_existing_ip_allowed is true. - default: 'no' - version_added: "2.9" -extends_documentation_fragment: - - aws - - ec2 -author: "Rick Mendes (@rickmendes) " -notes: - - There may be a delay between the time the EIP is assigned and when - the cloud instance is reachable via the new address. Use wait_for and - pause to delay further playbook execution until the instance is reachable, - if necessary. - - This module returns multiple changed statuses on disassociation or release. - It returns an overall status based on any changes occurring. It also returns - individual changed statuses for disassociation and release. -''' - -EXAMPLES = ''' -# Note: These examples do not set authentication details, see the AWS Guide for details. - -- name: associate an elastic IP with an instance - ec2_eip: - device_id: i-1212f003 - ip: 93.184.216.119 - -- name: associate an elastic IP with a device - ec2_eip: - device_id: eni-c8ad70f3 - ip: 93.184.216.119 - -- name: associate an elastic IP with a device and allow reassociation - ec2_eip: - device_id: eni-c8ad70f3 - public_ip: 93.184.216.119 - allow_reassociation: yes - -- name: disassociate an elastic IP from an instance - ec2_eip: - device_id: i-1212f003 - ip: 93.184.216.119 - state: absent - -- name: disassociate an elastic IP with a device - ec2_eip: - device_id: eni-c8ad70f3 - ip: 93.184.216.119 - state: absent - -- name: allocate a new elastic IP and associate it with an instance - ec2_eip: - device_id: i-1212f003 - -- name: allocate a new elastic IP without associating it to anything - ec2_eip: - state: present - register: eip - -- name: output the IP - debug: - msg: "Allocated IP is {{ eip.public_ip }}" - -- name: provision new instances with ec2 - ec2: - keypair: mykey - instance_type: c1.medium - image: ami-40603AD1 - wait: yes - group: webserver - count: 3 - register: ec2 - -- name: associate new elastic IPs with each of the instances - ec2_eip: - device_id: "{{ item }}" - loop: "{{ ec2.instance_ids }}" - -- name: allocate a new elastic IP inside a VPC in us-west-2 - ec2_eip: - region: us-west-2 - in_vpc: yes - register: eip - -- name: output the IP - debug: - msg: "Allocated IP inside a VPC is {{ eip.public_ip }}" - -- name: allocate eip - reuse unallocated ips (if found) with FREE tag - ec2_eip: - region: us-east-1 - in_vpc: yes - reuse_existing_ip_allowed: yes - tag_name: FREE - -- name: allocate eip - reuse unallocted ips if tag reserved is nope - ec2_eip: - region: us-east-1 - in_vpc: yes - reuse_existing_ip_allowed: yes - tag_name: reserved - tag_value: nope - -- name: allocate new eip - from servers given ipv4 pool - ec2_eip: - region: us-east-1 - in_vpc: yes - public_ipv4_pool: ipv4pool-ec2-0588c9b75a25d1a02 - -- name: allocate eip - from a given pool (if no free addresses where dev-servers tag is dynamic) - ec2_eip: - region: us-east-1 - in_vpc: yes - reuse_existing_ip_allowed: yes - tag_name: dev-servers - public_ipv4_pool: ipv4pool-ec2-0588c9b75a25d1a02 - -- name: allocate eip from pool - check if tag reserved_for exists and value is our hostname - ec2_eip: - region: us-east-1 - in_vpc: yes - reuse_existing_ip_allowed: yes - tag_name: reserved_for - tag_value: "{{ inventory_hostname }}" - public_ipv4_pool: ipv4pool-ec2-0588c9b75a25d1a02 -''' - -RETURN = ''' -allocation_id: - description: allocation_id of the elastic ip - returned: on success - type: str - sample: eipalloc-51aa3a6c -public_ip: - description: an elastic ip address - returned: on success - type: str - sample: 52.88.159.209 -''' - -try: - import botocore.exceptions -except ImportError: - pass # Taken care of by ec2.HAS_BOTO3 - -from ansible.module_utils.aws.core import AnsibleAWSModule, is_boto3_error_code -from ansible.module_utils.ec2 import AWSRetry, ansible_dict_to_boto3_filter_list, ec2_argument_spec - - -def associate_ip_and_device(ec2, module, address, private_ip_address, device_id, allow_reassociation, check_mode, is_instance=True): - if address_is_associated_with_device(ec2, module, address, device_id, is_instance): - return {'changed': False} - - # If we're in check mode, nothing else to do - if not check_mode: - if is_instance: - try: - params = dict( - InstanceId=device_id, - PrivateIpAddress=private_ip_address, - AllowReassociation=allow_reassociation, - ) - if address['Domain'] == "vpc": - params['AllocationId'] = address['AllocationId'] - else: - params['PublicIp'] = address['PublicIp'] - res = ec2.associate_address(**params) - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - msg = "Couldn't associate Elastic IP address with instance '{0}'".format(device_id) - module.fail_json_aws(e, msg=msg) - else: - params = dict( - NetworkInterfaceId=device_id, - AllocationId=address['AllocationId'], - AllowReassociation=allow_reassociation, - ) - - if private_ip_address: - params['PrivateIpAddress'] = private_ip_address - - try: - res = ec2.associate_address(aws_retry=True, **params) - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - msg = "Couldn't associate Elastic IP address with network interface '{0}'".format(device_id) - module.fail_json_aws(e, msg=msg) - if not res: - module.fail_json_aws(e, msg='Association failed.') - - return {'changed': True} - - -def disassociate_ip_and_device(ec2, module, address, device_id, check_mode, is_instance=True): - if not address_is_associated_with_device(ec2, module, address, device_id, is_instance): - return {'changed': False} - - # If we're in check mode, nothing else to do - if not check_mode: - try: - if address['Domain'] == 'vpc': - res = ec2.disassociate_address( - AssociationId=address['AssociationId'], aws_retry=True - ) - else: - res = ec2.disassociate_address( - PublicIp=address['PublicIp'], aws_retry=True - ) - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - module.fail_json_aws(e, msg="Dissassociation of Elastic IP failed") - - return {'changed': True} - - -@AWSRetry.jittered_backoff() -def find_address(ec2, module, public_ip, device_id, is_instance=True): - """ Find an existing Elastic IP address """ - filters = [] - kwargs = {} - - if public_ip: - kwargs["PublicIps"] = [public_ip] - elif device_id: - if is_instance: - filters.append({"Name": 'instance-id', "Values": [device_id]}) - else: - filters.append({'Name': 'network-interface-id', "Values": [device_id]}) - - if len(filters) > 0: - kwargs["Filters"] = filters - elif len(filters) == 0 and public_ip is None: - return None - - try: - addresses = ec2.describe_addresses(**kwargs) - except is_boto3_error_code('InvalidAddress.NotFound') as e: - module.fail_json_aws(e, msg="Couldn't obtain list of existing Elastic IP addresses") - - addresses = addresses["Addresses"] - if len(addresses) == 1: - return addresses[0] - elif len(addresses) > 1: - msg = "Found more than one address using args {0}".format(kwargs) - msg += "Addresses found: {0}".format(addresses) - module.fail_json_aws(botocore.exceptions.ClientError, msg=msg) - - -def address_is_associated_with_device(ec2, module, address, device_id, is_instance=True): - """ Check if the elastic IP is currently associated with the device """ - address = find_address(ec2, module, address["PublicIp"], device_id, is_instance) - if address: - if is_instance: - if "InstanceId" in address and address["InstanceId"] == device_id: - return address - else: - if "NetworkInterfaceId" in address and address["NetworkInterfaceId"] == device_id: - return address - return False - - -def allocate_address(ec2, module, domain, reuse_existing_ip_allowed, check_mode, tag_dict=None, public_ipv4_pool=None): - """ Allocate a new elastic IP address (when needed) and return it """ - if reuse_existing_ip_allowed: - filters = [] - if not domain: - domain = 'standard' - filters.append({'Name': 'domain', "Values": [domain]}) - - if tag_dict is not None: - filters += ansible_dict_to_boto3_filter_list(tag_dict) - - try: - all_addresses = ec2.describe_addresses(Filters=filters, aws_retry=True) - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - module.fail_json_aws(e, msg="Couldn't obtain list of existing Elastic IP addresses") - - all_addresses = all_addresses["Addresses"] - - if domain == 'vpc': - unassociated_addresses = [a for a in all_addresses - if not a.get('AssociationId', None)] - else: - unassociated_addresses = [a for a in all_addresses - if not a['InstanceId']] - if unassociated_addresses: - return unassociated_addresses[0], False - - if public_ipv4_pool: - return allocate_address_from_pool(ec2, module, domain, check_mode, public_ipv4_pool), True - - try: - result = ec2.allocate_address(Domain=domain, aws_retry=True), True - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - module.fail_json_aws(e, msg="Couldn't allocate Elastic IP address") - return result - - -def release_address(ec2, module, address, check_mode): - """ Release a previously allocated elastic IP address """ - - # If we're in check mode, nothing else to do - if not check_mode: - try: - result = ec2.release_address(AllocationId=address['AllocationId'], aws_retry=True) - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - module.fail_json_aws(e, msg="Couldn't release Elastic IP address") - - return {'changed': True} - - -@AWSRetry.jittered_backoff() -def describe_eni_with_backoff(ec2, module, device_id): - try: - return ec2.describe_network_interfaces(NetworkInterfaceIds=[device_id]) - except is_boto3_error_code('InvalidNetworkInterfaceID.NotFound') as e: - module.fail_json_aws(e, msg="Couldn't get list of network interfaces.") - - -def find_device(ec2, module, device_id, is_instance=True): - """ Attempt to find the EC2 instance and return it """ - - if is_instance: - try: - paginator = ec2.get_paginator('describe_instances') - reservations = list(paginator.paginate(InstanceIds=[device_id]).search('Reservations[]')) - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - module.fail_json_aws(e, msg="Couldn't get list of instances") - - if len(reservations) == 1: - instances = reservations[0]['Instances'] - if len(instances) == 1: - return instances[0] - else: - try: - interfaces = describe_eni_with_backoff(ec2, module, device_id) - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - module.fail_json_aws(e, msg="Couldn't get list of network interfaces.") - if len(interfaces) == 1: - return interfaces[0] - - -def ensure_present(ec2, module, domain, address, private_ip_address, device_id, - reuse_existing_ip_allowed, allow_reassociation, check_mode, is_instance=True): - changed = False - - # Return the EIP object since we've been given a public IP - if not address: - if check_mode: - return {'changed': True} - - address, changed = allocate_address(ec2, module, domain, reuse_existing_ip_allowed, check_mode) - - if device_id: - # Allocate an IP for instance since no public_ip was provided - if is_instance: - instance = find_device(ec2, module, device_id) - if reuse_existing_ip_allowed: - if instance.vpc_id and len(instance.vpc_id) > 0 and domain is None: - msg = "You must set 'in_vpc' to true to associate an instance with an existing ip in a vpc" - module.fail_json_aws(botocore.exceptions.ClientError, msg=msg) - - # Associate address object (provided or allocated) with instance - assoc_result = associate_ip_and_device( - ec2, module, address, private_ip_address, device_id, allow_reassociation, - check_mode - ) - else: - instance = find_device(ec2, module, device_id, is_instance=False) - # Associate address object (provided or allocated) with instance - assoc_result = associate_ip_and_device( - ec2, module, address, private_ip_address, device_id, allow_reassociation, - check_mode, is_instance=False - ) - - changed = changed or assoc_result['changed'] - - return {'changed': changed, 'public_ip': address['PublicIp'], 'allocation_id': address['AllocationId']} - - -def ensure_absent(ec2, module, address, device_id, check_mode, is_instance=True): - if not address: - return {'changed': False} - - # disassociating address from instance - if device_id: - if is_instance: - return disassociate_ip_and_device( - ec2, module, address, device_id, check_mode - ) - else: - return disassociate_ip_and_device( - ec2, module, address, device_id, check_mode, is_instance=False - ) - # releasing address - else: - return release_address(ec2, module, address, check_mode) - - -def allocate_address_from_pool(ec2, module, domain, check_mode, public_ipv4_pool): - # type: (EC2Connection, str, bool, str) -> Address - """ Overrides boto's allocate_address function to support BYOIP """ - params = {} - - if domain is not None: - params['Domain'] = domain - - if public_ipv4_pool is not None: - params['PublicIpv4Pool'] = public_ipv4_pool - - if check_mode: - params['DryRun'] = 'true' - - try: - result = ec2.allocate_address(aws_retry=True, **params) - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - module.fail_json_aws(e, msg="Couldn't allocate Elastic IP address") - return result - - -def generate_tag_dict(module, tag_name, tag_value): - # type: (AnsibleModule, str, str) -> Optional[Dict] - """ Generates a dictionary to be passed as a filter to Amazon """ - if tag_name and not tag_value: - if tag_name.startswith('tag:'): - tag_name = tag_name.strip('tag:') - return {'tag-key': tag_name} - - elif tag_name and tag_value: - if not tag_name.startswith('tag:'): - tag_name = 'tag:' + tag_name - return {tag_name: tag_value} - - elif tag_value and not tag_name: - module.fail_json(msg="parameters are required together: ('tag_name', 'tag_value')") - - -def main(): - argument_spec = ec2_argument_spec() - argument_spec.update(dict( - device_id=dict(required=False, aliases=['instance_id']), - public_ip=dict(required=False, aliases=['ip']), - state=dict(required=False, default='present', - choices=['present', 'absent']), - in_vpc=dict(required=False, type='bool', default=False), - reuse_existing_ip_allowed=dict(required=False, type='bool', - default=False), - release_on_disassociation=dict(required=False, type='bool', default=False), - allow_reassociation=dict(type='bool', default=False), - wait_timeout=dict(default=300, type='int'), - private_ip_address=dict(), - tag_name=dict(), - tag_value=dict(), - public_ipv4_pool=dict() - )) - - module = AnsibleAWSModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_by={ - 'private_ip_address': ['device_id'], - }, - ) - - ec2 = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) - - device_id = module.params.get('device_id') - instance_id = module.params.get('instance_id') - public_ip = module.params.get('public_ip') - private_ip_address = module.params.get('private_ip_address') - state = module.params.get('state') - in_vpc = module.params.get('in_vpc') - domain = 'vpc' if in_vpc else None - reuse_existing_ip_allowed = module.params.get('reuse_existing_ip_allowed') - release_on_disassociation = module.params.get('release_on_disassociation') - allow_reassociation = module.params.get('allow_reassociation') - tag_name = module.params.get('tag_name') - tag_value = module.params.get('tag_value') - public_ipv4_pool = module.params.get('public_ipv4_pool') - - if instance_id: - warnings = ["instance_id is no longer used, please use device_id going forward"] - is_instance = True - device_id = instance_id - else: - if device_id and device_id.startswith('i-'): - is_instance = True - elif device_id: - if device_id.startswith('eni-') and not in_vpc: - module.fail_json(msg="If you are specifying an ENI, in_vpc must be true") - is_instance = False - - tag_dict = generate_tag_dict(module, tag_name, tag_value) - - try: - if device_id: - address = find_address(ec2, module, public_ip, device_id, is_instance=is_instance) - else: - address = find_address(ec2, module, public_ip, None) - - if state == 'present': - if device_id: - result = ensure_present( - ec2, module, domain, address, private_ip_address, device_id, - reuse_existing_ip_allowed, allow_reassociation, - module.check_mode, is_instance=is_instance - ) - else: - if address: - changed = False - else: - address, changed = allocate_address( - ec2, module, domain, reuse_existing_ip_allowed, - module.check_mode, tag_dict, public_ipv4_pool - ) - result = { - 'changed': changed, - 'public_ip': address['PublicIp'], - 'allocation_id': address['AllocationId'] - } - else: - if device_id: - disassociated = ensure_absent( - ec2, module, address, device_id, module.check_mode, is_instance=is_instance - ) - - if release_on_disassociation and disassociated['changed']: - released = release_address(ec2, module, address, module.check_mode) - result = { - 'changed': True, - 'disassociated': disassociated, - 'released': released - } - else: - result = { - 'changed': disassociated['changed'], - 'disassociated': disassociated, - 'released': {'changed': False} - } - else: - released = release_address(ec2, module, address, module.check_mode) - result = { - 'changed': released['changed'], - 'disassociated': {'changed': False}, - 'released': released - } - - except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - module.fail_json_aws(str(e)) - - if instance_id: - result['warnings'] = warnings - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/modules/cloud/amazon/ec2_vpc_nat_gateway.py b/modules/cloud/amazon/ec2_vpc_nat_gateway.py deleted file mode 100644 index 407043a0b..000000000 --- a/modules/cloud/amazon/ec2_vpc_nat_gateway.py +++ /dev/null @@ -1,1097 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: ec2_vpc_nat_gateway -short_description: Manage AWS VPC NAT Gateways. -description: - - Ensure the state of AWS VPC NAT Gateways based on their id, allocation and subnet ids. -version_added: "2.2" -requirements: [boto3, botocore] -options: - state: - description: - - Ensure NAT Gateway is present or absent. - default: "present" - choices: ["present", "absent"] - nat_gateway_id: - description: - - The id AWS dynamically allocates to the NAT Gateway on creation. - This is required when the absent option is present. - subnet_id: - description: - - The id of the subnet to create the NAT Gateway in. This is required - with the present option. - allocation_id: - description: - - The id of the elastic IP allocation. If this is not passed and the - eip_address is not passed. An EIP is generated for this NAT Gateway. - eip_address: - description: - - The elastic IP address of the EIP you want attached to this NAT Gateway. - If this is not passed and the allocation_id is not passed, - an EIP is generated for this NAT Gateway. - if_exist_do_not_create: - description: - - if a NAT Gateway exists already in the subnet_id, then do not create a new one. - required: false - default: false - type: bool - release_eip: - description: - - Deallocate the EIP from the VPC. - - Option is only valid with the absent state. - - You should use this with the wait option. Since you can not release an address while a delete operation is happening. - default: 'yes' - type: bool - wait: - description: - - Wait for operation to complete before returning. - default: 'no' - type: bool - wait_timeout: - description: - - How many seconds to wait for an operation to complete before timing out. - default: 300 - client_token: - description: - - Optional unique token to be used during create to ensure idempotency. - When specifying this option, ensure you specify the eip_address parameter - as well otherwise any subsequent runs will fail. - tags: - description: - - A dictionary of tags to add to the new image; '{"key":"value"}' and '{"key":"value","key":"value"}' - version_added: "2.10" - purge_tags: - description: Whether to remove existing tags that aren't passed in the C(tags) parameter - version_added: "2.10" - default: "no" - type: bool -author: - - Allen Sanabria (@linuxdynasty) - - Jon Hadfield (@jonhadfield) - - Karen Cheng (@Etherdaemon) -extends_documentation_fragment: - - aws - - ec2 -''' - -EXAMPLES = ''' -# Note: These examples do not set authentication details, see the AWS Guide for details. - -- name: Create new nat gateway with client token. - ec2_vpc_nat_gateway: - state: present - subnet_id: subnet-12345678 - eip_address: 52.1.1.1 - region: ap-southeast-2 - client_token: abcd-12345678 - register: new_nat_gateway - -- name: Create new nat gateway using an allocation-id. - ec2_vpc_nat_gateway: - state: present - subnet_id: subnet-12345678 - allocation_id: eipalloc-12345678 - region: ap-southeast-2 - register: new_nat_gateway - -- name: Create new nat gateway, using an EIP address and wait for available status. - ec2_vpc_nat_gateway: - state: present - subnet_id: subnet-12345678 - eip_address: 52.1.1.1 - wait: yes - region: ap-southeast-2 - register: new_nat_gateway - -- name: Create new nat gateway and allocate new EIP. - ec2_vpc_nat_gateway: - state: present - subnet_id: subnet-12345678 - wait: yes - region: ap-southeast-2 - register: new_nat_gateway - -- name: Create new nat gateway and allocate new EIP if a nat gateway does not yet exist in the subnet. - ec2_vpc_nat_gateway: - state: present - subnet_id: subnet-12345678 - wait: yes - region: ap-southeast-2 - if_exist_do_not_create: true - register: new_nat_gateway - -- name: Delete nat gateway using discovered nat gateways from facts module. - ec2_vpc_nat_gateway: - state: absent - region: ap-southeast-2 - wait: yes - nat_gateway_id: "{{ item.NatGatewayId }}" - release_eip: yes - register: delete_nat_gateway_result - loop: "{{ gateways_to_remove.result }}" - -- name: Delete nat gateway and wait for deleted status. - ec2_vpc_nat_gateway: - state: absent - nat_gateway_id: nat-12345678 - wait: yes - wait_timeout: 500 - region: ap-southeast-2 - -- name: Delete nat gateway and release EIP. - ec2_vpc_nat_gateway: - state: absent - nat_gateway_id: nat-12345678 - release_eip: yes - wait: yes - wait_timeout: 300 - region: ap-southeast-2 -''' - -RETURN = ''' -create_time: - description: The ISO 8601 date time format in UTC. - returned: In all cases. - type: str - sample: "2016-03-05T05:19:20.282000+00:00'" -nat_gateway_id: - description: id of the VPC NAT Gateway - returned: In all cases. - type: str - sample: "nat-0d1e3a878585988f8" -subnet_id: - description: id of the Subnet - returned: In all cases. - type: str - sample: "subnet-12345" -state: - description: The current state of the NAT Gateway. - returned: In all cases. - type: str - sample: "available" -vpc_id: - description: id of the VPC. - returned: In all cases. - type: str - sample: "vpc-12345" -nat_gateway_addresses: - description: List of dictionairies containing the public_ip, network_interface_id, private_ip, and allocation_id. - returned: In all cases. - type: str - sample: [ - { - 'public_ip': '52.52.52.52', - 'network_interface_id': 'eni-12345', - 'private_ip': '10.0.0.100', - 'allocation_id': 'eipalloc-12345' - } - ] -tags: - description: A dictionary of tags assigned to gateway - returned: In all cases. - type: dict - sample: { - "Env": "devel", - "Name": "nat-server" - } -''' - -import datetime -import random -import time -import traceback - -try: - import botocore -except ImportError: - pass # caught by imported ec2.HAS_BOTO3 - -from ansible.module_utils._text import to_native -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.aws.core import AnsibleAWSModule -from ansible.module_utils.ec2 import (ec2_argument_spec, get_aws_connection_info, boto3_conn, - camel_dict_to_snake_dict, compare_aws_tags, - ansible_dict_to_boto3_tag_list, boto3_tag_list_to_ansible_dict, - AWSRetry) - -DRY_RUN_GATEWAYS = [ - { - "nat_gateway_id": "nat-123456789", - "subnet_id": "subnet-123456789", - "nat_gateway_addresses": [ - { - "public_ip": "55.55.55.55", - "network_interface_id": "eni-1234567", - "private_ip": "10.0.0.102", - "allocation_id": "eipalloc-1234567" - } - ], - "state": "available", - "create_time": "2016-03-05T05:19:20.282000+00:00", - "vpc_id": "vpc-12345678" - } -] - -DRY_RUN_ALLOCATION_UNCONVERTED = { - 'Addresses': [ - { - 'PublicIp': '55.55.55.55', - 'Domain': 'vpc', - 'AllocationId': 'eipalloc-1234567' - } - ] -} - -DRY_RUN_MSGS = 'DryRun Mode:' - - -def get_nat_gateways(client, subnet_id=None, nat_gateway_id=None, - states=None, check_mode=False): - """Retrieve a list of NAT Gateways - Args: - client (botocore.client.EC2): Boto3 client - - Kwargs: - subnet_id (str): The subnet_id the nat resides in. - nat_gateway_id (str): The Amazon nat id. - states (list): States available (pending, failed, available, deleting, and deleted) - default=None - - Basic Usage: - >>> client = boto3.client('ec2') - >>> subnet_id = 'subnet-12345678' - >>> get_nat_gateways(client, subnet_id) - [ - true, - "", - { - "nat_gateway_id": "nat-123456789", - "subnet_id": "subnet-123456789", - "nat_gateway_addresses": [ - { - "public_ip": "55.55.55.55", - "network_interface_id": "eni-1234567", - "private_ip": "10.0.0.102", - "allocation_id": "eipalloc-1234567" - } - ], - "state": "deleted", - "create_time": "2016-03-05T00:33:21.209000+00:00", - "delete_time": "2016-03-05T00:36:37.329000+00:00", - "vpc_id": "vpc-12345678" - } - - Returns: - Tuple (bool, str, list) - """ - params = dict() - err_msg = "" - gateways_retrieved = False - existing_gateways = list() - if not states: - states = ['available', 'pending'] - if nat_gateway_id: - params['NatGatewayIds'] = [nat_gateway_id] - else: - params['Filter'] = [ - { - 'Name': 'subnet-id', - 'Values': [subnet_id] - }, - { - 'Name': 'state', - 'Values': states - } - ] - - try: - if not check_mode: - gateways = client.describe_nat_gateways(**params)['NatGateways'] - if gateways: - for gw in gateways: - existing_gateways.append(camel_dict_to_snake_dict(gw)) - gateways_retrieved = True - else: - gateways_retrieved = True - if nat_gateway_id: - if DRY_RUN_GATEWAYS[0]['nat_gateway_id'] == nat_gateway_id: - existing_gateways = DRY_RUN_GATEWAYS - elif subnet_id: - if DRY_RUN_GATEWAYS[0]['subnet_id'] == subnet_id: - existing_gateways = DRY_RUN_GATEWAYS - err_msg = '{0} Retrieving gateways'.format(DRY_RUN_MSGS) - - except botocore.exceptions.ClientError as e: - err_msg = str(e) - - return gateways_retrieved, err_msg, existing_gateways - - -def wait_for_status(client, wait_timeout, nat_gateway_id, status, - check_mode=False): - """Wait for the NAT Gateway to reach a status - Args: - client (botocore.client.EC2): Boto3 client - wait_timeout (int): Number of seconds to wait, until this timeout is reached. - nat_gateway_id (str): The Amazon nat id. - status (str): The status to wait for. - examples. status=available, status=deleted - - Basic Usage: - >>> client = boto3.client('ec2') - >>> subnet_id = 'subnet-12345678' - >>> allocation_id = 'eipalloc-12345678' - >>> wait_for_status(client, subnet_id, allocation_id) - [ - true, - "", - { - "nat_gateway_id": "nat-123456789", - "subnet_id": "subnet-1234567", - "nat_gateway_addresses": [ - { - "public_ip": "55.55.55.55", - "network_interface_id": "eni-1234567", - "private_ip": "10.0.0.102", - "allocation_id": "eipalloc-12345678" - } - ], - "state": "deleted", - "create_time": "2016-03-05T00:33:21.209000+00:00", - "delete_time": "2016-03-05T00:36:37.329000+00:00", - "vpc_id": "vpc-12345677" - } - ] - - Returns: - Tuple (bool, str, dict) - """ - polling_increment_secs = 5 - wait_timeout = time.time() + wait_timeout - status_achieved = False - nat_gateway = dict() - states = ['pending', 'failed', 'available', 'deleting', 'deleted'] - err_msg = "" - - while wait_timeout > time.time(): - try: - gws_retrieved, err_msg, nat_gateways = ( - get_nat_gateways( - client, nat_gateway_id=nat_gateway_id, - states=states, check_mode=check_mode - ) - ) - if gws_retrieved and nat_gateways: - nat_gateway = nat_gateways[0] - if check_mode: - nat_gateway['state'] = status - - if nat_gateway.get('state') == status: - status_achieved = True - break - - elif nat_gateway.get('state') == 'failed': - err_msg = nat_gateway.get('failure_message') - break - - elif nat_gateway.get('state') == 'pending': - if 'failure_message' in nat_gateway: - err_msg = nat_gateway.get('failure_message') - status_achieved = False - break - - else: - time.sleep(polling_increment_secs) - - except botocore.exceptions.ClientError as e: - err_msg = str(e) - - if not status_achieved: - err_msg = "Wait time out reached, while waiting for results" - - return status_achieved, err_msg, nat_gateway - - -def gateway_in_subnet_exists(client, subnet_id, allocation_id=None, - check_mode=False): - """Retrieve all NAT Gateways for a subnet. - Args: - subnet_id (str): The subnet_id the nat resides in. - - Kwargs: - allocation_id (str): The EIP Amazon identifier. - default = None - - Basic Usage: - >>> client = boto3.client('ec2') - >>> subnet_id = 'subnet-1234567' - >>> allocation_id = 'eipalloc-1234567' - >>> gateway_in_subnet_exists(client, subnet_id, allocation_id) - ( - [ - { - "nat_gateway_id": "nat-123456789", - "subnet_id": "subnet-123456789", - "nat_gateway_addresses": [ - { - "public_ip": "55.55.55.55", - "network_interface_id": "eni-1234567", - "private_ip": "10.0.0.102", - "allocation_id": "eipalloc-1234567" - } - ], - "state": "deleted", - "create_time": "2016-03-05T00:33:21.209000+00:00", - "delete_time": "2016-03-05T00:36:37.329000+00:00", - "vpc_id": "vpc-1234567" - } - ], - False - ) - - Returns: - Tuple (list, bool) - """ - allocation_id_exists = False - gateways = [] - states = ['available', 'pending'] - gws_retrieved, _, gws = ( - get_nat_gateways( - client, subnet_id, states=states, check_mode=check_mode - ) - ) - if not gws_retrieved: - return gateways, allocation_id_exists - for gw in gws: - for address in gw['nat_gateway_addresses']: - if allocation_id: - if address.get('allocation_id') == allocation_id: - allocation_id_exists = True - gateways.append(gw) - else: - gateways.append(gw) - - return gateways, allocation_id_exists - - -def get_eip_allocation_id_by_address(client, eip_address, check_mode=False): - """Release an EIP from your EIP Pool - Args: - client (botocore.client.EC2): Boto3 client - eip_address (str): The Elastic IP Address of the EIP. - - Kwargs: - check_mode (bool): if set to true, do not run anything and - falsify the results. - - Basic Usage: - >>> client = boto3.client('ec2') - >>> eip_address = '52.87.29.36' - >>> get_eip_allocation_id_by_address(client, eip_address) - 'eipalloc-36014da3' - - Returns: - Tuple (str, str) - """ - params = { - 'PublicIps': [eip_address], - } - allocation_id = None - err_msg = "" - try: - if not check_mode: - allocations = client.describe_addresses(**params)['Addresses'] - if len(allocations) == 1: - allocation = allocations[0] - else: - allocation = None - else: - dry_run_eip = ( - DRY_RUN_ALLOCATION_UNCONVERTED['Addresses'][0]['PublicIp'] - ) - if dry_run_eip == eip_address: - allocation = DRY_RUN_ALLOCATION_UNCONVERTED['Addresses'][0] - else: - allocation = None - if allocation: - if allocation.get('Domain') != 'vpc': - err_msg = ( - "EIP {0} is a non-VPC EIP, please allocate a VPC scoped EIP" - .format(eip_address) - ) - else: - allocation_id = allocation.get('AllocationId') - else: - err_msg = ( - "EIP {0} does not exist".format(eip_address) - ) - - except botocore.exceptions.ClientError as e: - err_msg = str(e) - - return allocation_id, err_msg - - -def allocate_eip_address(client, check_mode=False): - """Release an EIP from your EIP Pool - Args: - client (botocore.client.EC2): Boto3 client - - Kwargs: - check_mode (bool): if set to true, do not run anything and - falsify the results. - - Basic Usage: - >>> client = boto3.client('ec2') - >>> allocate_eip_address(client) - True - - Returns: - Tuple (bool, str) - """ - ip_allocated = False - new_eip = None - err_msg = '' - params = { - 'Domain': 'vpc', - } - try: - if check_mode: - ip_allocated = True - random_numbers = ( - ''.join(str(x) for x in random.sample(range(0, 9), 7)) - ) - new_eip = 'eipalloc-{0}'.format(random_numbers) - else: - new_eip = client.allocate_address(**params)['AllocationId'] - ip_allocated = True - err_msg = 'eipalloc id {0} created'.format(new_eip) - - except botocore.exceptions.ClientError as e: - err_msg = str(e) - - return ip_allocated, err_msg, new_eip - - -def release_address(client, allocation_id, check_mode=False): - """Release an EIP from your EIP Pool - Args: - client (botocore.client.EC2): Boto3 client - allocation_id (str): The eip Amazon identifier. - - Kwargs: - check_mode (bool): if set to true, do not run anything and - falsify the results. - - Basic Usage: - >>> client = boto3.client('ec2') - >>> allocation_id = "eipalloc-123456" - >>> release_address(client, allocation_id) - True - - Returns: - Boolean, string - """ - err_msg = '' - if check_mode: - return True, '' - - ip_released = False - try: - client.describe_addresses(AllocationIds=[allocation_id]) - except botocore.exceptions.ClientError as e: - # IP address likely already released - # Happens with gateway in 'deleted' state that - # still lists associations - return True, str(e) - try: - client.release_address(AllocationId=allocation_id) - ip_released = True - except botocore.exceptions.ClientError as e: - err_msg = str(e) - - return ip_released, err_msg - - -def create(client, subnet_id, allocation_id, client_token=None, - wait=False, wait_timeout=0, if_exist_do_not_create=False, - check_mode=False): - """Create an Amazon NAT Gateway. - Args: - client (botocore.client.EC2): Boto3 client - subnet_id (str): The subnet_id the nat resides in. - allocation_id (str): The eip Amazon identifier. - - Kwargs: - if_exist_do_not_create (bool): if a nat gateway already exists in this - subnet, than do not create another one. - default = False - wait (bool): Wait for the nat to be in the deleted state before returning. - default = False - wait_timeout (int): Number of seconds to wait, until this timeout is reached. - default = 0 - client_token (str): - default = None - - Basic Usage: - >>> client = boto3.client('ec2') - >>> subnet_id = 'subnet-1234567' - >>> allocation_id = 'eipalloc-1234567' - >>> create(client, subnet_id, allocation_id, if_exist_do_not_create=True, wait=True, wait_timeout=500) - [ - true, - "", - { - "nat_gateway_id": "nat-123456789", - "subnet_id": "subnet-1234567", - "nat_gateway_addresses": [ - { - "public_ip": "55.55.55.55", - "network_interface_id": "eni-1234567", - "private_ip": "10.0.0.102", - "allocation_id": "eipalloc-1234567" - } - ], - "state": "deleted", - "create_time": "2016-03-05T00:33:21.209000+00:00", - "delete_time": "2016-03-05T00:36:37.329000+00:00", - "vpc_id": "vpc-1234567" - } - ] - - Returns: - Tuple (bool, str, list) - """ - params = { - 'SubnetId': subnet_id, - 'AllocationId': allocation_id - } - request_time = datetime.datetime.utcnow() - changed = False - success = False - token_provided = False - err_msg = "" - - if client_token: - token_provided = True - params['ClientToken'] = client_token - - try: - if not check_mode: - result = camel_dict_to_snake_dict(client.create_nat_gateway(**params)["NatGateway"]) - else: - result = DRY_RUN_GATEWAYS[0] - result['create_time'] = datetime.datetime.utcnow() - result['nat_gateway_addresses'][0]['allocation_id'] = allocation_id - result['subnet_id'] = subnet_id - - success = True - changed = True - create_time = result['create_time'].replace(tzinfo=None) - if token_provided and (request_time > create_time): - changed = False - elif wait: - success, err_msg, result = ( - wait_for_status( - client, wait_timeout, result['nat_gateway_id'], 'available', - check_mode=check_mode - ) - ) - if success: - err_msg = ( - 'NAT gateway {0} created'.format(result['nat_gateway_id']) - ) - - except botocore.exceptions.ClientError as e: - if "IdempotentParameterMismatch" in e.message: - err_msg = ( - 'NAT Gateway does not support update and token has already been provided: ' + str(e) - ) - else: - err_msg = str(e) - success = False - changed = False - result = None - - return success, changed, err_msg, result - - -def pre_create(client, subnet_id, allocation_id=None, eip_address=None, - if_exist_do_not_create=False, wait=False, wait_timeout=0, - client_token=None, check_mode=False): - """Create an Amazon NAT Gateway. - Args: - client (botocore.client.EC2): Boto3 client - subnet_id (str): The subnet_id the nat resides in. - - Kwargs: - allocation_id (str): The EIP Amazon identifier. - default = None - eip_address (str): The Elastic IP Address of the EIP. - default = None - if_exist_do_not_create (bool): if a nat gateway already exists in this - subnet, than do not create another one. - default = False - wait (bool): Wait for the nat to be in the deleted state before returning. - default = False - wait_timeout (int): Number of seconds to wait, until this timeout is reached. - default = 0 - client_token (str): - default = None - - Basic Usage: - >>> client = boto3.client('ec2') - >>> subnet_id = 'subnet-w4t12897' - >>> allocation_id = 'eipalloc-36014da3' - >>> pre_create(client, subnet_id, allocation_id, if_exist_do_not_create=True, wait=True, wait_timeout=500) - [ - true, - "", - { - "nat_gateway_id": "nat-03835afb6e31df79b", - "subnet_id": "subnet-w4t12897", - "nat_gateway_addresses": [ - { - "public_ip": "52.87.29.36", - "network_interface_id": "eni-5579742d", - "private_ip": "10.0.0.102", - "allocation_id": "eipalloc-36014da3" - } - ], - "state": "deleted", - "create_time": "2016-03-05T00:33:21.209000+00:00", - "delete_time": "2016-03-05T00:36:37.329000+00:00", - "vpc_id": "vpc-w68571b5" - } - ] - - Returns: - Tuple (bool, bool, str, list) - """ - success = False - changed = False - err_msg = "" - results = list() - - if not allocation_id and not eip_address: - existing_gateways, allocation_id_exists = ( - gateway_in_subnet_exists(client, subnet_id, check_mode=check_mode) - ) - - if len(existing_gateways) > 0 and if_exist_do_not_create: - success = True - changed = False - results = existing_gateways[0] - err_msg = ( - 'NAT Gateway {0} already exists in subnet_id {1}' - .format( - existing_gateways[0]['nat_gateway_id'], subnet_id - ) - ) - return success, changed, err_msg, results - else: - success, err_msg, allocation_id = ( - allocate_eip_address(client, check_mode=check_mode) - ) - if not success: - return success, 'False', err_msg, dict() - - elif eip_address or allocation_id: - if eip_address and not allocation_id: - allocation_id, err_msg = ( - get_eip_allocation_id_by_address( - client, eip_address, check_mode=check_mode - ) - ) - if not allocation_id: - success = False - changed = False - return success, changed, err_msg, dict() - - existing_gateways, allocation_id_exists = ( - gateway_in_subnet_exists( - client, subnet_id, allocation_id, check_mode=check_mode - ) - ) - if len(existing_gateways) > 0 and (allocation_id_exists or if_exist_do_not_create): - success = True - changed = False - results = existing_gateways[0] - err_msg = ( - 'NAT Gateway {0} already exists in subnet_id {1}' - .format( - existing_gateways[0]['nat_gateway_id'], subnet_id - ) - ) - return success, changed, err_msg, results - - success, changed, err_msg, results = create( - client, subnet_id, allocation_id, client_token, - wait, wait_timeout, if_exist_do_not_create, check_mode=check_mode - ) - - return success, changed, err_msg, results - - -def remove(client, nat_gateway_id, wait=False, wait_timeout=0, - release_eip=False, check_mode=False): - """Delete an Amazon NAT Gateway. - Args: - client (botocore.client.EC2): Boto3 client - nat_gateway_id (str): The Amazon nat id. - - Kwargs: - wait (bool): Wait for the nat to be in the deleted state before returning. - wait_timeout (int): Number of seconds to wait, until this timeout is reached. - release_eip (bool): Once the nat has been deleted, you can deallocate the eip from the vpc. - - Basic Usage: - >>> client = boto3.client('ec2') - >>> nat_gw_id = 'nat-03835afb6e31df79b' - >>> remove(client, nat_gw_id, wait=True, wait_timeout=500, release_eip=True) - [ - true, - "", - { - "nat_gateway_id": "nat-03835afb6e31df79b", - "subnet_id": "subnet-w4t12897", - "nat_gateway_addresses": [ - { - "public_ip": "52.87.29.36", - "network_interface_id": "eni-5579742d", - "private_ip": "10.0.0.102", - "allocation_id": "eipalloc-36014da3" - } - ], - "state": "deleted", - "create_time": "2016-03-05T00:33:21.209000+00:00", - "delete_time": "2016-03-05T00:36:37.329000+00:00", - "vpc_id": "vpc-w68571b5" - } - ] - - Returns: - Tuple (bool, str, list) - """ - params = { - 'NatGatewayId': nat_gateway_id - } - success = False - changed = False - err_msg = "" - results = list() - states = ['pending', 'available'] - try: - exist, _, gw = ( - get_nat_gateways( - client, nat_gateway_id=nat_gateway_id, - states=states, check_mode=check_mode - ) - ) - if exist and len(gw) == 1: - results = gw[0] - if not check_mode: - client.delete_nat_gateway(**params) - - allocation_id = ( - results['nat_gateway_addresses'][0]['allocation_id'] - ) - changed = True - success = True - err_msg = ( - 'NAT gateway {0} is in a deleting state. Delete was successful' - .format(nat_gateway_id) - ) - - if wait: - status_achieved, err_msg, results = ( - wait_for_status( - client, wait_timeout, nat_gateway_id, 'deleted', - check_mode=check_mode - ) - ) - if status_achieved: - err_msg = ( - 'NAT gateway {0} was deleted successfully' - .format(nat_gateway_id) - ) - - except botocore.exceptions.ClientError as e: - err_msg = str(e) - - if release_eip: - eip_released, eip_err = ( - release_address(client, allocation_id, check_mode) - ) - if not eip_released: - err_msg = ( - "{0}: Failed to release EIP {1}: {2}" - .format(err_msg, allocation_id, eip_err) - ) - success = False - - return success, changed, err_msg, results - - -@AWSRetry.jittered_backoff() -def manage_tags(client, nat_gateway_id, old_tags, new_tags, purge_tags, check_mode=False): - changed = False - err_msg = "" - - tags_to_set, tags_to_delete = compare_aws_tags( - old_tags, new_tags, - purge_tags=purge_tags, - ) - - if check_mode: - return changed, old_tags, new_tags - - try: - if tags_to_set: - client.create_tags( - Resources=[nat_gateway_id], - Tags=ansible_dict_to_boto3_tag_list(tags_to_set) - ) - changed = True - - if tags_to_delete: - delete_with_current_values = dict((k, old_tags.get(k)) for k in tags_to_delete) - client.delete_tags( - Resources=[nat_gateway_id], - Tags=ansible_dict_to_boto3_tag_list(delete_with_current_values) - ) - changed = True - except botocore.exceptions.ClientError as e: - err_msg = "{0}: Failed to modify tags".format(str(e)) - - return changed, err_msg - - -@AWSRetry.jittered_backoff() -def find_tags(client, module, resource_id): - """ Find the current tags associated to a nat_gateway_id - """ - try: - response = client.describe_tags(Filters=[ - {'Name': 'resource-id', 'Values': [resource_id]} - ]) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't get NAT Gateway tags") - - result = response['Tags'] - if result: - result = boto3_tag_list_to_ansible_dict(result) - else: - result = {} - return result - - -def main(): - argument_spec = ec2_argument_spec() - argument_spec.update( - dict( - subnet_id=dict(type='str'), - eip_address=dict(type='str'), - allocation_id=dict(type='str'), - if_exist_do_not_create=dict(type='bool', default=False), - state=dict(default='present', choices=['present', 'absent']), - wait=dict(type='bool', default=False), - wait_timeout=dict(type='int', default=320, required=False), - release_eip=dict(type='bool', default=False), - nat_gateway_id=dict(type='str'), - client_token=dict(type='str'), - tags=dict(type='dict'), - purge_tags=dict(type='bool', default=False) - ) - ) - module = AnsibleAWSModule( - argument_spec=argument_spec, - supports_check_mode=True, - mutually_exclusive=[ - ['allocation_id', 'eip_address'] - ], - required_if=[['state', 'absent', ['nat_gateway_id']], - ['state', 'present', ['subnet_id']]] - ) - - state = module.params.get('state').lower() - check_mode = module.check_mode - subnet_id = module.params.get('subnet_id') - allocation_id = module.params.get('allocation_id') - eip_address = module.params.get('eip_address') - nat_gateway_id = module.params.get('nat_gateway_id') - wait = module.params.get('wait') - wait_timeout = module.params.get('wait_timeout') - release_eip = module.params.get('release_eip') - client_token = module.params.get('client_token') - if_exist_do_not_create = module.params.get('if_exist_do_not_create') - purge_tags = module.params.get('purge_tags') - tags = module.params.get('tags') - - try: - region, ec2_url, aws_connect_kwargs = ( - get_aws_connection_info(module, boto3=True) - ) - client = ( - boto3_conn( - module, conn_type='client', resource='ec2', - region=region, endpoint=ec2_url, **aws_connect_kwargs - ) - ) - except botocore.exceptions.ClientError as e: - module.fail_json_aws(e, msg="Boto3 Client Error - " + str(e.msg)) - - changed = False - err_msg = '' - - if state == 'present': - success, changed, err_msg, results = ( - pre_create( - client, subnet_id, allocation_id, eip_address, - if_exist_do_not_create, wait, wait_timeout, - client_token, check_mode=check_mode - ) - ) - - if success: - if tags: - current_tags = find_tags(client, module, results['nat_gateway_id']) - changed, err_msg = manage_tags( - client, results['nat_gateway_id'], current_tags, tags, purge_tags - ) - if err_msg: - success = False - results["tags"] = find_tags(client, module, results['nat_gateway_id']) - else: - success, changed, err_msg, results = ( - remove( - client, nat_gateway_id, wait, wait_timeout, release_eip, - check_mode=check_mode - ) - ) - - if not success: - module.fail_json( - msg=err_msg, success=success, changed=changed - ) - else: - module.exit_json( - msg=err_msg, success=success, changed=changed, **results - ) - - -if __name__ == '__main__': - main() diff --git a/modules/cloud/amazon/ec2_vpc_route_table_info.py b/modules/cloud/amazon/ec2_vpc_route_table_info.py deleted file mode 100644 index d34e6dbcc..000000000 --- a/modules/cloud/amazon/ec2_vpc_route_table_info.py +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: ec2_vpc_route_table_info -short_description: Gather information about ec2 VPC route tables in AWS -description: - - Gather information about ec2 VPC route tables in AWS - - This module was called C(ec2_vpc_route_table_facts) before Ansible 2.9. The usage did not change. -version_added: "2.0" -author: "Rob White (@wimnat)" -options: - filters: - description: - - A dict of filters to apply. Each dict item consists of a filter key and a filter value. - See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeRouteTables.html) for possible filters. - type: dict - route_table_ids: - description: - - Get details of a specific route tables. This value should be provided as a list. - required: false - type: list - default: [] - version_added: "2.10" - elements: str -extends_documentation_fragment: - - aws - - ec2 -''' - -EXAMPLES = ''' -# Note: These examples do not set authentication details, see the AWS Guide for details. - -# Gather information about all VPC route tables -- ec2_vpc_route_table_info: - -# Gather information about a particular VPC route table using route table ID -- ec2_vpc_route_table_info: - filters: - route-table-id: rtb-00112233 - -# Gather information about any VPC route table with a tag key Name and value Example -- ec2_vpc_route_table_info: - filters: - "tag:Name": Example - -# Gather information about any VPC route table within VPC with ID vpc-abcdef00 -- ec2_vpc_route_table_info: - filters: - vpc-id: vpc-abcdef00 - -''' - -RETURN = ''' -route_tables: - description: Information about one or more route tables. - returned: always - type: complex - contains: - associations: - description: The associations between the route table and one or more subnets. - returned: always - type: complex - contains: - main: - description: Indicates whether this is the main route table. - returned: always - type: bool - sample: True - route_table_association_id: - description: The ID of the association between a route table and a subnet. - returned: always - type: str - sample: rtbassoc-4c3f71123 - route_table_id: - description: The ID of the route table. - returned: always - type: str - sample: rtb-05641160 - subnet_id: - description: The ID of the subnet. A subnet ID is not returned for an implicit association. - returned: when route table has subnet associated - type: str - sample: subnet-db73c0ac - id: - description: The ID of the route table. (Returned for backward compatibility. Use route_table_id instead.) - returned: always - type: str - sample: rtb-05641160 - propagating_vgws: - description: Any virtual private gateway (VGW) propagating routes. - returned: always - type: complex - contains: - gateway_id: - description: The ID of the virtual private gateway. - returned: when gateway_id available - type: str - sample: local - route_table_id: - description: The ID of the route table. - returned: always - type: str - sample: rtb-05641160 - routes: - description: The routes in the route table. - returned: always - type: complex - contains: - destination_cidr_block: - description: The IPv4 CIDR block used for the destination match. - returned: always - type: str - sample: 172.31.0.0/16 - destination_ipv6_cidr_block: - description: The IPv6 CIDR block used for the destination match. - returned: when ipv6 cidr block available - type: str - sample: 2001:db8:1234:1a00::/56 - destination_prefix_list_id: - description: The prefix of the AWS service. - returned: always - type: str - egress_only_internet_gateway_id : - description: The ID of the egress-only internet gateway. - returned: when egress_only_internet_gateway available - type: str - gateway_id: - description: The ID of a gateway attached to your VPC. - returned: when gateway available - type: str - instance_id: - description: The ID of a NAT instance in your VPC. - returned: when NAT instance is available - type: str - instance_owner_id: - description: The ID of a NAT instance owner in your VPC. - returned: when NAT instance owner is available - type: str - nat_gateway_id: - description: The ID of a NAT gateway. - returned: when NAT gateway is available - type: str - network_interface_id: - description: The ID of the network interface. - returned: when network interface is available - type: str - origin: - description: Describes how the route was created. - returned: always - type: str - sample: CreateRoute - state: - description: The state of the route. - returned: always - type: str - sample: active - vpc_peering_connection_id: - description: The ID of the VPC peering connection. - returned: when vpc peering available - type: str - tags: - description: Any tags assigned to the VPN connection. - returned: always - type: dict - sample: { - "Name": "test-conn" - } - vpc_id: - description: The ID of the VPC peering connection. - returned: when vpc peering available - type: str - sample: vpc-a01106c2 -''' - -import json - -try: - from botocore.exceptions import ClientError, BotoCoreError -except ImportError: - pass # caught by AnsibleAWSModule - -from ansible.module_utils.aws.core import AnsibleAWSModule -from ansible.module_utils.ec2 import (ansible_dict_to_boto3_filter_list, camel_dict_to_snake_dict, - boto3_tag_list_to_ansible_dict) - - -def date_handler(obj): - return obj.isoformat() if hasattr(obj, 'isoformat') else obj - - -def list_route_tables(route_table, module): - params = dict() - - params['Filters'] = ansible_dict_to_boto3_filter_list(module.params.get('filters')) - params['RouteTableIds'] = module.params.get('route_table_ids') - - try: - result = json.loads(json.dumps(route_table.describe_route_tables(**params), default=date_handler)) - except ValueError as e: - module.fail_json_aws(e, msg="Cannot validate JSON data") - except (ClientError, BotoCoreError) as e: - module.fail_json_aws(e, msg="Could not describe route tables") - snaked_route_tables = [camel_dict_to_snake_dict(route_table, - ignore_list=['Tags']) for route_table in result['RouteTables']] - if snaked_route_tables: - for route_table in snaked_route_tables: - route_table['id'] = route_table['route_table_id'] - for association in route_table['associations']: - association['id'] = association['route_table_association_id'] - if 'subnet_id' not in association: - association['subnet_id'] = None - for route in route_table['routes']: - for key in ['instance_id']: - if key not in route: - route[key] = None - route_table['tags'] = boto3_tag_list_to_ansible_dict(route_table.get('tags', [])) - module.exit_json(changed=False, route_tables=snaked_route_tables) - - -def main(): - argument_spec = dict( - route_table_ids=dict(default=[], type='list', elements='str'), - filters=dict(default={}, type='dict') - ) - - module = AnsibleAWSModule(argument_spec=argument_spec, - supports_check_mode=True) - - if module._name == 'ec2_vpc_route_table_facts': - module.deprecate("The 'ec2_vpc_route_table_facts' module has been renamed to 'ec2_vpc_route_table_info'", version='2.13') - - connection = module.client('ec2') - list_route_tables(connection, module) - - -if __name__ == '__main__': - main()