#!/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.2" 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 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. 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. - You should use this with the wait option. Since you can not release an address while a delete operation is happening. required: false default: true wait: description: - Wait for operation to complete before returning. required: false default: false 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 using an allocation-id. ec2_vpc_nat_gateway: state: present subnet_id: subnet-12345678 allocation_id: eipalloc-12345678 region: ap-southeast-2 register: new_nat_gateway - name: Create new nat gateway, using an EIP address and wait for available status. ec2_vpc_nat_gateway: state: present subnet_id: subnet-12345678 eip_address: 52.1.1.1 wait: yes region: ap-southeast-2 register: new_nat_gateway - name: Create new nat gateway and allocate new EIP. ec2_vpc_nat_gateway: state: present subnet_id: subnet-12345678 wait: yes region: ap-southeast-2 register: new_nat_gateway - name: Create new nat gateway and allocate new EIP if a nat gateway does not yet exist in the subnet. ec2_vpc_nat_gateway: state: present subnet_id: subnet-12345678 wait: yes region: ap-southeast-2 if_exist_do_not_create: true register: new_nat_gateway - name: Delete nat gateway using discovered nat gateways from facts module. ec2_vpc_nat_gateway: state: absent region: ap-southeast-2 wait: yes nat_gateway_id: "{{ item.NatGatewayId }}" release_eip: yes register: delete_nat_gateway_result 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 wait: yes wait_timeout: 300 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 datetime import random import re import time from dateutil.tz import tzutc DRY_RUN_GATEWAYS = [ { "nat_gateway_id": "nat-123456789", "subnet_id": "subnet-123456789", "nat_gateway_addresses": [ { "public_ip": "55.55.55.55", "network_interface_id": "eni-1234567", "private_ip": "10.0.0.102", "allocation_id": "eipalloc-1234567" } ], "state": "available", "create_time": "2016-03-05T05:19:20.282000+00:00", "vpc_id": "vpc-12345678" } ] DRY_RUN_GATEWAY_UNCONVERTED = [ { 'VpcId': 'vpc-12345678', 'State': 'available', 'NatGatewayId': 'nat-123456789', 'SubnetId': 'subnet-123456789', 'NatGatewayAddresses': [ { 'PublicIp': '55.55.55.55', 'NetworkInterfaceId': 'eni-1234567', 'AllocationId': 'eipalloc-1234567', 'PrivateIp': '10.0.0.102' } ], 'CreateTime': datetime.datetime(2016, 3, 5, 5, 19, 20, 282000, tzinfo=tzutc()) } ] DRY_RUN_ALLOCATION_UNCONVERTED = { 'Addresses': [ { 'PublicIp': '55.55.55.55', 'Domain': 'vpc', 'AllocationId': 'eipalloc-1234567' } ] } DRY_RUN_MSGS = 'DryRun Mode:' 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.. FooBar == foo_bar if a val is of type datetime.datetime, it will be converted to the ISO 8601 Basic Usage: >>> test = {'FooBar': []} >>> test = convert_to_lower(test) { 'foo_bar': [] } Returns: Dictionary """ results = dict() if isinstance(data, dict): for key, val in data.items(): key = re.sub(r'(([A-Z]{1,3}){1})', r'_\1', key).lower() if key[0] == '_': key = key[1:] if isinstance(val, datetime.datetime): results[key] = val.isoformat() elif isinstance(val, dict): results[key] = convert_to_lower(val) elif isinstance(val, list): converted = list() for item in val: converted.append(convert_to_lower(item)) results[key] = converted else: results[key] = val return results def get_nat_gateways(client, subnet_id=None, nat_gateway_id=None, states=None, check_mode=False): """Retrieve a list of NAT Gateways Args: client (botocore.client.EC2): Boto3 client Kwargs: subnet_id (str): The subnet_id the nat resides in. nat_gateway_id (str): The Amazon nat id. states (list): States available (pending, failed, available, deleting, and deleted) default=None Basic Usage: >>> client = boto3.client('ec2') >>> subnet_id = 'subnet-12345678' >>> get_nat_gateways(client, subnet_id) [ true, "", { "nat_gateway_id": "nat-123456789", "subnet_id": "subnet-123456789", "nat_gateway_addresses": [ { "public_ip": "55.55.55.55", "network_interface_id": "eni-1234567", "private_ip": "10.0.0.102", "allocation_id": "eipalloc-1234567" } ], "state": "deleted", "create_time": "2016-03-05T00:33:21.209000+00:00", "delete_time": "2016-03-05T00:36:37.329000+00:00", "vpc_id": "vpc-12345678" } Returns: Tuple (bool, str, list) """ params = dict() err_msg = "" gateways_retrieved = False existing_gateways = list() if not states: states = ['available', 'pending'] if nat_gateway_id: params['NatGatewayIds'] = [nat_gateway_id] else: params['Filter'] = [ { 'Name': 'subnet-id', 'Values': [subnet_id] }, { 'Name': 'state', 'Values': states } ] try: if not check_mode: gateways = client.describe_nat_gateways(**params)['NatGateways'] if gateways: for gw in gateways: existing_gateways.append(convert_to_lower(gw)) gateways_retrieved = True else: gateways_retrieved = True if nat_gateway_id: if DRY_RUN_GATEWAYS[0]['nat_gateway_id'] == nat_gateway_id: existing_gateways = DRY_RUN_GATEWAYS elif subnet_id: if DRY_RUN_GATEWAYS[0]['subnet_id'] == subnet_id: existing_gateways = DRY_RUN_GATEWAYS err_msg = '{0} Retrieving gateways'.format(DRY_RUN_MSGS) except botocore.exceptions.ClientError as e: err_msg = str(e) return gateways_retrieved, err_msg, existing_gateways def wait_for_status(client, wait_timeout, nat_gateway_id, status, check_mode=False): """Wait for the NAT Gateway to reach a status Args: client (botocore.client.EC2): Boto3 client wait_timeout (int): Number of seconds to wait, until this timeout is reached. nat_gateway_id (str): The Amazon nat id. status (str): The status to wait for. examples. status=available, status=deleted Basic Usage: >>> client = boto3.client('ec2') >>> subnet_id = 'subnet-12345678' >>> allocation_id = 'eipalloc-12345678' >>> wait_for_status(client, subnet_id, allocation_id) [ true, "", { "nat_gateway_id": "nat-123456789", "subnet_id": "subnet-1234567", "nat_gateway_addresses": [ { "public_ip": "55.55.55.55", "network_interface_id": "eni-1234567", "private_ip": "10.0.0.102", "allocation_id": "eipalloc-12345678" } ], "state": "deleted", "create_time": "2016-03-05T00:33:21.209000+00:00", "delete_time": "2016-03-05T00:36:37.329000+00:00", "vpc_id": "vpc-12345677" } ] Returns: Tuple (bool, str, dict) """ polling_increment_secs = 5 wait_timeout = time.time() + wait_timeout status_achieved = False nat_gateway = dict() states = ['pending', 'failed', 'available', 'deleting', 'deleted'] err_msg = "" while wait_timeout > time.time(): try: gws_retrieved, err_msg, nat_gateways = ( get_nat_gateways( client, nat_gateway_id=nat_gateway_id, states=states, check_mode=check_mode ) ) if gws_retrieved and nat_gateways: nat_gateway = nat_gateways[0] if check_mode: nat_gateway['state'] = status if nat_gateway.get('state') == status: status_achieved = True break elif nat_gateway.get('state') == 'failed': err_msg = nat_gateway.get('failure_message') break elif nat_gateway.get('state') == 'pending': if 'failure_message' in nat_gateway: err_msg = nat_gateway.get('failure_message') status_achieved = False break else: time.sleep(polling_increment_secs) except botocore.exceptions.ClientError as e: err_msg = str(e) if not status_achieved: err_msg = "Wait time out reached, while waiting for results" return status_achieved, err_msg, nat_gateway def gateway_in_subnet_exists(client, subnet_id, allocation_id=None, check_mode=False): """Retrieve all NAT Gateways for a subnet. Args: subnet_id (str): The subnet_id the nat resides in. Kwargs: allocation_id (str): The EIP Amazon identifier. default = None Basic Usage: >>> client = boto3.client('ec2') >>> subnet_id = 'subnet-1234567' >>> allocation_id = 'eipalloc-1234567' >>> gateway_in_subnet_exists(client, subnet_id, allocation_id) ( [ { "nat_gateway_id": "nat-123456789", "subnet_id": "subnet-123456789", "nat_gateway_addresses": [ { "public_ip": "55.55.55.55", "network_interface_id": "eni-1234567", "private_ip": "10.0.0.102", "allocation_id": "eipalloc-1234567" } ], "state": "deleted", "create_time": "2016-03-05T00:33:21.209000+00:00", "delete_time": "2016-03-05T00:36:37.329000+00:00", "vpc_id": "vpc-1234567" } ], False ) Returns: Tuple (list, bool) """ allocation_id_exists = False gateways = [] states = ['available', 'pending'] gws_retrieved, _, gws = ( get_nat_gateways( client, subnet_id, states=states, check_mode=check_mode ) ) if not gws_retrieved: return gateways, allocation_id_exists for gw in gws: for address in gw['nat_gateway_addresses']: if allocation_id: if address.get('allocation_id') == allocation_id: allocation_id_exists = True gateways.append(gw) else: gateways.append(gw) return gateways, allocation_id_exists def get_eip_allocation_id_by_address(client, eip_address, check_mode=False): """Release an EIP from your EIP Pool Args: client (botocore.client.EC2): Boto3 client eip_address (str): The Elastic IP Address of the EIP. Kwargs: check_mode (bool): if set to true, do not run anything and falsify the results. Basic Usage: >>> client = boto3.client('ec2') >>> eip_address = '52.87.29.36' >>> get_eip_allocation_id_by_address(client, eip_address) 'eipalloc-36014da3' Returns: Tuple (str, str) """ params = { 'PublicIps': [eip_address], } allocation_id = None err_msg = "" try: if not check_mode: allocations = client.describe_addresses(**params)['Addresses'] if len(allocations) == 1: allocation = allocations[0] else: allocation = None else: dry_run_eip = ( DRY_RUN_ALLOCATION_UNCONVERTED['Addresses'][0]['PublicIp'] ) if dry_run_eip == eip_address: allocation = DRY_RUN_ALLOCATION_UNCONVERTED['Addresses'][0] else: allocation = None if allocation: if allocation.get('Domain') != 'vpc': err_msg = ( "EIP {0} is a non-VPC EIP, please allocate a VPC scoped EIP" .format(eip_address) ) else: allocation_id = allocation.get('AllocationId') else: err_msg = ( "EIP {0} does not exist".format(eip_address) ) except botocore.exceptions.ClientError as e: err_msg = str(e) return allocation_id, err_msg def allocate_eip_address(client, check_mode=False): """Release an EIP from your EIP Pool Args: client (botocore.client.EC2): Boto3 client Kwargs: check_mode (bool): if set to true, do not run anything and falsify the results. Basic Usage: >>> client = boto3.client('ec2') >>> allocate_eip_address(client) True Returns: Tuple (bool, str) """ ip_allocated = False new_eip = None err_msg = '' params = { 'Domain': 'vpc', } try: if check_mode: ip_allocated = True random_numbers = ( ''.join(str(x) for x in random.sample(range(0, 9), 7)) ) new_eip = 'eipalloc-{0}'.format(random_numbers) else: new_eip = client.allocate_address(**params)['AllocationId'] ip_allocated = True err_msg = 'eipalloc id {0} created'.format(new_eip) except botocore.exceptions.ClientError as e: err_msg = str(e) return ip_allocated, err_msg, new_eip def release_address(client, allocation_id, check_mode=False): """Release an EIP from your EIP Pool Args: client (botocore.client.EC2): Boto3 client allocation_id (str): The eip Amazon identifier. Kwargs: check_mode (bool): if set to true, do not run anything and falsify the results. Basic Usage: >>> client = boto3.client('ec2') >>> allocation_id = "eipalloc-123456" >>> release_address(client, allocation_id) True Returns: Boolean, string """ err_msg = '' if check_mode: return True, '' ip_released = False params = { 'AllocationId': allocation_id, } try: client.release_address(**params) ip_released = True except botocore.exceptions.ClientError as e: err_msg = str(e) return ip_released, err_msg def create(client, subnet_id, allocation_id, client_token=None, wait=False, wait_timeout=0, if_exist_do_not_create=False, check_mode=False): """Create an Amazon NAT Gateway. Args: client (botocore.client.EC2): Boto3 client subnet_id (str): The subnet_id the nat resides in. allocation_id (str): The eip Amazon identifier. Kwargs: if_exist_do_not_create (bool): if a nat gateway already exists in this subnet, than do not create another one. default = False wait (bool): Wait for the nat to be in the deleted state before returning. default = False wait_timeout (int): Number of seconds to wait, until this timeout is reached. default = 0 client_token (str): default = None Basic Usage: >>> client = boto3.client('ec2') >>> subnet_id = 'subnet-1234567' >>> allocation_id = 'eipalloc-1234567' >>> create(client, subnet_id, allocation_id, if_exist_do_not_create=True, wait=True, wait_timeout=500) [ true, "", { "nat_gateway_id": "nat-123456789", "subnet_id": "subnet-1234567", "nat_gateway_addresses": [ { "public_ip": "55.55.55.55", "network_interface_id": "eni-1234567", "private_ip": "10.0.0.102", "allocation_id": "eipalloc-1234567" } ], "state": "deleted", "create_time": "2016-03-05T00:33:21.209000+00:00", "delete_time": "2016-03-05T00:36:37.329000+00:00", "vpc_id": "vpc-1234567" } ] Returns: Tuple (bool, str, list) """ params = { 'SubnetId': subnet_id, 'AllocationId': allocation_id } request_time = datetime.datetime.utcnow() changed = False success = False token_provided = False err_msg = "" if client_token: token_provided = True params['ClientToken'] = client_token try: if not check_mode: result = client.create_nat_gateway(**params)["NatGateway"] else: result = DRY_RUN_GATEWAY_UNCONVERTED[0] result['CreateTime'] = datetime.datetime.utcnow() result['NatGatewayAddresses'][0]['AllocationId'] = allocation_id result['SubnetId'] = subnet_id success = True changed = True create_time = result['CreateTime'].replace(tzinfo=None) if token_provided and (request_time > create_time): changed = False elif wait: success, err_msg, result = ( wait_for_status( client, wait_timeout, result['NatGatewayId'], 'available', check_mode=check_mode ) ) if success: err_msg = ( 'NAT gateway {0} created'.format(result['nat_gateway_id']) ) except botocore.exceptions.ClientError as e: if "IdempotentParameterMismatch" in e.message: err_msg = ( 'NAT Gateway does not support update and token has already been provided' ) else: err_msg = str(e) success = False changed = False result = None return success, changed, err_msg, result def pre_create(client, subnet_id, allocation_id=None, eip_address=None, if_exist_do_not_create=False, wait=False, wait_timeout=0, client_token=None, check_mode=False): """Create an Amazon NAT Gateway. Args: client (botocore.client.EC2): Boto3 client subnet_id (str): The subnet_id the nat resides in. Kwargs: allocation_id (str): The EIP Amazon identifier. default = None eip_address (str): The Elastic IP Address of the EIP. default = None if_exist_do_not_create (bool): if a nat gateway already exists in this subnet, than do not create another one. default = False wait (bool): Wait for the nat to be in the deleted state before returning. default = False wait_timeout (int): Number of seconds to wait, until this timeout is reached. default = 0 client_token (str): default = None Basic Usage: >>> client = boto3.client('ec2') >>> subnet_id = 'subnet-w4t12897' >>> allocation_id = 'eipalloc-36014da3' >>> pre_create(client, subnet_id, allocation_id, if_exist_do_not_create=True, wait=True, wait_timeout=500) [ true, "", { "nat_gateway_id": "nat-03835afb6e31df79b", "subnet_id": "subnet-w4t12897", "nat_gateway_addresses": [ { "public_ip": "52.87.29.36", "network_interface_id": "eni-5579742d", "private_ip": "10.0.0.102", "allocation_id": "eipalloc-36014da3" } ], "state": "deleted", "create_time": "2016-03-05T00:33:21.209000+00:00", "delete_time": "2016-03-05T00:36:37.329000+00:00", "vpc_id": "vpc-w68571b5" } ] Returns: Tuple (bool, bool, str, list) """ success = False changed = False err_msg = "" results = list() if not allocation_id and not eip_address: existing_gateways, allocation_id_exists = ( gateway_in_subnet_exists(client, subnet_id, check_mode=check_mode) ) if len(existing_gateways) > 0 and if_exist_do_not_create: success = True changed = False results = existing_gateways[0] err_msg = ( 'NAT Gateway {0} already exists in subnet_id {1}' .format( existing_gateways[0]['nat_gateway_id'], subnet_id ) ) return success, changed, err_msg, results else: success, err_msg, allocation_id = ( allocate_eip_address(client, check_mode=check_mode) ) if not success: return success, 'False', err_msg, dict() elif eip_address or allocation_id: if eip_address and not allocation_id: allocation_id, err_msg = ( get_eip_allocation_id_by_address( client, eip_address, check_mode=check_mode ) ) if not allocation_id: success = False changed = False return success, changed, err_msg, dict() existing_gateways, allocation_id_exists = ( gateway_in_subnet_exists( client, subnet_id, allocation_id, check_mode=check_mode ) ) if len(existing_gateways) > 0 and (allocation_id_exists or if_exist_do_not_create): success = True changed = False results = existing_gateways[0] err_msg = ( 'NAT Gateway {0} already exists in subnet_id {1}' .format( existing_gateways[0]['nat_gateway_id'], subnet_id ) ) return success, changed, err_msg, results success, changed, err_msg, results = create( client, subnet_id, allocation_id, client_token, wait, wait_timeout, if_exist_do_not_create, check_mode=check_mode ) return success, changed, err_msg, results def remove(client, nat_gateway_id, wait=False, wait_timeout=0, release_eip=False, check_mode=False): """Delete an Amazon NAT Gateway. Args: client (botocore.client.EC2): Boto3 client nat_gateway_id (str): The Amazon nat id. Kwargs: wait (bool): Wait for the nat to be in the deleted state before returning. wait_timeout (int): Number of seconds to wait, until this timeout is reached. release_eip (bool): Once the nat has been deleted, you can deallocate the eip from the vpc. Basic Usage: >>> client = boto3.client('ec2') >>> nat_gw_id = 'nat-03835afb6e31df79b' >>> remove(client, nat_gw_id, wait=True, wait_timeout=500, release_eip=True) [ true, "", { "nat_gateway_id": "nat-03835afb6e31df79b", "subnet_id": "subnet-w4t12897", "nat_gateway_addresses": [ { "public_ip": "52.87.29.36", "network_interface_id": "eni-5579742d", "private_ip": "10.0.0.102", "allocation_id": "eipalloc-36014da3" } ], "state": "deleted", "create_time": "2016-03-05T00:33:21.209000+00:00", "delete_time": "2016-03-05T00:36:37.329000+00:00", "vpc_id": "vpc-w68571b5" } ] Returns: Tuple (bool, str, list) """ params = { 'NatGatewayId': nat_gateway_id } success = False changed = False err_msg = "" results = list() states = ['pending', 'available' ] try: exist, _, gw = ( get_nat_gateways( client, nat_gateway_id=nat_gateway_id, states=states, check_mode=check_mode ) ) if exist and len(gw) == 1: results = gw[0] if not check_mode: client.delete_nat_gateway(**params) allocation_id = ( results['nat_gateway_addresses'][0]['allocation_id'] ) changed = True success = True err_msg = ( 'NAT gateway {0} is in a deleting state. Delete was successfull' .format(nat_gateway_id) ) if wait: status_achieved, err_msg, results = ( wait_for_status( client, wait_timeout, nat_gateway_id, 'deleted', check_mode=check_mode ) ) if status_achieved: err_msg = ( 'NAT gateway {0} was deleted successfully' .format(nat_gateway_id) ) except botocore.exceptions.ClientError as e: err_msg = str(e) if release_eip: eip_released, eip_err = ( release_address(client, allocation_id, check_mode) ) if not eip_released: err_msg = ( "{0}: Failed to release EIP {1}: {2}" .format(err_msg, allocation_id, eip_err) ) success = False return success, changed, err_msg, results def main(): argument_spec = ec2_argument_spec() argument_spec.update(dict( subnet_id=dict(type='str'), eip_address=dict(type='str'), allocation_id=dict(type='str'), if_exist_do_not_create=dict(type='bool', default=False), state=dict(default='present', choices=['present', 'absent']), wait=dict(type='bool', default=False), wait_timeout=dict(type='int', default=320, required=False), release_eip=dict(type='bool', default=False), nat_gateway_id=dict(type='str'), client_token=dict(type='str'), ) ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, mutually_exclusive=[ ['allocation_id', 'eip_address'] ] ) # Validate Requirements if not HAS_BOTO3: module.fail_json(msg='botocore/boto3 is required.') state = module.params.get('state').lower() check_mode = module.check_mode subnet_id = module.params.get('subnet_id') allocation_id = module.params.get('allocation_id') eip_address = module.params.get('eip_address') nat_gateway_id = module.params.get('nat_gateway_id') wait = module.params.get('wait') wait_timeout = module.params.get('wait_timeout') release_eip = module.params.get('release_eip') client_token = module.params.get('client_token') if_exist_do_not_create = module.params.get('if_exist_do_not_create') try: region, ec2_url, aws_connect_kwargs = ( get_aws_connection_info(module, boto3=True) ) client = ( boto3_conn( module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_kwargs ) ) except botocore.exceptions.ClientError as e: module.fail_json(msg="Boto3 Client Error - " + str(e.msg)) changed = False err_msg = '' if state == 'present': if not subnet_id: module.fail_json(msg='subnet_id is required for creation') success, changed, err_msg, results = ( pre_create( client, subnet_id, allocation_id, eip_address, if_exist_do_not_create, wait, wait_timeout, client_token, check_mode=check_mode ) ) else: if not nat_gateway_id: module.fail_json(msg='nat_gateway_id is required for removal') else: success, changed, err_msg, results = ( remove( client, nat_gateway_id, wait, wait_timeout, release_eip, check_mode=check_mode ) ) if not success: module.fail_json( msg=err_msg, success=success, changed=changed ) else: module.exit_json( msg=err_msg, success=success, changed=changed, **results ) # import module snippets from ansible.module_utils.basic import * from ansible.module_utils.ec2 import * if __name__ == '__main__': main()