From 728ba939610bdc7476a905200b1b65ff9e1bc6f9 Mon Sep 17 00:00:00 2001 From: jillr Date: Mon, 2 Mar 2020 19:25:18 +0000 Subject: [PATCH 01/29] Initial commit This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/eb75681585a23ea79e642b86a0f8e64e0f40a6d7 --- plugins/modules/ec2_vpc_nat_gateway.py | 1020 +++++++++++++++++ plugins/modules/ec2_vpc_nat_gateway_facts.py | 1 + plugins/modules/ec2_vpc_nat_gateway_info.py | 156 +++ .../targets/ec2_vpc_nat_gateway/aliases | 2 + .../ec2_vpc_nat_gateway/tasks/main.yml | 82 ++ 5 files changed, 1261 insertions(+) create mode 100644 plugins/modules/ec2_vpc_nat_gateway.py create mode 120000 plugins/modules/ec2_vpc_nat_gateway_facts.py create mode 100644 plugins/modules/ec2_vpc_nat_gateway_info.py create mode 100644 tests/integration/targets/ec2_vpc_nat_gateway/aliases create mode 100644 tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py new file mode 100644 index 00000000000..2e35459d438 --- /dev/null +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -0,0 +1,1020 @@ +#!/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. +requirements: [boto3, botocore] +options: + state: + description: + - Ensure NAT Gateway is present or absent. + default: "present" + choices: ["present", "absent"] + type: str + nat_gateway_id: + description: + - The id AWS dynamically allocates to the NAT Gateway on creation. + This is required when the absent option is present. + type: str + subnet_id: + description: + - The id of the subnet to create the NAT Gateway in. This is required + with the present option. + type: str + 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. + type: str + 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. + type: str + 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: false + type: bool + wait: + description: + - Wait for operation to complete before returning. + default: false + type: bool + wait_timeout: + description: + - How many seconds to wait for an operation to complete before timing out. + default: 320 + type: int + 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. + type: str +author: + - Allen Sanabria (@linuxdynasty) + - Jon Hadfield (@jonhadfield) + - Karen Cheng (@Etherdaemon) +extends_documentation_fragment: +- ansible.amazon.aws +- ansible.amazon.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: true + 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: true + 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: true + 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: true + nat_gateway_id: "{{ item.NatGatewayId }}" + release_eip: true + 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: true + 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: true + 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 dictionaries 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' + } + ] +''' + +import datetime +import random +import time + +try: + import botocore +except ImportError: + pass # caught by imported HAS_BOTO3 + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (ec2_argument_spec, get_aws_connection_info, boto3_conn, + camel_dict_to_snake_dict, HAS_BOTO3) + + +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, err_msg, 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, err_msg, 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 + + +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'), + ) + ) + module = AnsibleModule( + 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']]] + ) + + # Validate Requirements + if not HAS_BOTO3: + module.fail_json(msg='botocore/boto3 is required.') + + 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') + + 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(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 + ) + ) + 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/plugins/modules/ec2_vpc_nat_gateway_facts.py b/plugins/modules/ec2_vpc_nat_gateway_facts.py new file mode 120000 index 00000000000..fd969989977 --- /dev/null +++ b/plugins/modules/ec2_vpc_nat_gateway_facts.py @@ -0,0 +1 @@ +ec2_vpc_nat_gateway_info.py \ No newline at end of file diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py new file mode 100644 index 00000000000..b86e4bb8114 --- /dev/null +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -0,0 +1,156 @@ +#!/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_info +short_description: Retrieves AWS VPC Managed Nat Gateway details using AWS methods. +description: + - Gets various details related to AWS VPC Managed Nat Gateways + - This module was called C(ec2_vpc_nat_gateway_facts) before Ansible 2.9. The usage did not change. +requirements: [ boto3 ] +options: + nat_gateway_ids: + description: + - List of specific nat gateway IDs to fetch details for. + type: list + elements: str + 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_DescribeNatGateways.html) + for possible filters. + type: dict +author: Karen Cheng (@Etherdaemon) +extends_documentation_fragment: +- ansible.amazon.aws +- ansible.amazon.ec2 + +''' + +EXAMPLES = ''' +# Simple example of listing all nat gateways +- name: List all managed nat gateways in ap-southeast-2 + ec2_vpc_nat_gateway_info: + region: ap-southeast-2 + register: all_ngws + +- name: Debugging the result + debug: + msg: "{{ all_ngws.result }}" + +- name: Get details on specific nat gateways + ec2_vpc_nat_gateway_info: + nat_gateway_ids: + - nat-1234567891234567 + - nat-7654321987654321 + region: ap-southeast-2 + register: specific_ngws + +- name: Get all nat gateways with specific filters + ec2_vpc_nat_gateway_info: + region: ap-southeast-2 + filters: + state: ['pending'] + register: pending_ngws + +- name: Get nat gateways with specific filter + ec2_vpc_nat_gateway_info: + region: ap-southeast-2 + filters: + subnet-id: subnet-12345678 + state: ['available'] + register: existing_nat_gateways +''' + +RETURN = ''' +result: + description: The result of the describe, converted to ansible snake case style. + See http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.describe_nat_gateways for the response. + returned: success + type: list +''' + +import json + +try: + import botocore +except ImportError: + pass # will be detected by imported HAS_BOTO3 + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (ec2_argument_spec, get_aws_connection_info, boto3_conn, + camel_dict_to_snake_dict, ansible_dict_to_boto3_filter_list, boto3_tag_list_to_ansible_dict, HAS_BOTO3) + + +def date_handler(obj): + return obj.isoformat() if hasattr(obj, 'isoformat') else obj + + +def get_nat_gateways(client, module, nat_gateway_id=None): + params = dict() + nat_gateways = list() + + params['Filter'] = ansible_dict_to_boto3_filter_list(module.params.get('filters')) + params['NatGatewayIds'] = module.params.get('nat_gateway_ids') + + try: + result = json.loads(json.dumps(client.describe_nat_gateways(**params), default=date_handler)) + except Exception as e: + module.fail_json(msg=str(e.message)) + + for gateway in result['NatGateways']: + # Turn the boto3 result into ansible_friendly_snaked_names + converted_gateway = camel_dict_to_snake_dict(gateway) + if 'tags' in converted_gateway: + # Turn the boto3 result into ansible friendly tag dictionary + converted_gateway['tags'] = boto3_tag_list_to_ansible_dict(converted_gateway['tags']) + + nat_gateways.append(converted_gateway) + + return nat_gateways + + +def main(): + argument_spec = ec2_argument_spec() + argument_spec.update( + dict( + filters=dict(default={}, type='dict'), + nat_gateway_ids=dict(default=[], type='list'), + ) + ) + + module = AnsibleModule(argument_spec=argument_spec, + supports_check_mode=True) + if module._name == 'ec2_vpc_nat_gateway_facts': + module.deprecate("The 'ec2_vpc_nat_gateway_facts' module has been renamed to 'ec2_vpc_nat_gateway_info'", version='2.13') + + # Validate Requirements + if not HAS_BOTO3: + module.fail_json(msg='botocore/boto3 is required.') + + try: + region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True) + if region: + connection = boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_params) + else: + module.fail_json(msg="region must be specified") + except botocore.exceptions.NoCredentialsError as e: + module.fail_json(msg=str(e)) + + results = get_nat_gateways(connection, module) + + module.exit_json(result=results) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/aliases b/tests/integration/targets/ec2_vpc_nat_gateway/aliases new file mode 100644 index 00000000000..6e3860bee23 --- /dev/null +++ b/tests/integration/targets/ec2_vpc_nat_gateway/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group2 diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml new file mode 100644 index 00000000000..7cb7e986e0a --- /dev/null +++ b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml @@ -0,0 +1,82 @@ +# The tests for this module are incomplete. +# The tests below were migrated from unit tests. +# They take advantage of hard-coded results within the module to trigger both changed and unchanged responses. +# They were migrated to maintain test coverage while removing unit tests that depended on use of TaskQueueManager. + +- name: Create new nat gateway with eip allocation-id + ec2_vpc_nat_gateway: + subnet_id: subnet-12345678 + allocation_id: eipalloc-12345678 + wait: yes + region: us-west-2 + register: nat_gateway + check_mode: yes + +- assert: + that: + - nat_gateway.changed + +- name: Create new nat gateway with eip allocation-id + ec2_vpc_nat_gateway: + subnet_id: subnet-123456789 + allocation_id: eipalloc-1234567 + wait: yes + region: us-west-2 + register: nat_gateway + check_mode: yes + +- assert: + that: + - not nat_gateway.changed + +- name: Create new nat gateway with eip address + ec2_vpc_nat_gateway: + subnet_id: subnet-12345678 + eip_address: 55.55.55.55 + wait: yes + region: us-west-2 + register: nat_gateway + check_mode: yes + +- assert: + that: + - nat_gateway.changed + +- name: Create new nat gateway with eip address + ec2_vpc_nat_gateway: + subnet_id: subnet-123456789 + eip_address: 55.55.55.55 + wait: yes + region: us-west-2 + register: nat_gateway + check_mode: yes + +- assert: + that: + - not nat_gateway.changed + +- name: Create new nat gateway only if one does not exist already + ec2_vpc_nat_gateway: + if_exist_do_not_create: yes + subnet_id: subnet-123456789 + wait: yes + region: us-west-2 + register: nat_gateway + check_mode: yes + +- assert: + that: + - not nat_gateway.changed + +- name: Delete Nat Gateway + ec2_vpc_nat_gateway: + nat_gateway_id: nat-123456789 + state: absent + wait: yes + region: us-west-2 + register: nat_gateway + check_mode: yes + +- assert: + that: + - nat_gateway.changed From d87ed77f568a86530b45a3b4e7851d93c4f3ebd1 Mon Sep 17 00:00:00 2001 From: jillr Date: Tue, 3 Mar 2020 19:43:21 +0000 Subject: [PATCH 02/29] migration test cleanup This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/13b104b912784bb31a0bff23eed4c27b0f5e0283 --- plugins/modules/ec2_vpc_nat_gateway.py | 8 ++++++-- plugins/modules/ec2_vpc_nat_gateway_info.py | 10 ++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 2e35459d438..5cb3236885e 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -209,8 +209,12 @@ pass # caught by imported HAS_BOTO3 from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (ec2_argument_spec, get_aws_connection_info, boto3_conn, - camel_dict_to_snake_dict, HAS_BOTO3) +from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (ec2_argument_spec, + get_aws_connection_info, + boto3_conn, + camel_dict_to_snake_dict, + HAS_BOTO3, + ) DRY_RUN_GATEWAYS = [ diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index b86e4bb8114..bd1dde7ce7f 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -88,8 +88,14 @@ pass # will be detected by imported HAS_BOTO3 from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (ec2_argument_spec, get_aws_connection_info, boto3_conn, - camel_dict_to_snake_dict, ansible_dict_to_boto3_filter_list, boto3_tag_list_to_ansible_dict, HAS_BOTO3) +from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (ec2_argument_spec, + get_aws_connection_info, + boto3_conn, + camel_dict_to_snake_dict, + ansible_dict_to_boto3_filter_list, + boto3_tag_list_to_ansible_dict, + HAS_BOTO3, + ) def date_handler(obj): From 692945022bf2c71f6efad1b88668b646c331d966 Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Wed, 25 Mar 2020 15:39:40 -0700 Subject: [PATCH 03/29] Rename collection (#12) * Rename core collection Rename references to ansible.amazon to amazon.aws. * Rename community.amazon to community.aws Fix pep8 line lengths for rewritten amazon.aws imports * Missed a path in shippable.sh * Dependency repos moved This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/235c5db571cc45db5839476c94356c9b91e1f228 --- plugins/modules/ec2_vpc_nat_gateway.py | 16 ++++++++-------- plugins/modules/ec2_vpc_nat_gateway_info.py | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 5cb3236885e..09fc70de335 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -80,8 +80,8 @@ - Jon Hadfield (@jonhadfield) - Karen Cheng (@Etherdaemon) extends_documentation_fragment: -- ansible.amazon.aws -- ansible.amazon.ec2 +- amazon.aws.aws +- amazon.aws.ec2 ''' @@ -209,12 +209,12 @@ pass # caught by imported HAS_BOTO3 from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (ec2_argument_spec, - get_aws_connection_info, - boto3_conn, - camel_dict_to_snake_dict, - HAS_BOTO3, - ) +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import (ec2_argument_spec, + get_aws_connection_info, + boto3_conn, + camel_dict_to_snake_dict, + HAS_BOTO3, + ) DRY_RUN_GATEWAYS = [ diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index bd1dde7ce7f..a4891391854 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -32,8 +32,8 @@ type: dict author: Karen Cheng (@Etherdaemon) extends_documentation_fragment: -- ansible.amazon.aws -- ansible.amazon.ec2 +- amazon.aws.aws +- amazon.aws.ec2 ''' @@ -88,14 +88,14 @@ pass # will be detected by imported HAS_BOTO3 from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import (ec2_argument_spec, - get_aws_connection_info, - boto3_conn, - camel_dict_to_snake_dict, - ansible_dict_to_boto3_filter_list, - boto3_tag_list_to_ansible_dict, - HAS_BOTO3, - ) +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import (ec2_argument_spec, + get_aws_connection_info, + boto3_conn, + camel_dict_to_snake_dict, + ansible_dict_to_boto3_filter_list, + boto3_tag_list_to_ansible_dict, + HAS_BOTO3, + ) def date_handler(obj): From 687f71ef212dbbfbf0ddb693bc2cb961932990da Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Tue, 19 May 2020 16:06:12 -0700 Subject: [PATCH 04/29] Remove METADATA and cleanup galaxy.yml (#70) * Remove ANSIBLE_METADATA entirely, see ansible/ansible/pull/69454. Remove `license` field from galaxy.yml, in favor of `license_file`. This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/05672a64e2362cc2d865b5af6a57da6bc3cd08e3 --- plugins/modules/ec2_vpc_nat_gateway.py | 5 ----- plugins/modules/ec2_vpc_nat_gateway_info.py | 5 ----- 2 files changed, 10 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 09fc70de335..d8ee5167b67 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -6,11 +6,6 @@ __metaclass__ = type -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - DOCUMENTATION = ''' --- module: ec2_vpc_nat_gateway diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index a4891391854..a4e7ac6db99 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -6,11 +6,6 @@ __metaclass__ = type -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - DOCUMENTATION = ''' module: ec2_vpc_nat_gateway_info short_description: Retrieves AWS VPC Managed Nat Gateway details using AWS methods. From 4d16ccf924639245f7fcc14315ce843fc6087795 Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Tue, 16 Jun 2020 11:23:52 -0700 Subject: [PATCH 05/29] Collections related fixes for CI (#96) * Update module deprecations Switch version to `removed_at_date` * Don't install amazon.aws from galaxy We've been using galaxy to install amazon.aws in shippable, but that doesn't really work if we aren't publising faster. Get that collection from git so it is most up to date. * We need to declare python test deps now * missed a python dep This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/7cd211e9383db26bc2aa4cc06e657cf60ed0acc0 --- plugins/modules/ec2_vpc_nat_gateway_info.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index a4e7ac6db99..85f96cc7340 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -133,7 +133,8 @@ def main(): module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) if module._name == 'ec2_vpc_nat_gateway_facts': - module.deprecate("The 'ec2_vpc_nat_gateway_facts' module has been renamed to 'ec2_vpc_nat_gateway_info'", version='2.13') + module.deprecate("The 'ec2_vpc_nat_gateway_facts' module has been renamed to 'ec2_vpc_nat_gateway_info'", + date='2021-12-01', collection_name='community.aws') # Validate Requirements if not HAS_BOTO3: From f8623741b857416b396acb2b0f10a8eef8379a73 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Wed, 17 Jun 2020 01:24:54 +0530 Subject: [PATCH 06/29] Update Examples with FQCN (#67) Updated module examples with FQCN Signed-off-by: Abhijeet Kasurde This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/98173aefbbceed7fc0d9db62687b73f96a55a999 --- plugins/modules/ec2_vpc_nat_gateway.py | 16 ++++++++-------- plugins/modules/ec2_vpc_nat_gateway_info.py | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index d8ee5167b67..306c8ac49c4 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -84,7 +84,7 @@ # 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: + community.aws.ec2_vpc_nat_gateway: state: present subnet_id: subnet-12345678 eip_address: 52.1.1.1 @@ -93,7 +93,7 @@ register: new_nat_gateway - name: Create new nat gateway using an allocation-id. - ec2_vpc_nat_gateway: + community.aws.ec2_vpc_nat_gateway: state: present subnet_id: subnet-12345678 allocation_id: eipalloc-12345678 @@ -101,7 +101,7 @@ register: new_nat_gateway - name: Create new nat gateway, using an EIP address and wait for available status. - ec2_vpc_nat_gateway: + community.aws.ec2_vpc_nat_gateway: state: present subnet_id: subnet-12345678 eip_address: 52.1.1.1 @@ -110,7 +110,7 @@ register: new_nat_gateway - name: Create new nat gateway and allocate new EIP. - ec2_vpc_nat_gateway: + community.aws.ec2_vpc_nat_gateway: state: present subnet_id: subnet-12345678 wait: true @@ -118,7 +118,7 @@ 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: + community.aws.ec2_vpc_nat_gateway: state: present subnet_id: subnet-12345678 wait: true @@ -127,7 +127,7 @@ register: new_nat_gateway - name: Delete nat gateway using discovered nat gateways from facts module. - ec2_vpc_nat_gateway: + community.aws.ec2_vpc_nat_gateway: state: absent region: ap-southeast-2 wait: true @@ -137,7 +137,7 @@ loop: "{{ gateways_to_remove.result }}" - name: Delete nat gateway and wait for deleted status. - ec2_vpc_nat_gateway: + community.aws.ec2_vpc_nat_gateway: state: absent nat_gateway_id: nat-12345678 wait: true @@ -145,7 +145,7 @@ region: ap-southeast-2 - name: Delete nat gateway and release EIP. - ec2_vpc_nat_gateway: + community.aws.ec2_vpc_nat_gateway: state: absent nat_gateway_id: nat-12345678 release_eip: true diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index 85f96cc7340..83fb9b0f182 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -35,7 +35,7 @@ EXAMPLES = ''' # Simple example of listing all nat gateways - name: List all managed nat gateways in ap-southeast-2 - ec2_vpc_nat_gateway_info: + community.aws.ec2_vpc_nat_gateway_info: region: ap-southeast-2 register: all_ngws @@ -44,7 +44,7 @@ msg: "{{ all_ngws.result }}" - name: Get details on specific nat gateways - ec2_vpc_nat_gateway_info: + community.aws.ec2_vpc_nat_gateway_info: nat_gateway_ids: - nat-1234567891234567 - nat-7654321987654321 @@ -52,14 +52,14 @@ register: specific_ngws - name: Get all nat gateways with specific filters - ec2_vpc_nat_gateway_info: + community.aws.ec2_vpc_nat_gateway_info: region: ap-southeast-2 filters: state: ['pending'] register: pending_ngws - name: Get nat gateways with specific filter - ec2_vpc_nat_gateway_info: + community.aws.ec2_vpc_nat_gateway_info: region: ap-southeast-2 filters: subnet-id: subnet-12345678 From f4314fed75e6479d4db8ec9c3d0f2787cecefb93 Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Wed, 17 Jun 2020 09:31:32 -0700 Subject: [PATCH 07/29] Update docs (#99) * Update docs Remove .git from repo url so links in readme will generate correctly Add required ansible version Run latest version of add_docs.py Add version_added string to modules * galaxy.yml was missing authors This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/96ee268e5267f5b12c3d59892bc1279f75aa3135 --- plugins/modules/ec2_vpc_nat_gateway.py | 1 + plugins/modules/ec2_vpc_nat_gateway_info.py | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 306c8ac49c4..4272dc648c4 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -9,6 +9,7 @@ DOCUMENTATION = ''' --- module: ec2_vpc_nat_gateway +version_added: 1.0.0 short_description: Manage AWS VPC NAT Gateways. description: - Ensure the state of AWS VPC NAT Gateways based on their id, allocation and subnet ids. diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index 83fb9b0f182..b734721b5ea 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -9,6 +9,7 @@ DOCUMENTATION = ''' module: ec2_vpc_nat_gateway_info short_description: Retrieves AWS VPC Managed Nat Gateway details using AWS methods. +version_added: 1.0.0 description: - Gets various details related to AWS VPC Managed Nat Gateways - This module was called C(ec2_vpc_nat_gateway_facts) before Ansible 2.9. The usage did not change. From 2ef35aaca579f5ee1277b93184616a418ae3e5a7 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Thu, 16 Jul 2020 01:31:41 +0530 Subject: [PATCH 08/29] Docs: sanity fixes (#133) Signed-off-by: Abhijeet Kasurde This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/059cf9efc95bb976de21ab4f8e4d9ddd001983fc --- plugins/modules/ec2_vpc_nat_gateway_info.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index b734721b5ea..bb164a2b50b 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -6,7 +6,7 @@ __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = r''' module: ec2_vpc_nat_gateway_info short_description: Retrieves AWS VPC Managed Nat Gateway details using AWS methods. version_added: 1.0.0 @@ -33,7 +33,7 @@ ''' -EXAMPLES = ''' +EXAMPLES = r''' # Simple example of listing all nat gateways - name: List all managed nat gateways in ap-southeast-2 community.aws.ec2_vpc_nat_gateway_info: @@ -68,7 +68,7 @@ register: existing_nat_gateways ''' -RETURN = ''' +RETURN = r''' result: description: The result of the describe, converted to ansible snake case style. See http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.describe_nat_gateways for the response. @@ -127,7 +127,7 @@ def main(): argument_spec.update( dict( filters=dict(default={}, type='dict'), - nat_gateway_ids=dict(default=[], type='list'), + nat_gateway_ids=dict(default=[], type='list', elements='str'), ) ) From 3094e81c1946a16383fa3e7fd4f7aecfb0eedab3 Mon Sep 17 00:00:00 2001 From: Andrew Klychkov Date: Fri, 17 Jul 2020 21:10:09 +0300 Subject: [PATCH 09/29] aws modules: fix examples to use FQCN for builtin modules/plugins (#144) This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/92bebdd5ab3019bbdeee55e8a69c9d903deeac49 --- plugins/modules/ec2_vpc_nat_gateway_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index bb164a2b50b..f076d38a833 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -41,7 +41,7 @@ register: all_ngws - name: Debugging the result - debug: + ansible.builtin.debug: msg: "{{ all_ngws.result }}" - name: Get details on specific nat gateways From 91f0a9fbf634bd87a79856d4e654c7ea5c890999 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 12 Aug 2020 13:06:35 +0200 Subject: [PATCH 10/29] Bulk migration to AnsibleAWSModule (#173) * Update comments to reference AnsibleAWSModule rather than AnsibleModule * Bulk re-order imports and split onto one from import per-line. * Add AnsibleAWSModule imports * Migrate boto 2 based modules to AnsibleAWSModule * Move boto3-only modules over to AnsibleAWSModule * Remove extra ec2_argument_spec calls - not needed now we're using AnsibleAWSModule * Remove most HAS_BOTO3 code, it's handled by AnsibleAWSModule * Handle missing Boto 2 consistently (HAS_BOTO) * Remove AnsibleModule imports * Changelog fragment This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/818c6d2faa046974a9bdfa9346122d11e5bef3b1 --- plugins/modules/ec2_vpc_nat_gateway.py | 46 ++++++++------------- plugins/modules/ec2_vpc_nat_gateway_info.py | 34 ++++++--------- 2 files changed, 30 insertions(+), 50 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 4272dc648c4..2216ffe2276 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -202,15 +202,12 @@ try: import botocore except ImportError: - pass # caught by imported HAS_BOTO3 + pass # Handled by AnsibleAWSModule -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import (ec2_argument_spec, - get_aws_connection_info, - boto3_conn, - camel_dict_to_snake_dict, - HAS_BOTO3, - ) +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict DRY_RUN_GATEWAYS = [ @@ -933,35 +930,28 @@ def remove(client, nat_gateway_id, wait=False, wait_timeout=0, 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'), - ) + argument_spec = 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'), ) - module = AnsibleModule( + 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', 'present', ['subnet_id']]], ) - # Validate Requirements - if not HAS_BOTO3: - module.fail_json(msg='botocore/boto3 is required.') - state = module.params.get('state').lower() check_mode = module.check_mode subnet_id = module.params.get('subnet_id') diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index f076d38a833..7f49c708857 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -81,17 +81,14 @@ try: import botocore except ImportError: - pass # will be detected by imported HAS_BOTO3 + pass # Handled by AnsibleAWSModule -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import (ec2_argument_spec, - get_aws_connection_info, - boto3_conn, - camel_dict_to_snake_dict, - ansible_dict_to_boto3_filter_list, - boto3_tag_list_to_ansible_dict, - HAS_BOTO3, - ) +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict def date_handler(obj): @@ -123,24 +120,17 @@ def get_nat_gateways(client, module, nat_gateway_id=None): def main(): - argument_spec = ec2_argument_spec() - argument_spec.update( - dict( - filters=dict(default={}, type='dict'), - nat_gateway_ids=dict(default=[], type='list', elements='str'), - ) + argument_spec = dict( + filters=dict(default={}, type='dict'), + nat_gateway_ids=dict(default=[], type='list', elements='str'), ) - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) + module = AnsibleAWSModule(argument_spec=argument_spec, + supports_check_mode=True,) if module._name == 'ec2_vpc_nat_gateway_facts': module.deprecate("The 'ec2_vpc_nat_gateway_facts' module has been renamed to 'ec2_vpc_nat_gateway_info'", date='2021-12-01', collection_name='community.aws') - # Validate Requirements - if not HAS_BOTO3: - module.fail_json(msg='botocore/boto3 is required.') - try: region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True) if region: From 28db71cbcdb21a42eff63faa1b52ac4ffeeb0a9a Mon Sep 17 00:00:00 2001 From: Vincent Vinet Date: Sat, 15 Aug 2020 09:11:59 -0400 Subject: [PATCH 11/29] =?UTF-8?q?Python=203=20compatibility=20error=20hand?= =?UTF-8?q?ling:=20use=20to=5Fnative(e)=20instead=20of=20str(e)=20or=20e.m?= =?UTF-8?q?e=E2=80=A6=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Py3 compat error handling: use to_native(e) instead of str(e) or e.message * PR comment changes, use fail_json_aws and is_boto3_error_code This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/ffe14f95186399dc080019554035021015765872 --- plugins/modules/ec2_vpc_nat_gateway.py | 18 +++++++++++------- plugins/modules/ec2_vpc_nat_gateway_info.py | 3 ++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 2216ffe2276..37dd9160084 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -204,7 +204,9 @@ except ImportError: pass # Handled by AnsibleAWSModule +from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict @@ -698,13 +700,15 @@ def create(client, subnet_id, allocation_id, client_token=None, '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) + except is_boto3_error_code('IdempotentParameterMismatch'): + err_msg = ( + 'NAT Gateway does not support update and token has already been provided: ' + err_msg + ) + success = False + changed = False + result = None + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + err_msg = to_native(e) success = False changed = False result = None diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index 7f49c708857..9ebeb63fcbb 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -83,6 +83,7 @@ except ImportError: pass # Handled by AnsibleAWSModule +from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn @@ -105,7 +106,7 @@ def get_nat_gateways(client, module, nat_gateway_id=None): try: result = json.loads(json.dumps(client.describe_nat_gateways(**params), default=date_handler)) except Exception as e: - module.fail_json(msg=str(e.message)) + module.fail_json(msg=to_native(e)) for gateway in result['NatGateways']: # Turn the boto3 result into ansible_friendly_snaked_names From a25b83d3aa69a0cde0cfc12812bc7f53cdc56ea9 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 26 Aug 2020 11:35:32 +0200 Subject: [PATCH 12/29] Cleanup: Bulk Migration from boto3_conn to module.client() (#188) * Migrate from boto3_conn to module.client * Simplify error handling when creating connections * Simplify Region handling * Remove unused imports * Changelog This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/6bdf00d2198927bdaa119ae76ddd379a8b6eeb3d --- plugins/modules/ec2_vpc_nat_gateway.py | 16 +++------------- plugins/modules/ec2_vpc_nat_gateway_info.py | 12 +++--------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 37dd9160084..9072a8e32b6 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -207,8 +207,6 @@ from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict @@ -969,17 +967,9 @@ def main(): if_exist_do_not_create = module.params.get('if_exist_do_not_create') 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(msg="Boto3 Client Error - " + str(e.msg)) + client = module.client('ec2') + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to connect to AWS') changed = False err_msg = '' diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index 9ebeb63fcbb..97816c72362 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -85,8 +85,6 @@ from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict @@ -133,13 +131,9 @@ def main(): date='2021-12-01', collection_name='community.aws') try: - region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True) - if region: - connection = boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_params) - else: - module.fail_json(msg="region must be specified") - except botocore.exceptions.NoCredentialsError as e: - module.fail_json(msg=str(e)) + connection = module.client('ec2') + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to connect to AWS') results = get_nat_gateways(connection, module) From ecaa5daad73c36c6d30b9cf814bc9d08b2d16af4 Mon Sep 17 00:00:00 2001 From: Alina Buzachis <49211501+alinabuzachis@users.noreply.github.com> Date: Mon, 1 Feb 2021 09:21:05 +0100 Subject: [PATCH 13/29] Tags feature: support tags specification for NAT gateway (#345) (#372) * implement tags feature for NAT gateway * add integration test tasks for tags feature * refactor integration tests (overall) removing hard-coded parameters * add missing integration test tasks without CHECK_MODE * include until loop for some tasks as they failed during the integration * added code to support tags in ec2_vap_nat_gateway - return error 'NoneType' object has no attribute 'get' because of curr_tags seems to remain None * removed tests in check_mode because not working due to DRY_RUN_GATEWAY * Addressed reviewers comments Signed-off-by: Alina Buzachis Co-authored-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/b7afd183d03ba23abb63bb371b51b788f295156b --- plugins/modules/ec2_vpc_nat_gateway.py | 151 ++++- .../ec2_vpc_nat_gateway/defaults/main.yml | 5 + .../ec2_vpc_nat_gateway/tasks/main.yml | 620 +++++++++++++++--- 3 files changed, 679 insertions(+), 97 deletions(-) create mode 100644 tests/integration/targets/ec2_vpc_nat_gateway/defaults/main.yml diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 9072a8e32b6..c4e3cbfd797 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -48,6 +48,19 @@ required: false default: false type: bool + tags: + description: + - A dict of tags to apply to the internet gateway. + - To remove all tags set I(tags={}) and I(purge_tags=true). + aliases: [ 'resource_tags' ] + type: dict + version_added: 1.4.0 + purge_tags: + description: + - Remove tags not listed in I(tags). + type: bool + default: true + version_added: 1.4.0 release_eip: description: - Deallocate the EIP from the VPC. @@ -153,6 +166,17 @@ wait: yes wait_timeout: 300 region: ap-southeast-2 + +- name: Create new nat gateway using an allocation-id and tags. + community.aws.ec2_vpc_nat_gateway: + state: present + subnet_id: subnet-12345678 + allocation_id: eipalloc-12345678 + region: ap-southeast-2 + tags: + Tag1: tag1 + Tag2: tag2 + register: new_nat_gateway ''' RETURN = ''' @@ -176,6 +200,13 @@ returned: In all cases. type: str sample: "available" +tags: + description: The tags associated the VPC NAT Gateway. + type: dict + returned: When tags are present. + sample: + tags: + "Ansible": "Test" vpc_id: description: id of the VPC. returned: In all cases. @@ -204,11 +235,17 @@ except ImportError: pass # Handled by AnsibleAWSModule -from ansible.module_utils._text import to_native + +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict - +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags +from ansible.module_utils.six import string_types +from ansible.module_utils._text import to_native DRY_RUN_GATEWAYS = [ { @@ -451,6 +488,7 @@ def gateway_in_subnet_exists(client, subnet_id, allocation_id=None, allocation_id_exists = False gateways = [] states = ['available', 'pending'] + gws_retrieved, err_msg, gws = ( get_nat_gateways( client, subnet_id, states=states, check_mode=check_mode @@ -609,7 +647,7 @@ def release_address(client, allocation_id, check_mode=False): return ip_released, err_msg -def create(client, subnet_id, allocation_id, client_token=None, +def create(client, module, subnet_id, allocation_id, tags, purge_tags, client_token=None, wait=False, wait_timeout=0, if_exist_do_not_create=False, check_mode=False): """Create an Amazon NAT Gateway. @@ -680,7 +718,6 @@ def create(client, subnet_id, allocation_id, client_token=None, 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) @@ -689,15 +726,18 @@ def create(client, subnet_id, allocation_id, client_token=None, elif wait: success, err_msg, result = ( wait_for_status( - client, wait_timeout, result['nat_gateway_id'], 'available', - check_mode=check_mode + 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']) ) - + result['tags'], tags_update_exists = ensure_tags( + client, module, nat_gw_id=result['nat_gateway_id'], tags=tags, + purge_tags=purge_tags, check_mode=check_mode + ) except is_boto3_error_code('IdempotentParameterMismatch'): err_msg = ( 'NAT Gateway does not support update and token has already been provided: ' + err_msg @@ -714,7 +754,7 @@ def create(client, subnet_id, allocation_id, client_token=None, return success, changed, err_msg, result -def pre_create(client, subnet_id, allocation_id=None, eip_address=None, +def pre_create(client, module, subnet_id, tags, purge_tags, 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. @@ -772,14 +812,18 @@ def pre_create(client, subnet_id, allocation_id=None, eip_address=None, 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) - ) - + 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: + results = existing_gateways[0] + results['tags'], tags_update_exists = ensure_tags(client, module, results['nat_gateway_id'], tags, purge_tags, check_mode) + + if tags_update_exists: + success = True + changed = True + return success, changed, err_msg, results + success = True changed = False - results = existing_gateways[0] err_msg = ( 'NAT Gateway {0} already exists in subnet_id {1}' .format( @@ -805,16 +849,22 @@ def pre_create(client, subnet_id, allocation_id=None, eip_address=None, 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): + results = existing_gateways[0] + results['tags'], tags_update_exists = ensure_tags(client, module, results['nat_gateway_id'], tags, purge_tags, check_mode) + if tags_update_exists: + success = True + changed = True + return success, changed, err_msg, results + success = True changed = False - results = existing_gateways[0] err_msg = ( 'NAT Gateway {0} already exists in subnet_id {1}' .format( @@ -824,7 +874,7 @@ def pre_create(client, subnet_id, allocation_id=None, eip_address=None, return success, changed, err_msg, results success, changed, err_msg, results = create( - client, subnet_id, allocation_id, client_token, + client, module, subnet_id, allocation_id, tags, purge_tags, client_token, wait, wait_timeout, if_exist_do_not_create, check_mode=check_mode ) @@ -919,8 +969,7 @@ def remove(client, nat_gateway_id, wait=False, wait_timeout=0, if release_eip: eip_released, eip_err = ( - release_address(client, allocation_id, check_mode) - ) + release_address(client, allocation_id, check_mode)) if not eip_released: err_msg = ( "{0}: Failed to release EIP {1}: {2}" @@ -931,6 +980,64 @@ def remove(client, nat_gateway_id, wait=False, wait_timeout=0, return success, changed, err_msg, results +def ensure_tags(client, module, nat_gw_id, tags, purge_tags, check_mode): + final_tags = [] + changed = False + + filters = ansible_dict_to_boto3_filter_list({'resource-id': nat_gw_id, 'resource-type': 'natgateway'}) + cur_tags = None + try: + cur_tags = client.describe_tags(aws_retry=True, Filters=filters) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, 'Couldnt describe tags') + if tags is None: + return boto3_tag_list_to_ansible_dict(cur_tags['Tags']), changed + + to_update, to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')), tags, purge_tags) + final_tags = boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')) + + if to_update: + try: + if check_mode: + # update tags + final_tags.update(to_update) + else: + client.create_tags( + aws_retry=True, + Resources=[nat_gw_id], + Tags=ansible_dict_to_boto3_tag_list(to_update) + ) + + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Couldn't create tags") + + if to_delete: + try: + if check_mode: + # update tags + for key in to_delete: + del final_tags[key] + else: + tags_list = [] + for key in to_delete: + tags_list.append({'Key': key}) + + client.delete_tags(aws_retry=True, Resources=[nat_gw_id], Tags=tags_list) + + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Couldn't delete tags") + + if not check_mode and (to_update or to_delete): + try: + response = client.describe_tags(aws_retry=True, Filters=filters) + final_tags = boto3_tag_list_to_ansible_dict(response.get('Tags')) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Couldn't describe tags") + return final_tags, changed + + def main(): argument_spec = dict( subnet_id=dict(type='str'), @@ -943,6 +1050,8 @@ def main(): release_eip=dict(type='bool', default=False), nat_gateway_id=dict(type='str'), client_token=dict(type='str'), + tags=dict(required=False, type='dict', aliases=['resource_tags']), + purge_tags=dict(default=True, type='bool'), ) module = AnsibleAWSModule( argument_spec=argument_spec, @@ -965,9 +1074,11 @@ def main(): 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') + tags = module.params.get('tags') + purge_tags = module.params.get('purge_tags') try: - client = module.client('ec2') + client = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Failed to connect to AWS') @@ -977,7 +1088,7 @@ def main(): if state == 'present': success, changed, err_msg, results = ( pre_create( - client, subnet_id, allocation_id, eip_address, + client, module, subnet_id, tags, purge_tags, allocation_id, eip_address, if_exist_do_not_create, wait, wait_timeout, client_token, check_mode=check_mode ) diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/defaults/main.yml b/tests/integration/targets/ec2_vpc_nat_gateway/defaults/main.yml new file mode 100644 index 00000000000..6ea912c898e --- /dev/null +++ b/tests/integration/targets/ec2_vpc_nat_gateway/defaults/main.yml @@ -0,0 +1,5 @@ +--- +vpc_name: "{{ resource_prefix }}-vpc" +vpc_seed: "{{ resource_prefix }}" +vpc_cidr: "10.0.0.0/16" +subnet_cidr: "10.0.{{ 256 | random(seed=vpc_seed) }}.0/24" diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml index 7cb7e986e0a..2b411340149 100644 --- a/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml +++ b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml @@ -3,80 +3,546 @@ # They take advantage of hard-coded results within the module to trigger both changed and unchanged responses. # They were migrated to maintain test coverage while removing unit tests that depended on use of TaskQueueManager. -- name: Create new nat gateway with eip allocation-id - ec2_vpc_nat_gateway: - subnet_id: subnet-12345678 - allocation_id: eipalloc-12345678 - wait: yes - region: us-west-2 - register: nat_gateway - check_mode: yes - -- assert: - that: - - nat_gateway.changed - -- name: Create new nat gateway with eip allocation-id - ec2_vpc_nat_gateway: - subnet_id: subnet-123456789 - allocation_id: eipalloc-1234567 - wait: yes - region: us-west-2 - register: nat_gateway - check_mode: yes - -- assert: - that: - - not nat_gateway.changed - -- name: Create new nat gateway with eip address - ec2_vpc_nat_gateway: - subnet_id: subnet-12345678 - eip_address: 55.55.55.55 - wait: yes - region: us-west-2 - register: nat_gateway - check_mode: yes - -- assert: - that: - - nat_gateway.changed - -- name: Create new nat gateway with eip address - ec2_vpc_nat_gateway: - subnet_id: subnet-123456789 - eip_address: 55.55.55.55 - wait: yes - region: us-west-2 - register: nat_gateway - check_mode: yes - -- assert: - that: - - not nat_gateway.changed - -- name: Create new nat gateway only if one does not exist already - ec2_vpc_nat_gateway: - if_exist_do_not_create: yes - subnet_id: subnet-123456789 - wait: yes - region: us-west-2 - register: nat_gateway - check_mode: yes - -- assert: - that: - - not nat_gateway.changed - -- name: Delete Nat Gateway - ec2_vpc_nat_gateway: - nat_gateway_id: nat-123456789 - state: absent - wait: yes - region: us-west-2 - register: nat_gateway - check_mode: yes - -- assert: - that: - - nat_gateway.changed +--- +# ============================================================ +# Known issues: +# +# `check_mode` is not working correctly due to the hard-coded DRY_RUN_GATEWAY (module code). The values passed here, +# when CHECK_MODE is used, don't correspond to those used for the DRY_RUN_GATEWAY and all test +# (except when the NAT gateway is created for the first time) fail. +# +# `Create new NAT gateway with eip address` - when the task is run for the first time, do we expect changed=true? +# As we use the same EIP, I think changed should be false (if this is correct, lines 194-218 are redundant and +# lines 177 and 190 should report `not create_ngw.changed`). +# ============================================================ + +- name: ec2_vpc_nat_gateway tests + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + collections: + - amazon.aws + + block: + + # ============================================================ + - name: Create a VPC + ec2_vpc_net: + name: "{{ vpc_name }}" + state: present + cidr_block: "{{ vpc_cidr }}" + register: vpc_result + + - name: Assert success + assert: + that: + - vpc_result is successful + + - name: "set fact: VPC ID" + set_fact: + vpc_id: "{{ vpc_result.vpc.id }}" + + # ============================================================ + - name: Allocate a new EIP + ec2_eip: + in_vpc: true + reuse_existing_ip_allowed: true + tag_name: FREE + register: eip_result + + - name: Assert success + assert: + that: + - eip_result is successful + + - name: "set fact: EIP allocation ID and EIP public IP" + set_fact: + eip_address: "{{ eip_result.public_ip }}" + allocation_id: "{{ eip_result.allocation_id }}" + + + # ============================================================ + - name: Create subnet and associate to the VPC + ec2_vpc_subnet: + state: present + vpc_id: "{{ vpc_id }}" + cidr: "{{ subnet_cidr }}" + register: subnet_result + + - name: Assert success + assert: + that: + - subnet_result is successful + + - name: "set fact: VPC subnet ID" + set_fact: + subnet_id: "{{ subnet_result.subnet.id }}" + + + # ============================================================ + - name: Search for NAT gateways by subnet - no matches + ec2_vpc_nat_gateway_info: + filters: + subnet-id: "{{ subnet_id }}" + state: ['available'] + register: existing_ngws + retries: 10 + until: existing_ngws is not failed + + - name: Assert no NAT gateway found + assert: + that: + - existing_ngws is successful + - (existing_ngws.result|length) == 0 + + + + # ============================================================ + - name: Create IGW + ec2_vpc_igw: + vpc_id: "{{ vpc_id }}" + register: create_igw + + - name: Assert success + assert: + that: + - create_igw is successful + + + # ============================================================ + - name: Create new NAT gateway with eip allocation-id + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + wait: yes + register: create_ngw + retries: 10 + until: create_ngw is not failed + + - name: Assert creation happened (expected changed=true) + assert: + that: + - create_ngw.changed + + - name: "set facts: NAT gateway ID" + set_fact: + nat_gateway_id: "{{ create_ngw.nat_gateway_id }}" + network_interface_id: "{{ create_ngw.nat_gateway_addresses[0].network_interface_id }}" + + # - name: Create new NAT gateway with eip allocation-id - CHECK_MODE + # ec2_vpc_nat_gateway: + # subnet_id: "{{ subnet_id }}" + # allocation_id: "{{ allocation_id }}" + # wait: yes + # register: create_ngw + # check_mode: yes + # + # - name: Assert creation happened (expected changed=true) - CHECK_MODE + # assert: + # that: + # - create_ngw.changed + + # ============================================================ + - name: Trying this again for idempotency - create new NAT gateway with eip allocation-id + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + wait: yes + register: create_ngw + retries: 10 + until: create_ngw is not failed + + - name: Assert recreation would do nothing (expected changed=false) + assert: + that: + - not create_ngw.changed + + # - name: Trying this again for idempotency - create new NAT gateway with eip allocation-id - CHECK_MODE + # ec2_vpc_nat_gateway: + # subnet_id: "{{ subnet_id }}" + # allocation_id: "{{ allocation_id }}" + # wait: yes + # register: create_ngw + # check_mode: yes + + # - name: Assert recreation would do nothing (expected changed=false) - CHECK_MODE + # assert: + # that: + # - not create_ngw.changed + + + # ============================================================ + #- name: Create new NAT gateway with eip address + # ec2_vpc_nat_gateway: + # subnet_id: "{{ subnet_id }}" + # eip_address: "{{ eip_address }}" + # wait: yes + # register: create_ngw + # + #- name: Assert creation happened (expected changed=true) + # assert: + # that: + # - create_ngw.changed + + # - name: Create new nat gateway with eip address - CHECK_MODE + # ec2_vpc_nat_gateway: + # subnet_id: "{{ subnet_id }}" + # eip_address: "{{ eip_address }}" + # wait: yes + # register: create_ngw + # check_mode: yes + + # - name: Assert creation happened (expected changed=true) - CHECK_MODE + # assert: + # that: + # - create_ngw.changed + + + # ============================================================ + # - name: Trying this again for idempotency - create new nat gateway with eip address + # ec2_vpc_nat_gateway: + # subnet_id: "{{ subnet_id }}" + # eip_address: "{{ eip_address }}" + # wait: yes + # register: create_ngw + + # - name: Assert recreation would do nothing (expected changed=false) + # assert: + # that: + # - not create_ngw.changed + + # - name: Trying this again for idempotency - create new nat gateway with eip address - CHECK_MODE + # ec2_vpc_nat_gateway: + # subnet_id: "{{ subnet_id }}" + # eip_address: "{{ eip_address }}" + # wait: yes + # register: create_ngw + # check_mode: yes + + # - name: Assert recreation would do nothing (expected changed=false) - CHECK_MODE + # assert: + # that: + # - not create_ngw.changed + + + # ============================================================ + - name: Create new NAT gateway only if one does not exist already + ec2_vpc_nat_gateway: + if_exist_do_not_create: yes + subnet_id: "{{ subnet_id }}" + wait: yes + register: create_ngw + retries: 10 + until: create_ngw is not failed + + - name: Assert recreation would do nothing (expected changed=false) + assert: + that: + - not create_ngw.changed + + # - name: Create new nat gateway only if one does not exist already - CHECK_MODE + # ec2_vpc_nat_gateway: + # if_exist_do_not_create: yes + # subnet_id: "{{ subnet_id }}" + # wait: yes + # register: create_ngw + # check_mode: yes + + # - name: Assert recreation would do nothing (expected changed=false) - CHECK_MODE + # assert: + # that: + # - not create_ngw.changed + + + # ============================================================ + - name: Delete NAT gateway + ec2_vpc_nat_gateway: + nat_gateway_id: "{{ nat_gateway_id }}" + state: absent + wait: yes + register: delete_nat_gateway + retries: 10 + until: delete_nat_gateway is not failed + + - name: Assert state=absent (expected changed=true) + assert: + that: + - delete_nat_gateway.changed + + # - name: Delete NAT gateway - CHECK_MODE + # ec2_vpc_nat_gateway: + # nat_gateway_id: "{{ nat_gateway_id }}" + # state: absent + # wait: yes + # register: delete_nat_gateway + # check_mode: yes + + # - name: Assert state=absent (expected changed=true) - CHECK_MODE + # assert: + # that: + # - delete_nat_gateway.changed + + # ============================================================ + - name: Create new NAT gateway with eip allocation-id and tags + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + tags: + tag_one: '{{ resource_prefix }} One' + "Tag Two": 'two {{ resource_prefix }}' + wait: yes + register: create_ngw + + - name: Assert creation happened (expected changed=true) + assert: + that: + - create_ngw.changed + + # - name: Create new NAT gateway with eip allocation-id and tags - CHECK_MODE + # ec2_vpc_nat_gateway: + # subnet_id: "{{ subnet_id }}" + # allocation_id: "{{ allocation_id }}" + # tags: + # tag_one: '{{ resource_prefix }} One' + # "Tag Two": 'two {{ resource_prefix }}' + # wait: yes + # register: create_ngw + # check_mode: yes + + # - name: Assert creation happened (expected changed=true) - CHECK_MODE + # assert: + # that: + # - create_ngw.changed + + + # ============================================================ + - name: Update the tags (no change) + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + tags: + tag_one: '{{ resource_prefix }} One' + "Tag Two": 'two {{ resource_prefix }}' + wait: yes + register: update_tags_ngw + + - name: assert tag update would do nothing (expected changed=false) + assert: + that: + - not update_tags_ngw.changed + + # - name: Update the tags (no change) - CHECK_MODE + # ec2_vpc_nat_gateway: + # subnet_id: "{{ subnet_id }}" + # allocation_id: "{{ allocation_id }}" + # tags: + # tag_one: '{{ resource_prefix }} One' + # "Tag Two": 'two {{ resource_prefix }}' + # wait: yes + # register: update_tags_ngw + # check_mode: yes + + # - name: assert tag update would do nothing (expected changed=false) - CHECK_MODE + # assert: + # that: + # - not update_tags_ngw.changed + + + # ============================================================ + - name: Update the tags - remove and add + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + tags: + tag_three: '{{ resource_prefix }} Three' + "Tag Two": 'two {{ resource_prefix }}' + wait: yes + register: update_tags_ngw + + - name: Assert tag update would happen (expected changed=true) + assert: + that: + - update_tags_ngw.changed + + # - name: Update the tags - remove and add - CHECK_MODE + # ec2_vpc_nat_gateway: + # subnet_id: "{{ subnet_id }}" + # allocation_id: "{{ allocation_id }}" + # tags: + # tag_three: '{{ resource_prefix }} Three' + # "Tag Two": 'two {{ resource_prefix }}' + # wait: yes + # register: update_tags_ngw + # check_mode: yes + + # - name: Assert tag update would happen (expected changed=true) - CHECK_MODE + # assert: + # that: + # - update_tags_ngw.changed + + + # ============================================================ + - name: Update the tags add without purge + ec2_vpc_nat_gateway: + if_exist_do_not_create: yes + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + purge_tags: no + tags: + tag_one: '{{ resource_prefix }} One' + wait: yes + register: update_tags_ngw + + - name: Assert tags would be added + assert: + that: + - update_tags_ngw.changed + + # - name: Update the tags add without purge - CHECK_MODE + # ec2_vpc_nat_gateway: + # if_exist_do_not_create: yes + # subnet_id: "{{ subnet_id }}" + # allocation_id: "{{ allocation_id }}" + # purge_tags: no + # tags: + # tag_one: '{{ resource_prefix }} One' + # wait: yes + # register: update_tags_ngw + # check_mode: yes + + # - name: Assert tags would be added - CHECK_MODE + # assert: + # that: + # - update_tags_ngw.changed + + + # ============================================================ + - name: Remove all tags + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + tags: {} + register: delete_tags_ngw + + - name: assert tags would be removed + assert: + that: + - delete_tags_ngw.changed + + # - name: Remove all tags - CHECK_MODE + # ec2_vpc_nat_gateway: + # subnet_id: "{{ subnet_id }}" + # allocation_id: "{{ allocation_id }}" + # tags: {} + # register: delete_tags_ngw + # check_mode: yes + + # - name: assert tags would be removed - CHECK_MODE + # assert: + # that: + # - delete_tags_ngw.changed + + + + # ============================================================ + - name: Update with CamelCase tags + ec2_vpc_nat_gateway: + if_exist_do_not_create: yes + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + purge_tags: no + tags: + "lowercase spaced": 'hello cruel world ❤️' + "Title Case": 'Hello Cruel World ❤️' + CamelCase: 'SimpleCamelCase ❤️' + snake_case: 'simple_snake_case ❤️' + wait: yes + register: update_tags_ngw + + - name: Assert tags would be added + assert: + that: + - update_tags_ngw.changed + - update_tags_ngw.tags["lowercase spaced"] == 'hello cruel world ❤️' + - update_tags_ngw.tags["Title Case"] == 'Hello Cruel World ❤️' + - update_tags_ngw.tags["CamelCase"] == 'SimpleCamelCase ❤️' + - update_tags_ngw.tags["snake_case"] == 'simple_snake_case ❤️' + + + # - name: Update with CamelCase tags - CHECK_MODE + # ec2_vpc_nat_gateway: + # if_exist_do_not_create: yes + # subnet_id: "{{ subnet_id }}" + # allocation_id: "{{ allocation_id }}" + # purge_tags: no + # tags: + # "lowercase spaced": 'hello cruel world ❤️' + # "Title Case": 'Hello Cruel World ❤️' + # CamelCase: 'SimpleCamelCase ❤️' + # snake_case: 'simple_snake_case ❤️' + # wait: yes + # register: update_tags_ngw + + #- name: Assert tags would be added - CHECK_MODE + # assert: + # that: + # - update_tags_ngw.changed + # - update_tags_ngw.tags["lowercase spaced"] == 'hello cruel world ❤️' + # - update_tags_ngw.tags["Title Case"] == 'Hello Cruel World ❤️' + # - update_tags_ngw.tags["CamelCase"] == 'SimpleCamelCase ❤️' + # - update_tags_ngw.tags["snake_case"] == 'simple_snake_case ❤️' + + # ============================================================ + + + always: + - name: Get NAT gateways + ec2_vpc_nat_gateway_info: + filters: + vpc-id: "{{ vpc_id }}" + register: existing_ngws + retries: 10 + until: existing_ngws is not failed + ignore_errors: true + + - name: Tidy up NAT gateway + ec2_vpc_nat_gateway: + subnet_id: "{{ item.subnet_id }}" + nat_gateway_id: "{{ item.nat_gateway_id }}" + #release_eip: yes + state: absent + wait: yes + with_items: "{{ existing_ngws.result }}" + ignore_errors: true + + - name: Delete IGW + ec2_vpc_igw: + vpc_id: "{{ vpc_id }}" + state: absent + ignore_errors: true + + - name: Remove subnet + ec2_vpc_subnet: + state: absent + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_id }}" + ignore_errors: true + + - name: Ensure EIP is actually released + ec2_eip: + state: absent + device_id: "{{ item.nat_gateway_addresses[0].network_interface_id }}" + in_vpc: yes + with_items: "{{ existing_ngws.result }}" + ignore_errors: yes + + - name: Delete VPC + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: "{{ vpc_cidr }}" + state: absent + purge_cidrs: yes + ignore_errors: yes From 8f50678fe1ceb95786a8421116ee3afdfd0eebd8 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 11 Feb 2021 19:32:05 +0100 Subject: [PATCH 14/29] Remove ec2_vpc_nat_gateway unit tests (#407) * Remove ec2_vpc_nat_gateway unit tests * update comments in ec2_vpc_nat_gateway integration tests - they're not based on the hard coded check_mode results any more This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/701153a6592739ec83db85284f4e7649444e61f9 --- .../ec2_vpc_nat_gateway/tasks/main.yml | 75 +++++++++---------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml index 2b411340149..36f035c81d1 100644 --- a/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml +++ b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml @@ -1,19 +1,14 @@ -# The tests for this module are incomplete. -# The tests below were migrated from unit tests. -# They take advantage of hard-coded results within the module to trigger both changed and unchanged responses. -# They were migrated to maintain test coverage while removing unit tests that depended on use of TaskQueueManager. - --- # ============================================================ # Known issues: # -# `check_mode` is not working correctly due to the hard-coded DRY_RUN_GATEWAY (module code). The values passed here, -# when CHECK_MODE is used, don't correspond to those used for the DRY_RUN_GATEWAY and all test +# `check_mode` is not working correctly due to the hard-coded DRY_RUN_GATEWAY (module code). The values passed here, +# when CHECK_MODE is used, don't correspond to those used for the DRY_RUN_GATEWAY and all test # (except when the NAT gateway is created for the first time) fail. # -# `Create new NAT gateway with eip address` - when the task is run for the first time, do we expect changed=true? -# As we use the same EIP, I think changed should be false (if this is correct, lines 194-218 are redundant and -# lines 177 and 190 should report `not create_ngw.changed`). +# `Create new NAT gateway with eip address` - when the task is run for the first time, do we expect changed=true? +# As we use the same EIP, I think changed should be false (if this is correct, lines 194-218 are redundant and +# lines 177 and 190 should report `not create_ngw.changed`). # ============================================================ - name: ec2_vpc_nat_gateway tests @@ -26,8 +21,8 @@ collections: - amazon.aws - block: - + block: + # ============================================================ - name: Create a VPC ec2_vpc_net: @@ -52,7 +47,7 @@ reuse_existing_ip_allowed: true tag_name: FREE register: eip_result - + - name: Assert success assert: that: @@ -62,7 +57,7 @@ set_fact: eip_address: "{{ eip_result.public_ip }}" allocation_id: "{{ eip_result.allocation_id }}" - + # ============================================================ - name: Create subnet and associate to the VPC @@ -80,7 +75,7 @@ - name: "set fact: VPC subnet ID" set_fact: subnet_id: "{{ subnet_result.subnet.id }}" - + # ============================================================ - name: Search for NAT gateways by subnet - no matches @@ -91,12 +86,12 @@ register: existing_ngws retries: 10 until: existing_ngws is not failed - + - name: Assert no NAT gateway found assert: that: - existing_ngws is successful - - (existing_ngws.result|length) == 0 + - (existing_ngws.result|length) == 0 @@ -105,7 +100,7 @@ ec2_vpc_igw: vpc_id: "{{ vpc_id }}" register: create_igw - + - name: Assert success assert: that: @@ -121,7 +116,7 @@ register: create_ngw retries: 10 until: create_ngw is not failed - + - name: Assert creation happened (expected changed=true) assert: that: @@ -131,7 +126,7 @@ set_fact: nat_gateway_id: "{{ create_ngw.nat_gateway_id }}" network_interface_id: "{{ create_ngw.nat_gateway_addresses[0].network_interface_id }}" - + # - name: Create new NAT gateway with eip allocation-id - CHECK_MODE # ec2_vpc_nat_gateway: # subnet_id: "{{ subnet_id }}" @@ -159,7 +154,7 @@ assert: that: - not create_ngw.changed - + # - name: Trying this again for idempotency - create new NAT gateway with eip allocation-id - CHECK_MODE # ec2_vpc_nat_gateway: # subnet_id: "{{ subnet_id }}" @@ -167,7 +162,7 @@ # wait: yes # register: create_ngw # check_mode: yes - + # - name: Assert recreation would do nothing (expected changed=false) - CHECK_MODE # assert: # that: @@ -175,7 +170,7 @@ # ============================================================ - #- name: Create new NAT gateway with eip address + #- name: Create new NAT gateway with eip address # ec2_vpc_nat_gateway: # subnet_id: "{{ subnet_id }}" # eip_address: "{{ eip_address }}" @@ -186,7 +181,7 @@ # assert: # that: # - create_ngw.changed - + # - name: Create new nat gateway with eip address - CHECK_MODE # ec2_vpc_nat_gateway: # subnet_id: "{{ subnet_id }}" @@ -242,7 +237,7 @@ assert: that: - not create_ngw.changed - + # - name: Create new nat gateway only if one does not exist already - CHECK_MODE # ec2_vpc_nat_gateway: # if_exist_do_not_create: yes @@ -257,7 +252,7 @@ # - not create_ngw.changed - # ============================================================ + # ============================================================ - name: Delete NAT gateway ec2_vpc_nat_gateway: nat_gateway_id: "{{ nat_gateway_id }}" @@ -271,7 +266,7 @@ assert: that: - delete_nat_gateway.changed - + # - name: Delete NAT gateway - CHECK_MODE # ec2_vpc_nat_gateway: # nat_gateway_id: "{{ nat_gateway_id }}" @@ -332,8 +327,8 @@ - name: assert tag update would do nothing (expected changed=false) assert: that: - - not update_tags_ngw.changed - + - not update_tags_ngw.changed + # - name: Update the tags (no change) - CHECK_MODE # ec2_vpc_nat_gateway: # subnet_id: "{{ subnet_id }}" @@ -366,7 +361,7 @@ assert: that: - update_tags_ngw.changed - + # - name: Update the tags - remove and add - CHECK_MODE # ec2_vpc_nat_gateway: # subnet_id: "{{ subnet_id }}" @@ -431,7 +426,7 @@ assert: that: - delete_tags_ngw.changed - + # - name: Remove all tags - CHECK_MODE # ec2_vpc_nat_gateway: # subnet_id: "{{ subnet_id }}" @@ -445,8 +440,8 @@ # that: # - delete_tags_ngw.changed - - + + # ============================================================ - name: Update with CamelCase tags ec2_vpc_nat_gateway: @@ -496,8 +491,8 @@ # - update_tags_ngw.tags["snake_case"] == 'simple_snake_case ❤️' # ============================================================ - - + + always: - name: Get NAT gateways ec2_vpc_nat_gateway_info: @@ -507,7 +502,7 @@ retries: 10 until: existing_ngws is not failed ignore_errors: true - + - name: Tidy up NAT gateway ec2_vpc_nat_gateway: subnet_id: "{{ item.subnet_id }}" @@ -517,19 +512,19 @@ wait: yes with_items: "{{ existing_ngws.result }}" ignore_errors: true - + - name: Delete IGW ec2_vpc_igw: vpc_id: "{{ vpc_id }}" state: absent - ignore_errors: true - + ignore_errors: true + - name: Remove subnet ec2_vpc_subnet: state: absent cidr: "{{ subnet_cidr }}" vpc_id: "{{ vpc_id }}" - ignore_errors: true + ignore_errors: true - name: Ensure EIP is actually released ec2_eip: From 4af437f8792dfb4eb135621cf27f3fab621acd5d Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sat, 20 Feb 2021 19:20:44 +0100 Subject: [PATCH 15/29] Mark the ec2_vpc_nat_gateway integration tests 'unstable' (#429) This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/cd1430d5e0d50848ad423730d92c878e5cb8c673 --- tests/integration/targets/ec2_vpc_nat_gateway/aliases | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/aliases b/tests/integration/targets/ec2_vpc_nat_gateway/aliases index 6e3860bee23..269c6f5f0a5 100644 --- a/tests/integration/targets/ec2_vpc_nat_gateway/aliases +++ b/tests/integration/targets/ec2_vpc_nat_gateway/aliases @@ -1,2 +1,5 @@ +# https://github.com/ansible-collections/community.aws/issues/428 +unstable + cloud/aws shippable/aws/group2 From 40f71388f19834a92f6c913577029d3f82c9247f Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 23 Feb 2021 14:35:02 +0100 Subject: [PATCH 16/29] improve ec2_vpc_nat_gateway stability (#427) * attempt to add more information when failures occur * Use pagination and add more retry wrappers * Mark ec2_vpc_nat_gateway stable again. Using paginator and retries seems to have fixed things * Add changelog This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/38be7f997b8b844aaa27e62d5d42b70a790fa4c3 --- plugins/modules/ec2_vpc_nat_gateway.py | 29 ++++++++++++++----- .../targets/ec2_vpc_nat_gateway/aliases | 3 -- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index c4e3cbfd797..0fdd6c96b6d 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -278,6 +278,18 @@ DRY_RUN_MSGS = 'DryRun Mode:' +@AWSRetry.jittered_backoff(retries=10) +def _describe_addresses(client, **params): + paginator = client.get_paginator('describe_addresses') + return paginator.paginate(**params).build_full_result()['Addresses'] + + +@AWSRetry.jittered_backoff(retries=10) +def _describe_nat_gateways(client, **params): + paginator = client.get_paginator('describe_nat_gateways') + return paginator.paginate(**params).build_full_result()['NatGateways'] + + def get_nat_gateways(client, subnet_id=None, nat_gateway_id=None, states=None, check_mode=False): """Retrieve a list of NAT Gateways @@ -339,7 +351,7 @@ def get_nat_gateways(client, subnet_id=None, nat_gateway_id=None, try: if not check_mode: - gateways = client.describe_nat_gateways(**params)['NatGateways'] + gateways = _describe_nat_gateways(client, **params) if gateways: for gw in gateways: existing_gateways.append(camel_dict_to_snake_dict(gw)) @@ -534,7 +546,7 @@ def get_eip_allocation_id_by_address(client, eip_address, check_mode=False): err_msg = "" try: if not check_mode: - allocations = client.describe_addresses(**params)['Addresses'] + allocations = _describe_addresses(client, **params) if len(allocations) == 1: allocation = allocations[0] else: @@ -597,7 +609,7 @@ def allocate_eip_address(client, check_mode=False): ) new_eip = 'eipalloc-{0}'.format(random_numbers) else: - new_eip = client.allocate_address(**params)['AllocationId'] + new_eip = client.allocate_address(aws_retry=True, **params)['AllocationId'] ip_allocated = True err_msg = 'eipalloc id {0} created'.format(new_eip) @@ -632,14 +644,14 @@ def release_address(client, allocation_id, check_mode=False): ip_released = False try: - client.describe_addresses(AllocationIds=[allocation_id]) + _describe_addresses(client, aws_retry=True, 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) + client.release_address(aws_retry=True, AllocationId=allocation_id) ip_released = True except botocore.exceptions.ClientError as e: err_msg = str(e) @@ -712,7 +724,7 @@ def create(client, module, subnet_id, allocation_id, tags, purge_tags, client_to try: if not check_mode: - result = camel_dict_to_snake_dict(client.create_nat_gateway(**params)["NatGateway"]) + result = camel_dict_to_snake_dict(client.create_nat_gateway(aws_retry=True, **params)["NatGateway"]) else: result = DRY_RUN_GATEWAYS[0] result['create_time'] = datetime.datetime.utcnow() @@ -939,7 +951,7 @@ def remove(client, nat_gateway_id, wait=False, wait_timeout=0, if exist and len(gw) == 1: results = gw[0] if not check_mode: - client.delete_nat_gateway(**params) + client.delete_nat_gateway(aws_retry=True, **params) allocation_id = ( results['nat_gateway_addresses'][0]['allocation_id'] @@ -1102,8 +1114,9 @@ def main(): ) if not success: + results = results or {} module.fail_json( - msg=err_msg, success=success, changed=changed + msg=err_msg, success=success, changed=changed, **results ) else: module.exit_json( diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/aliases b/tests/integration/targets/ec2_vpc_nat_gateway/aliases index 269c6f5f0a5..6e3860bee23 100644 --- a/tests/integration/targets/ec2_vpc_nat_gateway/aliases +++ b/tests/integration/targets/ec2_vpc_nat_gateway/aliases @@ -1,5 +1,2 @@ -# https://github.com/ansible-collections/community.aws/issues/428 -unstable - cloud/aws shippable/aws/group2 From 505c5b97be2425fa1deba134017bb84582b81f1c Mon Sep 17 00:00:00 2001 From: Alina Buzachis <49211501+alinabuzachis@users.noreply.github.com> Date: Wed, 24 Feb 2021 17:51:05 +0100 Subject: [PATCH 17/29] ec2_vpc_nat_gateway: increase integration tests coverage (#387) * NAT gateway: increase integration tests coverage * Add additional integration tests for ec2_vpc_nat_gateway_info and ec2_vpc_nat_gatewy modules * Add ec2_vpc_nat_gateway_info in aliases * Fix NAT gateway search Signed-off-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/fbfc351e38ec858d6e8c079670b1c9ce00af33fc --- .../targets/ec2_vpc_nat_gateway/aliases | 1 + .../ec2_vpc_nat_gateway/tasks/main.yml | 539 +++++++++++++++++- 2 files changed, 520 insertions(+), 20 deletions(-) diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/aliases b/tests/integration/targets/ec2_vpc_nat_gateway/aliases index 6e3860bee23..e2557d59343 100644 --- a/tests/integration/targets/ec2_vpc_nat_gateway/aliases +++ b/tests/integration/targets/ec2_vpc_nat_gateway/aliases @@ -1,2 +1,3 @@ cloud/aws shippable/aws/group2 +ec2_vpc_nat_gateway_info diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml index 36f035c81d1..f43a3ece55c 100644 --- a/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml +++ b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml @@ -52,6 +52,7 @@ assert: that: - eip_result is successful + - 'eip_result.allocation_id.startswith("eipalloc-")' - name: "set fact: EIP allocation ID and EIP public IP" set_fact: @@ -71,6 +72,10 @@ assert: that: - subnet_result is successful + - subnet_result.subnet.id.startswith("subnet-") + - subnet_result.subnet.cidr_block == subnet_cidr + - subnet_result.subnet.state == 'available' + - subnet_result.subnet.vpc_id == vpc_id - name: "set fact: VPC subnet ID" set_fact: @@ -93,6 +98,19 @@ - existing_ngws is successful - (existing_ngws.result|length) == 0 + # - name: Search for NAT gateways by subnet (no matches) - CHECK_MODE + # ec2_vpc_nat_gateway_info: + # filters: + # subnet-id: "{{ subnet_id }}" + # state: ['available'] + # register: existing_ngws + # check_mode: yes + + # - name: Assert no NAT gateway found - CHECK_MODE + # assert: + # that: + # - existing_ngws is successful + # - (existing_ngws.result|length) == 0 # ============================================================ @@ -105,6 +123,9 @@ assert: that: - create_igw is successful + - create_igw.gateway_id.startswith("igw-") + - create_igw.vpc_id == vpc_id + - '"gateway_id" in create_igw' # ============================================================ @@ -121,24 +142,105 @@ assert: that: - create_ngw.changed + - '"create_time" in create_ngw' + - '"nat_gateway_addresses" in create_ngw' + - '"nat_gateway_id" in create_ngw' + - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + - create_ngw.nat_gateway_id.startswith("nat-") + - '"state" in create_ngw' + - create_ngw.state == 'available' + - '"subnet_id" in create_ngw' + - create_ngw.subnet_id == subnet_id + - '"tags" in create_ngw' + - '"vpc_id" in create_ngw' + - create_ngw.vpc_id == vpc_id - name: "set facts: NAT gateway ID" set_fact: nat_gateway_id: "{{ create_ngw.nat_gateway_id }}" network_interface_id: "{{ create_ngw.nat_gateway_addresses[0].network_interface_id }}" - # - name: Create new NAT gateway with eip allocation-id - CHECK_MODE - # ec2_vpc_nat_gateway: - # subnet_id: "{{ subnet_id }}" - # allocation_id: "{{ allocation_id }}" - # wait: yes - # register: create_ngw - # check_mode: yes - # - # - name: Assert creation happened (expected changed=true) - CHECK_MODE - # assert: - # that: - # - create_ngw.changed + # - name: Create new NAT gateway with eip allocation-id - CHECK_MODE + # ec2_vpc_nat_gateway: + # subnet_id: "{{ subnet_id }}" + # allocation_id: "{{ allocation_id }}" + # wait: yes + # register: create_ngw + # check_mode: yes + # + # - name: Assert creation happened (expected changed=true) - CHECK_MODE + # assert: + # that: + # - create_ngw.changed + # - '"create_time" in create_ngw' + # - '"nat_gateway_addresses" in create_ngw' + # - '"nat_gateway_id" in create_ngw' + # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + # - create_ngw.nat_gateway_id.startswith("nat-") + # - '"state" in create_ngw' + # - create_ngw.state == 'available' + # - '"subnet_id" in create_ngw' + # - create_ngw.subnet_id == subnet_id + # - '"tags" in create_ngw' + # - '"vpc_id" in create_ngw' + # - create_ngw.vpc_id == vpc_id + + + # ============================================================ + - name: Get NAT gateway with specific filters (state and subnet) + ec2_vpc_nat_gateway_info: + filters: + subnet-id: "{{ subnet_id }}" + state: ['available'] + register: avalaible_ngws + retries: 10 + until: avalaible_ngws is not failed + + - name: Assert success + assert: + that: + - avalaible_ngws is successful + - avalaible_ngws.result | length == 1 + - '"create_time" in first_ngw' + - '"nat_gateway_addresses" in first_ngw' + - '"nat_gateway_id" in first_ngw' + - first_ngw.nat_gateway_id == nat_gateway_id + - '"state" in first_ngw' + - first_ngw.state == 'available' + - '"subnet_id" in first_ngw' + - first_ngw.subnet_id == subnet_id + - '"tags" in first_ngw' + - '"vpc_id" in first_ngw' + - first_ngw.vpc_id == vpc_id + vars: + first_ngw: '{{ avalaible_ngws.result[0] }}' + + #- name: Get all NAT gateways with specific filters (state and subnet) - CHECK_MODE + # ec2_vpc_nat_gateway_info: + # filters: + # state: ['available'] + # subnet-id: "{{ subnet_id }}" + # register: avalaible_ngws + + #- name: Assert success - CHECK_MODE + # assert: + # that: + # - avalaible_ngws is successful + # - avalaible_ngws.result | length == 1 + # - '"create_time" in first_ngw' + # - '"nat_gateway_addresses" in first_ngw' + # - '"nat_gateway_id" in first_ngw' + # - first_ngw.nat_gateway_id == nat_gateway_id + # - '"state" in first_ngw' + # - first_ngw.state == 'available' + # - '"subnet_id" in first_ngw' + # - first_ngw.subnet_id == subnet_id + # - '"tags" in first_ngw' + # - '"vpc_id" in first_ngw' + # - first_ngw.vpc_id == vpc_id + # vars: + # first_ngw: '{{ avalaible_ngws.result[0] }}' + # ============================================================ - name: Trying this again for idempotency - create new NAT gateway with eip allocation-id @@ -154,7 +256,19 @@ assert: that: - not create_ngw.changed - + - '"create_time" in create_ngw' + - '"nat_gateway_addresses" in create_ngw' + - '"nat_gateway_id" in create_ngw' + - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + - create_ngw.nat_gateway_id.startswith("nat-") + - '"state" in create_ngw' + - create_ngw.state == 'available' + - '"subnet_id" in create_ngw' + - create_ngw.subnet_id == subnet_id + - '"tags" in create_ngw' + - '"vpc_id" in create_ngw' + - create_ngw.vpc_id == vpc_id + # - name: Trying this again for idempotency - create new NAT gateway with eip allocation-id - CHECK_MODE # ec2_vpc_nat_gateway: # subnet_id: "{{ subnet_id }}" @@ -166,7 +280,19 @@ # - name: Assert recreation would do nothing (expected changed=false) - CHECK_MODE # assert: # that: - # - not create_ngw.changed + # - not create_ngw.changed + # - '"create_time" in create_ngw' + # - '"nat_gateway_addresses" in create_ngw' + # - '"nat_gateway_id" in create_ngw' + # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + # - create_ngw.nat_gateway_id.startswith("nat-") + # - '"state" in create_ngw' + # - create_ngw.state == 'available' + # - '"subnet_id" in create_ngw' + # - create_ngw.subnet_id == subnet_id + # - '"tags" in create_ngw' + # - '"vpc_id" in create_ngw' + # - create_ngw.vpc_id == vpc_id # ============================================================ @@ -181,6 +307,18 @@ # assert: # that: # - create_ngw.changed + # - '"create_time" in create_ngw' + # - '"nat_gateway_addresses" in create_ngw' + # - '"nat_gateway_id" in create_ngw' + # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + # - create_ngw.nat_gateway_id.startswith("nat-") + # - '"state" in create_ngw' + # - create_ngw.state == 'available' + # - '"subnet_id" in create_ngw' + # - create_ngw.subnet_id == subnet_id + # - '"tags" in create_ngw' + # - '"vpc_id" in create_ngw' + # - create_ngw.vpc_id == vpc_id # - name: Create new nat gateway with eip address - CHECK_MODE # ec2_vpc_nat_gateway: @@ -194,6 +332,18 @@ # assert: # that: # - create_ngw.changed + # - '"create_time" in create_ngw' + # - '"nat_gateway_addresses" in create_ngw' + # - '"nat_gateway_id" in create_ngw' + # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + # - create_ngw.nat_gateway_id.startswith("nat-") + # - '"state" in create_ngw' + # - create_ngw.state == 'available' + # - '"subnet_id" in create_ngw' + # - create_ngw.subnet_id == subnet_id + # - '"tags" in create_ngw' + # - '"vpc_id" in create_ngw' + # - create_ngw.vpc_id == vpc_id # ============================================================ @@ -208,6 +358,18 @@ # assert: # that: # - not create_ngw.changed + # - '"create_time" in create_ngw' + # - '"nat_gateway_addresses" in create_ngw' + # - '"nat_gateway_id" in create_ngw' + # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + # - create_ngw.nat_gateway_id.startswith("nat-") + # - '"state" in create_ngw' + # - create_ngw.state == 'available' + # - '"subnet_id" in create_ngw' + # - create_ngw.subnet_id == subnet_id + # - '"tags" in create_ngw' + # - '"vpc_id" in create_ngw' + # - create_ngw.vpc_id == vpc_id # - name: Trying this again for idempotency - create new nat gateway with eip address - CHECK_MODE # ec2_vpc_nat_gateway: @@ -221,6 +383,18 @@ # assert: # that: # - not create_ngw.changed + # - '"create_time" in create_ngw' + # - '"nat_gateway_addresses" in create_ngw' + # - '"nat_gateway_id" in create_ngw' + # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + # - create_ngw.nat_gateway_id.startswith("nat-") + # - '"state" in create_ngw' + # - create_ngw.state == 'available' + # - '"subnet_id" in create_ngw' + # - create_ngw.subnet_id == subnet_id + # - '"tags" in create_ngw' + # - '"vpc_id" in create_ngw' + # - create_ngw.vpc_id == vpc_id # ============================================================ @@ -237,6 +411,18 @@ assert: that: - not create_ngw.changed + - '"create_time" in create_ngw' + - '"nat_gateway_addresses" in create_ngw' + - '"nat_gateway_id" in create_ngw' + - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + - create_ngw.nat_gateway_id.startswith("nat-") + - '"state" in create_ngw' + - create_ngw.state == 'available' + - '"subnet_id" in create_ngw' + - create_ngw.subnet_id == subnet_id + - '"tags" in create_ngw' + - '"vpc_id" in create_ngw' + - create_ngw.vpc_id == vpc_id # - name: Create new nat gateway only if one does not exist already - CHECK_MODE # ec2_vpc_nat_gateway: @@ -250,7 +436,71 @@ # assert: # that: # - not create_ngw.changed + # - '"create_time" in create_ngw' + # - '"nat_gateway_addresses" in create_ngw' + # - '"nat_gateway_id" in create_ngw' + # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + # - create_ngw.nat_gateway_id.startswith("nat-") + # - '"state" in create_ngw' + # - create_ngw.state == 'available' + # - '"subnet_id" in create_ngw' + # - create_ngw.subnet_id == subnet_id + # - '"tags" in create_ngw' + # - '"vpc_id" in create_ngw' + # - create_ngw.vpc_id == vpc_id + + - name: Fetch NAT gateway by ID (list) + ec2_vpc_nat_gateway_info: + nat_gateway_ids: + - "{{ nat_gateway_id }}" + register: ngw_info + retries: 10 + until: ngw_info is not failed + - name: Check NAT gateway exists + assert: + that: + - ngw_info is successful + - ngw_info.result | length == 1 + - '"create_time" in first_ngw' + - '"nat_gateway_addresses" in first_ngw' + - '"nat_gateway_id" in first_ngw' + - first_ngw.nat_gateway_id == nat_gateway_id + - '"state" in first_ngw' + - first_ngw.state == 'available' + - '"subnet_id" in first_ngw' + - first_ngw.subnet_id == subnet_id + - '"tags" in first_ngw' + - '"vpc_id" in first_ngw' + - first_ngw.vpc_id == vpc_id + vars: + first_ngw: '{{ ngw_info.result[0] }}' + + # - name: Fetch NAT gateway by ID (list) - CHECK_MODE + # ec2_vpc_nat_gateway_info: + # nat_gateway_ids: + # - "{{ nat_gateway_id }}" + # register: ngw_info + # check_mode: yes + + # - name: Check NAT gateway exists - CHECK_MODE + # assert: + # that: + # - '"internet_gateways" in igw_info' + # - avalaible_ngws.result | length == 1 + # - '"create_time" in first_ngw' + # - '"nat_gateway_addresses" in first_ngw' + # - '"nat_gateway_id" in first_ngw' + # - first_ngw.nat_gateway_id == nat_gateway_id + # - '"state" in first_ngw' + # - first_ngw.state == 'available' + # - '"subnet_id" in first_ngw' + # - first_ngw.subnet_id == subnet_id + # - '"tags" in first_ngw' + # - '"vpc_id" in first_ngw' + # - first_ngw.vpc_id == vpc_id + # vars: + # first_ngw: '{{ ngw_info.result[0] }}' # ============================================================ - name: Delete NAT gateway @@ -266,6 +516,17 @@ assert: that: - delete_nat_gateway.changed + - '"delete_time" in delete_nat_gateway' + - '"nat_gateway_addresses" in delete_nat_gateway' + - '"nat_gateway_id" in delete_nat_gateway' + - delete_nat_gateway.nat_gateway_id == nat_gateway_id + - '"state" in delete_nat_gateway' + - delete_nat_gateway.state == 'deleted' + - '"subnet_id" in delete_nat_gateway' + - delete_nat_gateway.subnet_id == subnet_id + - '"tags" in delete_nat_gateway' + - '"vpc_id" in delete_nat_gateway' + - delete_nat_gateway.vpc_id == vpc_id # - name: Delete NAT gateway - CHECK_MODE # ec2_vpc_nat_gateway: @@ -278,7 +539,19 @@ # - name: Assert state=absent (expected changed=true) - CHECK_MODE # assert: # that: - # - delete_nat_gateway.changed + # - delete_nat_gateway.changed + # - '"delete_time" in delete_nat_gateway' + # - '"nat_gateway_addresses" in delete_nat_gateway' + # - '"nat_gateway_id" in delete_nat_gateway' + # - delete_nat_gateway.nat_gateway_id == nat_gateway_id + # - '"state" in delete_nat_gateway' + # - delete_nat_gateway.state == 'deleted' + # - '"subnet_id" in delete_nat_gateway' + # - delete_nat_gateway.subnet_id == subnet_id + # - '"tags" in delete_nat_gateway' + # - '"vpc_id" in delete_nat_gateway' + # - delete_nat_gateway.vpc_id == vpc_id + # ============================================================ - name: Create new NAT gateway with eip allocation-id and tags @@ -295,6 +568,25 @@ assert: that: - create_ngw.changed + - '"create_time" in create_ngw' + - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + - '"nat_gateway_id" in create_ngw' + - create_ngw.nat_gateway_id.startswith("nat-") + - '"state" in create_ngw' + - create_ngw.state == 'available' + - '"subnet_id" in create_ngw' + - create_ngw.subnet_id == subnet_id + - '"tags" in create_ngw' + - create_ngw.tags | length == 2 + - create_ngw.tags["tag_one"] == '{{ resource_prefix }} One' + - create_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + - '"vpc_id" in create_ngw' + - create_ngw.vpc_id == vpc_id + + + - name: "set facts: NAT gateway ID" + set_fact: + ngw_id: "{{ create_ngw.nat_gateway_id }}" # - name: Create new NAT gateway with eip allocation-id and tags - CHECK_MODE # ec2_vpc_nat_gateway: @@ -310,7 +602,21 @@ # - name: Assert creation happened (expected changed=true) - CHECK_MODE # assert: # that: - # - create_ngw.changed + # - create_ngw.changed + # - '"create_time" in create_ngw' + # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + # - '"nat_gateway_id" in create_ngw' + # - create_ngw.nat_gateway_id.startswith("nat-") + # - '"state" in create_ngw' + # - create_ngw.state == 'available' + # - '"subnet_id" in create_ngw' + # - create_ngw.subnet_id == subnet_id + # - '"tags" in create_ngw' + # - create_ngw.tags | length == 2 + # - create_ngw.tags["tag_one"] == '{{ resource_prefix }} One' + # - create_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + # - '"vpc_id" in create_ngw' + # - create_ngw.vpc_id == vpc_id # ============================================================ @@ -328,6 +634,18 @@ assert: that: - not update_tags_ngw.changed + - '"nat_gateway_id" in update_tags_ngw' + - update_tags_ngw.nat_gateway_id == ngw_id + - '"subnet_id" in update_tags_ngw' + - update_tags_ngw.subnet_id == subnet_id + - '"tags" in update_tags_ngw' + - update_tags_ngw.tags | length == 2 + - update_tags_ngw.tags["tag_one"] == '{{ resource_prefix }} One' + - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + - '"vpc_id" in update_tags_ngw' + - update_tags_ngw.vpc_id == vpc_id + + # - name: Update the tags (no change) - CHECK_MODE # ec2_vpc_nat_gateway: @@ -344,6 +662,82 @@ # assert: # that: # - not update_tags_ngw.changed + # - '"nat_gateway_id" in update_tags_ngw' + # - update_tags_ngw.nat_gateway_id == ngw_id + # - '"subnet_id" in update_tags_ngw' + # - update_tags_ngw.subnet_id == subnet_id + # - '"tags" in update_tags_ngw' + # - update_tags_ngw.tags | length == 2 + # - update_tags_ngw.tags["tag_one"] == '{{ resource_prefix }} One' + # - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + # - '"vpc_id" in update_tags_ngw' + # - update_tags_ngw.vpc_id == vpc_id + + + # ============================================================ + - name: Gather information about a filtered list of NAT Gateways using tags and state + ec2_vpc_nat_gateway_info: + filters: + "tag:Tag Two": 'two {{ resource_prefix }}' + state: ['available'] + register: ngw_info + + - name: Assert success + assert: + that: + - ngw_info is successful + - ngw_info.result | length == 1 + - '"create_time" in second_ngw' + - '"nat_gateway_addresses" in second_ngw' + - '"nat_gateway_id" in second_ngw' + - second_ngw.nat_gateway_id == ngw_id + - '"state" in second_ngw' + - second_ngw.state == 'available' + - '"subnet_id" in second_ngw' + - second_ngw.subnet_id == subnet_id + - '"tags" in second_ngw' + - second_ngw.tags | length == 2 + - '"tag_one" in second_ngw.tags' + - '"Tag Two" in second_ngw.tags' + - second_ngw.tags["tag_one"] == '{{ resource_prefix }} One' + - second_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + - '"vpc_id" in second_ngw' + - second_ngw.vpc_id == vpc_id + vars: + second_ngw: '{{ ngw_info.result[0] }}' + + + #- name: Gather information about a filtered list of NAT Gateways using tags and state - CHECK_MODE + # ec2_vpc_nat_gateway_info: + # filters: + # "tag:Tag Two": 'two {{ resource_prefix }}' + # state: ['available'] + # register: ngw_info + # check_mode: yes + + #- name: Assert success - CHECK_MODE + # assert: + # that: + # - ngw_info is successful + # - ngw_info.result | length == 1 + # - '"create_time" in second_ngw' + # - '"nat_gateway_addresses" in second_ngw' + # - '"nat_gateway_id" in second_ngw' + # - second_ngw.nat_gateway_id == ngw_id + # - '"state" in second_ngw' + # - second_ngw.state == 'available' + # - '"subnet_id" in second_ngw' + # - second_ngw.subnet_id == subnet_id + # - '"tags" in second_ngw' + # - second_ngw.tags | length == 2 + # - '"tag_one" in second_ngw.tags' + # - '"Tag Two" in second_ngw.tags' + # - second_ngw.tags["tag_one"] == '{{ resource_prefix }} One' + # - second_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + # - '"vpc_id" in second_ngw' + # - second_ngw.vpc_id == vpc_id + # vars: + # second_ngw: '{{ ngw_info.result[0] }}' # ============================================================ @@ -360,7 +754,18 @@ - name: Assert tag update would happen (expected changed=true) assert: that: - - update_tags_ngw.changed + - update_tags_ngw.changed + - '"nat_gateway_id" in update_tags_ngw' + - update_tags_ngw.nat_gateway_id == ngw_id + - '"subnet_id" in update_tags_ngw' + - update_tags_ngw.subnet_id == subnet_id + - '"tags" in update_tags_ngw' + - update_tags_ngw.tags | length == 2 + - update_tags_ngw.tags["tag_three"] == '{{ resource_prefix }} Three' + - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + - '"vpc_id" in update_tags_ngw' + - update_tags_ngw.vpc_id == vpc_id + # - name: Update the tags - remove and add - CHECK_MODE # ec2_vpc_nat_gateway: @@ -376,7 +781,44 @@ # - name: Assert tag update would happen (expected changed=true) - CHECK_MODE # assert: # that: - # - update_tags_ngw.changed + # - update_tags_ngw.changed + # - '"nat_gateway_id" in update_tags_ngw' + # - update_tags_ngw.nat_gateway_id == ngw_id + # - '"subnet_id" in update_tags_ngw' + # - update_tags_ngw.subnet_id == subnet_id + # - '"tags" in update_tags_ngw' + # - update_tags_ngw.tags | length == 2 + # - update_tags_ngw.tags["tag_three"] == '{{ resource_prefix }} Three' + # - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + # - '"vpc_id" in update_tags_ngw' + # - update_tags_ngw.vpc_id == vpc_id + + + # ============================================================ + - name: Gather information about a filtered list of NAT Gateways using tags (no match) + ec2_vpc_nat_gateway_info: + filters: + "tag:tag_one": '{{ resource_prefix }} One' + register: ngw_info + + - name: Assert success + assert: + that: + - ngw_info is successful + - ngw_info.result | length == 0 + + #- name: Gather information about a filtered list of NAT Gateways using tags (no match) - CHECK_MODE + # ec2_vpc_nat_gateway_info: + # filters: + # "tag:tag_one": '{{ resource_prefix }} One' + # register: ngw_info + # check_mode: yes + + #- name: Assert success - CHECK_MODE + # assert: + # that: + # - ngw_info is successful + # - ngw_info.result | length == 0 # ============================================================ @@ -395,6 +837,18 @@ assert: that: - update_tags_ngw.changed + - '"nat_gateway_id" in update_tags_ngw' + - update_tags_ngw.nat_gateway_id == ngw_id + - '"subnet_id" in update_tags_ngw' + - update_tags_ngw.subnet_id == subnet_id + - '"tags" in update_tags_ngw' + - update_tags_ngw.tags | length == 3 + - update_tags_ngw.tags["tag_one"] == '{{ resource_prefix }} One' + - update_tags_ngw.tags["tag_three"] == '{{ resource_prefix }} Three' + - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + - '"vpc_id" in update_tags_ngw' + - update_tags_ngw.vpc_id == vpc_id + # - name: Update the tags add without purge - CHECK_MODE # ec2_vpc_nat_gateway: @@ -411,7 +865,19 @@ # - name: Assert tags would be added - CHECK_MODE # assert: # that: - # - update_tags_ngw.changed + # - update_tags_ngw.changed + # - '"nat_gateway_id" in update_tags_ngw' + # - update_tags_ngw.nat_gateway_id == ngw_id + # - '"subnet_id" in update_tags_ngw' + # - update_tags_ngw.subnet_id == subnet_id + # - '"tags" in update_tags_ngw' + # - update_tags_ngw.tags | length == 3 + # - update_tags_ngw.tags["tag_one"] == '{{ resource_prefix }} One' + # - update_tags_ngw.tags["tag_three"] == '{{ resource_prefix }} Three' + # - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + # - '"vpc_id" in update_tags_ngw' + # - update_tags_ngw.vpc_id == vpc_id + # ============================================================ @@ -426,6 +892,14 @@ assert: that: - delete_tags_ngw.changed + - '"nat_gateway_id" in delete_tags_ngw' + - delete_tags_ngw.nat_gateway_id == ngw_id + - '"subnet_id" in delete_tags_ngw' + - delete_tags_ngw.subnet_id == subnet_id + - '"tags" in delete_tags_ngw' + - delete_tags_ngw.tags | length == 0 + - '"vpc_id" in delete_tags_ngw' + - delete_tags_ngw.vpc_id == vpc_id # - name: Remove all tags - CHECK_MODE # ec2_vpc_nat_gateway: @@ -438,7 +912,15 @@ # - name: assert tags would be removed - CHECK_MODE # assert: # that: - # - delete_tags_ngw.changed + # - delete_tags_ngw.changed + # - '"nat_gateway_id" in delete_tags_ngw' + # - delete_tags_ngw.nat_gateway_id == ngw_id + # - '"subnet_id" in delete_tags_ngw' + # - delete_tags_ngw.subnet_id == subnet_id + # - '"tags" in delete_tags_ngw' + # - delete_tags_ngw.tags | length == 0 + # - '"vpc_id" in delete_tags_ngw' + # - delete_tags_ngw.vpc_id == vpc_id @@ -461,10 +943,18 @@ assert: that: - update_tags_ngw.changed + - '"nat_gateway_id" in update_tags_ngw' + - update_tags_ngw.nat_gateway_id == ngw_id + - '"subnet_id" in update_tags_ngw' + - update_tags_ngw.subnet_id == subnet_id + - '"tags" in update_tags_ngw' + - update_tags_ngw.tags | length == 4 - update_tags_ngw.tags["lowercase spaced"] == 'hello cruel world ❤️' - update_tags_ngw.tags["Title Case"] == 'Hello Cruel World ❤️' - update_tags_ngw.tags["CamelCase"] == 'SimpleCamelCase ❤️' - update_tags_ngw.tags["snake_case"] == 'simple_snake_case ❤️' + - '"vpc_id" in update_tags_ngw' + - update_tags_ngw.vpc_id == vpc_id # - name: Update with CamelCase tags - CHECK_MODE @@ -480,15 +970,24 @@ # snake_case: 'simple_snake_case ❤️' # wait: yes # register: update_tags_ngw + # check_mode: yes #- name: Assert tags would be added - CHECK_MODE # assert: # that: # - update_tags_ngw.changed + # - '"nat_gateway_id" in update_tags_ngw' + # - update_tags_ngw.nat_gateway_id == ngw_id + # - '"subnet_id" in update_tags_ngw' + # - update_tags_ngw.subnet_id == subnet_id + # - '"tags" in update_tags_ngw' + # - update_tags_ngw.tags | length == 4 # - update_tags_ngw.tags["lowercase spaced"] == 'hello cruel world ❤️' # - update_tags_ngw.tags["Title Case"] == 'Hello Cruel World ❤️' # - update_tags_ngw.tags["CamelCase"] == 'SimpleCamelCase ❤️' # - update_tags_ngw.tags["snake_case"] == 'simple_snake_case ❤️' + # - '"vpc_id" in update_tags_ngw' + # - update_tags_ngw.vpc_id == vpc_id # ============================================================ From 6c5ef695b8349e7cfa0b532b9043da7c741c8ad6 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 25 Feb 2021 01:21:24 +0100 Subject: [PATCH 18/29] move describe_addresses call back to non-paginated (pagination not supported) (#441) This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/2fe5c31f4d5d761d13f1ba146855901231c2525a --- plugins/modules/ec2_vpc_nat_gateway.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 0fdd6c96b6d..428f82b392b 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -278,12 +278,6 @@ DRY_RUN_MSGS = 'DryRun Mode:' -@AWSRetry.jittered_backoff(retries=10) -def _describe_addresses(client, **params): - paginator = client.get_paginator('describe_addresses') - return paginator.paginate(**params).build_full_result()['Addresses'] - - @AWSRetry.jittered_backoff(retries=10) def _describe_nat_gateways(client, **params): paginator = client.get_paginator('describe_nat_gateways') @@ -546,7 +540,7 @@ def get_eip_allocation_id_by_address(client, eip_address, check_mode=False): err_msg = "" try: if not check_mode: - allocations = _describe_addresses(client, **params) + allocations = client.describe_addresses(aws_retry=True, **params) if len(allocations) == 1: allocation = allocations[0] else: @@ -644,7 +638,7 @@ def release_address(client, allocation_id, check_mode=False): ip_released = False try: - _describe_addresses(client, aws_retry=True, AllocationIds=[allocation_id]) + client.describe_addresses(aws_retry=True, AllocationIds=[allocation_id]) except botocore.exceptions.ClientError as e: # IP address likely already released # Happens with gateway in 'deleted' state that From e74cbe3c00d4d43780be91e389ac7919f1c2a16b Mon Sep 17 00:00:00 2001 From: Alina Buzachis <49211501+alinabuzachis@users.noreply.github.com> Date: Thu, 25 Feb 2021 21:11:13 +0100 Subject: [PATCH 19/29] ec2_vpc_nat_gateway_info: add retry decorator (#446) * Solve RequestLimitExceeded error by adding the retry decorator Signed-off-by: Alina Buzachis Co-authored-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/52fb23054101acbd78e13a45048ddcf935899586 --- plugins/modules/ec2_vpc_nat_gateway_info.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index 97816c72362..7d31eeac993 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -84,6 +84,7 @@ pass # Handled by AnsibleAWSModule from ansible.module_utils._text import to_native +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list @@ -102,7 +103,7 @@ def get_nat_gateways(client, module, nat_gateway_id=None): params['NatGatewayIds'] = module.params.get('nat_gateway_ids') try: - result = json.loads(json.dumps(client.describe_nat_gateways(**params), default=date_handler)) + result = json.loads(json.dumps(client.describe_nat_gateways(aws_retry=True, **params), default=date_handler)) except Exception as e: module.fail_json(msg=to_native(e)) @@ -131,7 +132,7 @@ def main(): date='2021-12-01', collection_name='community.aws') try: - connection = module.client('ec2') + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Failed to connect to AWS') From ee67b1fe9711b9bf62608fda197607ab231ba1e4 Mon Sep 17 00:00:00 2001 From: Alina Buzachis <49211501+alinabuzachis@users.noreply.github.com> Date: Wed, 10 Mar 2021 11:47:13 +0100 Subject: [PATCH 20/29] Fix broken check_mode (#436) * ec2_vpc_nat_gateway: fix broken check_mode * fix broken check_mode (remove hard coded values) Signed-off-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/044b30c7459b9bc2937c444a2f2641ba5adfdc91 --- plugins/modules/ec2_vpc_nat_gateway.py | 127 +-- .../ec2_vpc_nat_gateway/tasks/main.yml | 968 ++++++++---------- 2 files changed, 481 insertions(+), 614 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 428f82b392b..11c271434d9 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -247,36 +247,6 @@ from ansible.module_utils.six import string_types from ansible.module_utils._text import to_native -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:' - @AWSRetry.jittered_backoff(retries=10) def _describe_nat_gateways(client, **params): @@ -344,22 +314,11 @@ def get_nat_gateways(client, subnet_id=None, nat_gateway_id=None, ] try: - if not check_mode: - gateways = _describe_nat_gateways(client, **params) - 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) - + gateways = _describe_nat_gateways(client, **params) + if gateways: + for gw in gateways: + existing_gateways.append(camel_dict_to_snake_dict(gw)) + gateways_retrieved = True except botocore.exceptions.ClientError as e: err_msg = str(e) @@ -422,8 +381,6 @@ def wait_for_status(client, wait_timeout, nat_gateway_id, status, ) 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 @@ -500,6 +457,7 @@ def gateway_in_subnet_exists(client, subnet_id, allocation_id=None, client, subnet_id, states=states, check_mode=check_mode ) ) + if not gws_retrieved: return gateways, allocation_id_exists for gw in gws: @@ -538,21 +496,14 @@ def get_eip_allocation_id_by_address(client, eip_address, check_mode=False): } allocation_id = None err_msg = "" + try: - if not check_mode: - allocations = client.describe_addresses(aws_retry=True, **params) - if len(allocations) == 1: - allocation = allocations[0] - else: - allocation = None + allocations = client.describe_addresses(aws_retry=True, **params)['Addresses'] + if len(allocations) == 1: + allocation = allocations[0] 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 + allocation = None + if allocation: if allocation.get('Domain') != 'vpc': err_msg = ( @@ -595,16 +546,15 @@ def allocate_eip_address(client, check_mode=False): params = { 'Domain': 'vpc', } + + if check_mode: + ip_allocated = True + new_eip = None + return ip_allocated, err_msg, new_eip + 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(aws_retry=True, **params)['AllocationId'] - ip_allocated = True + new_eip = client.allocate_address(aws_retry=True, **params)['AllocationId'] + ip_allocated = True err_msg = 'eipalloc id {0} created'.format(new_eip) except botocore.exceptions.ClientError as e: @@ -633,6 +583,7 @@ def release_address(client, allocation_id, check_mode=False): Boolean, string """ err_msg = '' + if check_mode: return True, '' @@ -711,22 +662,24 @@ def create(client, module, subnet_id, allocation_id, tags, purge_tags, client_to success = False token_provided = False err_msg = "" + result = {} if client_token: token_provided = True params['ClientToken'] = client_token + if check_mode: + success = True + changed = True + return success, changed, err_msg, result + try: - if not check_mode: - result = camel_dict_to_snake_dict(client.create_nat_gateway(aws_retry=True, **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 + result = camel_dict_to_snake_dict(client.create_nat_gateway(aws_retry=True, **params)["NatGateway"]) success = True changed = True + create_time = result['create_time'].replace(tzinfo=None) + if token_provided and (request_time > create_time): changed = False elif wait: @@ -815,10 +768,11 @@ def pre_create(client, module, subnet_id, tags, purge_tags, allocation_id=None, success = False changed = False err_msg = "" - results = list() + results = {} 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: results = existing_gateways[0] results['tags'], tags_update_exists = ensure_tags(client, module, results['nat_gateway_id'], tags, purge_tags, check_mode) @@ -855,6 +809,7 @@ def pre_create(client, module, subnet_id, tags, purge_tags, allocation_id=None, 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 @@ -933,8 +888,14 @@ def remove(client, nat_gateway_id, wait=False, wait_timeout=0, success = False changed = False err_msg = "" - results = list() + results = {} states = ['pending', 'available'] + + if check_mode: + changed = True + success = True + return success, changed, err_msg, results + try: exist, err_msg, gw = ( get_nat_gateways( @@ -944,8 +905,7 @@ def remove(client, nat_gateway_id, wait=False, wait_timeout=0, ) if exist and len(gw) == 1: results = gw[0] - if not check_mode: - client.delete_nat_gateway(aws_retry=True, **params) + client.delete_nat_gateway(aws_retry=True, **params) allocation_id = ( results['nat_gateway_addresses'][0]['allocation_id'] @@ -990,6 +950,10 @@ def ensure_tags(client, module, nat_gw_id, tags, purge_tags, check_mode): final_tags = [] changed = False + if check_mode and nat_gw_id is None: + # We can't describe tags without an EIP id, we might get here when creating a new EIP in check_mode + return final_tags, changed + filters = ansible_dict_to_boto3_filter_list({'resource-id': nat_gw_id, 'resource-type': 'natgateway'}) cur_tags = None try: @@ -1041,6 +1005,7 @@ def ensure_tags(client, module, nat_gw_id, tags, purge_tags, check_mode): final_tags = boto3_tag_list_to_ansible_dict(response.get('Tags')) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Couldn't describe tags") + return final_tags, changed diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml index f43a3ece55c..3dcb70a153e 100644 --- a/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml +++ b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml @@ -1,16 +1,4 @@ --- -# ============================================================ -# Known issues: -# -# `check_mode` is not working correctly due to the hard-coded DRY_RUN_GATEWAY (module code). The values passed here, -# when CHECK_MODE is used, don't correspond to those used for the DRY_RUN_GATEWAY and all test -# (except when the NAT gateway is created for the first time) fail. -# -# `Create new NAT gateway with eip address` - when the task is run for the first time, do we expect changed=true? -# As we use the same EIP, I think changed should be false (if this is correct, lines 194-218 are redundant and -# lines 177 and 190 should report `not create_ngw.changed`). -# ============================================================ - - name: ec2_vpc_nat_gateway tests module_defaults: group/aws: @@ -22,7 +10,7 @@ - amazon.aws block: - + # ============================================================ - name: Create a VPC ec2_vpc_net: @@ -35,11 +23,20 @@ assert: that: - vpc_result is successful + - '"vpc" in vpc_result' + - '"cidr_block" in vpc_result.vpc' + - vpc_result.vpc.cidr_block == vpc_cidr + - '"id" in vpc_result.vpc' + - vpc_result.vpc.id.startswith("vpc-") + - '"state" in vpc_result.vpc' + - vpc_result.vpc.state == 'available' + - '"tags" in vpc_result.vpc' - name: "set fact: VPC ID" set_fact: vpc_id: "{{ vpc_result.vpc.id }}" + # ============================================================ - name: Allocate a new EIP ec2_eip: @@ -52,7 +49,9 @@ assert: that: - eip_result is successful + - '"allocation_id" in eip_result' - 'eip_result.allocation_id.startswith("eipalloc-")' + - '"public_ip" in eip_result' - name: "set fact: EIP allocation ID and EIP public IP" set_fact: @@ -72,9 +71,14 @@ assert: that: - subnet_result is successful - - subnet_result.subnet.id.startswith("subnet-") + - '"subnet" in subnet_result' + - '"cidr_block" in subnet_result.subnet' - subnet_result.subnet.cidr_block == subnet_cidr + - '"id" in subnet_result.subnet' + - subnet_result.subnet.id.startswith("subnet-") + - '"state" in subnet_result.subnet' - subnet_result.subnet.state == 'available' + - '"tags" in subnet_result.subnet' - subnet_result.subnet.vpc_id == vpc_id - name: "set fact: VPC subnet ID" @@ -83,34 +87,32 @@ # ============================================================ - - name: Search for NAT gateways by subnet - no matches + - name: Search for NAT gateways by subnet (no matches) - CHECK_MODE ec2_vpc_nat_gateway_info: filters: subnet-id: "{{ subnet_id }}" state: ['available'] register: existing_ngws - retries: 10 - until: existing_ngws is not failed + check_mode: yes - - name: Assert no NAT gateway found + - name: Assert no NAT gateway found - CHECK_MODE assert: that: - existing_ngws is successful - (existing_ngws.result|length) == 0 - # - name: Search for NAT gateways by subnet (no matches) - CHECK_MODE - # ec2_vpc_nat_gateway_info: - # filters: - # subnet-id: "{{ subnet_id }}" - # state: ['available'] - # register: existing_ngws - # check_mode: yes + - name: Search for NAT gateways by subnet - no matches + ec2_vpc_nat_gateway_info: + filters: + subnet-id: "{{ subnet_id }}" + state: ['available'] + register: existing_ngws - # - name: Assert no NAT gateway found - CHECK_MODE - # assert: - # that: - # - existing_ngws is successful - # - (existing_ngws.result|length) == 0 + - name: Assert no NAT gateway found + assert: + that: + - existing_ngws is successful + - (existing_ngws.result|length) == 0 # ============================================================ @@ -129,14 +131,25 @@ # ============================================================ + - name: Create new NAT gateway with eip allocation-id - CHECK_MODE + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + wait: yes + register: create_ngw + check_mode: yes + + - name: Assert creation happened (expected changed=true) - CHECK_MODE + assert: + that: + - create_ngw.changed + - name: Create new NAT gateway with eip allocation-id ec2_vpc_nat_gateway: subnet_id: "{{ subnet_id }}" allocation_id: "{{ allocation_id }}" wait: yes register: create_ngw - retries: 10 - until: create_ngw is not failed - name: Assert creation happened (expected changed=true) assert: @@ -160,99 +173,44 @@ nat_gateway_id: "{{ create_ngw.nat_gateway_id }}" network_interface_id: "{{ create_ngw.nat_gateway_addresses[0].network_interface_id }}" - # - name: Create new NAT gateway with eip allocation-id - CHECK_MODE - # ec2_vpc_nat_gateway: - # subnet_id: "{{ subnet_id }}" - # allocation_id: "{{ allocation_id }}" - # wait: yes - # register: create_ngw - # check_mode: yes - # - # - name: Assert creation happened (expected changed=true) - CHECK_MODE - # assert: - # that: - # - create_ngw.changed - # - '"create_time" in create_ngw' - # - '"nat_gateway_addresses" in create_ngw' - # - '"nat_gateway_id" in create_ngw' - # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id - # - create_ngw.nat_gateway_id.startswith("nat-") - # - '"state" in create_ngw' - # - create_ngw.state == 'available' - # - '"subnet_id" in create_ngw' - # - create_ngw.subnet_id == subnet_id - # - '"tags" in create_ngw' - # - '"vpc_id" in create_ngw' - # - create_ngw.vpc_id == vpc_id - - - # ============================================================ + + # ============================================================ - name: Get NAT gateway with specific filters (state and subnet) ec2_vpc_nat_gateway_info: - filters: - subnet-id: "{{ subnet_id }}" - state: ['available'] + filters: + subnet-id: "{{ subnet_id }}" + state: ['available'] register: avalaible_ngws - retries: 10 - until: avalaible_ngws is not failed - name: Assert success assert: that: - - avalaible_ngws is successful - - avalaible_ngws.result | length == 1 - - '"create_time" in first_ngw' - - '"nat_gateway_addresses" in first_ngw' - - '"nat_gateway_id" in first_ngw' - - first_ngw.nat_gateway_id == nat_gateway_id - - '"state" in first_ngw' - - first_ngw.state == 'available' - - '"subnet_id" in first_ngw' - - first_ngw.subnet_id == subnet_id - - '"tags" in first_ngw' - - '"vpc_id" in first_ngw' - - first_ngw.vpc_id == vpc_id + - avalaible_ngws is successful + - avalaible_ngws.result | length == 1 + - '"create_time" in first_ngw' + - '"nat_gateway_addresses" in first_ngw' + - '"nat_gateway_id" in first_ngw' + - first_ngw.nat_gateway_id == nat_gateway_id + - '"state" in first_ngw' + - first_ngw.state == 'available' + - '"subnet_id" in first_ngw' + - first_ngw.subnet_id == subnet_id + - '"tags" in first_ngw' + - '"vpc_id" in first_ngw' + - first_ngw.vpc_id == vpc_id vars: first_ngw: '{{ avalaible_ngws.result[0] }}' - - #- name: Get all NAT gateways with specific filters (state and subnet) - CHECK_MODE - # ec2_vpc_nat_gateway_info: - # filters: - # state: ['available'] - # subnet-id: "{{ subnet_id }}" - # register: avalaible_ngws - - #- name: Assert success - CHECK_MODE - # assert: - # that: - # - avalaible_ngws is successful - # - avalaible_ngws.result | length == 1 - # - '"create_time" in first_ngw' - # - '"nat_gateway_addresses" in first_ngw' - # - '"nat_gateway_id" in first_ngw' - # - first_ngw.nat_gateway_id == nat_gateway_id - # - '"state" in first_ngw' - # - first_ngw.state == 'available' - # - '"subnet_id" in first_ngw' - # - first_ngw.subnet_id == subnet_id - # - '"tags" in first_ngw' - # - '"vpc_id" in first_ngw' - # - first_ngw.vpc_id == vpc_id - # vars: - # first_ngw: '{{ avalaible_ngws.result[0] }}' - # ============================================================ - - name: Trying this again for idempotency - create new NAT gateway with eip allocation-id + - name: Trying this again for idempotency - create new NAT gateway with eip allocation-id - CHECK_MODE ec2_vpc_nat_gateway: subnet_id: "{{ subnet_id }}" allocation_id: "{{ allocation_id }}" wait: yes register: create_ngw - retries: 10 - until: create_ngw is not failed + check_mode: yes - - name: Assert recreation would do nothing (expected changed=false) + - name: Assert recreation would do nothing (expected changed=false) - CHECK_MODE assert: that: - not create_ngw.changed @@ -269,143 +227,64 @@ - '"vpc_id" in create_ngw' - create_ngw.vpc_id == vpc_id - # - name: Trying this again for idempotency - create new NAT gateway with eip allocation-id - CHECK_MODE - # ec2_vpc_nat_gateway: - # subnet_id: "{{ subnet_id }}" - # allocation_id: "{{ allocation_id }}" - # wait: yes - # register: create_ngw - # check_mode: yes - - # - name: Assert recreation would do nothing (expected changed=false) - CHECK_MODE - # assert: - # that: - # - not create_ngw.changed - # - '"create_time" in create_ngw' - # - '"nat_gateway_addresses" in create_ngw' - # - '"nat_gateway_id" in create_ngw' - # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id - # - create_ngw.nat_gateway_id.startswith("nat-") - # - '"state" in create_ngw' - # - create_ngw.state == 'available' - # - '"subnet_id" in create_ngw' - # - create_ngw.subnet_id == subnet_id - # - '"tags" in create_ngw' - # - '"vpc_id" in create_ngw' - # - create_ngw.vpc_id == vpc_id - - - # ============================================================ - #- name: Create new NAT gateway with eip address - # ec2_vpc_nat_gateway: - # subnet_id: "{{ subnet_id }}" - # eip_address: "{{ eip_address }}" - # wait: yes - # register: create_ngw - # - #- name: Assert creation happened (expected changed=true) - # assert: - # that: - # - create_ngw.changed - # - '"create_time" in create_ngw' - # - '"nat_gateway_addresses" in create_ngw' - # - '"nat_gateway_id" in create_ngw' - # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id - # - create_ngw.nat_gateway_id.startswith("nat-") - # - '"state" in create_ngw' - # - create_ngw.state == 'available' - # - '"subnet_id" in create_ngw' - # - create_ngw.subnet_id == subnet_id - # - '"tags" in create_ngw' - # - '"vpc_id" in create_ngw' - # - create_ngw.vpc_id == vpc_id - - # - name: Create new nat gateway with eip address - CHECK_MODE - # ec2_vpc_nat_gateway: - # subnet_id: "{{ subnet_id }}" - # eip_address: "{{ eip_address }}" - # wait: yes - # register: create_ngw - # check_mode: yes - - # - name: Assert creation happened (expected changed=true) - CHECK_MODE - # assert: - # that: - # - create_ngw.changed - # - '"create_time" in create_ngw' - # - '"nat_gateway_addresses" in create_ngw' - # - '"nat_gateway_id" in create_ngw' - # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id - # - create_ngw.nat_gateway_id.startswith("nat-") - # - '"state" in create_ngw' - # - create_ngw.state == 'available' - # - '"subnet_id" in create_ngw' - # - create_ngw.subnet_id == subnet_id - # - '"tags" in create_ngw' - # - '"vpc_id" in create_ngw' - # - create_ngw.vpc_id == vpc_id - - - # ============================================================ - # - name: Trying this again for idempotency - create new nat gateway with eip address - # ec2_vpc_nat_gateway: - # subnet_id: "{{ subnet_id }}" - # eip_address: "{{ eip_address }}" - # wait: yes - # register: create_ngw - - # - name: Assert recreation would do nothing (expected changed=false) - # assert: - # that: - # - not create_ngw.changed - # - '"create_time" in create_ngw' - # - '"nat_gateway_addresses" in create_ngw' - # - '"nat_gateway_id" in create_ngw' - # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id - # - create_ngw.nat_gateway_id.startswith("nat-") - # - '"state" in create_ngw' - # - create_ngw.state == 'available' - # - '"subnet_id" in create_ngw' - # - create_ngw.subnet_id == subnet_id - # - '"tags" in create_ngw' - # - '"vpc_id" in create_ngw' - # - create_ngw.vpc_id == vpc_id - - # - name: Trying this again for idempotency - create new nat gateway with eip address - CHECK_MODE - # ec2_vpc_nat_gateway: - # subnet_id: "{{ subnet_id }}" - # eip_address: "{{ eip_address }}" - # wait: yes - # register: create_ngw - # check_mode: yes - - # - name: Assert recreation would do nothing (expected changed=false) - CHECK_MODE - # assert: - # that: - # - not create_ngw.changed - # - '"create_time" in create_ngw' - # - '"nat_gateway_addresses" in create_ngw' - # - '"nat_gateway_id" in create_ngw' - # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id - # - create_ngw.nat_gateway_id.startswith("nat-") - # - '"state" in create_ngw' - # - create_ngw.state == 'available' - # - '"subnet_id" in create_ngw' - # - create_ngw.subnet_id == subnet_id - # - '"tags" in create_ngw' - # - '"vpc_id" in create_ngw' - # - create_ngw.vpc_id == vpc_id + - name: Trying this again for idempotency - create new NAT gateway with eip allocation-id + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + wait: yes + register: create_ngw + - name: Assert recreation would do nothing (expected changed=false) + assert: + that: + - not create_ngw.changed + - '"create_time" in create_ngw' + - '"nat_gateway_addresses" in create_ngw' + - '"nat_gateway_id" in create_ngw' + - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + - create_ngw.nat_gateway_id.startswith("nat-") + - '"state" in create_ngw' + - create_ngw.state == 'available' + - '"subnet_id" in create_ngw' + - create_ngw.subnet_id == subnet_id + - '"tags" in create_ngw' + - '"vpc_id" in create_ngw' + - create_ngw.vpc_id == vpc_id + + # ============================================================ + - name: Create new NAT gateway only if one does not exist already - CHECK_MODE + ec2_vpc_nat_gateway: + if_exist_do_not_create: yes + subnet_id: "{{ subnet_id }}" + wait: yes + register: create_ngw + check_mode: yes + + - name: Assert recreation would do nothing (expected changed=false) - CHECK_MODE + assert: + that: + - not create_ngw.changed + - '"create_time" in create_ngw' + - '"nat_gateway_addresses" in create_ngw' + - '"nat_gateway_id" in create_ngw' + - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id + - create_ngw.nat_gateway_id.startswith("nat-") + - '"state" in create_ngw' + - create_ngw.state == 'available' + - '"subnet_id" in create_ngw' + - create_ngw.subnet_id == subnet_id + - '"tags" in create_ngw' + - '"vpc_id" in create_ngw' + - create_ngw.vpc_id == vpc_id + - name: Create new NAT gateway only if one does not exist already ec2_vpc_nat_gateway: if_exist_do_not_create: yes subnet_id: "{{ subnet_id }}" wait: yes register: create_ngw - retries: 10 - until: create_ngw is not failed - name: Assert recreation would do nothing (expected changed=false) assert: @@ -423,39 +302,124 @@ - '"tags" in create_ngw' - '"vpc_id" in create_ngw' - create_ngw.vpc_id == vpc_id + + + # ============================================================ + - name: Allocate a new EIP + ec2_eip: + in_vpc: true + reuse_existing_ip_allowed: true + tag_name: FREE + register: eip_result + + - name: Assert success + assert: + that: + - eip_result is successful + - '"allocation_id" in eip_result' + - 'eip_result.allocation_id.startswith("eipalloc-")' + - '"public_ip" in eip_result' + + - name: "set fact: EIP allocation ID and EIP public IP" + set_fact: + second_eip_address: "{{ eip_result.public_ip }}" + second_allocation_id: "{{ eip_result.allocation_id }}" - # - name: Create new nat gateway only if one does not exist already - CHECK_MODE - # ec2_vpc_nat_gateway: - # if_exist_do_not_create: yes - # subnet_id: "{{ subnet_id }}" - # wait: yes - # register: create_ngw - # check_mode: yes - - # - name: Assert recreation would do nothing (expected changed=false) - CHECK_MODE - # assert: - # that: - # - not create_ngw.changed - # - '"create_time" in create_ngw' - # - '"nat_gateway_addresses" in create_ngw' - # - '"nat_gateway_id" in create_ngw' - # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id - # - create_ngw.nat_gateway_id.startswith("nat-") - # - '"state" in create_ngw' - # - create_ngw.state == 'available' - # - '"subnet_id" in create_ngw' - # - create_ngw.subnet_id == subnet_id - # - '"tags" in create_ngw' - # - '"vpc_id" in create_ngw' - # - create_ngw.vpc_id == vpc_id + - name: Create new nat gateway with eip address - CHECK_MODE + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + eip_address: "{{ second_eip_address }}" + wait: yes + register: create_ngw + check_mode: yes + + - name: Assert creation happened (expected changed=true) - CHECK_MODE + assert: + that: + - create_ngw.changed + + - name: Create new NAT gateway with eip address + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + eip_address: "{{ second_eip_address }}" + wait: yes + register: create_ngw + - name: Assert creation happened (expected changed=true) + assert: + that: + - create_ngw.changed + - '"create_time" in create_ngw' + - '"nat_gateway_addresses" in create_ngw' + - '"nat_gateway_id" in create_ngw' + - create_ngw.nat_gateway_addresses[0].allocation_id == second_allocation_id + - create_ngw.nat_gateway_id.startswith("nat-") + - '"state" in create_ngw' + - create_ngw.state == 'available' + - '"subnet_id" in create_ngw' + - create_ngw.subnet_id == subnet_id + - '"tags" in create_ngw' + - '"vpc_id" in create_ngw' + - create_ngw.vpc_id == vpc_id + + + # ============================================================ + - name: Trying this again for idempotency - create new NAT gateway with eip address - CHECK_MODE + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + eip_address: "{{ second_eip_address }}" + wait: yes + register: create_ngw + check_mode: yes + + - name: Assert recreation would do nothing (expected changed=false) - CHECK_MODE + assert: + that: + - not create_ngw.changed + - '"create_time" in create_ngw' + - '"nat_gateway_addresses" in create_ngw' + - '"nat_gateway_id" in create_ngw' + - create_ngw.nat_gateway_addresses[0].allocation_id == second_allocation_id + - create_ngw.nat_gateway_id.startswith("nat-") + - '"state" in create_ngw' + - create_ngw.state == 'available' + - '"subnet_id" in create_ngw' + - create_ngw.subnet_id == subnet_id + - '"tags" in create_ngw' + - '"vpc_id" in create_ngw' + - create_ngw.vpc_id == vpc_id + + - name: Trying this again for idempotency - create new NAT gateway with eip address + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + eip_address: "{{ second_eip_address }}" + wait: yes + register: create_ngw + + - name: Assert recreation would do nothing (expected changed=false) + assert: + that: + - not create_ngw.changed + - '"create_time" in create_ngw' + - '"nat_gateway_addresses" in create_ngw' + - '"nat_gateway_id" in create_ngw' + - create_ngw.nat_gateway_addresses[0].allocation_id == second_allocation_id + - create_ngw.nat_gateway_id.startswith("nat-") + - '"state" in create_ngw' + - create_ngw.state == 'available' + - '"subnet_id" in create_ngw' + - create_ngw.subnet_id == subnet_id + - '"tags" in create_ngw' + - '"vpc_id" in create_ngw' + - create_ngw.vpc_id == vpc_id + + + # ============================================================ - name: Fetch NAT gateway by ID (list) ec2_vpc_nat_gateway_info: nat_gateway_ids: - "{{ nat_gateway_id }}" register: ngw_info - retries: 10 - until: ngw_info is not failed - name: Check NAT gateway exists assert: @@ -476,41 +440,27 @@ vars: first_ngw: '{{ ngw_info.result[0] }}' - # - name: Fetch NAT gateway by ID (list) - CHECK_MODE - # ec2_vpc_nat_gateway_info: - # nat_gateway_ids: - # - "{{ nat_gateway_id }}" - # register: ngw_info - # check_mode: yes - - # - name: Check NAT gateway exists - CHECK_MODE - # assert: - # that: - # - '"internet_gateways" in igw_info' - # - avalaible_ngws.result | length == 1 - # - '"create_time" in first_ngw' - # - '"nat_gateway_addresses" in first_ngw' - # - '"nat_gateway_id" in first_ngw' - # - first_ngw.nat_gateway_id == nat_gateway_id - # - '"state" in first_ngw' - # - first_ngw.state == 'available' - # - '"subnet_id" in first_ngw' - # - first_ngw.subnet_id == subnet_id - # - '"tags" in first_ngw' - # - '"vpc_id" in first_ngw' - # - first_ngw.vpc_id == vpc_id - # vars: - # first_ngw: '{{ ngw_info.result[0] }}' # ============================================================ + - name: Delete NAT gateway - CHECK_MODE + ec2_vpc_nat_gateway: + nat_gateway_id: "{{ nat_gateway_id }}" + state: absent + wait: yes + register: delete_nat_gateway + check_mode: yes + + - name: Assert state=absent (expected changed=true) - CHECK_MODE + assert: + that: + - delete_nat_gateway.changed + - name: Delete NAT gateway ec2_vpc_nat_gateway: nat_gateway_id: "{{ nat_gateway_id }}" state: absent wait: yes register: delete_nat_gateway - retries: 10 - until: delete_nat_gateway is not failed - name: Assert state=absent (expected changed=true) assert: @@ -528,32 +478,24 @@ - '"vpc_id" in delete_nat_gateway' - delete_nat_gateway.vpc_id == vpc_id - # - name: Delete NAT gateway - CHECK_MODE - # ec2_vpc_nat_gateway: - # nat_gateway_id: "{{ nat_gateway_id }}" - # state: absent - # wait: yes - # register: delete_nat_gateway - # check_mode: yes - - # - name: Assert state=absent (expected changed=true) - CHECK_MODE - # assert: - # that: - # - delete_nat_gateway.changed - # - '"delete_time" in delete_nat_gateway' - # - '"nat_gateway_addresses" in delete_nat_gateway' - # - '"nat_gateway_id" in delete_nat_gateway' - # - delete_nat_gateway.nat_gateway_id == nat_gateway_id - # - '"state" in delete_nat_gateway' - # - delete_nat_gateway.state == 'deleted' - # - '"subnet_id" in delete_nat_gateway' - # - delete_nat_gateway.subnet_id == subnet_id - # - '"tags" in delete_nat_gateway' - # - '"vpc_id" in delete_nat_gateway' - # - delete_nat_gateway.vpc_id == vpc_id - # ============================================================ + - name: Create new NAT gateway with eip allocation-id and tags - CHECK_MODE + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + tags: + tag_one: '{{ resource_prefix }} One' + "Tag Two": 'two {{ resource_prefix }}' + wait: yes + register: create_ngw + check_mode: yes + + - name: Assert creation happened (expected changed=true) - CHECK_MODE + assert: + that: + - create_ngw.changed + - name: Create new NAT gateway with eip allocation-id and tags ec2_vpc_nat_gateway: subnet_id: "{{ subnet_id }}" @@ -583,44 +525,13 @@ - '"vpc_id" in create_ngw' - create_ngw.vpc_id == vpc_id - - name: "set facts: NAT gateway ID" set_fact: ngw_id: "{{ create_ngw.nat_gateway_id }}" - # - name: Create new NAT gateway with eip allocation-id and tags - CHECK_MODE - # ec2_vpc_nat_gateway: - # subnet_id: "{{ subnet_id }}" - # allocation_id: "{{ allocation_id }}" - # tags: - # tag_one: '{{ resource_prefix }} One' - # "Tag Two": 'two {{ resource_prefix }}' - # wait: yes - # register: create_ngw - # check_mode: yes - - # - name: Assert creation happened (expected changed=true) - CHECK_MODE - # assert: - # that: - # - create_ngw.changed - # - '"create_time" in create_ngw' - # - create_ngw.nat_gateway_addresses[0].allocation_id == allocation_id - # - '"nat_gateway_id" in create_ngw' - # - create_ngw.nat_gateway_id.startswith("nat-") - # - '"state" in create_ngw' - # - create_ngw.state == 'available' - # - '"subnet_id" in create_ngw' - # - create_ngw.subnet_id == subnet_id - # - '"tags" in create_ngw' - # - create_ngw.tags | length == 2 - # - create_ngw.tags["tag_one"] == '{{ resource_prefix }} One' - # - create_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' - # - '"vpc_id" in create_ngw' - # - create_ngw.vpc_id == vpc_id - # ============================================================ - - name: Update the tags (no change) + - name: Update the tags (no change) - CHECK_MODE ec2_vpc_nat_gateway: subnet_id: "{{ subnet_id }}" allocation_id: "{{ allocation_id }}" @@ -629,8 +540,9 @@ "Tag Two": 'two {{ resource_prefix }}' wait: yes register: update_tags_ngw + check_mode: yes - - name: assert tag update would do nothing (expected changed=false) + - name: assert tag update would do nothing (expected changed=false) - CHECK_MODE assert: that: - not update_tags_ngw.changed @@ -645,44 +557,42 @@ - '"vpc_id" in update_tags_ngw' - update_tags_ngw.vpc_id == vpc_id + - name: Update the tags (no change) + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + tags: + tag_one: '{{ resource_prefix }} One' + "Tag Two": 'two {{ resource_prefix }}' + wait: yes + register: update_tags_ngw - - # - name: Update the tags (no change) - CHECK_MODE - # ec2_vpc_nat_gateway: - # subnet_id: "{{ subnet_id }}" - # allocation_id: "{{ allocation_id }}" - # tags: - # tag_one: '{{ resource_prefix }} One' - # "Tag Two": 'two {{ resource_prefix }}' - # wait: yes - # register: update_tags_ngw - # check_mode: yes - - # - name: assert tag update would do nothing (expected changed=false) - CHECK_MODE - # assert: - # that: - # - not update_tags_ngw.changed - # - '"nat_gateway_id" in update_tags_ngw' - # - update_tags_ngw.nat_gateway_id == ngw_id - # - '"subnet_id" in update_tags_ngw' - # - update_tags_ngw.subnet_id == subnet_id - # - '"tags" in update_tags_ngw' - # - update_tags_ngw.tags | length == 2 - # - update_tags_ngw.tags["tag_one"] == '{{ resource_prefix }} One' - # - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' - # - '"vpc_id" in update_tags_ngw' - # - update_tags_ngw.vpc_id == vpc_id + - name: assert tag update would do nothing (expected changed=false) + assert: + that: + - not update_tags_ngw.changed + - '"nat_gateway_id" in update_tags_ngw' + - update_tags_ngw.nat_gateway_id == ngw_id + - '"subnet_id" in update_tags_ngw' + - update_tags_ngw.subnet_id == subnet_id + - '"tags" in update_tags_ngw' + - update_tags_ngw.tags | length == 2 + - update_tags_ngw.tags["tag_one"] == '{{ resource_prefix }} One' + - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + - '"vpc_id" in update_tags_ngw' + - update_tags_ngw.vpc_id == vpc_id # ============================================================ - - name: Gather information about a filtered list of NAT Gateways using tags and state + - name: Gather information about a filtered list of NAT Gateways using tags and state - CHECK_MODE ec2_vpc_nat_gateway_info: filters: "tag:Tag Two": 'two {{ resource_prefix }}' state: ['available'] register: ngw_info + check_mode: yes - - name: Assert success + - name: Assert success - CHECK_MODE assert: that: - ngw_info is successful @@ -705,43 +615,41 @@ - second_ngw.vpc_id == vpc_id vars: second_ngw: '{{ ngw_info.result[0] }}' - - #- name: Gather information about a filtered list of NAT Gateways using tags and state - CHECK_MODE - # ec2_vpc_nat_gateway_info: - # filters: - # "tag:Tag Two": 'two {{ resource_prefix }}' - # state: ['available'] - # register: ngw_info - # check_mode: yes - - #- name: Assert success - CHECK_MODE - # assert: - # that: - # - ngw_info is successful - # - ngw_info.result | length == 1 - # - '"create_time" in second_ngw' - # - '"nat_gateway_addresses" in second_ngw' - # - '"nat_gateway_id" in second_ngw' - # - second_ngw.nat_gateway_id == ngw_id - # - '"state" in second_ngw' - # - second_ngw.state == 'available' - # - '"subnet_id" in second_ngw' - # - second_ngw.subnet_id == subnet_id - # - '"tags" in second_ngw' - # - second_ngw.tags | length == 2 - # - '"tag_one" in second_ngw.tags' - # - '"Tag Two" in second_ngw.tags' - # - second_ngw.tags["tag_one"] == '{{ resource_prefix }} One' - # - second_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' - # - '"vpc_id" in second_ngw' - # - second_ngw.vpc_id == vpc_id - # vars: - # second_ngw: '{{ ngw_info.result[0] }}' + - name: Gather information about a filtered list of NAT Gateways using tags and state + ec2_vpc_nat_gateway_info: + filters: + "tag:Tag Two": 'two {{ resource_prefix }}' + state: ['available'] + register: ngw_info + + - name: Assert success + assert: + that: + - ngw_info is successful + - ngw_info.result | length == 1 + - '"create_time" in second_ngw' + - '"nat_gateway_addresses" in second_ngw' + - '"nat_gateway_id" in second_ngw' + - second_ngw.nat_gateway_id == ngw_id + - '"state" in second_ngw' + - second_ngw.state == 'available' + - '"subnet_id" in second_ngw' + - second_ngw.subnet_id == subnet_id + - '"tags" in second_ngw' + - second_ngw.tags | length == 2 + - '"tag_one" in second_ngw.tags' + - '"Tag Two" in second_ngw.tags' + - second_ngw.tags["tag_one"] == '{{ resource_prefix }} One' + - second_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + - '"vpc_id" in second_ngw' + - second_ngw.vpc_id == vpc_id + vars: + second_ngw: '{{ ngw_info.result[0] }}' # ============================================================ - - name: Update the tags - remove and add + - name: Update the tags - remove and add - CHECK_MODE ec2_vpc_nat_gateway: subnet_id: "{{ subnet_id }}" allocation_id: "{{ allocation_id }}" @@ -750,8 +658,9 @@ "Tag Two": 'two {{ resource_prefix }}' wait: yes register: update_tags_ngw + check_mode: yes - - name: Assert tag update would happen (expected changed=true) + - name: Assert tag update would happen (expected changed=true) - CHECK_MODE assert: that: - update_tags_ngw.changed @@ -766,62 +675,90 @@ - '"vpc_id" in update_tags_ngw' - update_tags_ngw.vpc_id == vpc_id + - name: Update the tags - remove and add + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + tags: + tag_three: '{{ resource_prefix }} Three' + "Tag Two": 'two {{ resource_prefix }}' + wait: yes + register: update_tags_ngw - # - name: Update the tags - remove and add - CHECK_MODE - # ec2_vpc_nat_gateway: - # subnet_id: "{{ subnet_id }}" - # allocation_id: "{{ allocation_id }}" - # tags: - # tag_three: '{{ resource_prefix }} Three' - # "Tag Two": 'two {{ resource_prefix }}' - # wait: yes - # register: update_tags_ngw - # check_mode: yes - - # - name: Assert tag update would happen (expected changed=true) - CHECK_MODE - # assert: - # that: - # - update_tags_ngw.changed - # - '"nat_gateway_id" in update_tags_ngw' - # - update_tags_ngw.nat_gateway_id == ngw_id - # - '"subnet_id" in update_tags_ngw' - # - update_tags_ngw.subnet_id == subnet_id - # - '"tags" in update_tags_ngw' - # - update_tags_ngw.tags | length == 2 - # - update_tags_ngw.tags["tag_three"] == '{{ resource_prefix }} Three' - # - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' - # - '"vpc_id" in update_tags_ngw' - # - update_tags_ngw.vpc_id == vpc_id + - name: Assert tag update would happen (expected changed=true) + assert: + that: + - update_tags_ngw.changed + - '"nat_gateway_id" in update_tags_ngw' + - update_tags_ngw.nat_gateway_id == ngw_id + - '"subnet_id" in update_tags_ngw' + - update_tags_ngw.subnet_id == subnet_id + - '"tags" in update_tags_ngw' + - update_tags_ngw.tags | length == 2 + - update_tags_ngw.tags["tag_three"] == '{{ resource_prefix }} Three' + - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + - '"vpc_id" in update_tags_ngw' + - update_tags_ngw.vpc_id == vpc_id # ============================================================ - - name: Gather information about a filtered list of NAT Gateways using tags (no match) + - name: Gather information about a filtered list of NAT Gateways using tags and state (no match) - CHECK_MODE ec2_vpc_nat_gateway_info: filters: "tag:tag_one": '{{ resource_prefix }} One' + state: ['available'] register: ngw_info + check_mode: yes - - name: Assert success + - name: Assert success - CHECK_MODE assert: that: - ngw_info is successful - ngw_info.result | length == 0 - #- name: Gather information about a filtered list of NAT Gateways using tags (no match) - CHECK_MODE - # ec2_vpc_nat_gateway_info: - # filters: - # "tag:tag_one": '{{ resource_prefix }} One' - # register: ngw_info - # check_mode: yes + - name: Gather information about a filtered list of NAT Gateways using tags and state (no match) + ec2_vpc_nat_gateway_info: + filters: + "tag:tag_one": '{{ resource_prefix }} One' + state: ['available'] + register: ngw_info - #- name: Assert success - CHECK_MODE - # assert: - # that: - # - ngw_info is successful - # - ngw_info.result | length == 0 + - name: Assert success + assert: + that: + - ngw_info is successful + - ngw_info.result | length == 0 # ============================================================ + - name: Update the tags add without purge - CHECK_MODE + ec2_vpc_nat_gateway: + if_exist_do_not_create: yes + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + purge_tags: no + tags: + tag_one: '{{ resource_prefix }} One' + wait: yes + register: update_tags_ngw + check_mode: yes + + - name: Assert tags would be added - CHECK_MODE + assert: + that: + - update_tags_ngw.changed + - '"nat_gateway_id" in update_tags_ngw' + - update_tags_ngw.nat_gateway_id == ngw_id + - '"subnet_id" in update_tags_ngw' + - update_tags_ngw.subnet_id == subnet_id + - '"tags" in update_tags_ngw' + - update_tags_ngw.tags | length == 3 + - update_tags_ngw.tags["tag_one"] == '{{ resource_prefix }} One' + - update_tags_ngw.tags["tag_three"] == '{{ resource_prefix }} Three' + - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' + - '"vpc_id" in update_tags_ngw' + - update_tags_ngw.vpc_id == vpc_id + - name: Update the tags add without purge ec2_vpc_nat_gateway: if_exist_do_not_create: yes @@ -850,37 +787,28 @@ - update_tags_ngw.vpc_id == vpc_id - # - name: Update the tags add without purge - CHECK_MODE - # ec2_vpc_nat_gateway: - # if_exist_do_not_create: yes - # subnet_id: "{{ subnet_id }}" - # allocation_id: "{{ allocation_id }}" - # purge_tags: no - # tags: - # tag_one: '{{ resource_prefix }} One' - # wait: yes - # register: update_tags_ngw - # check_mode: yes - - # - name: Assert tags would be added - CHECK_MODE - # assert: - # that: - # - update_tags_ngw.changed - # - '"nat_gateway_id" in update_tags_ngw' - # - update_tags_ngw.nat_gateway_id == ngw_id - # - '"subnet_id" in update_tags_ngw' - # - update_tags_ngw.subnet_id == subnet_id - # - '"tags" in update_tags_ngw' - # - update_tags_ngw.tags | length == 3 - # - update_tags_ngw.tags["tag_one"] == '{{ resource_prefix }} One' - # - update_tags_ngw.tags["tag_three"] == '{{ resource_prefix }} Three' - # - update_tags_ngw.tags["Tag Two"] == 'two {{ resource_prefix }}' - # - '"vpc_id" in update_tags_ngw' - # - update_tags_ngw.vpc_id == vpc_id - + # ============================================================ + - name: Remove all tags - CHECK_MODE + ec2_vpc_nat_gateway: + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + tags: {} + register: delete_tags_ngw + check_mode: yes + - name: assert tags would be removed - CHECK_MODE + assert: + that: + - delete_tags_ngw.changed + - '"nat_gateway_id" in delete_tags_ngw' + - delete_tags_ngw.nat_gateway_id == ngw_id + - '"subnet_id" in delete_tags_ngw' + - delete_tags_ngw.subnet_id == subnet_id + - '"tags" in delete_tags_ngw' + - delete_tags_ngw.tags | length == 0 + - '"vpc_id" in delete_tags_ngw' + - delete_tags_ngw.vpc_id == vpc_id - # ============================================================ - name: Remove all tags ec2_vpc_nat_gateway: subnet_id: "{{ subnet_id }}" @@ -901,30 +829,40 @@ - '"vpc_id" in delete_tags_ngw' - delete_tags_ngw.vpc_id == vpc_id - # - name: Remove all tags - CHECK_MODE - # ec2_vpc_nat_gateway: - # subnet_id: "{{ subnet_id }}" - # allocation_id: "{{ allocation_id }}" - # tags: {} - # register: delete_tags_ngw - # check_mode: yes - - # - name: assert tags would be removed - CHECK_MODE - # assert: - # that: - # - delete_tags_ngw.changed - # - '"nat_gateway_id" in delete_tags_ngw' - # - delete_tags_ngw.nat_gateway_id == ngw_id - # - '"subnet_id" in delete_tags_ngw' - # - delete_tags_ngw.subnet_id == subnet_id - # - '"tags" in delete_tags_ngw' - # - delete_tags_ngw.tags | length == 0 - # - '"vpc_id" in delete_tags_ngw' - # - delete_tags_ngw.vpc_id == vpc_id + # ============================================================ + - name: Update with CamelCase tags - CHECK_MODE + ec2_vpc_nat_gateway: + if_exist_do_not_create: yes + subnet_id: "{{ subnet_id }}" + allocation_id: "{{ allocation_id }}" + purge_tags: no + tags: + "lowercase spaced": 'hello cruel world ❤️' + "Title Case": 'Hello Cruel World ❤️' + CamelCase: 'SimpleCamelCase ❤️' + snake_case: 'simple_snake_case ❤️' + wait: yes + register: update_tags_ngw + check_mode: yes + - name: Assert tags would be added - CHECK_MODE + assert: + that: + - update_tags_ngw.changed + - '"nat_gateway_id" in update_tags_ngw' + - update_tags_ngw.nat_gateway_id == ngw_id + - '"subnet_id" in update_tags_ngw' + - update_tags_ngw.subnet_id == subnet_id + - '"tags" in update_tags_ngw' + - update_tags_ngw.tags | length == 4 + - update_tags_ngw.tags["lowercase spaced"] == 'hello cruel world ❤️' + - update_tags_ngw.tags["Title Case"] == 'Hello Cruel World ❤️' + - update_tags_ngw.tags["CamelCase"] == 'SimpleCamelCase ❤️' + - update_tags_ngw.tags["snake_case"] == 'simple_snake_case ❤️' + - '"vpc_id" in update_tags_ngw' + - update_tags_ngw.vpc_id == vpc_id - # ============================================================ - name: Update with CamelCase tags ec2_vpc_nat_gateway: if_exist_do_not_create: yes @@ -957,56 +895,20 @@ - update_tags_ngw.vpc_id == vpc_id - # - name: Update with CamelCase tags - CHECK_MODE - # ec2_vpc_nat_gateway: - # if_exist_do_not_create: yes - # subnet_id: "{{ subnet_id }}" - # allocation_id: "{{ allocation_id }}" - # purge_tags: no - # tags: - # "lowercase spaced": 'hello cruel world ❤️' - # "Title Case": 'Hello Cruel World ❤️' - # CamelCase: 'SimpleCamelCase ❤️' - # snake_case: 'simple_snake_case ❤️' - # wait: yes - # register: update_tags_ngw - # check_mode: yes - - #- name: Assert tags would be added - CHECK_MODE - # assert: - # that: - # - update_tags_ngw.changed - # - '"nat_gateway_id" in update_tags_ngw' - # - update_tags_ngw.nat_gateway_id == ngw_id - # - '"subnet_id" in update_tags_ngw' - # - update_tags_ngw.subnet_id == subnet_id - # - '"tags" in update_tags_ngw' - # - update_tags_ngw.tags | length == 4 - # - update_tags_ngw.tags["lowercase spaced"] == 'hello cruel world ❤️' - # - update_tags_ngw.tags["Title Case"] == 'Hello Cruel World ❤️' - # - update_tags_ngw.tags["CamelCase"] == 'SimpleCamelCase ❤️' - # - update_tags_ngw.tags["snake_case"] == 'simple_snake_case ❤️' - # - '"vpc_id" in update_tags_ngw' - # - update_tags_ngw.vpc_id == vpc_id - # ============================================================ - - always: - name: Get NAT gateways ec2_vpc_nat_gateway_info: filters: vpc-id: "{{ vpc_id }}" register: existing_ngws - retries: 10 - until: existing_ngws is not failed ignore_errors: true - name: Tidy up NAT gateway ec2_vpc_nat_gateway: subnet_id: "{{ item.subnet_id }}" nat_gateway_id: "{{ item.nat_gateway_id }}" - #release_eip: yes + release_eip: yes state: absent wait: yes with_items: "{{ existing_ngws.result }}" From 1e28bee0bf0ff50921110cc57de56f3df06714d4 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sat, 13 Mar 2021 20:10:09 +0100 Subject: [PATCH 21/29] More no_log=False to fix sanity tests (#474) * Add no_log=False to mark some more false-positives of the no_log check. * More false-positives confirmed by tremble. This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/67b6d751cc4ee5728577af2fe7b330f8cc7cd5c2 --- plugins/modules/ec2_vpc_nat_gateway.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 11c271434d9..b85d8ed97e8 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -1020,7 +1020,7 @@ def main(): 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'), + client_token=dict(type='str', no_log=False), tags=dict(required=False, type='dict', aliases=['resource_tags']), purge_tags=dict(default=True, type='bool'), ) From 5b202074f185bee0bfb7aa6ad5f48aa558ff1911 Mon Sep 17 00:00:00 2001 From: Alina Buzachis <49211501+alinabuzachis@users.noreply.github.com> Date: Tue, 16 Mar 2021 23:28:33 +0100 Subject: [PATCH 22/29] ec2_vpc_nat_gateway cleanup (#445) * ec2_vpc_nat_gateway overall cleanup * use custm waiters to manage NAT gateway states (deleted and available) * imporve error handling * improve documentation examples * code cleaning Signed-off-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/9b60a2d5d50ff1d77e74cf79715ccebc4632757f --- plugins/modules/ec2_vpc_nat_gateway.py | 578 ++++++++---------- .../ec2_vpc_nat_gateway/tasks/main.yml | 6 +- 2 files changed, 250 insertions(+), 334 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index b85d8ed97e8..87511fa2582 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -6,7 +6,7 @@ __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: ec2_vpc_nat_gateway version_added: 1.0.0 @@ -50,7 +50,7 @@ type: bool tags: description: - - A dict of tags to apply to the internet gateway. + - A dict of tags to apply to the NAT gateway. - To remove all tags set I(tags={}) and I(purge_tags=true). aliases: [ 'resource_tags' ] type: dict @@ -88,13 +88,13 @@ - Allen Sanabria (@linuxdynasty) - Jon Hadfield (@jonhadfield) - Karen Cheng (@Etherdaemon) + - Alina Buzachis (@alinabuzachis) extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 - ''' -EXAMPLES = ''' +EXAMPLES = r''' # Note: These examples do not set authentication details, see the AWS Guide for details. - name: Create new nat gateway with client token. @@ -167,19 +167,30 @@ wait_timeout: 300 region: ap-southeast-2 -- name: Create new nat gateway using an allocation-id and tags. +- name: Create new nat gateway using allocation-id and tags. community.aws.ec2_vpc_nat_gateway: state: present subnet_id: subnet-12345678 allocation_id: eipalloc-12345678 region: ap-southeast-2 tags: - Tag1: tag1 - Tag2: tag2 + Tag1: tag1 + Tag2: tag2 register: new_nat_gateway + +- name: Update tags without purge + community.aws.ec2_vpc_nat_gateway: + subnet_id: subnet-12345678 + allocation_id: eipalloc-12345678 + region: ap-southeast-2 + purge_tags: no + tags: + Tag3: tag3 + wait: yes + register: update_tags_nat_gateway ''' -RETURN = ''' +RETURN = r''' create_time: description: The ISO 8601 date time format in UTC. returned: In all cases. @@ -206,7 +217,7 @@ returned: When tags are present. sample: tags: - "Ansible": "Test" + "Ansible": "Test" vpc_id: description: id of the VPC. returned: In all cases. @@ -218,64 +229,79 @@ type: str sample: [ { - 'public_ip': '52.52.52.52', - 'network_interface_id': 'eni-12345', - 'private_ip': '10.0.0.100', - 'allocation_id': 'eipalloc-12345' + 'public_ip': '52.52.52.52', + 'network_interface_id': 'eni-12345', + 'private_ip': '10.0.0.100', + 'allocation_id': 'eipalloc-12345' } ] ''' import datetime -import random -import time try: import botocore except ImportError: pass # Handled by AnsibleAWSModule - from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.waiters import get_waiter from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags -from ansible.module_utils.six import string_types -from ansible.module_utils._text import to_native @AWSRetry.jittered_backoff(retries=10) def _describe_nat_gateways(client, **params): - paginator = client.get_paginator('describe_nat_gateways') - return paginator.paginate(**params).build_full_result()['NatGateways'] + try: + paginator = client.get_paginator('describe_nat_gateways') + return paginator.paginate(**params).build_full_result()['NatGateways'] + except is_boto3_error_code('InvalidNatGatewayID.NotFound'): + return None -def get_nat_gateways(client, subnet_id=None, nat_gateway_id=None, - states=None, check_mode=False): +def wait_for_status(client, module, waiter_name, nat_gateway_id): + wait_timeout = module.params.get('wait_timeout') + try: + waiter = get_waiter(client, waiter_name) + attempts = 1 + int(wait_timeout / waiter.config.delay) + waiter.wait( + NatGatewayIds=[nat_gateway_id], + WaiterConfig={'MaxAttempts': attempts} + ) + except botocore.exceptions.WaiterError as e: + module.fail_json_aws(e, msg="NAT gateway failed to reach expected state.") + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Unable to wait for NAT gateway state to update.") + + +def get_nat_gateways(client, module, subnet_id=None, nat_gateway_id=None, states=None): """Retrieve a list of NAT Gateways Args: client (botocore.client.EC2): Boto3 client + module: AnsibleAWSModule class instance Kwargs: subnet_id (str): The subnet_id the nat resides in. - nat_gateway_id (str): The Amazon nat id. + 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') + >>> module = AnsibleAWSModule(...) >>> subnet_id = 'subnet-12345678' - >>> get_nat_gateways(client, subnet_id) + >>> get_nat_gateways(client, module, subnet_id) [ true, "", { - "nat_gateway_id": "nat-123456789", - "subnet_id": "subnet-123456789", + "create_time": "2016-03-05T00:33:21.209000+00:00", + "delete_time": "2016-03-05T00:36:37.329000+00:00", "nat_gateway_addresses": [ { "public_ip": "55.55.55.55", @@ -284,19 +310,20 @@ def get_nat_gateways(client, subnet_id=None, nat_gateway_id=None, "allocation_id": "eipalloc-1234567" } ], + "nat_gateway_id": "nat-123456789", "state": "deleted", - "create_time": "2016-03-05T00:33:21.209000+00:00", - "delete_time": "2016-03-05T00:36:37.329000+00:00", + "subnet_id": "subnet-123456789", + "tags": {}, "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: @@ -318,100 +345,17 @@ def get_nat_gateways(client, subnet_id=None, nat_gateway_id=None, if gateways: for gw in gateways: existing_gateways.append(camel_dict_to_snake_dict(gw)) - gateways_retrieved = True - 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 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" + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e) - return status_achieved, err_msg, nat_gateway + return existing_gateways -def gateway_in_subnet_exists(client, subnet_id, allocation_id=None, - check_mode=False): +def gateway_in_subnet_exists(client, module, subnet_id, allocation_id=None): """Retrieve all NAT Gateways for a subnet. Args: + client (botocore.client.EC2): Boto3 client + module: AnsibleAWSModule class instance subnet_id (str): The subnet_id the nat resides in. Kwargs: @@ -420,14 +364,15 @@ def gateway_in_subnet_exists(client, subnet_id, allocation_id=None, Basic Usage: >>> client = boto3.client('ec2') + >>> module = AnsibleAWSModule(...) >>> subnet_id = 'subnet-1234567' >>> allocation_id = 'eipalloc-1234567' - >>> gateway_in_subnet_exists(client, subnet_id, allocation_id) + >>> gateway_in_subnet_exists(client, module, subnet_id, allocation_id) ( [ { - "nat_gateway_id": "nat-123456789", - "subnet_id": "subnet-123456789", + "create_time": "2016-03-05T00:33:21.209000+00:00", + "delete_time": "2016-03-05T00:36:37.329000+00:00", "nat_gateway_addresses": [ { "public_ip": "55.55.55.55", @@ -436,9 +381,10 @@ def gateway_in_subnet_exists(client, subnet_id, allocation_id=None, "allocation_id": "eipalloc-1234567" } ], + "nat_gateway_id": "nat-123456789", "state": "deleted", - "create_time": "2016-03-05T00:33:21.209000+00:00", - "delete_time": "2016-03-05T00:36:37.329000+00:00", + "subnet_id": "subnet-123456789", + "tags": {}, "vpc_id": "vpc-1234567" } ], @@ -448,57 +394,53 @@ def gateway_in_subnet_exists(client, subnet_id, allocation_id=None, Returns: Tuple (list, bool) """ + allocation_id_exists = False gateways = [] states = ['available', 'pending'] - gws_retrieved, err_msg, gws = ( - get_nat_gateways( - client, subnet_id, states=states, check_mode=check_mode - ) - ) + gws_retrieved = (get_nat_gateways(client, module, subnet_id, states=states)) - 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 + if gws_retrieved: + for gw in gws_retrieved: + 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) - else: - gateways.append(gw) return gateways, allocation_id_exists -def get_eip_allocation_id_by_address(client, eip_address, check_mode=False): +def get_eip_allocation_id_by_address(client, module, eip_address): """Release an EIP from your EIP Pool Args: client (botocore.client.EC2): Boto3 client + module: AnsibleAWSModule class instance 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') + >>> module = AnsibleAWSModule(...) >>> eip_address = '52.87.29.36' - >>> get_eip_allocation_id_by_address(client, eip_address) + >>> get_eip_allocation_id_by_address(client, module, eip_address) 'eipalloc-36014da3' Returns: Tuple (str, str) """ + params = { 'PublicIps': [eip_address], } allocation_id = None - err_msg = "" + msg = '' try: allocations = client.describe_addresses(aws_retry=True, **params)['Addresses'] + if len(allocations) == 1: allocation = allocations[0] else: @@ -506,135 +448,137 @@ def get_eip_allocation_id_by_address(client, eip_address, check_mode=False): if allocation: if allocation.get('Domain') != 'vpc': - err_msg = ( + 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) + except is_boto3_error_code('InvalidAddress.Malformed') as e: + module.fail_json(msg='EIP address {0} is invalid.'.format(eip_address)) + except is_boto3_error_code('InvalidAddress.NotFound') as e: # pylint: disable=duplicate-except + msg = ( + "EIP {0} does not exist".format(eip_address) + ) + allocation_id = None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e) - return allocation_id, err_msg + return allocation_id, msg -def allocate_eip_address(client, check_mode=False): +def allocate_eip_address(client, module): """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. + module: AnsibleAWSModule class instance Basic Usage: >>> client = boto3.client('ec2') - >>> allocate_eip_address(client) + >>> module = AnsibleAWSModule(...) + >>> allocate_eip_address(client, module) True Returns: Tuple (bool, str) """ - ip_allocated = False + new_eip = None - err_msg = '' + msg = '' params = { 'Domain': 'vpc', } - if check_mode: + if module.check_mode: ip_allocated = True new_eip = None - return ip_allocated, err_msg, new_eip + return ip_allocated, msg, new_eip try: new_eip = client.allocate_address(aws_retry=True, **params)['AllocationId'] ip_allocated = True - err_msg = 'eipalloc id {0} created'.format(new_eip) - - except botocore.exceptions.ClientError as e: - err_msg = str(e) + msg = 'eipalloc id {0} created'.format(new_eip) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e) - return ip_allocated, err_msg, new_eip + return ip_allocated, msg, new_eip -def release_address(client, allocation_id, check_mode=False): +def release_address(client, module, allocation_id): """Release an EIP from your EIP Pool Args: client (botocore.client.EC2): Boto3 client + module: AnsibleAWSModule class instance 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') + >>> module = AnsibleAWSModule(...) >>> allocation_id = "eipalloc-123456" - >>> release_address(client, allocation_id) + >>> release_address(client, module, allocation_id) True Returns: Boolean, string """ - err_msg = '' - if check_mode: + msg = '' + + if module.check_mode: return True, '' ip_released = False + try: client.describe_addresses(aws_retry=True, AllocationIds=[allocation_id]) - except botocore.exceptions.ClientError as e: + except is_boto3_error_code('InvalidAllocationID.NotFound') as e: # IP address likely already released # Happens with gateway in 'deleted' state that # still lists associations - return True, str(e) + return True, e + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e) + try: client.release_address(aws_retry=True, AllocationId=allocation_id) ip_released = True - except botocore.exceptions.ClientError as e: - err_msg = str(e) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e) - return ip_released, err_msg + return ip_released, msg def create(client, module, subnet_id, allocation_id, tags, purge_tags, client_token=None, - wait=False, wait_timeout=0, if_exist_do_not_create=False, - check_mode=False): + wait=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. + module: AnsibleAWSModule class instance + subnet_id (str): The subnet_id the nat resides in + allocation_id (str): The eip Amazon identifier + tags (dict): Tags to associate to the NAT gateway + purge_tags (bool): If true, remove tags not listed in I(tags) + type: bool 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') + >>> module = AnsibleAWSModule(...) >>> 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) + >>> create(client, module, subnet_id, allocation_id, wait=True) [ true, "", { - "nat_gateway_id": "nat-123456789", - "subnet_id": "subnet-1234567", + "create_time": "2016-03-05T00:33:21.209000+00:00", + "delete_time": "2016-03-05T00:36:37.329000+00:00", "nat_gateway_addresses": [ { "public_ip": "55.55.55.55", @@ -643,9 +587,10 @@ def create(client, module, subnet_id, allocation_id, tags, purge_tags, client_to "allocation_id": "eipalloc-1234567" } ], + "nat_gateway_id": "nat-123456789", "state": "deleted", - "create_time": "2016-03-05T00:33:21.209000+00:00", - "delete_time": "2016-03-05T00:36:37.329000+00:00", + "subnet_id": "subnet-1234567", + "tags": {}, "vpc_id": "vpc-1234567" } ] @@ -653,73 +598,67 @@ def create(client, module, subnet_id, allocation_id, tags, purge_tags, client_to 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 = "" result = {} + msg = '' if client_token: token_provided = True params['ClientToken'] = client_token - if check_mode: - success = True + if module.check_mode: changed = True - return success, changed, err_msg, result + return changed, result, msg try: result = camel_dict_to_snake_dict(client.create_nat_gateway(aws_retry=True, **params)["NatGateway"]) - 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 - ) + + elif wait and result.get('state') != 'available': + wait_for_status(client, module, 'nat_gateway_available', result['nat_gateway_id']) + + # Get new result + result = camel_dict_to_snake_dict( + _describe_nat_gateways(client, NatGatewayIds=[result['nat_gateway_id']])[0] ) - if success: - err_msg = ( - 'NAT gateway {0} created'.format(result['nat_gateway_id']) - ) - result['tags'], tags_update_exists = ensure_tags( + + result['tags'], _tags_update_exists = ensure_tags( client, module, nat_gw_id=result['nat_gateway_id'], tags=tags, - purge_tags=purge_tags, check_mode=check_mode + purge_tags=purge_tags ) - except is_boto3_error_code('IdempotentParameterMismatch'): - err_msg = ( - 'NAT Gateway does not support update and token has already been provided: ' + err_msg + except is_boto3_error_code('IdempotentParameterMismatch') as e: + msg = ( + 'NAT Gateway does not support update and token has already been provided:' + e ) - success = False - changed = False - result = None - except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except - err_msg = to_native(e) - success = False changed = False result = None + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e) - return success, changed, err_msg, result + return changed, result, msg def pre_create(client, module, subnet_id, tags, purge_tags, allocation_id=None, eip_address=None, - if_exist_do_not_create=False, wait=False, wait_timeout=0, - client_token=None, check_mode=False): + if_exist_do_not_create=False, wait=False, client_token=None): """Create an Amazon NAT Gateway. Args: client (botocore.client.EC2): Boto3 client - subnet_id (str): The subnet_id the nat resides in. + module: AnsibleAWSModule class instance + subnet_id (str): The subnet_id the nat resides in + tags (dict): Tags to associate to the NAT gateway + purge_tags (bool): If true, remove tags not listed in I(tags) Kwargs: allocation_id (str): The EIP Amazon identifier. @@ -731,22 +670,21 @@ def pre_create(client, module, subnet_id, tags, purge_tags, allocation_id=None, 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') + >>> module = AnsibleAWSModule(...) >>> 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) + >>> pre_create(client, module, subnet_id, allocation_id, if_exist_do_not_create=True, wait=True) [ true, "", { - "nat_gateway_id": "nat-03835afb6e31df79b", - "subnet_id": "subnet-w4t12897", + "create_time": "2016-03-05T00:33:21.209000+00:00", + "delete_time": "2016-03-05T00:36:37.329000+00:00", "nat_gateway_addresses": [ { "public_ip": "52.87.29.36", @@ -755,9 +693,10 @@ def pre_create(client, module, subnet_id, tags, purge_tags, allocation_id=None, "allocation_id": "eipalloc-36014da3" } ], + "nat_gateway_id": "nat-03835afb6e31df79b", "state": "deleted", - "create_time": "2016-03-05T00:33:21.209000+00:00", - "delete_time": "2016-03-05T00:36:37.329000+00:00", + "subnet_id": "subnet-w4t12897", + "tags": {}, "vpc_id": "vpc-w68571b5" } ] @@ -765,105 +704,104 @@ def pre_create(client, module, subnet_id, tags, purge_tags, allocation_id=None, Returns: Tuple (bool, bool, str, list) """ - success = False + changed = False - err_msg = "" + msg = '' results = {} if not allocation_id and not eip_address: - existing_gateways, allocation_id_exists = (gateway_in_subnet_exists(client, subnet_id, check_mode=check_mode)) + existing_gateways, allocation_id_exists = (gateway_in_subnet_exists(client, module, subnet_id)) if len(existing_gateways) > 0 and if_exist_do_not_create: results = existing_gateways[0] - results['tags'], tags_update_exists = ensure_tags(client, module, results['nat_gateway_id'], tags, purge_tags, check_mode) + results['tags'], tags_update_exists = ensure_tags( + client, module, results['nat_gateway_id'], tags, purge_tags + ) if tags_update_exists: - success = True changed = True - return success, changed, err_msg, results + return changed, msg, results - success = True changed = False - err_msg = ( + 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 + return changed, msg, results else: - success, err_msg, allocation_id = ( - allocate_eip_address(client, check_mode=check_mode) + changed, msg, allocation_id = ( + allocate_eip_address(client, module) ) - if not success: - return success, 'False', err_msg, dict() + if not changed: + return changed, msg, dict() elif eip_address or allocation_id: if eip_address and not allocation_id: - allocation_id, err_msg = ( + allocation_id, msg = ( get_eip_allocation_id_by_address( - client, eip_address, check_mode=check_mode + client, module, eip_address ) ) if not allocation_id: - success = False changed = False - return success, changed, err_msg, dict() + return changed, msg, dict() existing_gateways, allocation_id_exists = ( gateway_in_subnet_exists( - client, subnet_id, allocation_id, check_mode=check_mode + client, module, subnet_id, allocation_id ) ) if len(existing_gateways) > 0 and (allocation_id_exists or if_exist_do_not_create): results = existing_gateways[0] - results['tags'], tags_update_exists = ensure_tags(client, module, results['nat_gateway_id'], tags, purge_tags, check_mode) + results['tags'], tags_update_exists = ensure_tags( + client, module, results['nat_gateway_id'], tags, purge_tags + ) + if tags_update_exists: - success = True changed = True - return success, changed, err_msg, results + return changed, msg, results - success = True changed = False - err_msg = ( + 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 + return changed, msg, results - success, changed, err_msg, results = create( - client, module, subnet_id, allocation_id, tags, purge_tags, client_token, - wait, wait_timeout, if_exist_do_not_create, check_mode=check_mode + changed, results, msg = create( + client, module, subnet_id, allocation_id, tags, purge_tags, client_token, wait ) - return success, changed, err_msg, results + return changed, msg, results -def remove(client, nat_gateway_id, wait=False, wait_timeout=0, - release_eip=False, check_mode=False): +def remove(client, module, nat_gateway_id, wait=False, release_eip=False): """Delete an Amazon NAT Gateway. Args: client (botocore.client.EC2): Boto3 client - nat_gateway_id (str): The Amazon nat id. + module: AnsibleAWSModule class instance + 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') + >>> module = AnsibleAWSModule(...) >>> nat_gw_id = 'nat-03835afb6e31df79b' - >>> remove(client, nat_gw_id, wait=True, wait_timeout=500, release_eip=True) + >>> remove(client, module, nat_gw_id, wait=True, release_eip=True) [ true, "", { - "nat_gateway_id": "nat-03835afb6e31df79b", - "subnet_id": "subnet-w4t12897", + "create_time": "2016-03-05T00:33:21.209000+00:00", + "delete_time": "2016-03-05T00:36:37.329000+00:00", "nat_gateway_addresses": [ { "public_ip": "52.87.29.36", @@ -872,9 +810,10 @@ def remove(client, nat_gateway_id, wait=False, wait_timeout=0, "allocation_id": "eipalloc-36014da3" } ], + "nat_gateway_id": "nat-03835afb6e31df79b", "state": "deleted", - "create_time": "2016-03-05T00:33:21.209000+00:00", - "delete_time": "2016-03-05T00:36:37.329000+00:00", + "subnet_id": "subnet-w4t12897", + "tags": {}, "vpc_id": "vpc-w68571b5" } ] @@ -882,75 +821,65 @@ def remove(client, nat_gateway_id, wait=False, wait_timeout=0, Returns: Tuple (bool, str, list) """ + params = { 'NatGatewayId': nat_gateway_id } - success = False changed = False - err_msg = "" results = {} states = ['pending', 'available'] + msg = '' - if check_mode: + if module.check_mode: changed = True - success = True - return success, changed, err_msg, results + return changed, msg, results try: - exist, err_msg, gw = ( + gw_list = ( get_nat_gateways( - client, nat_gateway_id=nat_gateway_id, - states=states, check_mode=check_mode + client, module, nat_gateway_id=nat_gateway_id, + states=states ) ) - if exist and len(gw) == 1: - results = gw[0] - client.delete_nat_gateway(aws_retry=True, **params) + if len(gw_list) == 1: + results = gw_list[0] + client.delete_nat_gateway(aws_retry=True, **params) allocation_id = ( results['nat_gateway_addresses'][0]['allocation_id'] ) changed = True - success = True - err_msg = ( + 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) - ) + if wait and results.get('state') != 'deleted': + wait_for_status(client, module, 'nat_gateway_deleted', nat_gateway_id) - except botocore.exceptions.ClientError as e: - err_msg = str(e) + # Get new results + results = camel_dict_to_snake_dict( + _describe_nat_gateways(client, NatGatewayIds=[nat_gateway_id])[0] + ) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e) if release_eip: - eip_released, eip_err = ( - release_address(client, allocation_id, check_mode)) + eip_released, msg = ( + release_address(client, module, allocation_id)) if not eip_released: - err_msg = ( - "{0}: Failed to release EIP {1}: {2}" - .format(err_msg, allocation_id, eip_err) + module.fail_json( + msg="Failed to release EIP {0}: {1}".format(allocation_id, msg) ) - success = False - return success, changed, err_msg, results + return changed, msg, results -def ensure_tags(client, module, nat_gw_id, tags, purge_tags, check_mode): +def ensure_tags(client, module, nat_gw_id, tags, purge_tags): final_tags = [] changed = False - if check_mode and nat_gw_id is None: + if module.check_mode and nat_gw_id is None: # We can't describe tags without an EIP id, we might get here when creating a new EIP in check_mode return final_tags, changed @@ -968,8 +897,7 @@ def ensure_tags(client, module, nat_gw_id, tags, purge_tags, check_mode): if to_update: try: - if check_mode: - # update tags + if module.check_mode: final_tags.update(to_update) else: client.create_tags( @@ -977,15 +905,13 @@ def ensure_tags(client, module, nat_gw_id, tags, purge_tags, check_mode): Resources=[nat_gw_id], Tags=ansible_dict_to_boto3_tag_list(to_update) ) - changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Couldn't create tags") if to_delete: try: - if check_mode: - # update tags + if module.check_mode: for key in to_delete: del final_tags[key] else: @@ -994,12 +920,11 @@ def ensure_tags(client, module, nat_gw_id, tags, purge_tags, check_mode): tags_list.append({'Key': key}) client.delete_tags(aws_retry=True, Resources=[nat_gw_id], Tags=tags_list) - changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, "Couldn't delete tags") - if not check_mode and (to_update or to_delete): + if not module.check_mode and (to_update or to_delete): try: response = client.describe_tags(aws_retry=True, Filters=filters) final_tags = boto3_tag_list_to_ansible_dict(response.get('Tags')) @@ -1024,6 +949,7 @@ def main(): tags=dict(required=False, type='dict', aliases=['resource_tags']), purge_tags=dict(default=True, type='bool'), ) + module = AnsibleAWSModule( argument_spec=argument_spec, supports_check_mode=True, @@ -1035,13 +961,11 @@ def main(): ) 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') @@ -1051,36 +975,26 @@ def main(): try: client = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg='Failed to connect to AWS') + module.fail_json_aws(e, msg='Failed to connect to AWS.') changed = False - err_msg = '' + msg = '' if state == 'present': - success, changed, err_msg, results = ( + changed, msg, results = ( pre_create( client, module, subnet_id, tags, purge_tags, allocation_id, eip_address, - if_exist_do_not_create, wait, wait_timeout, - client_token, check_mode=check_mode + if_exist_do_not_create, wait, client_token ) ) else: - success, changed, err_msg, results = ( + changed, msg, results = ( remove( - client, nat_gateway_id, wait, wait_timeout, release_eip, - check_mode=check_mode + client, module, nat_gateway_id, wait, release_eip ) ) - if not success: - results = results or {} - module.fail_json( - msg=err_msg, success=success, changed=changed, **results - ) - else: - module.exit_json( - msg=err_msg, success=success, changed=changed, **results - ) + module.exit_json(msg=msg, changed=changed, **results) if __name__ == '__main__': diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml index 3dcb70a153e..e7e215559f9 100644 --- a/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml +++ b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml @@ -227,7 +227,6 @@ - '"vpc_id" in create_ngw' - create_ngw.vpc_id == vpc_id - - name: Trying this again for idempotency - create new NAT gateway with eip allocation-id ec2_vpc_nat_gateway: subnet_id: "{{ subnet_id }}" @@ -325,6 +324,8 @@ second_eip_address: "{{ eip_result.public_ip }}" second_allocation_id: "{{ eip_result.allocation_id }}" + + # ============================================================ - name: Create new nat gateway with eip address - CHECK_MODE ec2_vpc_nat_gateway: subnet_id: "{{ subnet_id }}" @@ -900,7 +901,8 @@ - name: Get NAT gateways ec2_vpc_nat_gateway_info: filters: - vpc-id: "{{ vpc_id }}" + vpc-id: "{{ vpc_id }}" + state: ['available'] register: existing_ngws ignore_errors: true From 9a54b5134bcab1d296d43d6fc096bee9d8dce3d7 Mon Sep 17 00:00:00 2001 From: Alina Buzachis <49211501+alinabuzachis@users.noreply.github.com> Date: Thu, 18 Mar 2021 17:59:02 +0100 Subject: [PATCH 23/29] ec2_vpc_nat_gateway_info: module stabilization (#472) * ec2_vpc_nat_gateway_info: stability * Catches and handles (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) on boto API calls * Add paginator * Document returned data Signed-off-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/b0f438b3200b7fc3decdf8b95c70a220ef25bd79 --- plugins/modules/ec2_vpc_nat_gateway_info.py | 102 +++++++++++++++++--- 1 file changed, 88 insertions(+), 14 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index 7d31eeac993..a9337ecd9f8 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -30,7 +30,6 @@ extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 - ''' EXAMPLES = r''' @@ -69,33 +68,109 @@ ''' RETURN = r''' +changed: + description: True if listing the internet gateways succeeds + type: bool + returned: always + sample: false result: - description: The result of the describe, converted to ansible snake case style. - See http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.describe_nat_gateways for the response. - returned: success + description: + - The result of the describe, converted to ansible snake case style. + - See also U(http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.describe_nat_gateways) + returned: suceess type: list + contains: + create_time: + description: The date and time the NAT gateway was created + returned: always + type: str + sample: "2021-03-11T22:43:25+00:00" + delete_time: + description: The date and time the NAT gateway was deleted + returned: when the NAT gateway has been deleted + type: str + sample: "2021-03-11T22:43:25+00:00" + nat_gateway_addresses: + description: List containing a dictionary with the IP addresses and network interface associated with the NAT gateway + returned: always + type: dict + contains: + allocation_id: + description: The allocation ID of the Elastic IP address that's associated with the NAT gateway + returned: always + type: str + sample: eipalloc-0853e66a40803da76 + network_interface_id: + description: The ID of the network interface associated with the NAT gateway + returned: always + type: str + sample: eni-0a37acdbe306c661c + private_ip: + description: The private IP address associated with the Elastic IP address + returned: always + type: str + sample: 10.0.238.227 + public_ip: + description: The Elastic IP address associated with the NAT gateway + returned: always + type: str + sample: 34.204.123.52 + nat_gateway_id: + description: The ID of the NAT gateway + returned: always + type: str + sample: nat-0c242a2397acf6173 + state: + description: state of the NAT gateway + returned: always + type: str + sample: available + subnet_id: + description: The ID of the subnet in which the NAT gateway is located + returned: always + type: str + sample: subnet-098c447465d4344f9 + vpc_id: + description: The ID of the VPC in which the NAT gateway is located + returned: always + type: str + sample: vpc-02f37f48438ab7d4c + tags: + description: Tags applied to the NAT gateway + returned: always + type: dict + sample: + Tag1: tag1 + Tag_2: tag_2 ''' -import json try: import botocore except ImportError: pass # Handled by AnsibleAWSModule -from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.core import normalize_boto3_result -def date_handler(obj): - return obj.isoformat() if hasattr(obj, 'isoformat') else obj +@AWSRetry.jittered_backoff(retries=10) +def _describe_nat_gateways(client, module, **params): + try: + paginator = client.get_paginator('describe_nat_gateways') + return paginator.paginate(**params).build_full_result()['NatGateways'] + except is_boto3_error_code('InvalidNatGatewayID.NotFound'): + module.exit_json(msg="NAT gateway not found.") + except is_boto3_error_code('NatGatewayMalformed'): # pylint: disable=duplicate-except + module.fail_json_aws(msg="NAT gateway id is malformed.") -def get_nat_gateways(client, module, nat_gateway_id=None): +def get_nat_gateways(client, module): params = dict() nat_gateways = list() @@ -103,17 +178,16 @@ def get_nat_gateways(client, module, nat_gateway_id=None): params['NatGatewayIds'] = module.params.get('nat_gateway_ids') try: - result = json.loads(json.dumps(client.describe_nat_gateways(aws_retry=True, **params), default=date_handler)) - except Exception as e: - module.fail_json(msg=to_native(e)) + result = normalize_boto3_result(_describe_nat_gateways(client, module, **params)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, 'Unable to describe NAT gateways.') - for gateway in result['NatGateways']: + for gateway in result: # Turn the boto3 result into ansible_friendly_snaked_names converted_gateway = camel_dict_to_snake_dict(gateway) if 'tags' in converted_gateway: # Turn the boto3 result into ansible friendly tag dictionary converted_gateway['tags'] = boto3_tag_list_to_ansible_dict(converted_gateway['tags']) - nat_gateways.append(converted_gateway) return nat_gateways From 32c864fc06a8d5a59d6fa1e2e83d6b0bb28bef7b Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 6 May 2021 21:01:46 +0200 Subject: [PATCH 24/29] Update the default module requirements from python 2.6/boto to python 3.6/boto3 This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/c097c55293be0834a2b9d394733ec28965d142d7 --- plugins/modules/ec2_vpc_nat_gateway.py | 1 - plugins/modules/ec2_vpc_nat_gateway_info.py | 1 - 2 files changed, 2 deletions(-) diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py index 87511fa2582..30a28ca1391 100644 --- a/plugins/modules/ec2_vpc_nat_gateway.py +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -13,7 +13,6 @@ short_description: Manage AWS VPC NAT Gateways. description: - Ensure the state of AWS VPC NAT Gateways based on their id, allocation and subnet ids. -requirements: [boto3, botocore] options: state: description: diff --git a/plugins/modules/ec2_vpc_nat_gateway_info.py b/plugins/modules/ec2_vpc_nat_gateway_info.py index a9337ecd9f8..5acd59a819a 100644 --- a/plugins/modules/ec2_vpc_nat_gateway_info.py +++ b/plugins/modules/ec2_vpc_nat_gateway_info.py @@ -13,7 +13,6 @@ description: - Gets various details related to AWS VPC Managed Nat Gateways - This module was called C(ec2_vpc_nat_gateway_facts) before Ansible 2.9. The usage did not change. -requirements: [ boto3 ] options: nat_gateway_ids: description: From 37a1a0c93500450c2f81364e6eb2fa40d86af79e Mon Sep 17 00:00:00 2001 From: jillr Date: Thu, 29 Apr 2021 21:58:50 +0000 Subject: [PATCH 25/29] Remove shippable references from repo This collection has been operating on Zuul CI for some weeks now This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/4e0d83c65568a99a24307e37a14e6e0b173c948b --- tests/integration/targets/ec2_vpc_nat_gateway/aliases | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/targets/ec2_vpc_nat_gateway/aliases b/tests/integration/targets/ec2_vpc_nat_gateway/aliases index e2557d59343..f5291520b8b 100644 --- a/tests/integration/targets/ec2_vpc_nat_gateway/aliases +++ b/tests/integration/targets/ec2_vpc_nat_gateway/aliases @@ -1,3 +1,2 @@ cloud/aws -shippable/aws/group2 ec2_vpc_nat_gateway_info From 2c8bf85193a63c89cffbd0713146c2c4eaac8814 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Tue, 20 Jul 2021 10:20:06 +0200 Subject: [PATCH 26/29] Update ignore files --- tests/sanity/ignore-2.10.txt | 24 ++++++++++++++++++++++++ tests/sanity/ignore-2.11.txt | 24 ++++++++++++++++++++++++ tests/sanity/ignore-2.12.txt | 24 ++++++++++++++++++++++++ tests/sanity/ignore-2.9.txt | 24 ++++++++++++++++++++++++ 4 files changed, 96 insertions(+) diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index 77d93927cf4..8b323404d07 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -314,6 +314,30 @@ plugins/modules/ec2_vpc_dhcp_option_info.py import-3.5!skip plugins/modules/ec2_vpc_dhcp_option_info.py import-3.6!skip plugins/modules/ec2_vpc_dhcp_option_info.py import-3.7!skip plugins/modules/ec2_vpc_dhcp_option_info.py metaclass-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-2.6!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-2.7!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.5!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.6!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.7!skip +plugins/modules/ec2_vpc_nat_gateway.py future-import-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway.py import-2.6!skip +plugins/modules/ec2_vpc_nat_gateway.py import-2.7!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.5!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.6!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.7!skip +plugins/modules/ec2_vpc_nat_gateway.py metaclass-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-2.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-2.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.5!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py future-import-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-2.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-2.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.5!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py metaclass-boilerplate!skip plugins/modules/ec2_vpc_net.py compile-2.6!skip plugins/modules/ec2_vpc_net.py compile-2.7!skip plugins/modules/ec2_vpc_net.py compile-3.5!skip diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index 77d93927cf4..8b323404d07 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -314,6 +314,30 @@ plugins/modules/ec2_vpc_dhcp_option_info.py import-3.5!skip plugins/modules/ec2_vpc_dhcp_option_info.py import-3.6!skip plugins/modules/ec2_vpc_dhcp_option_info.py import-3.7!skip plugins/modules/ec2_vpc_dhcp_option_info.py metaclass-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-2.6!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-2.7!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.5!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.6!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.7!skip +plugins/modules/ec2_vpc_nat_gateway.py future-import-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway.py import-2.6!skip +plugins/modules/ec2_vpc_nat_gateway.py import-2.7!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.5!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.6!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.7!skip +plugins/modules/ec2_vpc_nat_gateway.py metaclass-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-2.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-2.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.5!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py future-import-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-2.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-2.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.5!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py metaclass-boilerplate!skip plugins/modules/ec2_vpc_net.py compile-2.6!skip plugins/modules/ec2_vpc_net.py compile-2.7!skip plugins/modules/ec2_vpc_net.py compile-3.5!skip diff --git a/tests/sanity/ignore-2.12.txt b/tests/sanity/ignore-2.12.txt index 77d93927cf4..8b323404d07 100644 --- a/tests/sanity/ignore-2.12.txt +++ b/tests/sanity/ignore-2.12.txt @@ -314,6 +314,30 @@ plugins/modules/ec2_vpc_dhcp_option_info.py import-3.5!skip plugins/modules/ec2_vpc_dhcp_option_info.py import-3.6!skip plugins/modules/ec2_vpc_dhcp_option_info.py import-3.7!skip plugins/modules/ec2_vpc_dhcp_option_info.py metaclass-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-2.6!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-2.7!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.5!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.6!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.7!skip +plugins/modules/ec2_vpc_nat_gateway.py future-import-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway.py import-2.6!skip +plugins/modules/ec2_vpc_nat_gateway.py import-2.7!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.5!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.6!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.7!skip +plugins/modules/ec2_vpc_nat_gateway.py metaclass-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-2.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-2.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.5!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py future-import-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-2.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-2.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.5!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py metaclass-boilerplate!skip plugins/modules/ec2_vpc_net.py compile-2.6!skip plugins/modules/ec2_vpc_net.py compile-2.7!skip plugins/modules/ec2_vpc_net.py compile-3.5!skip diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt index d41b9e62fd8..7bc606b5248 100644 --- a/tests/sanity/ignore-2.9.txt +++ b/tests/sanity/ignore-2.9.txt @@ -327,6 +327,30 @@ plugins/modules/ec2_vpc_dhcp_option_info.py import-3.6!skip plugins/modules/ec2_vpc_dhcp_option_info.py import-3.7!skip plugins/modules/ec2_vpc_dhcp_option_info.py metaclass-boilerplate!skip plugins/modules/ec2_vpc_dhcp_option_info.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability +plugins/modules/ec2_vpc_nat_gateway.py compile-2.6!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-2.7!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.5!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.6!skip +plugins/modules/ec2_vpc_nat_gateway.py compile-3.7!skip +plugins/modules/ec2_vpc_nat_gateway.py future-import-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway.py import-2.6!skip +plugins/modules/ec2_vpc_nat_gateway.py import-2.7!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.5!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.6!skip +plugins/modules/ec2_vpc_nat_gateway.py import-3.7!skip +plugins/modules/ec2_vpc_nat_gateway.py metaclass-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-2.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-2.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.5!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py compile-3.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py future-import-boilerplate!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-2.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-2.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.5!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.6!skip +plugins/modules/ec2_vpc_nat_gateway_info.py import-3.7!skip +plugins/modules/ec2_vpc_nat_gateway_info.py metaclass-boilerplate!skip plugins/modules/ec2_vpc_net.py compile-2.6!skip plugins/modules/ec2_vpc_net.py compile-2.7!skip plugins/modules/ec2_vpc_net.py compile-3.5!skip From b01c58cc5d88c1dc5cafb6a04a14f12f7992c1d8 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Tue, 20 Jul 2021 10:20:07 +0200 Subject: [PATCH 27/29] refresh runtime --- meta/runtime.yml | 59 ++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/meta/runtime.yml b/meta/runtime.yml index df3b4370954..ce1387bb51a 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -49,74 +49,83 @@ action_groups: - ec2_vpc_subnet - ec2_vpc_subnet_info - s3_bucket - + - ec2_vpc_nat_gateway_facts + - ec2_vpc_nat_gateway + - ec2_vpc_nat_gateway_info plugin_routing: modules: aws_az_facts: deprecation: removal_date: 2022-06-01 warning_text: >- - aws_az_facts was renamed in Ansible 2.9 to aws_az_info. - Please update your tasks. + aws_az_facts was renamed in Ansible 2.9 to aws_az_info. + Please update your tasks. aws_caller_facts: deprecation: removal_date: 2021-12-01 warning_text: >- - aws_caller_facts was renamed in Ansible 2.9 to aws_caller_info. - Please update your tasks. + aws_caller_facts was renamed in Ansible 2.9 to aws_caller_info. + Please update your tasks. cloudformation_facts: deprecation: removal_date: 2021-12-01 warning_text: >- - cloudformation_facts has been deprecated and will be removed. - The cloudformation_info module returns the same information, but - not as ansible_facts. See the module documentation for more - information. + cloudformation_facts has been deprecated and will be removed. + The cloudformation_info module returns the same information, but + not as ansible_facts. See the module documentation for more + information. ec2_ami_facts: deprecation: removal_date: 2021-12-01 warning_text: >- - ec2_ami_facts was renamed in Ansible 2.9 to ec2_ami_info. - Please update your tasks. + ec2_ami_facts was renamed in Ansible 2.9 to ec2_ami_info. + Please update your tasks. ec2_eni_facts: deprecation: removal_date: 2021-12-01 warning_text: >- - ec2_eni_facts was renamed in Ansible 2.9 to ec2_eni_info. - Please update your tasks. + ec2_eni_facts was renamed in Ansible 2.9 to ec2_eni_info. + Please update your tasks. ec2_group_facts: deprecation: removal_date: 2021-12-01 warning_text: >- - ec2_group_facts was renamed in Ansible 2.9 to ec2_group_info. - Please update your tasks. + ec2_group_facts was renamed in Ansible 2.9 to ec2_group_info. + Please update your tasks. ec2_snapshot_facts: deprecation: removal_date: 2021-12-01 warning_text: >- - ec2_snapshot_facts was renamed in Ansible 2.9 to ec2_snapshot_info. - Please update your tasks. + ec2_snapshot_facts was renamed in Ansible 2.9 to ec2_snapshot_info. + Please update your tasks. ec2_vol_facts: deprecation: removal_date: 2021-12-01 warning_text: >- - ec2_vol_facts was renamed in Ansible 2.9 to ec2_vol_info. - Please update your tasks. + ec2_vol_facts was renamed in Ansible 2.9 to ec2_vol_info. + Please update your tasks. ec2_vpc_dhcp_option_facts: deprecation: removal_date: 2021-12-01 warning_text: >- - ec2_vpc_dhcp_option_facts was renamed in Ansible 2.9 to - ec2_vpc_dhcp_option_info. Please update your tasks. + ec2_vpc_dhcp_option_facts was renamed in Ansible 2.9 to + ec2_vpc_dhcp_option_info. Please update your tasks. ec2_vpc_net_facts: deprecation: removal_date: 2021-12-01 warning_text: >- - ec2_vpc_net_facts was renamed in Ansible 2.9 to ec2_vpc_net_info. - Please update your tasks. + ec2_vpc_net_facts was renamed in Ansible 2.9 to ec2_vpc_net_info. + Please update your tasks. ec2_vpc_subnet_facts: deprecation: removal_date: 2021-12-01 warning_text: >- - ec2_vpc_subnet_facts was renamed in Ansible 2.9 to - ec2_vpc_subnet_info. Please update your tasks. + ec2_vpc_subnet_facts was renamed in Ansible 2.9 to + ec2_vpc_subnet_info. Please update your tasks. + ec2_vpc_nat_gateway_facts: + deprecation: + removal_date: 2021-12-01 + warning_text: >- + ec2_vpc_nat_gateway_facts was renamed in Ansible 2.9 to + ec2_vpc_nat_gateway_info. + Please update your tasks. From 7c0548faed32f5e4074eda81f17b23c2744a7851 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Tue, 20 Jul 2021 10:20:07 +0200 Subject: [PATCH 28/29] Update FQDN --- plugins/modules/ec2_vpc_nat_gateway_facts.py | 219 ++++++++++++++++++- 1 file changed, 218 insertions(+), 1 deletion(-) mode change 120000 => 100755 plugins/modules/ec2_vpc_nat_gateway_facts.py diff --git a/plugins/modules/ec2_vpc_nat_gateway_facts.py b/plugins/modules/ec2_vpc_nat_gateway_facts.py deleted file mode 120000 index fd969989977..00000000000 --- a/plugins/modules/ec2_vpc_nat_gateway_facts.py +++ /dev/null @@ -1 +0,0 @@ -ec2_vpc_nat_gateway_info.py \ No newline at end of file diff --git a/plugins/modules/ec2_vpc_nat_gateway_facts.py b/plugins/modules/ec2_vpc_nat_gateway_facts.py new file mode 100755 index 00000000000..5acd59a819a --- /dev/null +++ b/plugins/modules/ec2_vpc_nat_gateway_facts.py @@ -0,0 +1,218 @@ +#!/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 + + +DOCUMENTATION = r''' +module: ec2_vpc_nat_gateway_info +short_description: Retrieves AWS VPC Managed Nat Gateway details using AWS methods. +version_added: 1.0.0 +description: + - Gets various details related to AWS VPC Managed Nat Gateways + - This module was called C(ec2_vpc_nat_gateway_facts) before Ansible 2.9. The usage did not change. +options: + nat_gateway_ids: + description: + - List of specific nat gateway IDs to fetch details for. + type: list + elements: str + 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_DescribeNatGateways.html) + for possible filters. + type: dict +author: Karen Cheng (@Etherdaemon) +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 +''' + +EXAMPLES = r''' +# Simple example of listing all nat gateways +- name: List all managed nat gateways in ap-southeast-2 + community.aws.ec2_vpc_nat_gateway_info: + region: ap-southeast-2 + register: all_ngws + +- name: Debugging the result + ansible.builtin.debug: + msg: "{{ all_ngws.result }}" + +- name: Get details on specific nat gateways + community.aws.ec2_vpc_nat_gateway_info: + nat_gateway_ids: + - nat-1234567891234567 + - nat-7654321987654321 + region: ap-southeast-2 + register: specific_ngws + +- name: Get all nat gateways with specific filters + community.aws.ec2_vpc_nat_gateway_info: + region: ap-southeast-2 + filters: + state: ['pending'] + register: pending_ngws + +- name: Get nat gateways with specific filter + community.aws.ec2_vpc_nat_gateway_info: + region: ap-southeast-2 + filters: + subnet-id: subnet-12345678 + state: ['available'] + register: existing_nat_gateways +''' + +RETURN = r''' +changed: + description: True if listing the internet gateways succeeds + type: bool + returned: always + sample: false +result: + description: + - The result of the describe, converted to ansible snake case style. + - See also U(http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.describe_nat_gateways) + returned: suceess + type: list + contains: + create_time: + description: The date and time the NAT gateway was created + returned: always + type: str + sample: "2021-03-11T22:43:25+00:00" + delete_time: + description: The date and time the NAT gateway was deleted + returned: when the NAT gateway has been deleted + type: str + sample: "2021-03-11T22:43:25+00:00" + nat_gateway_addresses: + description: List containing a dictionary with the IP addresses and network interface associated with the NAT gateway + returned: always + type: dict + contains: + allocation_id: + description: The allocation ID of the Elastic IP address that's associated with the NAT gateway + returned: always + type: str + sample: eipalloc-0853e66a40803da76 + network_interface_id: + description: The ID of the network interface associated with the NAT gateway + returned: always + type: str + sample: eni-0a37acdbe306c661c + private_ip: + description: The private IP address associated with the Elastic IP address + returned: always + type: str + sample: 10.0.238.227 + public_ip: + description: The Elastic IP address associated with the NAT gateway + returned: always + type: str + sample: 34.204.123.52 + nat_gateway_id: + description: The ID of the NAT gateway + returned: always + type: str + sample: nat-0c242a2397acf6173 + state: + description: state of the NAT gateway + returned: always + type: str + sample: available + subnet_id: + description: The ID of the subnet in which the NAT gateway is located + returned: always + type: str + sample: subnet-098c447465d4344f9 + vpc_id: + description: The ID of the VPC in which the NAT gateway is located + returned: always + type: str + sample: vpc-02f37f48438ab7d4c + tags: + description: Tags applied to the NAT gateway + returned: always + type: dict + sample: + Tag1: tag1 + Tag_2: tag_2 +''' + + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.core import normalize_boto3_result + + +@AWSRetry.jittered_backoff(retries=10) +def _describe_nat_gateways(client, module, **params): + try: + paginator = client.get_paginator('describe_nat_gateways') + return paginator.paginate(**params).build_full_result()['NatGateways'] + except is_boto3_error_code('InvalidNatGatewayID.NotFound'): + module.exit_json(msg="NAT gateway not found.") + except is_boto3_error_code('NatGatewayMalformed'): # pylint: disable=duplicate-except + module.fail_json_aws(msg="NAT gateway id is malformed.") + + +def get_nat_gateways(client, module): + params = dict() + nat_gateways = list() + + params['Filter'] = ansible_dict_to_boto3_filter_list(module.params.get('filters')) + params['NatGatewayIds'] = module.params.get('nat_gateway_ids') + + try: + result = normalize_boto3_result(_describe_nat_gateways(client, module, **params)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, 'Unable to describe NAT gateways.') + + for gateway in result: + # Turn the boto3 result into ansible_friendly_snaked_names + converted_gateway = camel_dict_to_snake_dict(gateway) + if 'tags' in converted_gateway: + # Turn the boto3 result into ansible friendly tag dictionary + converted_gateway['tags'] = boto3_tag_list_to_ansible_dict(converted_gateway['tags']) + nat_gateways.append(converted_gateway) + + return nat_gateways + + +def main(): + argument_spec = dict( + filters=dict(default={}, type='dict'), + nat_gateway_ids=dict(default=[], type='list', elements='str'), + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, + supports_check_mode=True,) + if module._name == 'ec2_vpc_nat_gateway_facts': + module.deprecate("The 'ec2_vpc_nat_gateway_facts' module has been renamed to 'ec2_vpc_nat_gateway_info'", + date='2021-12-01', collection_name='community.aws') + + try: + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to connect to AWS') + + results = get_nat_gateways(connection, module) + + module.exit_json(result=results) + + +if __name__ == '__main__': + main() From 603c8f1780bd7d53a0c713521477789e5ccaa1c4 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Tue, 20 Jul 2021 10:20:07 +0200 Subject: [PATCH 29/29] Add changelog fragment --- changelogs/fragments/migrate_ec2_vpc_nat_gateway.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 changelogs/fragments/migrate_ec2_vpc_nat_gateway.yml diff --git a/changelogs/fragments/migrate_ec2_vpc_nat_gateway.yml b/changelogs/fragments/migrate_ec2_vpc_nat_gateway.yml new file mode 100644 index 00000000000..fc978309a23 --- /dev/null +++ b/changelogs/fragments/migrate_ec2_vpc_nat_gateway.yml @@ -0,0 +1,10 @@ +major_changes: +- ec2_vpc_nat_gateway_facts - The module has been migrated from the ``community.aws`` + collection. Playbooks using the Fully Qualified Collection Name for this module + should be updated to use ``amazon.aws.ec2_vpc_nat_gateway_facts``. +- ec2_vpc_nat_gateway - The module has been migrated from the ``community.aws`` collection. + Playbooks using the Fully Qualified Collection Name for this module should be updated + to use ``amazon.aws.ec2_vpc_nat_gateway``. +- ec2_vpc_nat_gateway_info - The module has been migrated from the ``community.aws`` + collection. Playbooks using the Fully Qualified Collection Name for this module + should be updated to use ``amazon.aws.ec2_vpc_nat_gateway_info``.