PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/boto_vpc.py |
""" Connection module for Amazon VPC .. versionadded:: 2014.7.0 :depends: - boto >= 2.8.0 - boto3 >= 1.2.6 :configuration: 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 or in the minion's config file: .. code-block:: yaml vpc.keyid: GKTADJGHEIQSXMKKRBJ08H vpc.key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs A region may also be specified in the configuration: .. code-block:: yaml vpc.region: us-east-1 If a region is not specified, the default is us-east-1. It's also possible to specify key, keyid and region via a profile, either as a passed in dict, or as a string to pull from pillars or minion config: .. code-block:: yaml myprofile: keyid: GKTADJGHEIQSXMKKRBJ08H key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs region: us-east-1 .. versionchanged:: 2015.8.0 All methods now return a dictionary. Create and delete methods return: .. code-block:: yaml created: true or .. code-block:: yaml created: false error: message: error message Request methods (e.g., `describe_vpc`) return: .. code-block:: yaml vpcs: - {...} - {...} or .. code-block:: yaml error: message: error message .. versionadded:: 2016.11.0 Functions to request, accept, delete and describe VPC peering connections. Named VPC peering connections can be requested using these modules. VPC owner accounts can accept VPC peering connections (named or otherwise). Examples showing creation of VPC peering connection .. code-block:: bash # Create a named VPC peering connection salt myminion boto_vpc.request_vpc_peering_connection vpc-4a3e622e vpc-be82e9da name=my_vpc_connection # Without a name salt myminion boto_vpc.request_vpc_peering_connection vpc-4a3e622e vpc-be82e9da # Specify a region salt myminion boto_vpc.request_vpc_peering_connection vpc-4a3e622e vpc-be82e9da region=us-west-2 Check to see if VPC peering connection is pending .. code-block:: bash salt myminion boto_vpc.is_peering_connection_pending name=salt-vpc # Specify a region salt myminion boto_vpc.is_peering_connection_pending name=salt-vpc region=us-west-2 # specify an id salt myminion boto_vpc.is_peering_connection_pending conn_id=pcx-8a8939e3 Accept VPC peering connection .. code-block:: bash salt myminion boto_vpc.accept_vpc_peering_connection name=salt-vpc # Specify a region salt myminion boto_vpc.accept_vpc_peering_connection name=salt-vpc region=us-west-2 # specify an id salt myminion boto_vpc.accept_vpc_peering_connection conn_id=pcx-8a8939e3 Deleting VPC peering connection via this module .. code-block:: bash # Delete a named VPC peering connection salt myminion boto_vpc.delete_vpc_peering_connection name=salt-vpc # Specify a region salt myminion boto_vpc.delete_vpc_peering_connection name=salt-vpc region=us-west-2 # specify an id salt myminion boto_vpc.delete_vpc_peering_connection conn_id=pcx-8a8939e3 """ # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 import logging import random import socket import time import salt.utils.compat import salt.utils.versions from salt.exceptions import CommandExecutionError, SaltInvocationError # from salt.utils import exactly_one # TODO: Uncomment this and s/_exactly_one/exactly_one/ # See note in utils.boto PROVISIONING = "provisioning" PENDING_ACCEPTANCE = "pending-acceptance" ACTIVE = "active" log = logging.getLogger(__name__) # pylint: disable=import-error try: # pylint: disable=unused-import import boto import boto.vpc import botocore # pylint: enable=unused-import from boto.exception import BotoServerError logging.getLogger("boto").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False # pylint: enable=import-error try: # pylint: disable=unused-import import boto3 # pylint: enable=unused-import logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO3 = True except ImportError: HAS_BOTO3 = False def __virtual__(): """ Only load if boto libraries exist and if boto libraries are greater than a given version. """ # the boto_vpc execution module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 # the boto_vpc execution module relies on the create_nat_gateway() method # which was added in boto3 1.2.6 return salt.utils.versions.check_boto_reqs(boto_ver="2.8.0", boto3_ver="1.2.6") def __init__(opts): if HAS_BOTO: __utils__["boto.assign_funcs"](__name__, "vpc", pack=__salt__) if HAS_BOTO3: __utils__["boto3.assign_funcs"]( __name__, "ec2", get_conn_funcname="_get_conn3", cache_id_funcname="_cache_id3", exactly_one_funcname=None, ) def check_vpc( vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, profile=None, ): """ Check whether a VPC with the given name or id exists. Returns the vpc_id or None. Raises SaltInvocationError if both vpc_id and vpc_name are None. Optionally raise a CommandExecutionError if the VPC does not exist. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt myminion boto_vpc.check_vpc vpc_name=myvpc profile=awsprofile """ if not _exactly_one((vpc_name, vpc_id)): raise SaltInvocationError( "One (but not both) of vpc_id or vpc_name must be provided." ) if vpc_name: vpc_id = _get_id( vpc_name=vpc_name, region=region, key=key, keyid=keyid, profile=profile ) elif not _find_vpcs( vpc_id=vpc_id, region=region, key=key, keyid=keyid, profile=profile ): log.info("VPC %s does not exist.", vpc_id) return None return vpc_id def _create_resource( resource, name=None, tags=None, region=None, key=None, keyid=None, profile=None, **kwargs, ): """ Create a VPC resource. Returns the resource id if created, or False if not created. """ try: try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) create_resource = getattr(conn, "create_" + resource) except AttributeError: raise AttributeError( "{} function does not exist for boto VPC connection.".format( "create_" + resource ) ) if name and _get_resource_id( resource, name, region=region, key=key, keyid=keyid, profile=profile ): return { "created": False, "error": {"message": f"A {resource} named {name} already exists."}, } r = create_resource(**kwargs) if r: if isinstance(r, bool): return {"created": True} else: log.info("A %s with id %s was created", resource, r.id) _maybe_set_name_tag(name, r) _maybe_set_tags(tags, r) if name: _cache_id( name, sub_resource=resource, resource_id=r.id, region=region, key=key, keyid=keyid, profile=profile, ) return {"created": True, "id": r.id} else: if name: e = f"{resource} {name} was not created." else: e = f"{resource} was not created." log.warning(e) return {"created": False, "error": {"message": e}} except BotoServerError as e: return {"created": False, "error": __utils__["boto.get_error"](e)} def _delete_resource( resource, name=None, resource_id=None, region=None, key=None, keyid=None, profile=None, **kwargs, ): """ Delete a VPC resource. Returns True if successful, otherwise False. """ if not _exactly_one((name, resource_id)): raise SaltInvocationError("One (but not both) of name or id must be provided.") try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: delete_resource = getattr(conn, "delete_" + resource) except AttributeError: raise AttributeError( "{} function does not exist for boto VPC connection.".format( "delete_" + resource ) ) if name: resource_id = _get_resource_id( resource, name, region=region, key=key, keyid=keyid, profile=profile ) if not resource_id: return { "deleted": False, "error": {"message": f"{resource} {name} does not exist."}, } if delete_resource(resource_id, **kwargs): _cache_id( name, sub_resource=resource, resource_id=resource_id, invalidate=True, region=region, key=key, keyid=keyid, profile=profile, ) return {"deleted": True} else: if name: e = f"{resource} {name} was not deleted." else: e = f"{resource} was not deleted." return {"deleted": False, "error": {"message": e}} except BotoServerError as e: return {"deleted": False, "error": __utils__["boto.get_error"](e)} def _get_resource( resource, name=None, resource_id=None, region=None, key=None, keyid=None, profile=None, ): """ Get a VPC resource based on resource type and name or id. Cache the id if name was provided. """ if not _exactly_one((name, resource_id)): raise SaltInvocationError("One (but not both) of name or id must be provided.") conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) f = f"get_all_{resource}" if not f.endswith("s"): f = f + "s" get_resources = getattr(conn, f) filter_parameters = {} if name: filter_parameters["filters"] = {"tag:Name": name} if resource_id: filter_parameters[f"{resource}_ids"] = resource_id try: r = get_resources(**filter_parameters) except BotoServerError as e: if e.code.endswith(".NotFound"): return None raise if r: if len(r) == 1: if name: _cache_id( name, sub_resource=resource, resource_id=r[0].id, region=region, key=key, keyid=keyid, profile=profile, ) return r[0] else: raise CommandExecutionError( f'Found more than one {resource} named "{name}"' ) else: return None def _find_resources( resource, name=None, resource_id=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Get VPC resources based on resource type and name, id, or tags. """ if all((resource_id, name)): raise SaltInvocationError("Only one of name or id may be provided.") if not any((resource_id, name, tags)): raise SaltInvocationError( "At least one of the following must be provided: id, name, or tags." ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) f = f"get_all_{resource}" if not f.endswith("s"): f = f + "s" get_resources = getattr(conn, f) filter_parameters = {} if name: filter_parameters["filters"] = {"tag:Name": name} if resource_id: filter_parameters[f"{resource}_ids"] = resource_id if tags: for tag_name, tag_value in tags.items(): filter_parameters["filters"][f"tag:{tag_name}"] = tag_value try: r = get_resources(**filter_parameters) except BotoServerError as e: if e.code.endswith(".NotFound"): return None raise return r def _get_resource_id(resource, name, region=None, key=None, keyid=None, profile=None): """ Get an AWS id for a VPC resource by type and name. """ _id = _cache_id( name, sub_resource=resource, region=region, key=key, keyid=keyid, profile=profile, ) if _id: return _id r = _get_resource( resource, name=name, region=region, key=key, keyid=keyid, profile=profile ) if r: return r.id def get_resource_id( resource, name=None, resource_id=None, region=None, key=None, keyid=None, profile=None, ): """ Get an AWS id for a VPC resource by type and name. .. versionadded:: 2015.8.0 CLI Example: .. code-block:: bash salt myminion boto_vpc.get_resource_id internet_gateway myigw """ try: return { "id": _get_resource_id( resource, name, region=region, key=key, keyid=keyid, profile=profile ) } except BotoServerError as e: return {"error": __utils__["boto.get_error"](e)} def resource_exists( resource, name=None, resource_id=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Given a resource type and name, return {exists: true} if it exists, {exists: false} if it does not exist, or {error: {message: error text} on error. .. versionadded:: 2015.8.0 CLI Example: .. code-block:: bash salt myminion boto_vpc.resource_exists internet_gateway myigw """ try: return { "exists": bool( _find_resources( resource, name=name, resource_id=resource_id, tags=tags, region=region, key=key, keyid=keyid, profile=profile, ) ) } except BotoServerError as e: return {"error": __utils__["boto.get_error"](e)} def _find_vpcs( vpc_id=None, vpc_name=None, cidr=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Given VPC properties, find and return matching VPC ids. """ if all((vpc_id, vpc_name)): raise SaltInvocationError("Only one of vpc_name or vpc_id may be provided.") conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) filter_parameters = {"filters": {}} if vpc_id: filter_parameters["vpc_ids"] = [vpc_id] if cidr: filter_parameters["filters"]["cidr"] = cidr if vpc_name: filter_parameters["filters"]["tag:Name"] = vpc_name if tags: for tag_name, tag_value in tags.items(): filter_parameters["filters"][f"tag:{tag_name}"] = tag_value vpcs = conn.get_all_vpcs(**filter_parameters) log.debug( "The filters criteria %s matched the following VPCs:%s", filter_parameters, vpcs ) if vpcs: if not any((vpc_id, vpc_name, cidr, tags)): return [vpc.id for vpc in vpcs if vpc.is_default] else: return [vpc.id for vpc in vpcs] else: return [] def _get_id( vpc_name=None, cidr=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Given VPC properties, return the VPC id if a match is found. """ if not any((vpc_name, tags, cidr)): raise SaltInvocationError( "At least one of the following must be provided: vpc_name, cidr or tags." ) if vpc_name and not any((cidr, tags)): vpc_id = _cache_id( vpc_name, region=region, key=key, keyid=keyid, profile=profile ) if vpc_id: return vpc_id vpc_ids = _find_vpcs( vpc_name=vpc_name, cidr=cidr, tags=tags, region=region, key=key, keyid=keyid, profile=profile, ) if vpc_ids: log.debug("Matching VPC: %s", " ".join(vpc_ids)) if len(vpc_ids) == 1: vpc_id = vpc_ids[0] if vpc_name: _cache_id( vpc_name, vpc_id, region=region, key=key, keyid=keyid, profile=profile, ) return vpc_id else: raise CommandExecutionError( "Found more than one VPC matching the criteria." ) else: log.info("No VPC found.") return None def get_id( name=None, cidr=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Given VPC properties, return the VPC id if a match is found. CLI Example: .. code-block:: bash salt myminion boto_vpc.get_id myvpc """ try: return { "id": _get_id( vpc_name=name, cidr=cidr, tags=tags, region=region, key=key, keyid=keyid, profile=profile, ) } except BotoServerError as e: return {"error": __utils__["boto.get_error"](e)} def exists( vpc_id=None, name=None, cidr=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Given a VPC ID, check to see if the given VPC ID exists. Returns True if the given VPC ID exists and returns False if the given VPC ID does not exist. CLI Example: .. code-block:: bash salt myminion boto_vpc.exists myvpc """ if not any((vpc_id, name, tags, cidr)): raise SaltInvocationError( "At least one of the following must be " "provided: vpc_id, vpc_name, cidr or tags." ) try: vpc_ids = _find_vpcs( vpc_id=vpc_id, vpc_name=name, cidr=cidr, tags=tags, region=region, key=key, keyid=keyid, profile=profile, ) except BotoServerError as err: boto_err = __utils__["boto.get_error"](err) if boto_err.get("aws", {}).get("code") == "InvalidVpcID.NotFound": # VPC was not found: handle the error and return False. return {"exists": False} return {"error": boto_err} return {"exists": bool(vpc_ids)} def create( cidr_block, instance_tenancy=None, vpc_name=None, enable_dns_support=None, enable_dns_hostnames=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Given a valid CIDR block, create a VPC. An optional instance_tenancy argument can be provided. If provided, the valid values are 'default' or 'dedicated' An optional vpc_name argument can be provided. Returns {created: true} if the VPC was created and returns {created: False} if the VPC was not created. CLI Example: .. code-block:: bash salt myminion boto_vpc.create '10.0.0.0/24' """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) vpc = conn.create_vpc(cidr_block, instance_tenancy=instance_tenancy) if vpc: log.info("The newly created VPC id is %s", vpc.id) _maybe_set_name_tag(vpc_name, vpc) _maybe_set_tags(tags, vpc) _maybe_set_dns(conn, vpc.id, enable_dns_support, enable_dns_hostnames) _maybe_name_route_table(conn, vpc.id, vpc_name) if vpc_name: _cache_id( vpc_name, vpc.id, region=region, key=key, keyid=keyid, profile=profile, ) return {"created": True, "id": vpc.id} else: log.warning("VPC was not created") return {"created": False} except BotoServerError as e: return {"created": False, "error": __utils__["boto.get_error"](e)} def delete( vpc_id=None, name=None, vpc_name=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Given a VPC ID or VPC name, delete the VPC. Returns {deleted: true} if the VPC was deleted and returns {deleted: false} if the VPC was not deleted. CLI Example: .. code-block:: bash salt myminion boto_vpc.delete vpc_id='vpc-6b1fe402' salt myminion boto_vpc.delete name='myvpc' """ if name: log.warning( "boto_vpc.delete: name parameter is deprecated use vpc_name instead." ) vpc_name = name if not _exactly_one((vpc_name, vpc_id)): raise SaltInvocationError( "One (but not both) of vpc_name or vpc_id must be provided." ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not vpc_id: vpc_id = _get_id( vpc_name=vpc_name, tags=tags, region=region, key=key, keyid=keyid, profile=profile, ) if not vpc_id: return { "deleted": False, "error": {"message": f"VPC {vpc_name} not found"}, } if conn.delete_vpc(vpc_id): log.info("VPC %s was deleted.", vpc_id) if vpc_name: _cache_id( vpc_name, resource_id=vpc_id, invalidate=True, region=region, key=key, keyid=keyid, profile=profile, ) return {"deleted": True} else: log.warning("VPC %s was not deleted.", vpc_id) return {"deleted": False} except BotoServerError as e: return {"deleted": False, "error": __utils__["boto.get_error"](e)} def describe( vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, profile=None, ): """ Describe a VPC's properties. If no VPC ID/Name is spcified then describe the default VPC. Returns a dictionary of interesting properties. .. versionchanged:: 2015.8.0 Added vpc_name argument CLI Example: .. code-block:: bash salt myminion boto_vpc.describe vpc_id=vpc-123456 salt myminion boto_vpc.describe vpc_name=myvpc """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) vpc_id = _find_vpcs( vpc_id=vpc_id, vpc_name=vpc_name, region=region, key=key, keyid=keyid, profile=profile, ) except BotoServerError as err: boto_err = __utils__["boto.get_error"](err) if boto_err.get("aws", {}).get("code") == "InvalidVpcID.NotFound": # VPC was not found: handle the error and return None. return {"vpc": None} return {"error": boto_err} if not vpc_id: return {"vpc": None} filter_parameters = {"vpc_ids": vpc_id} try: vpcs = conn.get_all_vpcs(**filter_parameters) except BotoServerError as err: return {"error": __utils__["boto.get_error"](err)} if vpcs: vpc = vpcs[0] # Found! log.debug("Found VPC: %s", vpc.id) keys = ( "id", "cidr_block", "is_default", "state", "tags", "dhcp_options_id", "instance_tenancy", ) _r = {k: getattr(vpc, k) for k in keys} _r.update({"region": getattr(vpc, "region").name}) return {"vpc": _r} else: return {"vpc": None} def describe_vpcs( vpc_id=None, name=None, cidr=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Describe all VPCs, matching the filter criteria if provided. Returns a list of dictionaries with interesting properties. .. versionadded:: 2015.8.0 CLI Example: .. code-block:: bash salt myminion boto_vpc.describe_vpcs """ keys = ( "id", "cidr_block", "is_default", "state", "tags", "dhcp_options_id", "instance_tenancy", ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) filter_parameters = {"filters": {}} if vpc_id: filter_parameters["vpc_ids"] = [vpc_id] if cidr: filter_parameters["filters"]["cidr"] = cidr if name: filter_parameters["filters"]["tag:Name"] = name if tags: for tag_name, tag_value in tags.items(): filter_parameters["filters"][f"tag:{tag_name}"] = tag_value vpcs = conn.get_all_vpcs(**filter_parameters) if vpcs: ret = [] for vpc in vpcs: _r = {k: getattr(vpc, k) for k in keys} _r.update({"region": getattr(vpc, "region").name}) ret.append(_r) return {"vpcs": ret} else: return {"vpcs": []} except BotoServerError as e: return {"error": __utils__["boto.get_error"](e)} def _find_subnets(subnet_name=None, vpc_id=None, cidr=None, tags=None, conn=None): """ Given subnet properties, find and return matching subnet ids """ if not any([subnet_name, tags, cidr]): raise SaltInvocationError( "At least one of the following must be " "specified: subnet_name, cidr or tags." ) filter_parameters = {"filters": {}} if cidr: filter_parameters["filters"]["cidr"] = cidr if subnet_name: filter_parameters["filters"]["tag:Name"] = subnet_name if vpc_id: filter_parameters["filters"]["VpcId"] = vpc_id if tags: for tag_name, tag_value in tags.items(): filter_parameters["filters"][f"tag:{tag_name}"] = tag_value subnets = conn.get_all_subnets(**filter_parameters) log.debug( "The filters criteria %s matched the following subnets: %s", filter_parameters, subnets, ) if subnets: return [subnet.id for subnet in subnets] else: return False def create_subnet( vpc_id=None, cidr_block=None, vpc_name=None, availability_zone=None, subnet_name=None, tags=None, region=None, key=None, keyid=None, profile=None, auto_assign_public_ipv4=False, ): """ Given a valid VPC ID or Name and a CIDR block, create a subnet for the VPC. An optional availability zone argument can be provided. Returns True if the VPC subnet was created and returns False if the VPC subnet was not created. .. versionchanged:: 2015.8.0 Added vpc_name argument CLI Examples: .. code-block:: bash salt myminion boto_vpc.create_subnet vpc_id='vpc-6b1fe402' \\ subnet_name='mysubnet' cidr_block='10.0.0.0/25' salt myminion boto_vpc.create_subnet vpc_name='myvpc' \\ subnet_name='mysubnet', cidr_block='10.0.0.0/25' """ try: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) if not vpc_id: return { "created": False, "error": {"message": f"VPC {vpc_name or vpc_id} does not exist."}, } except BotoServerError as e: return {"created": False, "error": __utils__["boto.get_error"](e)} subnet_object_dict = _create_resource( "subnet", name=subnet_name, tags=tags, vpc_id=vpc_id, availability_zone=availability_zone, cidr_block=cidr_block, region=region, key=key, keyid=keyid, profile=profile, ) # if auto_assign_public_ipv4 is requested set that to true using boto3 if auto_assign_public_ipv4: conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) conn3.modify_subnet_attribute( MapPublicIpOnLaunch={"Value": True}, SubnetId=subnet_object_dict["id"] ) return subnet_object_dict def delete_subnet( subnet_id=None, subnet_name=None, region=None, key=None, keyid=None, profile=None ): """ Given a subnet ID or name, delete the subnet. Returns True if the subnet was deleted and returns False if the subnet was not deleted. .. versionchanged:: 2015.8.0 Added subnet_name argument CLI Example: .. code-block:: bash salt myminion boto_vpc.delete_subnet 'subnet-6a1fe403' """ return _delete_resource( resource="subnet", name=subnet_name, resource_id=subnet_id, region=region, key=key, keyid=keyid, profile=profile, ) def subnet_exists( subnet_id=None, name=None, subnet_name=None, cidr=None, tags=None, zones=None, region=None, key=None, keyid=None, profile=None, ): """ Check if a subnet exists. Returns True if the subnet exists, otherwise returns False. .. versionchanged:: 2015.8.0 Added subnet_name argument Deprecated name argument CLI Example: .. code-block:: bash salt myminion boto_vpc.subnet_exists subnet_id='subnet-6a1fe403' """ if name: log.warning( "boto_vpc.subnet_exists: name parameter is deprecated " "use subnet_name instead." ) subnet_name = name if not any((subnet_id, subnet_name, cidr, tags, zones)): raise SaltInvocationError( "At least one of the following must be " "specified: subnet id, cidr, subnet_name, " "tags, or zones." ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) except BotoServerError as err: return {"error": __utils__["boto.get_error"](err)} filter_parameters = {"filters": {}} if subnet_id: filter_parameters["subnet_ids"] = [subnet_id] if subnet_name: filter_parameters["filters"]["tag:Name"] = subnet_name if cidr: filter_parameters["filters"]["cidr"] = cidr if tags: for tag_name, tag_value in tags.items(): filter_parameters["filters"][f"tag:{tag_name}"] = tag_value if zones: filter_parameters["filters"]["availability_zone"] = zones try: subnets = conn.get_all_subnets(**filter_parameters) except BotoServerError as err: boto_err = __utils__["boto.get_error"](err) if boto_err.get("aws", {}).get("code") == "InvalidSubnetID.NotFound": # Subnet was not found: handle the error and return False. return {"exists": False} return {"error": boto_err} log.debug( "The filters criteria %s matched the following subnets:%s", filter_parameters, subnets, ) if subnets: log.info("Subnet %s exists.", subnet_name or subnet_id) return {"exists": True} else: log.info("Subnet %s does not exist.", subnet_name or subnet_id) return {"exists": False} def get_subnet_association(subnets, region=None, key=None, keyid=None, profile=None): """ Given a subnet (aka: a vpc zone identifier) or list of subnets, returns vpc association. Returns a VPC ID if the given subnets are associated with the same VPC ID. Returns False on an error or if the given subnets are associated with different VPC IDs. CLI Examples: .. code-block:: bash salt myminion boto_vpc.get_subnet_association subnet-61b47516 .. code-block:: bash salt myminion boto_vpc.get_subnet_association ['subnet-61b47516','subnet-2cb9785b'] """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) # subnet_ids=subnets can accept either a string or a list subnets = conn.get_all_subnets(subnet_ids=subnets) except BotoServerError as e: return {"error": __utils__["boto.get_error"](e)} # using a set to store vpc_ids - the use of set prevents duplicate # vpc_id values vpc_ids = set() for subnet in subnets: log.debug("examining subnet id: %s for vpc_id", subnet.id) if subnet in subnets: log.debug( "subnet id: %s is associated with vpc id: %s", subnet.id, subnet.vpc_id ) vpc_ids.add(subnet.vpc_id) if not vpc_ids: return {"vpc_id": None} elif len(vpc_ids) == 1: return {"vpc_id": vpc_ids.pop()} else: return {"vpc_ids": list(vpc_ids)} def describe_subnet( subnet_id=None, subnet_name=None, region=None, key=None, keyid=None, profile=None ): """ Given a subnet id or name, describe its properties. Returns a dictionary of interesting properties. .. versionadded:: 2015.8.0 CLI Examples: .. code-block:: bash salt myminion boto_vpc.describe_subnet subnet_id=subnet-123456 salt myminion boto_vpc.describe_subnet subnet_name=mysubnet """ try: subnet = _get_resource( "subnet", name=subnet_name, resource_id=subnet_id, region=region, key=key, keyid=keyid, profile=profile, ) except BotoServerError as e: return {"error": __utils__["boto.get_error"](e)} if not subnet: return {"subnet": None} log.debug("Found subnet: %s", subnet.id) keys = ("id", "cidr_block", "availability_zone", "tags", "vpc_id") ret = {"subnet": {k: getattr(subnet, k) for k in keys}} explicit_route_table_assoc = _get_subnet_explicit_route_table( ret["subnet"]["id"], ret["subnet"]["vpc_id"], conn=None, region=region, key=key, keyid=keyid, profile=profile, ) if explicit_route_table_assoc: ret["subnet"][ "explicit_route_table_association_id" ] = explicit_route_table_assoc return ret def describe_subnets( subnet_ids=None, subnet_names=None, vpc_id=None, cidr=None, region=None, key=None, keyid=None, profile=None, ): """ Given a VPC ID or subnet CIDR, returns a list of associated subnets and their details. Return all subnets if VPC ID or CIDR are not provided. If a subnet id or CIDR is provided, only its associated subnet details will be returned. .. versionadded:: 2015.8.0 CLI Examples: .. code-block:: bash salt myminion boto_vpc.describe_subnets .. code-block:: bash salt myminion boto_vpc.describe_subnets subnet_ids=['subnet-ba1987ab', 'subnet-ba1987cd'] .. code-block:: bash salt myminion boto_vpc.describe_subnets vpc_id=vpc-123456 .. code-block:: bash salt myminion boto_vpc.describe_subnets cidr=10.0.0.0/21 """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) filter_parameters = {"filters": {}} if vpc_id: filter_parameters["filters"]["vpcId"] = vpc_id if cidr: filter_parameters["filters"]["cidrBlock"] = cidr if subnet_names: filter_parameters["filters"]["tag:Name"] = subnet_names subnets = conn.get_all_subnets(subnet_ids=subnet_ids, **filter_parameters) log.debug( "The filters criteria %s matched the following subnets: %s", filter_parameters, subnets, ) if not subnets: return {"subnets": None} subnets_list = [] keys = ("id", "cidr_block", "availability_zone", "tags", "vpc_id") for item in subnets: subnet = {} for key in keys: if hasattr(item, key): subnet[key] = getattr(item, key) explicit_route_table_assoc = _get_subnet_explicit_route_table( subnet["id"], subnet["vpc_id"], conn=conn ) if explicit_route_table_assoc: subnet["explicit_route_table_association_id"] = ( explicit_route_table_assoc ) subnets_list.append(subnet) return {"subnets": subnets_list} except BotoServerError as e: return {"error": __utils__["boto.get_error"](e)} def create_internet_gateway( internet_gateway_name=None, vpc_id=None, vpc_name=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Create an Internet Gateway, optionally attaching it to an existing VPC. Returns the internet gateway id if the internet gateway was created and returns False if the internet gateways was not created. .. versionadded:: 2015.8.0 CLI Example: .. code-block:: bash salt myminion boto_vpc.create_internet_gateway \\ internet_gateway_name=myigw vpc_name=myvpc """ try: if vpc_id or vpc_name: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) if not vpc_id: return { "created": False, "error": {"message": f"VPC {vpc_name or vpc_id} does not exist."}, } r = _create_resource( "internet_gateway", name=internet_gateway_name, tags=tags, region=region, key=key, keyid=keyid, profile=profile, ) if r.get("created") and vpc_id: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.attach_internet_gateway(r["id"], vpc_id) log.info( "Attached internet gateway %s to VPC %s", r["id"], vpc_name or vpc_id ) return r except BotoServerError as e: return {"created": False, "error": __utils__["boto.get_error"](e)} def delete_internet_gateway( internet_gateway_id=None, internet_gateway_name=None, detach=False, region=None, key=None, keyid=None, profile=None, ): """ Delete an internet gateway (by name or id). Returns True if the internet gateway was deleted and otherwise False. .. versionadded:: 2015.8.0 CLI Examples: .. code-block:: bash salt myminion boto_vpc.delete_internet_gateway internet_gateway_id=igw-1a2b3c salt myminion boto_vpc.delete_internet_gateway internet_gateway_name=myigw """ try: if internet_gateway_name: internet_gateway_id = _get_resource_id( "internet_gateway", internet_gateway_name, region=region, key=key, keyid=keyid, profile=profile, ) if not internet_gateway_id: return { "deleted": False, "error": { "message": "internet gateway {} does not exist.".format( internet_gateway_name ) }, } if detach: igw = _get_resource( "internet_gateway", resource_id=internet_gateway_id, region=region, key=key, keyid=keyid, profile=profile, ) if not igw: return { "deleted": False, "error": { "message": "internet gateway {} does not exist.".format( internet_gateway_id ) }, } if igw.attachments: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.detach_internet_gateway( internet_gateway_id, igw.attachments[0].vpc_id ) return _delete_resource( "internet_gateway", resource_id=internet_gateway_id, region=region, key=key, keyid=keyid, profile=profile, ) except BotoServerError as e: return {"deleted": False, "error": __utils__["boto.get_error"](e)} def _find_nat_gateways( nat_gateway_id=None, subnet_id=None, subnet_name=None, vpc_id=None, vpc_name=None, states=("pending", "available"), region=None, key=None, keyid=None, profile=None, ): """ Given gateway properties, find and return matching nat gateways """ if not any((nat_gateway_id, subnet_id, subnet_name, vpc_id, vpc_name)): raise SaltInvocationError( "At least one of the following must be " "provided: nat_gateway_id, subnet_id, " "subnet_name, vpc_id, or vpc_name." ) filter_parameters = {"Filter": []} if nat_gateway_id: filter_parameters["NatGatewayIds"] = [nat_gateway_id] if subnet_name: subnet_id = _get_resource_id( "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile ) if not subnet_id: return False if subnet_id: filter_parameters["Filter"].append({"Name": "subnet-id", "Values": [subnet_id]}) if vpc_name: vpc_id = _get_resource_id( "vpc", vpc_name, region=region, key=key, keyid=keyid, profile=profile ) if not vpc_id: return False if vpc_id: filter_parameters["Filter"].append({"Name": "vpc-id", "Values": [vpc_id]}) conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) nat_gateways = [] for ret in __utils__["boto3.paged_call"]( conn3.describe_nat_gateways, marker_flag="NextToken", marker_arg="NextToken", **filter_parameters, ): for gw in ret.get("NatGateways", []): if gw.get("State") in states: nat_gateways.append(gw) log.debug( "The filters criteria %s matched the following nat gateways: %s", filter_parameters, nat_gateways, ) if nat_gateways: return nat_gateways else: return False def nat_gateway_exists( nat_gateway_id=None, subnet_id=None, subnet_name=None, vpc_id=None, vpc_name=None, states=("pending", "available"), region=None, key=None, keyid=None, profile=None, ): """ Checks if a nat gateway exists. This function requires boto3 to be installed. .. versionadded:: 2016.11.0 CLI Example: .. code-block:: bash salt myminion boto_vpc.nat_gateway_exists nat_gateway_id='nat-03b02643b43216fe7' salt myminion boto_vpc.nat_gateway_exists subnet_id='subnet-5b05942d' """ return bool( _find_nat_gateways( nat_gateway_id=nat_gateway_id, subnet_id=subnet_id, subnet_name=subnet_name, vpc_id=vpc_id, vpc_name=vpc_name, states=states, region=region, key=key, keyid=keyid, profile=profile, ) ) def describe_nat_gateways( nat_gateway_id=None, subnet_id=None, subnet_name=None, vpc_id=None, vpc_name=None, states=("pending", "available"), region=None, key=None, keyid=None, profile=None, ): """ Return a description of nat gateways matching the selection criteria This function requires boto3 to be installed. CLI Example: .. code-block:: bash salt myminion boto_vpc.describe_nat_gateways nat_gateway_id='nat-03b02643b43216fe7' salt myminion boto_vpc.describe_nat_gateways subnet_id='subnet-5b05942d' """ return _find_nat_gateways( nat_gateway_id=nat_gateway_id, subnet_id=subnet_id, subnet_name=subnet_name, vpc_id=vpc_id, vpc_name=vpc_name, states=states, region=region, key=key, keyid=keyid, profile=profile, ) def create_nat_gateway( subnet_id=None, subnet_name=None, allocation_id=None, region=None, key=None, keyid=None, profile=None, ): """ Create a NAT Gateway within an existing subnet. If allocation_id is specified, the elastic IP address it references is associated with the gateway. Otherwise, a new allocation_id is created and used. This function requires boto3 to be installed. Returns the nat gateway id if the nat gateway was created and returns False if the nat gateway was not created. .. versionadded:: 2016.11.0 CLI Example: .. code-block:: bash salt myminion boto_vpc.create_nat_gateway subnet_name=mysubnet """ try: if all((subnet_id, subnet_name)): raise SaltInvocationError( "Only one of subnet_name or subnet_id may be provided." ) if subnet_name: subnet_id = _get_resource_id( "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile, ) if not subnet_id: return { "created": False, "error": {"message": f"Subnet {subnet_name} does not exist."}, } else: if not _get_resource( "subnet", resource_id=subnet_id, region=region, key=key, keyid=keyid, profile=profile, ): return { "created": False, "error": {"message": f"Subnet {subnet_id} does not exist."}, } conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) if not allocation_id: address = conn3.allocate_address(Domain="vpc") allocation_id = address.get("AllocationId") # Have to go to boto3 to create NAT gateway r = conn3.create_nat_gateway(SubnetId=subnet_id, AllocationId=allocation_id) return {"created": True, "id": r.get("NatGateway", {}).get("NatGatewayId")} except BotoServerError as e: return {"created": False, "error": __utils__["boto.get_error"](e)} def delete_nat_gateway( nat_gateway_id, release_eips=False, region=None, key=None, keyid=None, profile=None, wait_for_delete=False, wait_for_delete_retries=5, ): """ Delete a nat gateway (by id). Returns True if the internet gateway was deleted and otherwise False. This function requires boto3 to be installed. .. versionadded:: 2016.11.0 nat_gateway_id Id of the NAT Gateway release_eips whether to release the elastic IPs associated with the given NAT Gateway Id region Region to connect to. key Secret key to be used. keyid Access key to be used. profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. wait_for_delete whether to wait for delete of the NAT gateway to be in failed or deleted state after issuing the delete call. wait_for_delete_retries NAT gateway may take some time to be go into deleted or failed state. During the deletion process, subsequent release of elastic IPs may fail; this state will automatically retry this number of times to ensure the NAT gateway is in deleted or failed state before proceeding. CLI Examples: .. code-block:: bash salt myminion boto_vpc.delete_nat_gateway nat_gateway_id=igw-1a2b3c """ try: conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) gwinfo = conn3.describe_nat_gateways(NatGatewayIds=[nat_gateway_id]) if gwinfo: gwinfo = gwinfo.get("NatGateways", [None])[0] conn3.delete_nat_gateway(NatGatewayId=nat_gateway_id) # wait for deleting nat gateway to finish prior to attempt to release elastic ips if wait_for_delete: for retry in range(wait_for_delete_retries, 0, -1): if gwinfo and gwinfo["State"] not in ["deleted", "failed"]: time.sleep( (2 ** (wait_for_delete_retries - retry)) + (random.randint(0, 1000) / 1000.0) ) gwinfo = conn3.describe_nat_gateways(NatGatewayIds=[nat_gateway_id]) if gwinfo: gwinfo = gwinfo.get("NatGateways", [None])[0] continue break if release_eips and gwinfo: for addr in gwinfo.get("NatGatewayAddresses"): conn3.release_address(AllocationId=addr.get("AllocationId")) return {"deleted": True} except BotoServerError as e: return {"deleted": False, "error": __utils__["boto.get_error"](e)} def create_customer_gateway( vpn_connection_type, ip_address, bgp_asn, customer_gateway_name=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Given a valid VPN connection type, a static IP address and a customer gateway’s Border Gateway Protocol (BGP) Autonomous System Number, create a customer gateway. Returns the customer gateway id if the customer gateway was created and returns False if the customer gateway was not created. CLI Example: .. code-block:: bash salt myminion boto_vpc.create_customer_gateway 'ipsec.1', '12.1.2.3', 65534 """ return _create_resource( "customer_gateway", customer_gateway_name, type=vpn_connection_type, ip_address=ip_address, bgp_asn=bgp_asn, tags=tags, region=region, key=key, keyid=keyid, profile=profile, ) def delete_customer_gateway( customer_gateway_id=None, customer_gateway_name=None, region=None, key=None, keyid=None, profile=None, ): """ Given a customer gateway ID or name, delete the customer gateway. Returns True if the customer gateway was deleted and returns False if the customer gateway was not deleted. .. versionchanged:: 2015.8.0 Added customer_gateway_name argument CLI Example: .. code-block:: bash salt myminion boto_vpc.delete_customer_gateway 'cgw-b6a247df' """ return _delete_resource( resource="customer_gateway", name=customer_gateway_name, resource_id=customer_gateway_id, region=region, key=key, keyid=keyid, profile=profile, ) def customer_gateway_exists( customer_gateway_id=None, customer_gateway_name=None, region=None, key=None, keyid=None, profile=None, ): """ Given a customer gateway ID, check if the customer gateway ID exists. Returns True if the customer gateway ID exists; Returns False otherwise. CLI Example: .. code-block:: bash salt myminion boto_vpc.customer_gateway_exists cgw-b6a247df salt myminion boto_vpc.customer_gateway_exists customer_gatway_name=mycgw """ return resource_exists( "customer_gateway", name=customer_gateway_name, resource_id=customer_gateway_id, region=region, key=key, keyid=keyid, profile=profile, ) def create_dhcp_options( domain_name=None, domain_name_servers=None, ntp_servers=None, netbios_name_servers=None, netbios_node_type=None, dhcp_options_name=None, tags=None, vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, profile=None, ): """ Given valid DHCP options, create a DHCP options record, optionally associating it with an existing VPC. Returns True if the DHCP options record was created and returns False if the DHCP options record was not deleted. .. versionchanged:: 2015.8.0 Added vpc_name and vpc_id arguments CLI Example: .. code-block:: bash salt myminion boto_vpc.create_dhcp_options domain_name='example.com' \\ domain_name_servers='[1.2.3.4]' ntp_servers='[5.6.7.8]' \\ netbios_name_servers='[10.0.0.1]' netbios_node_type=1 \\ vpc_name='myvpc' """ try: if vpc_id or vpc_name: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) if not vpc_id: return { "created": False, "error": {"message": f"VPC {vpc_name or vpc_id} does not exist."}, } r = _create_resource( "dhcp_options", name=dhcp_options_name, 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, region=region, key=key, keyid=keyid, profile=profile, ) if r.get("created") and vpc_id: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.associate_dhcp_options(r["id"], vpc_id) log.info("Associated options %s to VPC %s", r["id"], vpc_name or vpc_id) return r except BotoServerError as e: return {"created": False, "error": __utils__["boto.get_error"](e)} def get_dhcp_options( dhcp_options_name=None, dhcp_options_id=None, region=None, key=None, keyid=None, profile=None, ): """ Return a dict with the current values of the requested DHCP options set CLI Example: .. code-block:: bash salt myminion boto_vpc.get_dhcp_options 'myfunnydhcpoptionsname' .. versionadded:: 2016.3.0 """ if not any((dhcp_options_name, dhcp_options_id)): raise SaltInvocationError( "At least one of the following must be specified: " "dhcp_options_name, dhcp_options_id." ) if not dhcp_options_id and dhcp_options_name: dhcp_options_id = _get_resource_id( "dhcp_options", dhcp_options_name, region=region, key=key, keyid=keyid, profile=profile, ) if not dhcp_options_id: return {"dhcp_options": {}} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = conn.get_all_dhcp_options(dhcp_options_ids=[dhcp_options_id]) except BotoServerError as e: return {"error": __utils__["boto.get_error"](e)} if not r: return {"dhcp_options": None} keys = ( "domain_name", "domain_name_servers", "ntp_servers", "netbios_name_servers", "netbios_node_type", ) return {"dhcp_options": {k: r[0].options.get(k) for k in keys}} def delete_dhcp_options( dhcp_options_id=None, dhcp_options_name=None, region=None, key=None, keyid=None, profile=None, ): """ Delete dhcp options by id or name. .. versionadded:: 2015.8.0 CLI Example: .. code-block:: bash salt myminion boto_vpc.delete_dhcp_options 'dopt-b6a247df' """ return _delete_resource( resource="dhcp_options", name=dhcp_options_name, resource_id=dhcp_options_id, region=region, key=key, keyid=keyid, profile=profile, ) def associate_dhcp_options_to_vpc( dhcp_options_id, vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, profile=None, ): """ Given valid DHCP options id and a valid VPC id, associate the DHCP options record with the VPC. Returns True if the DHCP options record were associated and returns False if the DHCP options record was not associated. CLI Example: .. code-block:: bash salt myminion boto_vpc.associate_dhcp_options_to_vpc 'dhcp-a0bl34pp' 'vpc-6b1fe402' """ try: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) if not vpc_id: return { "associated": False, "error": {"message": f"VPC {vpc_name or vpc_id} does not exist."}, } conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if conn.associate_dhcp_options(dhcp_options_id, vpc_id): log.info( "DHCP options with id %s were associated with VPC %s", dhcp_options_id, vpc_id, ) return {"associated": True} else: log.warning( "DHCP options with id %s were not associated with VPC %s", dhcp_options_id, vpc_id, ) return { "associated": False, "error": {"message": "DHCP options could not be associated."}, } except BotoServerError as e: return {"associated": False, "error": __utils__["boto.get_error"](e)} def dhcp_options_exists( dhcp_options_id=None, name=None, dhcp_options_name=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Check if a dhcp option exists. Returns True if the dhcp option exists; Returns False otherwise. CLI Example: .. code-block:: bash salt myminion boto_vpc.dhcp_options_exists dhcp_options_id='dhcp-a0bl34pp' """ if name: log.warning( "boto_vpc.dhcp_options_exists: name parameter is deprecated " "use dhcp_options_name instead." ) dhcp_options_name = name return resource_exists( "dhcp_options", name=dhcp_options_name, resource_id=dhcp_options_id, tags=tags, region=region, key=key, keyid=keyid, profile=profile, ) def create_network_acl( vpc_id=None, vpc_name=None, network_acl_name=None, subnet_id=None, subnet_name=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Given a vpc_id, creates a network acl. Returns the network acl id if successful, otherwise returns False. .. versionchanged:: 2015.8.0 Added vpc_name, subnet_id, and subnet_name arguments CLI Example: .. code-block:: bash salt myminion boto_vpc.create_network_acl 'vpc-6b1fe402' """ _id = vpc_name or vpc_id try: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) except BotoServerError as e: return {"created": False, "error": __utils__["boto.get_error"](e)} if not vpc_id: return { "created": False, "error": {"message": f"VPC {_id} does not exist."}, } if all((subnet_id, subnet_name)): raise SaltInvocationError( "Only one of subnet_name or subnet_id may be provided." ) if subnet_name: subnet_id = _get_resource_id( "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile ) if not subnet_id: return { "created": False, "error": {"message": f"Subnet {subnet_name} does not exist."}, } elif subnet_id: if not _get_resource( "subnet", resource_id=subnet_id, region=region, key=key, keyid=keyid, profile=profile, ): return { "created": False, "error": {"message": f"Subnet {subnet_id} does not exist."}, } r = _create_resource( "network_acl", name=network_acl_name, vpc_id=vpc_id, region=region, key=key, keyid=keyid, profile=profile, ) if r.get("created") and subnet_id: try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) association_id = conn.associate_network_acl(r["id"], subnet_id) except BotoServerError as e: return {"created": False, "error": __utils__["boto.get_error"](e)} r["association_id"] = association_id return r def delete_network_acl( network_acl_id=None, network_acl_name=None, disassociate=False, region=None, key=None, keyid=None, profile=None, ): """ Delete a network acl based on the network_acl_id or network_acl_name provided. CLI Examples: .. code-block:: bash salt myminion boto_vpc.delete_network_acl network_acl_id='acl-5fb85d36' \\ disassociate=false .. code-block:: bash salt myminion boto_vpc.delete_network_acl network_acl_name='myacl' \\ disassociate=true """ if disassociate: network_acl = _get_resource( "network_acl", name=network_acl_name, region=region, key=key, keyid=keyid, profile=profile, ) if network_acl and network_acl.associations: subnet_id = network_acl.associations[0].subnet_id try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.disassociate_network_acl(subnet_id) except BotoServerError: pass return _delete_resource( resource="network_acl", name=network_acl_name, resource_id=network_acl_id, region=region, key=key, keyid=keyid, profile=profile, ) def network_acl_exists( network_acl_id=None, name=None, network_acl_name=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Checks if a network acl exists. Returns True if the network acl exists or returns False if it doesn't exist. CLI Example: .. code-block:: bash salt myminion boto_vpc.network_acl_exists network_acl_id='acl-5fb85d36' """ if name: log.warning( "boto_vpc.network_acl_exists: name parameter is deprecated " "use network_acl_name instead." ) network_acl_name = name return resource_exists( "network_acl", name=network_acl_name, resource_id=network_acl_id, tags=tags, region=region, key=key, keyid=keyid, profile=profile, ) def associate_network_acl_to_subnet( network_acl_id=None, subnet_id=None, network_acl_name=None, subnet_name=None, region=None, key=None, keyid=None, profile=None, ): """ Given a network acl and subnet ids or names, associate a network acl to a subnet. CLI Example: .. code-block:: bash salt myminion boto_vpc.associate_network_acl_to_subnet \\ network_acl_id='acl-5fb85d36' subnet_id='subnet-6a1fe403' .. code-block:: bash salt myminion boto_vpc.associate_network_acl_to_subnet \\ network_acl_id='myacl' subnet_id='mysubnet' """ if network_acl_name: network_acl_id = _get_resource_id( "network_acl", network_acl_name, region=region, key=key, keyid=keyid, profile=profile, ) if not network_acl_id: return { "associated": False, "error": {"message": f"Network ACL {network_acl_name} does not exist."}, } if subnet_name: subnet_id = _get_resource_id( "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile ) if not subnet_id: return { "associated": False, "error": {"message": f"Subnet {subnet_name} does not exist."}, } try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) association_id = conn.associate_network_acl(network_acl_id, subnet_id) if association_id: log.info( "Network ACL with id %s was associated with subnet %s", network_acl_id, subnet_id, ) return {"associated": True, "id": association_id} else: log.warning( "Network ACL with id %s was not associated with subnet %s", network_acl_id, subnet_id, ) return { "associated": False, "error": {"message": "ACL could not be assocaited."}, } except BotoServerError as e: return {"associated": False, "error": __utils__["boto.get_error"](e)} def disassociate_network_acl( subnet_id=None, vpc_id=None, subnet_name=None, vpc_name=None, region=None, key=None, keyid=None, profile=None, ): """ Given a subnet ID, disassociates a network acl. CLI Example: .. code-block:: bash salt myminion boto_vpc.disassociate_network_acl 'subnet-6a1fe403' """ if not _exactly_one((subnet_name, subnet_id)): raise SaltInvocationError( "One (but not both) of subnet_id or subnet_name must be provided." ) if all((vpc_name, vpc_id)): raise SaltInvocationError("Only one of vpc_id or vpc_name may be provided.") try: if subnet_name: subnet_id = _get_resource_id( "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile, ) if not subnet_id: return { "disassociated": False, "error": {"message": f"Subnet {subnet_name} does not exist."}, } if vpc_name or vpc_id: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) association_id = conn.disassociate_network_acl(subnet_id, vpc_id=vpc_id) return {"disassociated": True, "association_id": association_id} except BotoServerError as e: return {"disassociated": False, "error": __utils__["boto.get_error"](e)} def _create_network_acl_entry( network_acl_id=None, rule_number=None, protocol=None, rule_action=None, cidr_block=None, egress=None, network_acl_name=None, icmp_code=None, icmp_type=None, port_range_from=None, port_range_to=None, replace=False, region=None, key=None, keyid=None, profile=None, ): if replace: rkey = "replaced" else: rkey = "created" if not _exactly_one((network_acl_name, network_acl_id)): raise SaltInvocationError( "One (but not both) of network_acl_id or network_acl_name must be provided." ) for v in ("rule_number", "protocol", "rule_action", "cidr_block"): if locals()[v] is None: raise SaltInvocationError(f"{v} is required.") if network_acl_name: network_acl_id = _get_resource_id( "network_acl", network_acl_name, region=region, key=key, keyid=keyid, profile=profile, ) if not network_acl_id: return { rkey: False, "error": { "message": "Network ACL {} does not exist.".format( network_acl_name or network_acl_id ) }, } if isinstance(protocol, str): if protocol == "all": protocol = -1 else: try: protocol = socket.getprotobyname(protocol) except OSError as e: raise SaltInvocationError(e) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if replace: f = conn.replace_network_acl_entry else: f = conn.create_network_acl_entry created = f( network_acl_id, rule_number, protocol, rule_action, cidr_block, egress=egress, icmp_code=icmp_code, icmp_type=icmp_type, port_range_from=port_range_from, port_range_to=port_range_to, ) if created: log.info("Network ACL entry was %s", rkey) else: log.warning("Network ACL entry was not %s", rkey) return {rkey: created} except BotoServerError as e: return {rkey: False, "error": __utils__["boto.get_error"](e)} def create_network_acl_entry( network_acl_id=None, rule_number=None, protocol=None, rule_action=None, cidr_block=None, egress=None, network_acl_name=None, icmp_code=None, icmp_type=None, port_range_from=None, port_range_to=None, region=None, key=None, keyid=None, profile=None, ): """ Creates a network acl entry. CLI Example: .. code-block:: bash salt myminion boto_vpc.create_network_acl_entry 'acl-5fb85d36' '32767' \\ 'all' 'deny' '0.0.0.0/0' egress=true """ kwargs = locals() return _create_network_acl_entry(**kwargs) def replace_network_acl_entry( network_acl_id=None, rule_number=None, protocol=None, rule_action=None, cidr_block=None, egress=None, network_acl_name=None, icmp_code=None, icmp_type=None, port_range_from=None, port_range_to=None, region=None, key=None, keyid=None, profile=None, ): """ Replaces a network acl entry. CLI Example: .. code-block:: bash salt myminion boto_vpc.replace_network_acl_entry 'acl-5fb85d36' '32767' \\ 'all' 'deny' '0.0.0.0/0' egress=true """ kwargs = locals() return _create_network_acl_entry(replace=True, **kwargs) def delete_network_acl_entry( network_acl_id=None, rule_number=None, egress=None, network_acl_name=None, region=None, key=None, keyid=None, profile=None, ): """ Deletes a network acl entry. CLI Example: .. code-block:: bash salt myminion boto_vpc.delete_network_acl_entry 'acl-5fb85d36' '32767' """ if not _exactly_one((network_acl_name, network_acl_id)): raise SaltInvocationError( "One (but not both) of network_acl_id or network_acl_name must be provided." ) for v in ("rule_number", "egress"): if locals()[v] is None: raise SaltInvocationError(f"{v} is required.") if network_acl_name: network_acl_id = _get_resource_id( "network_acl", network_acl_name, region=region, key=key, keyid=keyid, profile=profile, ) if not network_acl_id: return { "deleted": False, "error": { "message": "Network ACL {} does not exist.".format( network_acl_name or network_acl_id ) }, } try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) deleted = conn.delete_network_acl_entry( network_acl_id, rule_number, egress=egress ) if deleted: log.info("Network ACL entry was deleted") else: log.warning("Network ACL was not deleted") return {"deleted": deleted} except BotoServerError as e: return {"deleted": False, "error": __utils__["boto.get_error"](e)} def create_route_table( vpc_id=None, vpc_name=None, route_table_name=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Creates a route table. .. versionchanged:: 2015.8.0 Added vpc_name argument CLI Examples: .. code-block:: bash salt myminion boto_vpc.create_route_table vpc_id='vpc-6b1fe402' \\ route_table_name='myroutetable' salt myminion boto_vpc.create_route_table vpc_name='myvpc' \\ route_table_name='myroutetable' """ vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) if not vpc_id: return { "created": False, "error": {"message": f"VPC {vpc_name or vpc_id} does not exist."}, } return _create_resource( "route_table", route_table_name, tags=tags, vpc_id=vpc_id, region=region, key=key, keyid=keyid, profile=profile, ) def delete_route_table( route_table_id=None, route_table_name=None, region=None, key=None, keyid=None, profile=None, ): """ Deletes a route table. CLI Examples: .. code-block:: bash salt myminion boto_vpc.delete_route_table route_table_id='rtb-1f382e7d' salt myminion boto_vpc.delete_route_table route_table_name='myroutetable' """ return _delete_resource( resource="route_table", name=route_table_name, resource_id=route_table_id, region=region, key=key, keyid=keyid, profile=profile, ) def route_table_exists( route_table_id=None, name=None, route_table_name=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Checks if a route table exists. CLI Example: .. code-block:: bash salt myminion boto_vpc.route_table_exists route_table_id='rtb-1f382e7d' """ if name: log.warning( "boto_vpc.route_table_exists: name parameter is deprecated " "use route_table_name instead." ) route_table_name = name return resource_exists( "route_table", name=route_table_name, resource_id=route_table_id, tags=tags, region=region, key=key, keyid=keyid, profile=profile, ) def route_exists( destination_cidr_block, route_table_name=None, route_table_id=None, gateway_id=None, instance_id=None, interface_id=None, tags=None, region=None, key=None, keyid=None, profile=None, vpc_peering_connection_id=None, ): """ Checks if a route exists. .. versionadded:: 2015.8.0 CLI Example: .. code-block:: bash salt myminion boto_vpc.route_exists destination_cidr_block='10.0.0.0/20' gateway_id='local' route_table_name='test' """ if not any((route_table_name, route_table_id)): raise SaltInvocationError( "At least one of the following must be specified: route table name or route" " table id." ) if not any((gateway_id, instance_id, interface_id, vpc_peering_connection_id)): raise SaltInvocationError( "At least one of the following must be specified: gateway id, instance id, " "interface id or VPC peering connection id." ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) filter_parameters = {"filters": {}} if route_table_id: filter_parameters["route_table_ids"] = [route_table_id] if route_table_name: filter_parameters["filters"]["tag:Name"] = route_table_name if tags: for tag_name, tag_value in tags.items(): filter_parameters["filters"][f"tag:{tag_name}"] = tag_value route_tables = conn.get_all_route_tables(**filter_parameters) if len(route_tables) != 1: raise SaltInvocationError("Found more than one route table.") route_check = { "destination_cidr_block": destination_cidr_block, "gateway_id": gateway_id, "instance_id": instance_id, "interface_id": interface_id, "vpc_peering_connection_id": vpc_peering_connection_id, } for route_match in route_tables[0].routes: route_dict = { "destination_cidr_block": route_match.destination_cidr_block, "gateway_id": route_match.gateway_id, "instance_id": route_match.instance_id, "interface_id": route_match.interface_id, "vpc_peering_connection_id": vpc_peering_connection_id, } route_comp = set(route_dict.items()) ^ set(route_check.items()) if not route_comp: log.info("Route %s exists.", destination_cidr_block) return {"exists": True} log.warning("Route %s does not exist.", destination_cidr_block) return {"exists": False} except BotoServerError as e: return {"error": __utils__["boto.get_error"](e)} def associate_route_table( route_table_id=None, subnet_id=None, route_table_name=None, subnet_name=None, region=None, key=None, keyid=None, profile=None, ): """ Given a route table and subnet name or id, associates the route table with the subnet. CLI Example: .. code-block:: bash salt myminion boto_vpc.associate_route_table 'rtb-1f382e7d' 'subnet-6a1fe403' .. code-block:: bash salt myminion boto_vpc.associate_route_table route_table_name='myrtb' \\ subnet_name='mysubnet' """ if all((subnet_id, subnet_name)): raise SaltInvocationError( "Only one of subnet_name or subnet_id may be provided." ) if subnet_name: subnet_id = _get_resource_id( "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile ) if not subnet_id: return { "associated": False, "error": {"message": f"Subnet {subnet_name} does not exist."}, } if all((route_table_id, route_table_name)): raise SaltInvocationError( "Only one of route_table_name or route_table_id may be provided." ) if route_table_name: route_table_id = _get_resource_id( "route_table", route_table_name, region=region, key=key, keyid=keyid, profile=profile, ) if not route_table_id: return { "associated": False, "error": {"message": f"Route table {route_table_name} does not exist."}, } try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) association_id = conn.associate_route_table(route_table_id, subnet_id) log.info( "Route table %s was associated with subnet %s", route_table_id, subnet_id ) return {"association_id": association_id} except BotoServerError as e: return {"associated": False, "error": __utils__["boto.get_error"](e)} def disassociate_route_table( association_id, region=None, key=None, keyid=None, profile=None ): """ Disassociates a route table. association_id The Route Table Association ID to disassociate CLI Example: .. code-block:: bash salt myminion boto_vpc.disassociate_route_table 'rtbassoc-d8ccddba' """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if conn.disassociate_route_table(association_id): log.info( "Route table with association id %s has been disassociated.", association_id, ) return {"disassociated": True} else: log.warning( "Route table with association id %s has not been disassociated.", association_id, ) return {"disassociated": False} except BotoServerError as e: return {"disassociated": False, "error": __utils__["boto.get_error"](e)} def replace_route_table_association( association_id, route_table_id, region=None, key=None, keyid=None, profile=None ): """ Replaces a route table association. CLI Example: .. code-block:: bash salt myminion boto_vpc.replace_route_table_association 'rtbassoc-d8ccddba' 'rtb-1f382e7d' """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) association_id = conn.replace_route_table_association_with_assoc( association_id, route_table_id ) log.info( "Route table %s was reassociated with association id %s", route_table_id, association_id, ) return {"replaced": True, "association_id": association_id} except BotoServerError as e: return {"replaced": False, "error": __utils__["boto.get_error"](e)} def create_route( route_table_id=None, destination_cidr_block=None, route_table_name=None, gateway_id=None, internet_gateway_name=None, instance_id=None, interface_id=None, vpc_peering_connection_id=None, vpc_peering_connection_name=None, region=None, key=None, keyid=None, profile=None, nat_gateway_id=None, nat_gateway_subnet_name=None, nat_gateway_subnet_id=None, ): """ Creates a route. If a nat gateway is specified, boto3 must be installed CLI Example: .. code-block:: bash salt myminion boto_vpc.create_route 'rtb-1f382e7d' '10.0.0.0/16' gateway_id='vgw-a1b2c3' """ if not _exactly_one((route_table_name, route_table_id)): raise SaltInvocationError( "One (but not both) of route_table_id or route_table_name must be provided." ) if not _exactly_one( ( gateway_id, internet_gateway_name, instance_id, interface_id, vpc_peering_connection_id, nat_gateway_id, nat_gateway_subnet_id, nat_gateway_subnet_name, vpc_peering_connection_name, ) ): raise SaltInvocationError( "Only one of gateway_id, internet_gateway_name, instance_id, interface_id," " vpc_peering_connection_id, nat_gateway_id, nat_gateway_subnet_id," " nat_gateway_subnet_name or vpc_peering_connection_name may be provided." ) if destination_cidr_block is None: raise SaltInvocationError("destination_cidr_block is required.") try: if route_table_name: route_table_id = _get_resource_id( "route_table", route_table_name, region=region, key=key, keyid=keyid, profile=profile, ) if not route_table_id: return { "created": False, "error": { "message": "route table {} does not exist.".format( route_table_name ) }, } if internet_gateway_name: gateway_id = _get_resource_id( "internet_gateway", internet_gateway_name, region=region, key=key, keyid=keyid, profile=profile, ) if not gateway_id: return { "created": False, "error": { "message": "internet gateway {} does not exist.".format( internet_gateway_name ) }, } if vpc_peering_connection_name: vpc_peering_connection_id = _get_resource_id( "vpc_peering_connection", vpc_peering_connection_name, region=region, key=key, keyid=keyid, profile=profile, ) if not vpc_peering_connection_id: return { "created": False, "error": { "message": "VPC peering connection {} does not exist.".format( vpc_peering_connection_name ) }, } if nat_gateway_subnet_name: gws = describe_nat_gateways( subnet_name=nat_gateway_subnet_name, region=region, key=key, keyid=keyid, profile=profile, ) if not gws: return { "created": False, "error": { "message": "nat gateway for {} does not exist.".format( nat_gateway_subnet_name ) }, } nat_gateway_id = gws[0]["NatGatewayId"] if nat_gateway_subnet_id: gws = describe_nat_gateways( subnet_id=nat_gateway_subnet_id, region=region, key=key, keyid=keyid, profile=profile, ) if not gws: return { "created": False, "error": { "message": "nat gateway for {} does not exist.".format( nat_gateway_subnet_id ) }, } nat_gateway_id = gws[0]["NatGatewayId"] except BotoServerError as e: return {"created": False, "error": __utils__["boto.get_error"](e)} if not nat_gateway_id: return _create_resource( "route", route_table_id=route_table_id, destination_cidr_block=destination_cidr_block, gateway_id=gateway_id, instance_id=instance_id, interface_id=interface_id, vpc_peering_connection_id=vpc_peering_connection_id, region=region, key=key, keyid=keyid, profile=profile, ) # for nat gateway, boto3 is required try: conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) ret = conn3.create_route( RouteTableId=route_table_id, DestinationCidrBlock=destination_cidr_block, NatGatewayId=nat_gateway_id, ) return {"created": True, "id": ret.get("NatGatewayId")} except BotoServerError as e: return {"created": False, "error": __utils__["boto.get_error"](e)} def delete_route( route_table_id=None, destination_cidr_block=None, route_table_name=None, region=None, key=None, keyid=None, profile=None, ): """ Deletes a route. CLI Example: .. code-block:: bash salt myminion boto_vpc.delete_route 'rtb-1f382e7d' '10.0.0.0/16' """ if not _exactly_one((route_table_name, route_table_id)): raise SaltInvocationError( "One (but not both) of route_table_id or route_table_name must be provided." ) if destination_cidr_block is None: raise SaltInvocationError("destination_cidr_block is required.") try: if route_table_name: route_table_id = _get_resource_id( "route_table", route_table_name, region=region, key=key, keyid=keyid, profile=profile, ) if not route_table_id: return { "created": False, "error": { "message": "route table {} does not exist.".format( route_table_name ) }, } except BotoServerError as e: return {"created": False, "error": __utils__["boto.get_error"](e)} return _delete_resource( resource="route", resource_id=route_table_id, destination_cidr_block=destination_cidr_block, region=region, key=key, keyid=keyid, profile=profile, ) def replace_route( route_table_id=None, destination_cidr_block=None, route_table_name=None, gateway_id=None, instance_id=None, interface_id=None, region=None, key=None, keyid=None, profile=None, vpc_peering_connection_id=None, ): """ Replaces a route. CLI Example: .. code-block:: bash salt myminion boto_vpc.replace_route 'rtb-1f382e7d' '10.0.0.0/16' gateway_id='vgw-a1b2c3' """ if not _exactly_one((route_table_name, route_table_id)): raise SaltInvocationError( "One (but not both) of route_table_id or route_table_name must be provided." ) if destination_cidr_block is None: raise SaltInvocationError("destination_cidr_block is required.") try: if route_table_name: route_table_id = _get_resource_id( "route_table", route_table_name, region=region, key=key, keyid=keyid, profile=profile, ) if not route_table_id: return { "replaced": False, "error": { "message": "route table {} does not exist.".format( route_table_name ) }, } conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if conn.replace_route( route_table_id, destination_cidr_block, gateway_id=gateway_id, instance_id=instance_id, interface_id=interface_id, vpc_peering_connection_id=vpc_peering_connection_id, ): log.info( "Route with cidr block %s on route table %s was replaced", route_table_id, destination_cidr_block, ) return {"replaced": True} else: log.warning( "Route with cidr block %s on route table %s was not replaced", route_table_id, destination_cidr_block, ) return {"replaced": False} except BotoServerError as e: return {"replaced": False, "error": __utils__["boto.get_error"](e)} def describe_route_tables( route_table_id=None, route_table_name=None, vpc_id=None, tags=None, region=None, key=None, keyid=None, profile=None, ): """ Given route table properties, return details of all matching route tables. This function requires boto3 to be installed. .. versionadded:: 2016.11.0 CLI Example: .. code-block:: bash salt myminion boto_vpc.describe_route_tables vpc_id='vpc-a6a9efc3' """ if not any((route_table_id, route_table_name, tags, vpc_id)): raise SaltInvocationError( "At least one of the following must be specified: " "route table id, route table name, vpc_id, or tags." ) try: conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) filter_parameters = {"Filters": []} if route_table_id: filter_parameters["RouteTableIds"] = [route_table_id] if vpc_id: filter_parameters["Filters"].append({"Name": "vpc-id", "Values": [vpc_id]}) if route_table_name: filter_parameters["Filters"].append( {"Name": "tag:Name", "Values": [route_table_name]} ) if tags: for tag_name, tag_value in tags.items(): filter_parameters["Filters"].append( {"Name": f"tag:{tag_name}", "Values": [tag_value]} ) route_tables = conn3.describe_route_tables(**filter_parameters).get( "RouteTables", [] ) if not route_tables: return [] tables = [] keys = { "id": "RouteTableId", "vpc_id": "VpcId", "tags": "Tags", "routes": "Routes", "associations": "Associations", } route_keys = { "destination_cidr_block": "DestinationCidrBlock", "gateway_id": "GatewayId", "instance_id": "Instance", "interface_id": "NetworkInterfaceId", "nat_gateway_id": "NatGatewayId", "vpc_peering_connection_id": "VpcPeeringConnectionId", } assoc_keys = { "id": "RouteTableAssociationId", "main": "Main", "route_table_id": "RouteTableId", "SubnetId": "subnet_id", } for item in route_tables: route_table = {} for outkey, inkey in keys.items(): if inkey in item: if outkey == "routes": route_table[outkey] = _key_remap(inkey, route_keys, item) elif outkey == "associations": route_table[outkey] = _key_remap(inkey, assoc_keys, item) elif outkey == "tags": route_table[outkey] = {} for tagitem in item.get(inkey, []): route_table[outkey][tagitem.get("Key")] = tagitem.get( "Value" ) else: route_table[outkey] = item.get(inkey) tables.append(route_table) return tables except BotoServerError as e: return {"error": __utils__["boto.get_error"](e)} def _create_dhcp_options( conn, domain_name=None, domain_name_servers=None, ntp_servers=None, netbios_name_servers=None, netbios_node_type=None, ): return conn.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, ) def _maybe_set_name_tag(name, obj): if name: obj.add_tag("Name", name) log.debug("%s is now named as %s", obj, name) def _maybe_set_tags(tags, obj): if tags: # Not all objects in Boto have an 'add_tags()' method. try: obj.add_tags(tags) except AttributeError: for tag, value in tags.items(): obj.add_tag(tag, value) log.debug("The following tags: %s were added to %s", ", ".join(tags), obj) def _maybe_set_dns(conn, vpcid, dns_support, dns_hostnames): if dns_support: conn.modify_vpc_attribute(vpc_id=vpcid, enable_dns_support=dns_support) log.debug("DNS support was set to: %s on vpc %s", dns_support, vpcid) if dns_hostnames: conn.modify_vpc_attribute(vpc_id=vpcid, enable_dns_hostnames=dns_hostnames) log.debug("DNS hostnames was set to: %s on vpc %s", dns_hostnames, vpcid) def _maybe_name_route_table(conn, vpcid, vpc_name): route_tables = conn.get_all_route_tables(filters={"vpc_id": vpcid}) if not route_tables: log.warning("no default route table found") return default_table = None for table in route_tables: for association in getattr(table, "associations", {}): if getattr(association, "main", False): default_table = table break if not default_table: log.warning("no default route table found") return name = f"{vpc_name}-default-table" _maybe_set_name_tag(name, default_table) log.debug("Default route table name was set to: %s on vpc %s", name, vpcid) def _key_iter(key, keys, item): elements_list = [] for r_item in getattr(item, key): element = {} for r_key in keys: if hasattr(r_item, r_key): element[r_key] = getattr(r_item, r_key) elements_list.append(element) return elements_list def _key_remap(key, keys, item): elements_list = [] for r_item in item.get(key, []): element = {} for r_outkey, r_inkey in keys.items(): if r_inkey in r_item: element[r_outkey] = r_item.get(r_inkey) elements_list.append(element) return elements_list def _get_subnet_explicit_route_table( subnet_id, vpc_id, conn=None, region=None, key=None, keyid=None, profile=None ): """ helper function to find subnet explicit route table associations .. versionadded:: 2016.11.0 """ if not conn: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if conn: vpc_route_tables = conn.get_all_route_tables(filters={"vpc_id": vpc_id}) for vpc_route_table in vpc_route_tables: for rt_association in vpc_route_table.associations: if rt_association.subnet_id == subnet_id and not rt_association.main: return rt_association.id return None def request_vpc_peering_connection( requester_vpc_id=None, requester_vpc_name=None, peer_vpc_id=None, peer_vpc_name=None, name=None, peer_owner_id=None, peer_region=None, region=None, key=None, keyid=None, profile=None, dry_run=False, ): """ Request a VPC peering connection between two VPCs. .. versionadded:: 2016.11.0 requester_vpc_id ID of the requesting VPC. Exclusive with requester_vpc_name. requester_vpc_name Name tag of the requesting VPC. Exclusive with requester_vpc_id. peer_vpc_id ID of the VPC to create VPC peering connection with. This can be a VPC in another account. Exclusive with peer_vpc_name. peer_vpc_name Name tag of the VPC to create VPC peering connection with. This can only be a VPC in the same account and same region, else resolving it into a vpc ID will almost certainly fail. Exclusive with peer_vpc_id. name The name to use for this VPC peering connection. peer_owner_id ID of the owner of the peer VPC. Defaults to your account ID, so a value is required if peering with a VPC in a different account. peer_region Region of peer VPC. For inter-region vpc peering connections. Not required for intra-region peering connections. .. versionadded:: 3005 region Region to connect to. key Secret key to be used. keyid Access key to be used. profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. dry_run If True, skip application and return status. CLI Example: .. code-block:: bash # Create a named VPC peering connection salt myminion boto_vpc.request_vpc_peering_connection vpc-4a3e622e vpc-be82e9da name=my_vpc_connection # Without a name salt myminion boto_vpc.request_vpc_peering_connection vpc-4a3e622e vpc-be82e9da # Specify a region salt myminion boto_vpc.request_vpc_peering_connection vpc-4a3e622e vpc-be82e9da region=us-west-2 """ conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) if name and _vpc_peering_conn_id_for_name(name, conn): raise SaltInvocationError( "A VPC peering connection with this name already " "exists! Please specify a different name." ) if not _exactly_one((requester_vpc_id, requester_vpc_name)): raise SaltInvocationError( "Exactly one of requester_vpc_id or requester_vpc_name is required" ) if not _exactly_one((peer_vpc_id, peer_vpc_name)): raise SaltInvocationError( "Exactly one of peer_vpc_id or peer_vpc_name is required." ) if requester_vpc_name: requester_vpc_id = _get_id( vpc_name=requester_vpc_name, region=region, key=key, keyid=keyid, profile=profile, ) if not requester_vpc_id: return { "error": "Could not resolve VPC name {} to an ID".format( requester_vpc_name ) } if peer_vpc_name: peer_vpc_id = _get_id( vpc_name=peer_vpc_name, region=region, key=key, keyid=keyid, profile=profile ) if not peer_vpc_id: return {"error": f"Could not resolve VPC name {peer_vpc_name} to an ID"} peering_params = { "VpcId": requester_vpc_id, "PeerVpcId": peer_vpc_id, "DryRun": dry_run, } if peer_owner_id: peering_params.update({"PeerOwnerId": peer_owner_id}) if peer_region: peering_params.update({"PeerRegion": peer_region}) try: log.debug("Trying to request vpc peering connection") if not peer_owner_id: vpc_peering = conn.create_vpc_peering_connection(**peering_params) else: vpc_peering = conn.create_vpc_peering_connection(**peering_params) peering = vpc_peering.get("VpcPeeringConnection", {}) peering_conn_id = peering.get("VpcPeeringConnectionId", "ERROR") msg = f"VPC peering {peering_conn_id} requested." log.debug(msg) if name: log.debug("Adding name tag to vpc peering connection") conn.create_tags( Resources=[peering_conn_id], Tags=[{"Key": "Name", "Value": name}] ) log.debug("Applied name tag to vpc peering connection") msg += f" With name {name}." return {"msg": msg} except botocore.exceptions.ClientError as err: log.error("Got an error while trying to request vpc peering") return {"error": __utils__["boto.get_error"](err)} def _get_peering_connection_ids(name, conn): """ :param name: The name of the VPC peering connection. :type name: String :param conn: The boto aws ec2 connection. :return: The id associated with this peering connection Returns the VPC peering connection ids given the VPC peering connection name. """ filters = [ {"Name": "tag:Name", "Values": [name]}, {"Name": "status-code", "Values": [ACTIVE, PENDING_ACCEPTANCE, PROVISIONING]}, ] peerings = conn.describe_vpc_peering_connections(Filters=filters).get( "VpcPeeringConnections", [] ) return [x["VpcPeeringConnectionId"] for x in peerings] def describe_vpc_peering_connection( name, region=None, key=None, keyid=None, profile=None ): """ Returns any VPC peering connection id(s) for the given VPC peering connection name. VPC peering connection ids are only returned for connections that are in the ``active``, ``pending-acceptance`` or ``provisioning`` state. .. versionadded:: 2016.11.0 :param name: The string name for this VPC peering connection :param region: The aws region to use :param key: Your aws key :param keyid: The key id associated with this aws account :param profile: The profile to use :return: dict CLI Example: .. code-block:: bash salt myminion boto_vpc.describe_vpc_peering_connection salt-vpc # Specify a region salt myminion boto_vpc.describe_vpc_peering_connection salt-vpc region=us-west-2 """ conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) return {"VPC-Peerings": _get_peering_connection_ids(name, conn)} def accept_vpc_peering_connection( # pylint: disable=too-many-arguments conn_id="", name="", region=None, key=None, keyid=None, profile=None, dry_run=False ): """ Request a VPC peering connection between two VPCs. .. versionadded:: 2016.11.0 :param conn_id: The ID to use. String type. :param name: The name of this VPC peering connection. String type. :param region: The AWS region to use. Type string. :param key: The key to use for this connection. Type string. :param keyid: The key id to use. :param profile: The profile to use. :param dry_run: The dry_run flag to set. :return: dict Warning: Please specify either the ``vpc_peering_connection_id`` or ``name`` but not both. Specifying both will result in an error! CLI Example: .. code-block:: bash salt myminion boto_vpc.accept_vpc_peering_connection name=salt-vpc # Specify a region salt myminion boto_vpc.accept_vpc_peering_connection name=salt-vpc region=us-west-2 # specify an id salt myminion boto_vpc.accept_vpc_peering_connection conn_id=pcx-8a8939e3 """ if not _exactly_one((conn_id, name)): raise SaltInvocationError( "One (but not both) of vpc_peering_connection_id or name must be provided." ) conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) if name: conn_id = _vpc_peering_conn_id_for_name(name, conn) if not conn_id: raise SaltInvocationError( "No ID found for this " "VPC peering connection! ({}) " "Please make sure this VPC peering " "connection exists " "or invoke this function with " "a VPC peering connection " "ID".format(name) ) try: log.debug("Trying to accept vpc peering connection") conn.accept_vpc_peering_connection( DryRun=dry_run, VpcPeeringConnectionId=conn_id ) return {"msg": "VPC peering connection accepted."} except botocore.exceptions.ClientError as err: log.error("Got an error while trying to accept vpc peering") return {"error": __utils__["boto.get_error"](err)} def _vpc_peering_conn_id_for_name(name, conn): """ Get the ID associated with this name """ log.debug("Retrieving VPC peering connection id") ids = _get_peering_connection_ids(name, conn) if not ids: ids = [None] # Let callers handle the case where we have no id elif len(ids) > 1: raise SaltInvocationError( "Found multiple VPC peering connections " "with the same name!! " "Please make sure you have only " "one VPC peering connection named {} " "or invoke this function with a VPC " "peering connection ID".format(name) ) return ids[0] def delete_vpc_peering_connection( conn_id=None, conn_name=None, region=None, key=None, keyid=None, profile=None, dry_run=False, ): """ Delete a VPC peering connection. .. versionadded:: 2016.11.0 conn_id The connection ID to check. Exclusive with conn_name. conn_name The connection name to check. Exclusive with conn_id. region Region to connect to. key Secret key to be used. keyid Access key to be used. profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. dry_run If True, skip application and simply return projected status. CLI Example: .. code-block:: bash # Create a named VPC peering connection salt myminion boto_vpc.delete_vpc_peering_connection conn_name=salt-vpc # Specify a region salt myminion boto_vpc.delete_vpc_peering_connection conn_name=salt-vpc region=us-west-2 # specify an id salt myminion boto_vpc.delete_vpc_peering_connection conn_id=pcx-8a8939e3 """ if not _exactly_one((conn_id, conn_name)): raise SaltInvocationError( "Exactly one of conn_id or conn_name must be provided." ) conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) if conn_name: conn_id = _vpc_peering_conn_id_for_name(conn_name, conn) if not conn_id: raise SaltInvocationError( f"Couldn't resolve VPC peering connection {conn_name} to an ID" ) try: log.debug("Trying to delete vpc peering connection") conn.delete_vpc_peering_connection( DryRun=dry_run, VpcPeeringConnectionId=conn_id ) return {"msg": "VPC peering connection deleted."} except botocore.exceptions.ClientError as err: e = __utils__["boto.get_error"](err) log.error("Failed to delete VPC peering %s: %s", conn_name or conn_id, e) return {"error": e} def is_peering_connection_pending( conn_id=None, conn_name=None, region=None, key=None, keyid=None, profile=None ): """ Check if a VPC peering connection is in the pending state. .. versionadded:: 2016.11.0 conn_id The connection ID to check. Exclusive with conn_name. conn_name The connection name to check. Exclusive with conn_id. region Region to connect to. key Secret key to be used. keyid Access key to be used. profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. CLI Example: .. code-block:: bash salt myminion boto_vpc.is_peering_connection_pending conn_name=salt-vpc # Specify a region salt myminion boto_vpc.is_peering_connection_pending conn_name=salt-vpc region=us-west-2 # specify an id salt myminion boto_vpc.is_peering_connection_pending conn_id=pcx-8a8939e3 """ if not _exactly_one((conn_id, conn_name)): raise SaltInvocationError( "Exactly one of conn_id or conn_name must be provided." ) conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) if conn_id: vpcs = conn.describe_vpc_peering_connections( VpcPeeringConnectionIds=[conn_id] ).get("VpcPeeringConnections", []) else: filters = [ {"Name": "tag:Name", "Values": [conn_name]}, { "Name": "status-code", "Values": [ACTIVE, PENDING_ACCEPTANCE, PROVISIONING], }, ] vpcs = conn.describe_vpc_peering_connections(Filters=filters).get( "VpcPeeringConnections", [] ) if not vpcs: return False elif len(vpcs) > 1: raise SaltInvocationError( "Found more than one ID for the VPC peering " "connection ({}). Please call this function " "with an ID instead.".format(conn_id or conn_name) ) else: status = vpcs[0]["Status"]["Code"] return status == PENDING_ACCEPTANCE def peering_connection_pending_from_vpc( conn_id=None, conn_name=None, vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, profile=None, ): """ Check if a VPC peering connection is in the pending state, and requested from the given VPC. .. versionadded:: 2016.11.0 conn_id The connection ID to check. Exclusive with conn_name. conn_name The connection name to check. Exclusive with conn_id. vpc_id Is this the ID of the requesting VPC for this peering connection. Exclusive with vpc_name. vpc_name Is this the Name of the requesting VPC for this peering connection. Exclusive with vpc_id. region Region to connect to. key Secret key to be used. keyid Access key to be used. profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. CLI Example: .. code-block:: bash salt myminion boto_vpc.is_peering_connection_pending name=salt-vpc """ if not _exactly_one((conn_id, conn_name)): raise SaltInvocationError( "Exactly one of conn_id or conn_name must be provided." ) if not _exactly_one((vpc_id, vpc_name)): raise SaltInvocationError("Exactly one of vpc_id or vpc_name must be provided.") if vpc_name: vpc_id = check_vpc( vpc_name=vpc_name, region=region, key=key, keyid=keyid, profile=profile ) if not vpc_id: log.warning("Could not resolve VPC name %s to an ID", vpc_name) return False conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) filters = [ {"Name": "requester-vpc-info.vpc-id", "Values": [vpc_id]}, {"Name": "status-code", "Values": [ACTIVE, PENDING_ACCEPTANCE, PROVISIONING]}, ] if conn_id: filters += [{"Name": "vpc-peering-connection-id", "Values": [conn_id]}] else: filters += [{"Name": "tag:Name", "Values": [conn_name]}] vpcs = conn.describe_vpc_peering_connections(Filters=filters).get( "VpcPeeringConnections", [] ) if not vpcs: return False elif len(vpcs) > 1: raise SaltInvocationError( "Found more than one ID for the VPC peering " "connection ({}). Please call this function " "with an ID instead.".format(conn_id or conn_name) ) else: status = vpcs[0]["Status"]["Code"] return bool(status == PENDING_ACCEPTANCE)