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..8d45e11dae3 --- /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_info``. +- 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``. diff --git a/meta/runtime.yml b/meta/runtime.yml index ddff16a8d69..4f60be535ca 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -58,20 +58,23 @@ action_groups: - ec2_vpc_route_table_facts - ec2_vpc_route_table - ec2_vpc_route_table_info + - 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 @@ -91,44 +94,44 @@ plugin_routing: 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 @@ -154,3 +157,10 @@ plugin_routing: ec2_vpc_route_table_facts was renamed in Ansible 2.9 to ec2_vpc_route_table_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. diff --git a/plugins/modules/ec2_vpc_nat_gateway.py b/plugins/modules/ec2_vpc_nat_gateway.py new file mode 100644 index 00000000000..e184b60293c --- /dev/null +++ b/plugins/modules/ec2_vpc_nat_gateway.py @@ -0,0 +1,1000 @@ +#!/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 +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. +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 + tags: + description: + - 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 + 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. + - 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) + - Alina Buzachis (@alinabuzachis) +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 +''' + +EXAMPLES = r''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Create new nat gateway with client token. + amazon.aws.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. + amazon.aws.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. + amazon.aws.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. + amazon.aws.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. + amazon.aws.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. + amazon.aws.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. + amazon.aws.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. + amazon.aws.ec2_vpc_nat_gateway: + state: absent + nat_gateway_id: nat-12345678 + release_eip: true + wait: yes + wait_timeout: 300 + region: ap-southeast-2 + +- name: Create new nat gateway using allocation-id and tags. + amazon.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 + +- name: Update tags without purge + amazon.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 = r''' +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" +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. + 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 + +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 + + +@AWSRetry.jittered_backoff(retries=10) +def _describe_nat_gateways(client, **params): + 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 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. + 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, module, subnet_id) + [ + true, + "", + { + "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", + "network_interface_id": "eni-1234567", + "private_ip": "10.0.0.102", + "allocation_id": "eipalloc-1234567" + } + ], + "nat_gateway_id": "nat-123456789", + "state": "deleted", + "subnet_id": "subnet-123456789", + "tags": {}, + "vpc_id": "vpc-12345678" + } + + Returns: + Tuple (bool, str, list) + """ + + params = dict() + 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: + gateways = _describe_nat_gateways(client, **params) + if gateways: + for gw in gateways: + existing_gateways.append(camel_dict_to_snake_dict(gw)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e) + + return existing_gateways + + +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: + allocation_id (str): The EIP Amazon identifier. + default = None + + Basic Usage: + >>> client = boto3.client('ec2') + >>> module = AnsibleAWSModule(...) + >>> subnet_id = 'subnet-1234567' + >>> allocation_id = 'eipalloc-1234567' + >>> gateway_in_subnet_exists(client, module, subnet_id, allocation_id) + ( + [ + { + "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", + "network_interface_id": "eni-1234567", + "private_ip": "10.0.0.102", + "allocation_id": "eipalloc-1234567" + } + ], + "nat_gateway_id": "nat-123456789", + "state": "deleted", + "subnet_id": "subnet-123456789", + "tags": {}, + "vpc_id": "vpc-1234567" + } + ], + False + ) + + Returns: + Tuple (list, bool) + """ + + allocation_id_exists = False + gateways = [] + states = ['available', 'pending'] + + gws_retrieved = (get_nat_gateways(client, module, subnet_id, states=states)) + + 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) + + return gateways, allocation_id_exists + + +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. + + Basic Usage: + >>> client = boto3.client('ec2') + >>> module = AnsibleAWSModule(...) + >>> eip_address = '52.87.29.36' + >>> get_eip_allocation_id_by_address(client, module, eip_address) + 'eipalloc-36014da3' + + Returns: + Tuple (str, str) + """ + + params = { + 'PublicIps': [eip_address], + } + allocation_id = None + msg = '' + + try: + allocations = client.describe_addresses(aws_retry=True, **params)['Addresses'] + + if len(allocations) == 1: + allocation = allocations[0] + else: + allocation = None + + if allocation: + if allocation.get('Domain') != 'vpc': + msg = ( + "EIP {0} is a non-VPC EIP, please allocate a VPC scoped EIP" + .format(eip_address) + ) + else: + allocation_id = allocation.get('AllocationId') + + 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, msg + + +def allocate_eip_address(client, module): + """Release an EIP from your EIP Pool + Args: + client (botocore.client.EC2): Boto3 client + module: AnsibleAWSModule class instance + + Basic Usage: + >>> client = boto3.client('ec2') + >>> module = AnsibleAWSModule(...) + >>> allocate_eip_address(client, module) + True + + Returns: + Tuple (bool, str) + """ + + new_eip = None + msg = '' + params = { + 'Domain': 'vpc', + } + + if module.check_mode: + ip_allocated = True + new_eip = None + return ip_allocated, msg, new_eip + + try: + new_eip = client.allocate_address(aws_retry=True, **params)['AllocationId'] + ip_allocated = True + 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, msg, new_eip + + +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. + + Basic Usage: + >>> client = boto3.client('ec2') + >>> module = AnsibleAWSModule(...) + >>> allocation_id = "eipalloc-123456" + >>> release_address(client, module, allocation_id) + True + + Returns: + Boolean, string + """ + + msg = '' + + if module.check_mode: + return True, '' + + ip_released = False + + try: + client.describe_addresses(aws_retry=True, AllocationIds=[allocation_id]) + 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, 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, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e) + + return ip_released, msg + + +def create(client, module, subnet_id, allocation_id, tags, purge_tags, client_token=None, + wait=False): + """Create an Amazon NAT Gateway. + Args: + client (botocore.client.EC2): Boto3 client + 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: + wait (bool): Wait for the nat to be in the deleted state before returning. + default = False + client_token (str): + default = None + + Basic Usage: + >>> client = boto3.client('ec2') + >>> module = AnsibleAWSModule(...) + >>> subnet_id = 'subnet-1234567' + >>> allocation_id = 'eipalloc-1234567' + >>> create(client, module, subnet_id, allocation_id, wait=True) + [ + true, + "", + { + "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", + "network_interface_id": "eni-1234567", + "private_ip": "10.0.0.102", + "allocation_id": "eipalloc-1234567" + } + ], + "nat_gateway_id": "nat-123456789", + "state": "deleted", + "subnet_id": "subnet-1234567", + "tags": {}, + "vpc_id": "vpc-1234567" + } + ] + + Returns: + Tuple (bool, str, list) + """ + + params = { + 'SubnetId': subnet_id, + 'AllocationId': allocation_id + } + request_time = datetime.datetime.utcnow() + changed = False + token_provided = False + result = {} + msg = '' + + if client_token: + token_provided = True + params['ClientToken'] = client_token + + if module.check_mode: + changed = True + return changed, result, msg + + try: + result = camel_dict_to_snake_dict(client.create_nat_gateway(aws_retry=True, **params)["NatGateway"]) + changed = True + + create_time = result['create_time'].replace(tzinfo=None) + + if token_provided and (request_time > create_time): + changed = False + + 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] + ) + + result['tags'], _tags_update_exists = ensure_tags( + client, module, nat_gw_id=result['nat_gateway_id'], tags=tags, + purge_tags=purge_tags + ) + except is_boto3_error_code('IdempotentParameterMismatch') as e: + msg = ( + 'NAT Gateway does not support update and token has already been provided:' + e + ) + changed = False + result = None + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e) + + 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, client_token=None): + """Create an Amazon NAT Gateway. + Args: + client (botocore.client.EC2): Boto3 client + 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. + 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 + client_token (str): + default = None + + Basic Usage: + >>> client = boto3.client('ec2') + >>> module = AnsibleAWSModule(...) + >>> subnet_id = 'subnet-w4t12897' + >>> allocation_id = 'eipalloc-36014da3' + >>> pre_create(client, module, subnet_id, allocation_id, if_exist_do_not_create=True, wait=True) + [ + true, + "", + { + "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", + "network_interface_id": "eni-5579742d", + "private_ip": "10.0.0.102", + "allocation_id": "eipalloc-36014da3" + } + ], + "nat_gateway_id": "nat-03835afb6e31df79b", + "state": "deleted", + "subnet_id": "subnet-w4t12897", + "tags": {}, + "vpc_id": "vpc-w68571b5" + } + ] + + Returns: + Tuple (bool, bool, str, list) + """ + + changed = False + msg = '' + results = {} + + if not allocation_id and not eip_address: + 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 + ) + + if tags_update_exists: + changed = True + return changed, msg, results + + changed = False + msg = ( + 'NAT Gateway {0} already exists in subnet_id {1}' + .format( + existing_gateways[0]['nat_gateway_id'], subnet_id + ) + ) + return changed, msg, results + else: + changed, msg, allocation_id = ( + allocate_eip_address(client, module) + ) + if not changed: + return changed, msg, dict() + + elif eip_address or allocation_id: + if eip_address and not allocation_id: + allocation_id, msg = ( + get_eip_allocation_id_by_address( + client, module, eip_address + ) + ) + if not allocation_id: + changed = False + return changed, msg, dict() + + existing_gateways, allocation_id_exists = ( + gateway_in_subnet_exists( + 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 + ) + + if tags_update_exists: + changed = True + return changed, msg, results + + changed = False + msg = ( + 'NAT Gateway {0} already exists in subnet_id {1}' + .format( + existing_gateways[0]['nat_gateway_id'], subnet_id + ) + ) + return changed, msg, results + + changed, results, msg = create( + client, module, subnet_id, allocation_id, tags, purge_tags, client_token, wait + ) + + return changed, msg, results + + +def remove(client, module, nat_gateway_id, wait=False, release_eip=False): + """Delete an Amazon NAT Gateway. + Args: + client (botocore.client.EC2): Boto3 client + 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. + 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, module, nat_gw_id, wait=True, release_eip=True) + [ + true, + "", + { + "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", + "network_interface_id": "eni-5579742d", + "private_ip": "10.0.0.102", + "allocation_id": "eipalloc-36014da3" + } + ], + "nat_gateway_id": "nat-03835afb6e31df79b", + "state": "deleted", + "subnet_id": "subnet-w4t12897", + "tags": {}, + "vpc_id": "vpc-w68571b5" + } + ] + + Returns: + Tuple (bool, str, list) + """ + + params = { + 'NatGatewayId': nat_gateway_id + } + changed = False + results = {} + states = ['pending', 'available'] + msg = '' + + if module.check_mode: + changed = True + return changed, msg, results + + try: + gw_list = ( + get_nat_gateways( + client, module, nat_gateway_id=nat_gateway_id, + states=states + ) + ) + + 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 + msg = ( + 'NAT gateway {0} is in a deleting state. Delete was successful' + .format(nat_gateway_id) + ) + + if wait and results.get('state') != 'deleted': + wait_for_status(client, module, 'nat_gateway_deleted', nat_gateway_id) + + # 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, msg = ( + release_address(client, module, allocation_id)) + if not eip_released: + module.fail_json( + msg="Failed to release EIP {0}: {1}".format(allocation_id, msg) + ) + + return changed, msg, results + + +def ensure_tags(client, module, nat_gw_id, tags, purge_tags): + final_tags = [] + changed = False + + 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 + + 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 module.check_mode: + 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 module.check_mode: + 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 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')) + 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'), + 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', no_log=False), + 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, + mutually_exclusive=[ + ['allocation_id', 'eip_address'] + ], + required_if=[['state', 'absent', ['nat_gateway_id']], + ['state', 'present', ['subnet_id']]], + ) + + state = module.params.get('state').lower() + 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') + 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', 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.') + + changed = False + msg = '' + + if state == 'present': + changed, msg, results = ( + pre_create( + client, module, subnet_id, tags, purge_tags, allocation_id, eip_address, + if_exist_do_not_create, wait, client_token + ) + ) + else: + changed, msg, results = ( + remove( + client, module, nat_gateway_id, wait, release_eip + ) + ) + + module.exit_json(msg=msg, 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..f42688144c7 --- /dev/null +++ b/plugins/modules/ec2_vpc_nat_gateway_info.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 + amazon.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 + amazon.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 + amazon.aws.ec2_vpc_nat_gateway_info: + region: ap-southeast-2 + filters: + state: ['pending'] + register: pending_ngws + +- name: Get nat gateways with specific filter + amazon.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='amazon.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() 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..f5291520b8b --- /dev/null +++ b/tests/integration/targets/ec2_vpc_nat_gateway/aliases @@ -0,0 +1,2 @@ +cloud/aws +ec2_vpc_nat_gateway_info 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..3794da1020e --- /dev/null +++ b/tests/integration/targets/ec2_vpc_nat_gateway/defaults/main.yml @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000000..ec67f0fe0f8 --- /dev/null +++ b/tests/integration/targets/ec2_vpc_nat_gateway/tasks/main.yml @@ -0,0 +1,949 @@ +- 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 }}' + 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 + - '"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: + 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: + 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 + - '"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' + set_fact: + subnet_id: '{{ subnet_result.subnet.id }}' + + + # ============================================================ + - 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 + + - 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 + 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 + - create_igw.gateway_id.startswith("igw-") + - create_igw.vpc_id == vpc_id + - '"gateway_id" in create_igw' + + + # ============================================================ + - 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 + + - 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: '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: Get NAT gateway with specific filters (state and subnet) + ec2_vpc_nat_gateway_info: + filters: + subnet-id: '{{ subnet_id }}' + state: [available] + register: avalaible_ngws + + - 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: 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: 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 + + - 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: 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 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 + + - 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: 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 + + - name: Assert state=absent (expected changed=true) + 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 }}' + 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 + - '"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: 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: 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 + - '"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 - 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 - 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: 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 + - '"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 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 - 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 and + state (no match) + ec2_vpc_nat_gateway_info: + filters: + tag:tag_one: '{{ resource_prefix }} One' + state: [available] + register: ngw_info + + - 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 + 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 + - '"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 }}' + allocation_id: '{{ allocation_id }}' + tags: {} + register: delete_tags_ngw + + - name: assert tags would be removed + 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 \u2764\uFE0F" + Title Case: "Hello Cruel World \u2764\uFE0F" + CamelCase: "SimpleCamelCase \u2764\uFE0F" + snake_case: "simple_snake_case \u2764\uFE0F" + 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 \u2764\uFE0F\ + '" + - "update_tags_ngw.tags[\"Title Case\"] == 'Hello Cruel World \u2764\uFE0F'" + - "update_tags_ngw.tags[\"CamelCase\"] == 'SimpleCamelCase \u2764\uFE0F'" + - "update_tags_ngw.tags[\"snake_case\"] == 'simple_snake_case \u2764\uFE0F'" + - '"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 + subnet_id: '{{ subnet_id }}' + allocation_id: '{{ allocation_id }}' + purge_tags: no + tags: + lowercase spaced: "hello cruel world \u2764\uFE0F" + Title Case: "Hello Cruel World \u2764\uFE0F" + CamelCase: "SimpleCamelCase \u2764\uFE0F" + snake_case: "simple_snake_case \u2764\uFE0F" + wait: yes + register: update_tags_ngw + + - name: Assert tags would be added + 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 \u2764\uFE0F\ + '" + - "update_tags_ngw.tags[\"Title Case\"] == 'Hello Cruel World \u2764\uFE0F'" + - "update_tags_ngw.tags[\"CamelCase\"] == 'SimpleCamelCase \u2764\uFE0F'" + - "update_tags_ngw.tags[\"snake_case\"] == 'simple_snake_case \u2764\uFE0F'" + - '"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 }}' + state: [available] + register: existing_ngws + 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 diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt index 465a1ebcee2..4d111e4b63e 100644 --- a/tests/sanity/ignore-2.9.txt +++ b/tests/sanity/ignore-2.9.txt @@ -20,4 +20,5 @@ plugins/modules/ec2_vpc_net_info.py pylint:ansible-deprecated-no-version # We u plugins/modules/ec2_vpc_route_table_info.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability plugins/modules/ec2_vpc_subnet_info.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability plugins/modules/ec2_vpc_endpoint.py pylint:ansible-deprecated-no-version -plugins/modules/ec2_vpc_endpoint_info.py pylint:ansible-deprecated-no-version \ No newline at end of file +plugins/modules/ec2_vpc_endpoint_info.py pylint:ansible-deprecated-no-version +plugins/modules/ec2_vpc_nat_gateway_info.py pylint:ansible-deprecated-no-version