From 0d7d778c829cf99cb1deed2198f4fe12781c06bc Mon Sep 17 00:00:00 2001 From: Allen Sanabria Date: Mon, 21 Mar 2016 06:07:04 -0700 Subject: [PATCH] Manage AWS Nat Gateways * Create an AWS Nat Gateway. * Delete an AWS Nat Gateway. * If Nat Gateway exist in subnet and the option is passed to not create one, it will then return the Nat Gateway object. --- .../cloud/amazon/ec2_vpc_nat_gateway.py | 930 ++++++++++++++++++ 1 file changed, 930 insertions(+) create mode 100644 lib/ansible/modules/extras/cloud/amazon/ec2_vpc_nat_gateway.py diff --git a/lib/ansible/modules/extras/cloud/amazon/ec2_vpc_nat_gateway.py b/lib/ansible/modules/extras/cloud/amazon/ec2_vpc_nat_gateway.py new file mode 100644 index 0000000000..9b1fb235a6 --- /dev/null +++ b/lib/ansible/modules/extras/cloud/amazon/ec2_vpc_nat_gateway.py @@ -0,0 +1,930 @@ +#!/usr/bin/python +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +DOCUMENTATION = ''' +--- +module: ec2_vpc_nat_gateway +short_description: Manage AWS VPC NAT Gateways +description: + - Ensure the state of AWS VPC NAT Gateways based on their id, allocation and subnet ids. +version_added: "2.1" +requirements: [boto3, botocore] +options: + state: + description: + - Ensure NAT Gateway is present or absent + required: false + default: "present" + choices: ["present", "absent"] + nat_gateway_id: + description: + - The id AWS dynamically allocates to the NAT Gateway on creation. + This is required when the absent option is present. + required: false + default: None + subnet_id: + description: + - The id of the subnet to create the NAT Gateway in. This is required + with the present option. + required: false + default: None + 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 + required: false + default: None + eip_address: + description: + - The elasti 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 + required: false + 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 + release_eip: + description: + - Deallocate the EIP from the VPC. + - Option is only valid with the absent state. + required: false + default: true + wait: + description: + - Wait for operation to complete before returning + required: false + default: true + wait_timeout: + description: + - How many seconds to wait for an operation to complete before timing out + required: false + default: 300 + client_token: + description: + - Optional unique token to be used during create to ensure idempotency. + When specifying this option, ensure you specify the eip_address parameter + as well otherwise any subsequent runs will fail. + required: false + +author: + - "Allen Sanabria (@linuxdynasty)" + - "Jon Hadfield (@jonhadfield)" + - "Karen Cheng(@Etherdaemon)" +extends_documentation_fragment: + - aws + - ec2 +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Create new nat gateway with client token + ec2_vpc_nat_gateway: + state: present + subnet_id: subnet-12345678 + eip_address: 52.1.1.1 + region: ap-southeast-2 + client_token: abcd-12345678 + register: new_nat_gateway + +- name: Create new nat gateway 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 with when condition + ec2_vpc_nat_gateway: + state: present + subnet_id: subnet-12345678 + eip_address: 52.1.1.1 + region: ap-southeast-2 + register: new_nat_gateway + when: existing_nat_gateways.result == [] + +- name: Create new nat gateway and wait for available status + ec2_vpc_nat_gateway: + state: present + subnet_id: subnet-12345678 + eip_address: 52.1.1.1 + wait: yes + region: ap-southeast-2 + register: new_nat_gateway + +- name: Create new nat gateway and allocate new eip + ec2_vpc_nat_gateway: + state: present + subnet_id: subnet-12345678 + wait: yes + region: ap-southeast-2 + register: new_nat_gateway + +- name: Create new nat gateway and allocate new eip if a nat gateway does not yet exist in the subnet. + ec2_vpc_nat_gateway: + state: present + subnet_id: subnet-12345678 + wait: yes + region: ap-southeast-2 + if_exist_do_not_create: true + register: new_nat_gateway + +- name: Delete nat gateway using discovered nat gateways from facts module + ec2_vpc_nat_gateway: + state: absent + region: ap-southeast-2 + wait: yes + nat_gateway_id: "{{ item.NatGatewayId }}" + release_eip: yes + register: delete_nat_gateway_result + with_items: "{{ gateways_to_remove.result }}" + +- name: Delete nat gateway and wait for deleted status + ec2_vpc_nat_gateway: + state: absent + nat_gateway_id: nat-12345678 + wait: yes + wait_timeout: 500 + region: ap-southeast-2 + +- name: Delete nat gateway and release EIP + ec2_vpc_nat_gateway: + state: absent + nat_gateway_id: nat-12345678 + release_eip: yes + region: ap-southeast-2 +''' + +RETURN = ''' +create_time: + description: The ISO 8601 date time formatin UTC. + returned: In all cases. + type: string + sample: "2016-03-05T05:19:20.282000+00:00'" +nat_gateway_id: + description: id of the VPC NAT Gateway + returned: In all cases. + type: string + sample: "nat-0d1e3a878585988f8" +subnet_id: + description: id of the Subnet + returned: In all cases. + type: string + sample: "subnet-12345" +state: + description: The current state of the Nat Gateway. + returned: In all cases. + type: string + sample: "available" +vpc_id: + description: id of the VPC. + returned: In all cases. + type: string + sample: "vpc-12345" +nat_gateway_addresses: + description: List of dictionairies containing the public_ip, network_interface_id, private_ip, and allocation_id. + returned: In all cases. + type: string + sample: [ + { + 'public_ip': '52.52.52.52', + 'network_interface_id': 'eni-12345', + 'private_ip': '10.0.0.100', + 'allocation_id': 'eipalloc-12345' + } + ] +''' + +try: + import botocore + import boto3 + HAS_BOTO3 = True +except ImportError: + HAS_BOTO3 = False + +import time +import datetime + +def convert_to_lower(data): + """Convert all uppercase keys in dict with lowercase_ + Args: + data (dict): Dictionary with keys that have upper cases in them + Example.. NatGatewayAddresses == nat_gateway_addresses + if a val is of type datetime.datetime, it will be converted to + the ISO 8601 + + Basic Usage: + >>> test = {'NatGatewaysAddresses': []} + >>> test = convert_to_lower(test) + { + 'nat_gateways_addresses': [] + } + + Returns: + Dictionary + """ + results = dict() + for key, val in data.items(): + key = re.sub('([A-Z]{1})', r'_\1', key).lower()[1:] + if isinstance(val, datetime.datetime): + results[key] = val.isoformat() + else: + results[key] = val + return results + +def formatted_nat_gw_output(data): + """Format the results of NatGateways into lowercase with underscores. + Args: + data (list): List of dictionaries with keys that have upper cases in + them. Example.. NatGatewayAddresses == nat_gateway_addresses + if a val is of type datetime.datetime, it will be converted to + the ISO 8601 + + Basic Usage: + >>> test = [ + { + 'VpcId': 'vpc-12345', + 'State': 'available', + 'NatGatewayId': 'nat-0b2f9f2ac3f51a653', + 'SubnetId': 'subnet-12345', + 'NatGatewayAddresses': [ + { + 'PublicIp': '52.52.52.52', + 'NetworkInterfaceId': 'eni-12345', + 'AllocationId': 'eipalloc-12345', + 'PrivateIp': '10.0.0.100' + } + ], + 'CreateTime': datetime.datetime(2016, 3, 5, 5, 19, 20, 282000, tzinfo=tzutc()) + } + ] + >>> test = formatted_nat_gw_output(test) + [ + { + 'nat_gateway_id': 'nat-0b2f9f2ac3f51a653', + 'subnet_id': 'subnet-12345', + 'nat_gateway_addresses': [ + { + 'public_ip': '52.52.52.52', + 'network_interface_id': 'eni-12345', + 'private_ip': '10.0.0.100', + 'allocation_id': 'eipalloc-12345' + } + ], + 'state': 'available', + 'create_time': '2016-03-05T05:19:20.282000+00:00', + 'vpc_id': 'vpc-12345' + } + ] + + Returns: + List + """ + results = list() + for gw in data: + output = dict() + ng_addresses = gw.pop('NatGatewayAddresses') + output = convert_to_lower(gw) + output['nat_gateway_addresses'] = [] + for address in ng_addresses: + gw_data = convert_to_lower(address) + output['nat_gateway_addresses'].append(gw_data) + results.append(output) + + return results + +def get_nat_gateways(client, subnet_id=None, nat_gateway_id=None): + """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. + + Basic Usage: + >>> client = boto3.client('ec2') + >>> subnet_id = 'subnet-w4t12897' + >>> get_nat_gateways(client, subnet_id) + [ + 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 = dict() + err_msg = "" + gateways_retrieved = False + if nat_gateway_id: + params['NatGatewayIds'] = [nat_gateway_id] + else: + params['Filter'] = [ + { + 'Name': 'subnet-id', + 'Values': [subnet_id] + } + ] + + try: + gateways = client.describe_nat_gateways(**params)['NatGateways'] + existing_gateways = formatted_nat_gw_output(gateways) + 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): + """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-w4t12897' + >>> allocation_id = 'eipalloc-36014da3' + >>> wait_for_status(client, subnet_id, allocation_id) + [ + 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) + """ + polling_increment_secs = 5 + wait_timeout = time.time() + wait_timeout + status_achieved = False + nat_gateway = list() + err_msg = "" + + while wait_timeout > time.time(): + try: + gws_retrieved, err_msg, nat_gateway = ( + get_nat_gateways(client, nat_gateway_id=nat_gateway_id) + ) + if gws_retrieved and nat_gateway: + if nat_gateway[0].get('state') == status: + status_achieved = True + break + + elif nat_gateway[0].get('state') == 'failed': + err_msg = nat_gateway[0].get('failure_message') + 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): + """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-w4t12897' + >>> allocation_id = 'eipalloc-36014da3' + >>> gateway_in_subnet_exists(client, subnet_id, allocation_id) + ( + [ + { + "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" + } + ], + False + ) + + Returns: + Tuple (list, bool) + """ + allocation_id_exists = False + gateways = [] + gws_retrieved, _, gws = get_nat_gateways(client, subnet_id) + if not gws_retrieved: + return gateways, allocation_id_exists + for gw in gws: + for address in gw['nat_gateway_addresses']: + if gw.get('state') == 'available' or gw.get('state') == 'pending': + 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 = "" + if check_mode: + return "eipalloc-123456", err_msg + try: + allocation = client.describe_addresses(**params)['Addresses'][0] + if not allocation.get('Domain') != 'vpc': + err_msg = ( + "EIP provided is a non-VPC EIP, please allocate a VPC scoped EIP" + ) + else: + allocation_id = allocation.get('AllocationId') + 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) + """ + if check_mode: + return True, "eipalloc-123456" + + ip_allocated = False + params = { + 'Domain': 'vpc' + } + try: + new_eip = client.allocate_address(**params) + ip_allocated = True + except botocore.exceptions.ClientError: + pass + + return ip_allocated, new_eip['AllocationId'] + +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 + """ + if check_mode: + return True + + ip_released = False + params = { + 'AllocationId': allocation_id, + } + try: + client.release_address(**params) + ip_released = True + except botocore.exceptions.ClientError: + pass + + return ip_released + +def create(client, subnet_id, allocation_id, client_token=None, + wait=False, wait_timeout=0, if_exist_do_not_create=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-w4t12897' + >>> allocation_id = 'eipalloc-36014da3' + >>> 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, str, list) + """ + params = { + 'SubnetId': subnet_id, + 'AllocationId': allocation_id + } + request_time = datetime.datetime.utcnow() + changed = False + token_provided = False + err_msg = "" + + if client_token: + token_provided = True + params['ClientToken'] = client_token + + try: + result = client.create_nat_gateway(**params)["NatGateway"] + changed = True + create_time = result['CreateTime'].replace(tzinfo=None) + if token_provided and (request_time > create_time): + changed = False + elif wait: + status_achieved, err_msg, result = ( + wait_for_status( + client, wait_timeout, result['NatGatewayId'], 'available' + ) + ) + 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' + ) + else: + err_msg = str(e) + + return 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): + """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, str, list) + """ + 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) + ) + if len(existing_gateways) > 0 and if_exist_do_not_create: + results = existing_gateways + return changed, err_msg, results + else: + _, allocation_id = allocate_eip_address(client) + + elif eip_address or allocation_id: + if eip_address and not allocation_id: + allocation_id = get_eip_allocation_id_by_address(client) + + existing_gateways, allocation_id_exists = ( + gateway_in_subnet_exists(client, subnet_id, allocation_id) + ) + if len(existing_gateways) > 0 and (allocation_id_exists or if_exist_do_not_create): + results = existing_gateways + return changed, err_msg, results + + changed, err_msg, results = create( + client, subnet_id, allocation_id, client_token, + wait, wait_timeout,if_exist_do_not_create + ) + + return changed, err_msg, results + +def remove(client, nat_gateway_id, wait=False, wait_timeout=0, release_eip=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 + } + changed = False + err_msg = "" + results = list() + try: + exist, _, gw = get_nat_gateways(client, nat_gateway_id=nat_gateway_id) + if exist and len(gw) == 1: + results = gw[0] + result = client.delete_nat_gateway(**params) + result = convert_to_lower(result) + allocation_id = ( + results['nat_gateway_addresses'][0]['allocation_id'] + ) + changed = True + except botocore.exceptions.ClientError as e: + err_msg = str(e) + + if wait and not err_msg: + status_achieved, err_msg, results = ( + wait_for_status(client, wait_timeout, nat_gateway_id, 'deleted') + ) + + if release_eip: + eip_released = release_address(client, allocation_id) + if not eip_released: + err_msg = "Failed to release eip %s".format(allocation_id) + + return changed, err_msg, results + +def main(): + argument_spec = ec2_argument_spec() + argument_spec.update(dict( + subnet_id=dict(), + eip_address=dict(), + allocation_id=dict(), + if_exist_do_not_create=dict(type='bool', default=False), + state=dict(default='present', choices=['present', 'absent']), + wait=dict(type='bool', default=True), + wait_timeout=dict(type='int', default=320, required=False), + release_eip=dict(type='bool', default=False), + nat_gateway_id=dict(), + client_token=dict(), + ) + ) + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + mutually_exclusive=[ + ['allocation_id', 'eip_address'] + ] + ) + + # 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, e: + module.fail_json(msg="Boto3 Client Error - " + str(e.msg)) + + changed = False + err_msg = None + + #Ensure resource is present + if state == 'present': + if not subnet_id: + module.fail_json(msg='subnet_id is required for creation') + + elif check_mode: + changed = True + results = 'Would have created NAT Gateway if not in check mode' + else: + changed, err_msg, results = ( + pre_create( + client, subnet_id, allocation_id, eip_address, + if_exist_do_not_create, wait, wait_timeout, + client_token + ) + ) + else: + if not nat_gateway_id: + module.fail_json(msg='nat_gateway_id is required for removal') + + elif check_mode: + changed = True + results = 'Would have deleted NAT Gateway if not in check mode' + else: + changed, err_msg, results = ( + remove(client, nat_gateway_id, wait, wait_timeout, release_eip) + ) + + if err_msg: + module.fail_json(msg=err_msg) + else: + module.exit_json(changed=changed, **results[0]) + +# import module snippets +from ansible.module_utils.basic import * +from ansible.module_utils.ec2 import * + +if __name__ == '__main__': + main() +