"""
Manage VPCs using boto3.
========================
Renamed from ``boto_vpc`` to ``boto3_vpc`` and updated to call the
refactored ``boto3_vpc`` execution module.
Create and destroy VPCs. Be aware that this interacts with Amazon's
services, and so may incur charges.
:depends:
- boto3 >= 1.28.0
- botocore >= 1.31.0
This module uses ``boto3``, which can be installed via package, or pip.
This module accepts explicit VPC credentials but can also utilize
IAM roles assigned to the instance through Instance Profiles. Dynamic
credentials are then automatically obtained from AWS API and no further
configuration is necessary. More information available `here
<http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html>`_.
If IAM roles are not used you need to specify them either in a pillar file or
in the minion's config file:
.. code-block:: yaml
vpc.keyid: GKTADJGHEIQSXMKKRBJ08H
vpc.key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
It's also possible to specify ``key``, ``keyid`` and ``region`` via a profile, either
passed in as a dict, or as a string to pull from pillars or minion config:
.. code-block:: yaml
myprofile:
keyid: GKTADJGHEIQSXMKKRBJ08H
key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
region: us-east-1
.. code-block:: yaml
Ensure VPC exists:
boto3_vpc.present:
- name: myvpc
- cidr_block: 10.10.11.0/24
- region: us-east-1
Ensure subnet exists:
boto3_vpc.subnet_present:
- name: mysubnet
- vpc_id: vpc-123456
- cidr_block: 10.0.0.0/16
- region: us-east-1
- profile: myprofile
.. versionadded:: 1.0.0
"""
import logging
from salt.utils import dictupdate
__virtualname__ = "boto3_vpc"
log = logging.getLogger(__name__)
[docs]
def __virtual__():
"""
Only load if the boto3_vpc execution module is available.
"""
if "boto3_vpc.exists" in __salt__:
return __virtualname__
return (
False,
"The boto3_vpc state module requires the boto3_vpc execution module.",
)
[docs]
def present(
name,
cidr_block,
secondary_cidr_blocks=None,
instance_tenancy=None,
dns_support=None,
dns_hostnames=None,
tags=None,
region=None,
key=None,
keyid=None,
profile=None,
):
"""
Ensure a VPC with the supplied properties exists.
.. versionchanged:: 1.1.0
Support associating secondary CIDR blocks through
``secondary_cidr_blocks`` or backward-compatible list input.
name (str):
Name of the VPC.
cidr_block (str):
The primary VPC CIDR block, e.g. ``10.0.0.0/24``.
secondary_cidr_blocks (list, optional):
Optional list of additional CIDR blocks to associate to the VPC.
For backward compatibility, ``cidr_block`` can still be provided as
a list where the first entry is primary and the rest are secondary.
instance_tenancy (str, optional):
Tenancy for instances launched in this VPC (``default`` or
``dedicated``).
dns_support (bool, optional):
Whether DNS resolution is supported for the VPC.
dns_hostnames (bool, optional):
Whether instances launched in the VPC receive DNS hostnames.
tags (dict, optional):
Dict of tag key/values to apply. Defaults to None.
region (str, optional):
The AWS region where the VPC exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
ensure-present:
boto3_vpc.present:
- name: example
- cidr_block: 10.0.0.0/24
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
if isinstance(cidr_block, str):
primary_cidr = cidr_block.strip()
secondary_from_primary = []
elif isinstance(cidr_block, (list, tuple, set)):
cidr_items = [item for item in cidr_block if isinstance(item, str) and item.strip()]
if not cidr_items:
ret["result"] = False
ret["comment"] = "cidr_block must include at least one CIDR block."
return ret
primary_cidr = cidr_items[0].strip()
secondary_from_primary = [item.strip() for item in cidr_items[1:]]
else:
ret["result"] = False
ret["comment"] = "cidr_block must be a CIDR string or list of CIDR strings."
return ret
if not primary_cidr:
ret["result"] = False
ret["comment"] = "cidr_block must be a non-empty CIDR string."
return ret
normalized_secondary = []
for item in secondary_from_primary:
if item and item != primary_cidr and item not in normalized_secondary:
normalized_secondary.append(item)
if secondary_cidr_blocks is not None:
secondary_input = (
[secondary_cidr_blocks]
if isinstance(secondary_cidr_blocks, str)
else secondary_cidr_blocks
)
if not isinstance(secondary_input, (list, tuple, set)):
ret["result"] = False
ret["comment"] = "secondary_cidr_blocks must be a CIDR string or list of CIDR strings."
return ret
for item in secondary_input:
if not isinstance(item, str) or not item.strip():
ret["result"] = False
ret["comment"] = "secondary_cidr_blocks values must be non-empty CIDR strings."
return ret
normalized = item.strip()
if normalized != primary_cidr and normalized not in normalized_secondary:
normalized_secondary.append(normalized)
r = __salt__["boto3_vpc.exists"](
name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile
)
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to create VPC: {}.".format(r["error"]["message"])
return ret
if r.get("exists"):
if not normalized_secondary:
ret["comment"] = "VPC present."
return ret
if __opts__["test"]:
ret["result"] = None
ret["comment"] = f"Secondary CIDR blocks will be associated to VPC {name}."
ret["changes"] = {
"old": {"secondary_cidr_blocks": []},
"new": {"secondary_cidr_blocks": normalized_secondary},
}
return ret
assoc = __salt__["boto3_vpc.associate_vpc_cidr_blocks"](
normalized_secondary,
vpc_name=name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not assoc.get("associated"):
ret["result"] = False
ret["comment"] = "Failed to associate VPC CIDR blocks: {}.".format(
assoc["error"]["message"]
)
return ret
if assoc.get("associated_cidrs"):
ret["changes"] = {
"old": {"secondary_cidr_blocks": []},
"new": {"secondary_cidr_blocks": assoc["associated_cidrs"]},
}
ret["comment"] = f"VPC present; associated CIDR blocks to {name}."
return ret
ret["comment"] = "VPC present."
return ret
if __opts__["test"]:
ret["comment"] = f"VPC {name} is set to be created."
ret["result"] = None
ret["changes"] = {"old": {"vpc": None}, "new": {"vpc": name}}
return ret
r = __salt__["boto3_vpc.create"](
primary_cidr,
instance_tenancy=instance_tenancy,
vpc_name=name,
enable_dns_support=dns_support,
enable_dns_hostnames=dns_hostnames,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not r.get("created"):
ret["result"] = False
ret["comment"] = "Error in creating VPC: {}.".format(r["error"]["message"])
return ret
if normalized_secondary:
assoc = __salt__["boto3_vpc.associate_vpc_cidr_blocks"](
normalized_secondary,
vpc_id=r["id"],
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not assoc.get("associated"):
ret["result"] = False
ret["comment"] = (
"VPC created, but failed to associate secondary CIDR blocks: {}.".format(
assoc["error"]["message"]
)
)
return ret
described = __salt__["boto3_vpc.describe"](
vpc_id=r["id"], region=region, key=key, keyid=keyid, profile=profile
)
ret["changes"]["old"] = {"vpc": None}
ret["changes"]["new"] = described
if normalized_secondary:
ret["changes"]["secondary_cidr_blocks"] = normalized_secondary
ret["comment"] = f"VPC {name} created."
return ret
[docs]
def absent(name, tags=None, region=None, key=None, keyid=None, profile=None):
"""
Ensure the named VPC is absent.
name (str):
Name of the VPC.
tags (dict, optional):
Optional tag filter; all tags must match. Defaults to None.
region (str, optional):
The AWS region where the VPC exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
ensure-absent:
boto3_vpc.absent:
- name: example
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
r = __salt__["boto3_vpc.get_id"](
name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile
)
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to delete VPC: {}.".format(r["error"]["message"])
return ret
vpc_id = r.get("id")
if not vpc_id:
ret["comment"] = f"{name} VPC does not exist."
return ret
if __opts__["test"]:
ret["comment"] = f"VPC {name} is set to be removed."
ret["result"] = None
ret["changes"] = {"old": {"vpc": vpc_id}, "new": {"vpc": None}}
return ret
r = __salt__["boto3_vpc.delete"](
vpc_name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile
)
if not r.get("deleted"):
ret["result"] = False
ret["comment"] = "Failed to delete VPC: {}.".format(r["error"]["message"])
return ret
ret["changes"]["old"] = {"vpc": vpc_id}
ret["changes"]["new"] = {"vpc": None}
ret["comment"] = f"VPC {name} deleted."
return ret
[docs]
def dhcp_options_present(
name,
dhcp_options_id=None,
vpc_name=None,
vpc_id=None,
domain_name=None,
domain_name_servers=None,
ntp_servers=None,
netbios_name_servers=None,
netbios_node_type=None,
tags=None,
region=None,
key=None,
keyid=None,
profile=None,
):
"""
Ensure a DHCP options set with the given settings exists.
.. note::
This implementation only sets values during option set creation. It
cannot update an existing option set in place.
name (str):
Name of the DHCP options set.
dhcp_options_id (str, optional):
The ID of the DHCP options set. Defaults to None.
vpc_name (str, optional):
The name of the VPC to associate with the DHCP options set. Defaults to None.
vpc_id (str, optional):
The ID of the VPC to associate with the DHCP options set. Defaults to None.
domain_name (str, optional):
The domain name for the DHCP options set. Defaults to None.
domain_name_servers (list, optional):
A list of domain name servers for the DHCP options set. Defaults to None.
ntp_servers (list, optional):
A list of NTP servers for the DHCP options set. Defaults to None.
netbios_name_servers (list, optional):
A list of NetBIOS name servers for the DHCP options set. Defaults to None.
netbios_node_type (int, optional):
The NetBIOS node type for the DHCP options set. Defaults to None.
tags (dict, optional):
Dict of tag key/values to apply. Defaults to None.
region (str, optional):
The AWS region where the DHCP options set exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
.. versionchanged:: 1.0.1
Example:
.. code-block:: yaml
ensure-dhcp-options-present:
boto3_vpc.dhcp_options_present:
- name: example
- cidr_block: 10.0.0.0/24
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
new = {
"domain_name": domain_name,
"domain_name_servers": domain_name_servers,
"ntp_servers": ntp_servers,
"netbios_name_servers": netbios_name_servers,
"netbios_node_type": netbios_node_type,
}
desired_vpc = None
if vpc_id or vpc_name:
desired_vpc = __salt__["boto3_vpc.describe"](
vpc_id=vpc_id,
vpc_name=vpc_name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in desired_vpc:
ret["result"] = False
ret["comment"] = "Failed to validate VPC: {}.".format(desired_vpc["error"]["message"])
return ret
desired_vpc = desired_vpc.get("vpc")
if not desired_vpc:
ret["result"] = False
ret["comment"] = f"VPC {vpc_name or vpc_id} does not exist."
return ret
r = __salt__["boto3_vpc.dhcp_options_exists"](
dhcp_options_id=dhcp_options_id,
dhcp_options_name=name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to validate DHCP options: {}.".format(r["error"]["message"])
return ret
if r.get("exists"):
if desired_vpc:
dhcp_id = __salt__["boto3_vpc.get_resource_id"](
"dhcp_options",
name=name,
resource_id=dhcp_options_id,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in dhcp_id:
ret["result"] = False
ret["comment"] = "Failed to resolve DHCP options: {}.".format(
dhcp_id["error"]["message"]
)
return ret
dhcp_id = dhcp_id.get("id")
current_dhcp_id = desired_vpc.get("dhcp_options_id")
if not dhcp_id:
ret["comment"] = "DHCP options already present."
return ret
if current_dhcp_id == dhcp_id:
ret["comment"] = "DHCP options already present and associated."
return ret
if __opts__["test"]:
ret["comment"] = f"DHCP options {name} are set to be associated."
ret["result"] = None
ret["changes"] = {
"old": {"dhcp_options_id": current_dhcp_id},
"new": {"dhcp_options_id": dhcp_id, "vpc_id": desired_vpc.get("id")},
}
return ret
associated = __salt__["boto3_vpc.associate_dhcp_options_to_vpc"](
dhcp_options_id=dhcp_id,
vpc_id=desired_vpc.get("id"),
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not associated.get("associated"):
ret["result"] = False
ret["comment"] = "Failed to associate DHCP options: {}.".format(
associated["error"]["message"]
)
return ret
ret["changes"] = {
"old": {"dhcp_options_id": current_dhcp_id},
"new": {"dhcp_options_id": dhcp_id, "vpc_id": desired_vpc.get("id")},
}
ret["comment"] = f"DHCP options {name} associated to VPC {desired_vpc.get('id')}."
return ret
ret["comment"] = "DHCP options already present."
return ret
if __opts__["test"]:
ret["comment"] = f"DHCP options {name} are set to be created."
ret["result"] = None
ret["changes"] = {"old": {"dhcp_options": None}, "new": {"dhcp_options": new}}
return ret
r = __salt__["boto3_vpc.create_dhcp_options"](
domain_name=domain_name,
domain_name_servers=domain_name_servers,
ntp_servers=ntp_servers,
netbios_name_servers=netbios_name_servers,
netbios_node_type=netbios_node_type,
dhcp_options_name=name,
tags=tags,
vpc_id=vpc_id,
vpc_name=vpc_name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not r.get("created"):
ret["result"] = False
ret["comment"] = "Failed to create DHCP options: {}".format(r["error"]["message"])
return ret
ret["changes"]["old"] = {"dhcp_options": None}
ret["changes"]["new"] = {"dhcp_options": new}
ret["comment"] = f"DHCP options {name} created."
return ret
[docs]
def dhcp_options_absent(
name=None, dhcp_options_id=None, region=None, key=None, keyid=None, profile=None
):
"""
Ensure a DHCP options set is absent.
name (str):
The name of the DHCP options set.
dhcp_options_id (str, optional):
The ID of the DHCP options set. Defaults to None.
vpc_name (str, optional):
The name of the VPC to associate with the DHCP options set. Defaults to None.
vpc_id (str, optional):
The ID of the VPC to associate with the DHCP options set. Defaults to None.
domain_name (str, optional):
The domain name for the DHCP options set. Defaults to None.
domain_name_servers (list, optional):
A list of domain name servers for the DHCP options set. Defaults to None.
ntp_servers (list, optional):
A list of NTP servers for the DHCP options set. Defaults to None.
netbios_name_servers (list, optional):
A list of NetBIOS name servers for the DHCP options set. Defaults to None.
netbios_node_type (int, optional):
The NetBIOS node type for the DHCP options set. Defaults to None.
tags (dict, optional):
Dict of tag key/values to apply. Defaults to None.
region (str, optional):
The AWS region where the DHCP options set exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
ensure-dhcp-options-absent:
boto3_vpc.dhcp_options_absent:
- name: example
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
if dhcp_options_id:
r = __salt__["boto3_vpc.dhcp_options_exists"](
dhcp_options_id=dhcp_options_id,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
rid = dhcp_options_id if r.get("exists") else None
else:
r = __salt__["boto3_vpc.get_resource_id"](
"dhcp_options",
name=name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
rid = r.get("id")
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to delete DHCP options: {}.".format(r["error"]["message"])
return ret
if not rid:
ret["comment"] = f"DHCP options {name} do not exist."
return ret
if __opts__["test"]:
ret["comment"] = f"DHCP options {name} are set to be deleted."
ret["result"] = None
ret["changes"] = {
"old": {"dhcp_options": rid},
"new": {"dhcp_options": None},
}
return ret
r = __salt__["boto3_vpc.delete_dhcp_options"](
dhcp_options_id=rid,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not r.get("deleted"):
ret["result"] = False
ret["comment"] = "Failed to delete DHCP options: {}".format(r["error"]["message"])
return ret
ret["changes"]["old"] = {"dhcp_options": rid}
ret["changes"]["new"] = {"dhcp_options": None}
ret["comment"] = f"DHCP options {name} deleted."
return ret
[docs]
def subnet_present(
name,
cidr_block,
vpc_name=None,
vpc_id=None,
availability_zone=None,
tags=None,
region=None,
key=None,
keyid=None,
profile=None,
auto_assign_public_ipv4=False,
):
"""
Ensure a subnet exists.
.. note::
Route table association is not handled by the boto3_vpc subnet
states yet; that will land with the route_table port.
name (str):
Name of the subnet.
cidr_block (str):
The range of IPs for the subnet, in CIDR format.
vpc_name (str, optional):
The name of the VPC the subnet belongs to. Defaults to None.
vpc_id (str, optional):
The ID of the VPC the subnet belongs to. Defaults to None.
availability_zone (str, optional):
Optional AZ to place the subnet in. Defaults to None.
auto_assign_public_ipv4 (bool, optional):
If ``True``, instances launched into this subnet will be assigned a
public IPv4 address by default. Defaults to False.
Example:
.. code-block:: yaml
ensure-subnet-present:
boto3_vpc.subnet_present:
- name: example
- cidr_block: 10.0.0.0/24
- vpc_name: my-vpc
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
r = __salt__["boto3_vpc.subnet_exists"](
subnet_name=name,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to create subnet: {}.".format(r["error"]["message"])
return ret
if r.get("exists"):
ret["comment"] = "Subnet present."
return ret
if __opts__["test"]:
ret["comment"] = f"Subnet {name} is set to be created."
ret["result"] = None
ret["changes"] = {"old": {"subnet": None}, "new": {"subnet": name}}
return ret
r = __salt__["boto3_vpc.create_subnet"](
subnet_name=name,
cidr_block=cidr_block,
availability_zone=availability_zone,
auto_assign_public_ipv4=auto_assign_public_ipv4,
vpc_name=vpc_name,
vpc_id=vpc_id,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not r.get("created"):
ret["result"] = False
ret["comment"] = "Failed to create subnet: {}".format(r["error"]["message"])
return ret
described = __salt__["boto3_vpc.describe_subnet"](
subnet_id=r["id"], region=region, key=key, keyid=keyid, profile=profile
)
ret["changes"]["old"] = {"subnet": None}
ret["changes"]["new"] = described
ret["comment"] = f"Subnet {name} created."
return ret
[docs]
def subnet_absent(name=None, subnet_id=None, region=None, key=None, keyid=None, profile=None):
"""
Ensure a subnet is absent.
name (str):
The name of the subnet.
subnet_id (str, optional):
The ID of the subnet. Defaults to None.
region (str, optional):
The AWS region where the subnet exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
ensure-subnet-absent:
boto3_vpc.subnet_absent:
- name: example
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
if subnet_id:
r = __salt__["boto3_vpc.subnet_exists"](
subnet_id=subnet_id, region=region, key=key, keyid=keyid, profile=profile
)
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to delete subnet: {}.".format(r["error"]["message"])
return ret
rid = subnet_id if r.get("exists") else None
else:
r = __salt__["boto3_vpc.get_resource_id"](
"subnet", name=name, region=region, key=key, keyid=keyid, profile=profile
)
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to delete subnet: {}.".format(r["error"]["message"])
return ret
rid = r.get("id")
if not rid:
ret["comment"] = f"{name} subnet does not exist."
return ret
if __opts__["test"]:
ret["comment"] = f"Subnet {name} ({rid}) is set to be removed."
ret["result"] = None
ret["changes"] = {"old": {"subnet": rid}, "new": {"subnet": None}}
return ret
r = __salt__["boto3_vpc.delete_subnet"](
subnet_name=name, region=region, key=key, keyid=keyid, profile=profile
)
if not r.get("deleted"):
ret["result"] = False
ret["comment"] = "Failed to delete subnet: {}".format(r["error"]["message"])
return ret
ret["changes"]["old"] = {"subnet": rid}
ret["changes"]["new"] = {"subnet": None}
ret["comment"] = f"Subnet {name} deleted."
return ret
[docs]
def internet_gateway_present(
name,
vpc_name=None,
vpc_id=None,
tags=None,
region=None,
key=None,
keyid=None,
profile=None,
):
"""
Ensure an internet gateway exists.
name (str):
Name of the internet gateway.
vpc_name (str, optional):
Name of the VPC to which the internet gateway should be attached. Defaults to None.
vpc_id (str, optional):
Id of the VPC to which the internet_gateway should be attached.
Only one of vpc_name or vpc_id may be provided. Defaults to None.
tags (list, optional):
A list of tags. Defaults to None.
region (str, optional):
The AWS region where the internet gateway exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
ensure-internet-gateway-present:
boto3_vpc.internet_gateway_present:
- name: example
- vpc_name: my-vpc
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
r = __salt__["boto3_vpc.resource_exists"](
"internet_gateway",
name=name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to create internet gateway: {}.".format(r["error"]["message"])
return ret
if not r.get("exists"):
if __opts__["test"]:
ret["comment"] = f"Internet gateway {name} is set to be created."
ret["result"] = None
return ret
r = __salt__["boto3_vpc.create_internet_gateway"](
internet_gateway_name=name,
vpc_name=vpc_name,
vpc_id=vpc_id,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not r.get("created"):
ret["result"] = False
ret["comment"] = "Failed to create internet gateway: {}".format(r["error"]["message"])
return ret
ret["changes"]["old"] = {"internet_gateway": None}
ret["changes"]["new"] = {"internet_gateway": r["id"]}
ret["comment"] = f"Internet gateway {name} created."
return ret
ret["comment"] = f"Internet gateway {name} present."
return ret
[docs]
def internet_gateway_absent(name, detach=False, region=None, key=None, keyid=None, profile=None):
"""
Ensure the named internet gateway is absent.
name (str):
Name of the internet gateway.
detach (bool, optional):
First detach the internet gateway from a VPC, if attached. Defaults to False.
region (str, optional):
The AWS region where the internet gateway exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
ensure-internet-gateway-absent:
boto3_vpc.internet_gateway_absent:
- name: example
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
r = __salt__["boto3_vpc.get_resource_id"](
"internet_gateway",
name=name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to delete internet gateway: {}.".format(r["error"]["message"])
return ret
igw_id = r.get("id")
if not igw_id:
ret["comment"] = f"Internet gateway {name} does not exist."
return ret
if __opts__["test"]:
ret["comment"] = f"Internet gateway {name} is set to be removed."
ret["result"] = None
return ret
r = __salt__["boto3_vpc.delete_internet_gateway"](
internet_gateway_name=name,
detach=detach,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not r.get("deleted"):
ret["result"] = False
ret["comment"] = "Failed to delete internet gateway: {}.".format(r["error"]["message"])
return ret
ret["changes"]["old"] = {"internet_gateway": igw_id}
ret["changes"]["new"] = {"internet_gateway": None}
ret["comment"] = f"Internet gateway {name} deleted."
return ret
[docs]
def route_table_present(
name,
vpc_name=None,
vpc_id=None,
routes=None,
subnet_ids=None,
subnet_names=None,
tags=None,
region=None,
key=None,
keyid=None,
profile=None,
):
"""
Ensure route table with routes exists and is associated to a VPC.
name (str):
The name of the route table.
vpc_name (str, optional):
The name of the VPC to which the route table belongs. Defaults to None.
vpc_id (str, optional):
The ID of the VPC to which the route table belongs. Defaults to None.
routes (list, optional):
A list of routes to be added to the route table. Defaults to None.
subnet_ids (list, optional):
A list of subnet IDs to be associated with the route table. Defaults to None.
subnet_names (list, optional):
A list of subnet names to be associated with the route table. Defaults to None.
tags (list, optional):
A list of tags to be applied to the route table. Defaults to None.
region (str, optional):
The AWS region where the route table exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
boto3_vpc.route_table_present:
- name: my_route_table
- vpc_id: vpc-123456
- routes:
- destination_cidr_block: 0.0.0.0/0
internet_gateway_name: InternetGateway
- destination_cidr_block: 10.10.11.0/24
instance_id: i-123456
- destination_cidr_block: 10.10.12.0/24
interface_id: eni-123456
- subnet_names:
- subnet1
- subnet2
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
_ret = _route_table_present(
name=name,
vpc_name=vpc_name,
vpc_id=vpc_id,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
ret["changes"] = _ret["changes"]
ret["comment"] = " ".join([ret["comment"], _ret["comment"]])
if not _ret["result"]:
ret["result"] = _ret["result"]
if ret["result"] is False:
return ret
if ret["result"] is None and __opts__["test"]:
return ret
_ret = _routes_present(
route_table_name=name,
routes=routes,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"])
ret["comment"] = " ".join([ret["comment"], _ret["comment"]])
if not _ret["result"]:
ret["result"] = _ret["result"]
if ret["result"] is False:
return ret
_ret = _subnets_present(
route_table_name=name,
subnet_ids=subnet_ids,
subnet_names=subnet_names,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"])
ret["comment"] = " ".join([ret["comment"], _ret["comment"]])
if not _ret["result"]:
ret["result"] = _ret["result"]
if ret["result"] is False:
return ret
return ret
def _route_table_present(
name,
vpc_name=None,
vpc_id=None,
tags=None,
region=None,
key=None,
keyid=None,
profile=None,
):
ret = {"name": name, "result": True, "comment": "", "changes": {}}
r = __salt__["boto3_vpc.get_resource_id"](
resource="route_table",
name=name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to create route table: {}.".format(r["error"]["message"])
return ret
_id = r.get("id")
if not _id:
if __opts__["test"]:
ret["comment"] = f"Route table {name} is set to be created."
ret["result"] = None
return ret
r = __salt__["boto3_vpc.create_route_table"](
route_table_name=name,
vpc_name=vpc_name,
vpc_id=vpc_id,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not r.get("created"):
ret["result"] = False
ret["comment"] = "Failed to create route table: {}.".format(r["error"]["message"])
return ret
ret["changes"]["old"] = {"route_table": None}
ret["changes"]["new"] = {"route_table": r["id"]}
ret["comment"] = f"Route table {name} created."
return ret
ret["comment"] = f"Route table {name} ({_id}) present."
return ret
def _routes_present(
route_table_name, routes, tags=None, region=None, key=None, keyid=None, profile=None
):
ret = {"name": route_table_name, "result": True, "comment": "", "changes": {}}
tables = __salt__["boto3_vpc.describe_route_tables"](
route_table_name=route_table_name,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if isinstance(tables, dict) and "error" in tables:
ret["comment"] = (
f"Could not retrieve configuration for route table {route_table_name}: "
f"{tables['error']['message']}."
)
ret["result"] = False
return ret
if not tables:
ret["comment"] = f"Could not retrieve configuration for route table {route_table_name}."
ret["result"] = False
return ret
route_table = tables[0]
_routes = []
if routes:
route_keys = {
"gateway_id",
"instance_id",
"destination_cidr_block",
"interface_id",
"vpc_peering_connection_id",
"nat_gateway_id",
}
for i in routes:
_r = {k: v for k, v in i.items() if k in route_keys}
if i.get("internet_gateway_name"):
r = __salt__["boto3_vpc.get_resource_id"](
"internet_gateway",
name=i["internet_gateway_name"],
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in r:
ret["comment"] = (
"Error looking up id for internet gateway "
f"{i['internet_gateway_name']}: {r['error']['message']}"
)
ret["result"] = False
return ret
if r["id"] is None:
ret["comment"] = (
f"Internet gateway {i['internet_gateway_name']} does not exist."
)
ret["result"] = False
return ret
_r["gateway_id"] = r["id"]
if i.get("vpc_peering_connection_name"):
r = __salt__["boto3_vpc.get_resource_id"](
"vpc_peering_connection",
name=i["vpc_peering_connection_name"],
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in r:
ret["comment"] = (
"Error looking up id for VPC peering connection "
f"{i['vpc_peering_connection_name']}: {r['error']['message']}"
)
ret["result"] = False
return ret
if r["id"] is None:
ret["comment"] = (
f"VPC peering connection {i['vpc_peering_connection_name']} does not exist."
)
ret["result"] = False
return ret
_r["vpc_peering_connection_id"] = r["id"]
if i.get("instance_name"):
running_states = ("pending", "rebooting", "running", "stopping", "stopped")
r = __salt__["boto3_ec2.get_id"](
name=i["instance_name"],
region=region,
key=key,
keyid=keyid,
profile=profile,
in_states=running_states,
)
if r is None:
ret["comment"] = f"Instance {i['instance_name']} does not exist."
ret["result"] = False
return ret
_r["instance_id"] = r
if i.get("nat_gateway_subnet_name"):
r = __salt__["boto3_vpc.describe_nat_gateways"](
subnet_name=i["nat_gateway_subnet_name"],
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not r:
ret["comment"] = "Nat gateway does not exist."
ret["result"] = False
return ret
_r["nat_gateway_id"] = r[0]["NatGatewayId"]
_routes.append(_r)
to_delete = []
to_create = []
for route in _routes:
if route not in route_table["routes"]:
to_create.append(dict(route))
for route in route_table["routes"]:
if route not in _routes:
if route.get("gateway_id") != "local":
to_delete.append(route)
if to_create or to_delete:
if __opts__["test"]:
ret["comment"] = f"Route table {route_table_name} set to have routes modified."
ret["result"] = None
return ret
if to_delete:
for r in to_delete:
res = __salt__["boto3_vpc.delete_route"](
route_table_id=route_table["id"],
destination_cidr_block=r["destination_cidr_block"],
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not res.get("deleted"):
ret["comment"] = (
f"Failed to delete route {r['destination_cidr_block']} "
f"from route table {route_table_name}: {res['error']['message']}."
)
ret["result"] = False
return ret
ret["comment"] = (
f"Deleted route {r['destination_cidr_block']} from route table "
f"{route_table_name}."
)
if to_create:
for r in to_create:
res = __salt__["boto3_vpc.create_route"](
route_table_id=route_table["id"],
region=region,
key=key,
keyid=keyid,
profile=profile,
**r,
)
if not res.get("created"):
ret["comment"] = (
f"Failed to create route {r['destination_cidr_block']} "
f"in route table {route_table_name}: {res['error']['message']}."
)
ret["result"] = False
return ret
ret["comment"] = (
f"Created route {r['destination_cidr_block']} in route table "
f"{route_table_name}."
)
ret["changes"]["old"] = {"routes": route_table["routes"]}
new_tables = __salt__["boto3_vpc.describe_route_tables"](
route_table_name=route_table_name,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
ret["changes"]["new"] = {"routes": new_tables[0]["routes"]}
return ret
def _subnets_present(
route_table_name,
subnet_ids=None,
subnet_names=None,
tags=None,
region=None,
key=None,
keyid=None,
profile=None,
):
ret = {"name": route_table_name, "result": True, "comment": "", "changes": {}}
if not subnet_ids:
subnet_ids = []
if subnet_names:
for i in subnet_names:
r = __salt__["boto3_vpc.get_resource_id"](
"subnet", name=i, region=region, key=key, keyid=keyid, profile=profile
)
if "error" in r:
ret["comment"] = "Error looking up subnet ids: {}".format(r["error"]["message"])
ret["result"] = False
return ret
if r["id"] is None:
ret["comment"] = f"Subnet {i} does not exist."
ret["result"] = False
return ret
subnet_ids.append(r["id"])
tables = __salt__["boto3_vpc.describe_route_tables"](
route_table_name=route_table_name,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if isinstance(tables, dict) and "error" in tables:
ret["comment"] = (
f"Could not retrieve configuration for route table {route_table_name}: "
f"{tables['error']['message']}."
)
ret["result"] = False
return ret
if not tables:
ret["comment"] = f"Could not retrieve configuration for route table {route_table_name}."
ret["result"] = False
return ret
route_table = tables[0]
assoc_ids = [x.get("subnet_id") for x in route_table["associations"]]
to_create = [x for x in subnet_ids if x not in assoc_ids]
to_delete = []
for x in route_table["associations"]:
if x.get("subnet_id") not in subnet_ids and x.get("subnet_id") is not None:
to_delete.append(x["id"])
if to_create or to_delete:
if __opts__["test"]:
ret["comment"] = (
f"Subnet associations for route table {route_table_name} set to be modified."
)
ret["result"] = None
return ret
if to_delete:
for r_asc in to_delete:
r = __salt__["boto3_vpc.disassociate_route_table"](
r_asc, region, key, keyid, profile
)
if "error" in r:
ret["comment"] = (
f"Failed to dissociate {r_asc} from route table "
f"{route_table_name}: {r['error']['message']}."
)
ret["result"] = False
return ret
ret["comment"] = f"Dissociated subnet {r_asc} from route table {route_table_name}."
if to_create:
for sn in to_create:
r = __salt__["boto3_vpc.associate_route_table"](
route_table_id=route_table["id"],
subnet_id=sn,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in r:
ret["comment"] = (
f"Failed to associate subnet {sn} with route table "
f"{route_table_name}: {r['error']['message']}."
)
ret["result"] = False
return ret
ret["comment"] = f"Associated subnet {sn} with route table {route_table_name}."
ret["changes"]["old"] = {"subnets_associations": route_table["associations"]}
new_tables = __salt__["boto3_vpc.describe_route_tables"](
route_table_name=route_table_name,
tags=tags,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
ret["changes"]["new"] = {"subnets_associations": new_tables[0]["associations"]}
return ret
[docs]
def route_table_absent(name, region=None, key=None, keyid=None, profile=None):
"""
Ensure the named route table is absent.
.. versionchanged:: 1.1.0
Disassociate non-main route table associations before deletion.
name (str):
The name of the route table to be removed.
region (str, optional):
The AWS region where the route table exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
ensure-route-table-absent:
boto3_vpc.route_table_absent:
- name: example
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
r = __salt__["boto3_vpc.get_resource_id"](
"route_table", name=name, region=region, key=key, keyid=keyid, profile=profile
)
if "error" in r:
ret["result"] = False
ret["comment"] = r["error"]["message"]
return ret
rtbl_id = r.get("id")
if not rtbl_id:
ret["comment"] = f"Route table {name} does not exist."
return ret
if __opts__["test"]:
ret["comment"] = f"Route table {name} is set to be removed."
ret["result"] = None
return ret
tables = __salt__["boto3_vpc.describe_route_tables"](
route_table_name=name, region=region, key=key, keyid=keyid, profile=profile
)
if isinstance(tables, dict) and "error" in tables:
ret["result"] = False
ret["comment"] = tables["error"]["message"]
return ret
associations = tables[0].get("associations", []) if tables else []
for association in associations:
if association.get("main") or not association.get("id"):
continue
r = __salt__["boto3_vpc.disassociate_route_table"](
association["id"], region, key, keyid, profile
)
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to disassociate route table: {}".format(r["error"]["message"])
return ret
r = __salt__["boto3_vpc.delete_route_table"](
route_table_name=name, region=region, key=key, keyid=keyid, profile=profile
)
if "error" in r:
ret["result"] = False
ret["comment"] = "Failed to delete route table: {}".format(r["error"]["message"])
return ret
ret["changes"]["old"] = {"route_table": rtbl_id}
ret["changes"]["new"] = {"route_table": None}
ret["comment"] = f"Route table {name} deleted."
return ret
[docs]
def nat_gateway_present(
name,
subnet_name=None,
subnet_id=None,
region=None,
key=None,
keyid=None,
profile=None,
allocation_id=None,
):
"""
Ensure a nat gateway exists within the specified subnet.
name (str):
The name of the nat gateway.
subnet_name (str, optional):
The name of the subnet where the nat gateway should exist. Defaults to None.
subnet_id (str, optional):
The ID of the subnet where the nat gateway should exist. Defaults to None.
region (str, optional):
The AWS region where the nat gateway exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
allocation_id (str, optional):
The allocation ID for the nat gateway. Defaults to None.
Example:
.. code-block:: yaml
boto3_vpc.nat_gateway_present:
- subnet_name: my-subnet
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
r = __salt__["boto3_vpc.describe_nat_gateways"](
subnet_name=subnet_name,
subnet_id=subnet_id,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not r:
if __opts__["test"]:
ret["comment"] = "Nat gateway is set to be created."
ret["result"] = None
return ret
r = __salt__["boto3_vpc.create_nat_gateway"](
subnet_name=subnet_name,
subnet_id=subnet_id,
region=region,
key=key,
keyid=keyid,
profile=profile,
allocation_id=allocation_id,
)
if not r.get("created"):
ret["result"] = False
ret["comment"] = "Failed to create nat gateway: {}.".format(r["error"]["message"])
return ret
ret["changes"]["old"] = {"nat_gateway": None}
ret["changes"]["new"] = {"nat_gateway": r["id"]}
ret["comment"] = "Nat gateway created."
return ret
inst = r[0]
_id = inst.get("NatGatewayId")
ret["comment"] = f"Nat gateway {_id} present."
return ret
[docs]
def nat_gateway_absent(
name=None,
subnet_name=None,
subnet_id=None,
region=None,
key=None,
keyid=None,
profile=None,
wait_for_delete_retries=0,
):
"""
Ensure the nat gateway in the named subnet is absent.
name (str):
The name of the nat gateway.
subnet_name (str, optional):
The name of the subnet where the nat gateway exists. Defaults to None.
subnet_id (str, optional):
The ID of the subnet where the nat gateway exists. Defaults to None.
region (str, optional):
The AWS region where the nat gateway exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
wait_for_delete_retries (int, optional):
The number of retries to wait for the nat gateway to be deleted. Defaults to 0.
Example:
.. code-block:: yaml
ensure-nat-gateway-absent:
boto3_vpc.nat_gateway_absent:
- name: example
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
r = __salt__["boto3_vpc.describe_nat_gateways"](
subnet_name=subnet_name,
subnet_id=subnet_id,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if not r:
ret["comment"] = "Nat gateway does not exist."
return ret
if __opts__["test"]:
ret["comment"] = "Nat gateway is set to be removed."
ret["result"] = None
return ret
rtbl_id = None
for gw in r:
rtbl_id = gw.get("NatGatewayId")
res = __salt__["boto3_vpc.delete_nat_gateway"](
nat_gateway_id=rtbl_id,
release_eips=True,
region=region,
key=key,
keyid=keyid,
profile=profile,
wait_for_delete=True,
wait_for_delete_retries=wait_for_delete_retries,
)
if "error" in res:
ret["result"] = False
ret["comment"] = "Failed to delete nat gateway: {}".format(res["error"]["message"])
return ret
ret["comment"] = ", ".join((ret["comment"], f"Nat gateway {rtbl_id} deleted."))
ret["changes"]["old"] = {"nat_gateway": rtbl_id}
ret["changes"]["new"] = {"nat_gateway": None}
return ret
[docs]
def accept_vpc_peering_connection(
name=None,
conn_id=None,
conn_name=None,
region=None,
key=None,
keyid=None,
profile=None,
):
"""
Accept a VPC pending requested peering connection between two VPCs.
name (str):
The name of the VPC peering connection to accept.
conn_id (str, optional):
The ID of the VPC peering connection to accept. Defaults to None.
conn_name (str, optional):
The name of the VPC peering connection to accept. Defaults to None.
region (str, optional):
The AWS region where the VPC peering connection exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
boto3_vpc.accept_vpc_peering_connection:
- conn_name: salt_peering_connection
"""
log.debug("Called state to accept VPC peering connection")
pending = __salt__["boto3_vpc.is_peering_connection_pending"](
conn_id=conn_id,
conn_name=conn_name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
ret = {
"name": name,
"result": True,
"changes": {},
"comment": "Boto VPC peering state",
}
if not pending:
ret["result"] = True
ret["changes"].update(
{"old": "No pending VPC peering connection found. Nothing to be done."}
)
return ret
if __opts__["test"]:
ret["changes"].update({"old": "Pending VPC peering connection found and can be accepted"})
return ret
result = __salt__["boto3_vpc.accept_vpc_peering_connection"](
conn_id=conn_id,
name=conn_name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in result:
ret["comment"] = "Failed to accept VPC peering: {}".format(result["error"])
ret["result"] = False
return ret
ret["changes"].update({"old": "", "new": result["msg"]})
return ret
[docs]
def request_vpc_peering_connection(
name,
requester_vpc_id=None,
requester_vpc_name=None,
peer_vpc_id=None,
peer_vpc_name=None,
conn_name=None,
peer_owner_id=None,
peer_region=None,
region=None,
key=None,
keyid=None,
profile=None,
):
"""
Request a VPC peering connection between two VPCs.
name (str):
The name of the VPC peering connection to request.
requester_vpc_id (str, optional):
The ID of the requester VPC. Defaults to None.
requester_vpc_name (str, optional):
The name of the requester VPC. Defaults to None.
peer_vpc_id (str, optional):
The ID of the peer VPC. Defaults to None.
peer_vpc_name (str, optional):
The name of the peer VPC. Defaults to None.
conn_name (str, optional):
The name of the VPC peering connection. Defaults to None.
peer_owner_id (str, optional):
The AWS account ID of the owner of the peer VPC. Defaults to None.
peer_region (str, optional):
The AWS region where the peer VPC exists. Defaults to None.
region (str, optional):
The AWS region where the requester VPC exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
request a vpc peering connection:
boto3_vpc.request_vpc_peering_connection:
- requester_vpc_id: vpc-4b3522e
- peer_vpc_id: vpc-ae83f9ca
- conn_name: salt_peering_connection
"""
log.debug("Called state to request VPC peering connection")
ret = {
"name": name,
"result": True,
"changes": {},
"comment": "Boto VPC peering state",
}
if conn_name:
vpc_ids = __salt__["boto3_vpc.describe_vpc_peering_connection"](
conn_name, region=region, key=key, keyid=keyid, profile=profile
).get("VPC-Peerings", [])
else:
vpc_ids = []
if vpc_ids:
ret["comment"] = "VPC peering connection already exists, nothing to be done."
return ret
if __opts__["test"]:
if not vpc_ids:
ret["comment"] = "VPC peering connection will be created"
return ret
result = __salt__["boto3_vpc.request_vpc_peering_connection"](
requester_vpc_id=requester_vpc_id,
requester_vpc_name=requester_vpc_name,
peer_vpc_id=peer_vpc_id,
peer_vpc_name=peer_vpc_name,
name=conn_name,
peer_owner_id=peer_owner_id,
peer_region=peer_region,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in result:
ret["comment"] = "Failed to request VPC peering: {}".format(result["error"])
ret["result"] = False
return ret
ret["changes"].update({"old": "", "new": result["msg"]})
return ret
[docs]
def vpc_peering_connection_present(
name,
requester_vpc_id=None,
requester_vpc_name=None,
peer_vpc_id=None,
peer_vpc_name=None,
conn_name=None,
peer_owner_id=None,
peer_region=None,
region=None,
key=None,
keyid=None,
profile=None,
):
"""
Ensure a VPC peering connection is present.
name (str):
The name of the VPC peering connection to ensure is present.
requester_vpc_id (str, optional):
The ID of the requester VPC. Defaults to None.
requester_vpc_name (str, optional):
The name of the requester VPC. Defaults to None.
peer_vpc_id (str, optional):
The ID of the peer VPC. Defaults to None.
peer_vpc_name (str, optional):
The name of the peer VPC. Defaults to None.
conn_name (str, optional):
The name of the VPC peering connection. Defaults to None.
peer_owner_id (str, optional):
The AWS account ID of the owner of the peer VPC. Defaults to None.
peer_region (str, optional):
The AWS region where the peer VPC exists. Defaults to None.
region (str, optional):
The AWS region where the requester VPC exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
ensure-vpc-peering-connection-present:
boto3_vpc.vpc_peering_connection_present:
- name: example
- vpc_name: my-vpc
"""
ret = {"name": name, "result": True, "comment": "", "changes": {}}
if __salt__["boto3_vpc.is_peering_connection_pending"](
conn_name=conn_name, region=region, key=key, keyid=keyid, profile=profile
):
if __salt__["boto3_vpc.peering_connection_pending_from_vpc"](
conn_name=conn_name,
vpc_id=requester_vpc_id,
vpc_name=requester_vpc_name,
region=region,
key=key,
keyid=keyid,
profile=profile,
):
ret["comment"] = (
f"VPC peering {conn_name} already requested - pending acceptance by "
f"{peer_owner_id or peer_vpc_name or peer_vpc_id}"
)
log.info(ret["comment"])
return ret
return accept_vpc_peering_connection(
name=name,
conn_name=conn_name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
return request_vpc_peering_connection(
name=name,
requester_vpc_id=requester_vpc_id,
requester_vpc_name=requester_vpc_name,
peer_vpc_id=peer_vpc_id,
peer_vpc_name=peer_vpc_name,
conn_name=conn_name,
peer_owner_id=peer_owner_id,
peer_region=peer_region,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
[docs]
def vpc_peering_connection_absent(
name, conn_id=None, conn_name=None, region=None, key=None, keyid=None, profile=None
):
"""
Ensure a VPC peering connection is absent.
name (str):
The name of the VPC peering connection to ensure is absent.
conn_id (str, optional):
The ID of the VPC peering connection to ensure is absent. Defaults to None.
conn_name (str, optional):
The name of the VPC peering connection to ensure is absent. Defaults to None.
region (str, optional):
The AWS region where the VPC peering connection exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
ensure-vpc-peering-connection-absent:
boto3_vpc.vpc_peering_connection_absent:
- name: example
"""
return delete_vpc_peering_connection(name, conn_id, conn_name, region, key, keyid, profile)
[docs]
def delete_vpc_peering_connection(
name, conn_id=None, conn_name=None, region=None, key=None, keyid=None, profile=None
):
"""
Delete a VPC peering connection.
name (str):
The name of the VPC peering connection to delete.
conn_id (str, optional):
The ID of the VPC peering connection to delete. Defaults to None.
conn_name (str, optional):
The name of the VPC peering connection to delete. Defaults to None.
region (str, optional):
The AWS region where the VPC peering connection exists. Defaults to None.
key (str, optional):
The AWS access key. Defaults to None.
keyid (str, optional):
The AWS secret key. Defaults to None.
profile (str, optional):
The AWS profile to use. Defaults to None.
Example:
.. code-block:: yaml
ensure-delete-vpc-peering-connection:
boto3_vpc.delete_vpc_peering_connection:
- name: example
"""
log.debug("Called state to delete VPC peering connection")
ret = {
"name": name,
"result": True,
"changes": {},
"comment": "Boto VPC peering state",
}
if conn_name:
vpc_ids = __salt__["boto3_vpc.describe_vpc_peering_connection"](
conn_name, region=region, key=key, keyid=keyid, profile=profile
).get("VPC-Peerings", [])
else:
vpc_ids = [conn_id]
if not vpc_ids:
ret["comment"] = "No VPC connection found, nothing to be done."
return ret
if __opts__["test"]:
if vpc_ids:
ret["comment"] = "VPC peering connection would be deleted"
return ret
result = __salt__["boto3_vpc.delete_vpc_peering_connection"](
conn_id=conn_id,
conn_name=conn_name,
region=region,
key=key,
keyid=keyid,
profile=profile,
)
if "error" in result:
ret["comment"] = "Failed to delete VPC peering: {}".format(result["error"])
ret["result"] = False
return ret
ret["changes"].update({"old": "", "new": result["msg"]})
return ret