summaryrefslogtreecommitdiff
path: root/lib/ansible/modules/extras/cloud/amazon/ec2_vpc_nat_gateway.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ansible/modules/extras/cloud/amazon/ec2_vpc_nat_gateway.py')
-rw-r--r--lib/ansible/modules/extras/cloud/amazon/ec2_vpc_nat_gateway.py1085
1 files changed, 1085 insertions, 0 deletions
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..ee53d7bb13
--- /dev/null
+++ b/lib/ansible/modules/extras/cloud/amazon/ec2_vpc_nat_gateway.py
@@ -0,0 +1,1085 @@
+#!/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 <http://www.gnu.org/licenses/>.
+
+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()