f86c5f8465
* 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.
930 lines
29 KiB
Python
930 lines
29 KiB
Python
#!/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.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()
|
|
|