diff --git a/changelogs/fragments/migrate_ec2_vpc_peer.yml b/changelogs/fragments/migrate_ec2_vpc_peer.yml new file mode 100644 index 00000000000..af4bf2d1b7b --- /dev/null +++ b/changelogs/fragments/migrate_ec2_vpc_peer.yml @@ -0,0 +1,8 @@ +--- +major_changes: + - ec2_vpc_peer - The module has been migrated from the ``community.aws`` collection. + Playbooks using the Fully Qualified Collection Name for this module should be + updated to use ``amazon.aws.ec2_vpc_peer``. + - ec2_vpc_peering_info - The module has been migrated from the ``community.aws`` + collection. Playbooks using the Fully Qualified Collection Name for this module + should be updated to use ``amazon.aws.ec2_vpc_peering_info``. diff --git a/meta/runtime.yml b/meta/runtime.yml index eafe427801a..68f4e6aa7a2 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -74,6 +74,8 @@ action_groups: - ec2_vpc_nat_gateway_info - ec2_vpc_net - ec2_vpc_net_info + - ec2_vpc_peer + - ec2_vpc_peering_info - ec2_vpc_route_table - ec2_vpc_route_table_info - ec2_vpc_subnet diff --git a/plugins/modules/ec2_vpc_peer.py b/plugins/modules/ec2_vpc_peer.py new file mode 100644 index 00000000000..4f2927090bb --- /dev/null +++ b/plugins/modules/ec2_vpc_peer.py @@ -0,0 +1,614 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r""" +module: ec2_vpc_peer +short_description: create, delete, accept, and reject VPC peering connections between two VPCs. +version_added: 1.0.0 +version_added_collection: community.aws +description: + - Read the AWS documentation for VPC Peering Connections + U(https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/vpc-peering.html). +options: + vpc_id: + description: + - VPC id of the requesting VPC. + required: false + type: str + peering_id: + description: + - Peering connection id. + required: false + type: str + peer_region: + description: + - Region of the accepting VPC. + required: false + type: str + peer_vpc_id: + description: + - VPC id of the accepting VPC. + required: false + type: str + peer_owner_id: + description: + - The AWS account number for cross account peering. + required: false + type: str + state: + description: + - Create, delete, accept, reject a peering connection. + required: false + default: present + choices: ['present', 'absent', 'accept', 'reject'] + type: str + wait: + description: + - Wait for peering state changes to complete. + required: false + default: false + type: bool +notes: + - Support for O(purge_tags) was added in release 2.0.0. +author: + - Mike Mochan (@mmochan) + - Alina Buzachis (@alinabuzachis) +extends_documentation_fragment: + - amazon.aws.common.modules + - amazon.aws.region.modules + - amazon.aws.tags + - amazon.aws.boto3 +""" + +EXAMPLES = r""" +# Complete example to create and accept a local peering connection. +- name: Create local account EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + vpc_id: "vpc-12345678" + peer_vpc_id: "vpc-87654321" + state: "present" + tags: + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" + register: vpc_peer + +- name: Accept local EC2 VPC Peering request + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + peering_id: "{{ vpc_peer.peering_id }}" + state: "accept" + register: action_peer + +# Complete example to delete a local peering connection. +- name: Create local account EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + vpc_id: "vpc-12345678" + peer_vpc_id: "vpc-87654321" + state: "present" + tags: + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" + register: vpc_peer + +- name: Delete a local EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + peering_id: "{{ vpc_peer.peering_id }}" + state: "absent" + register: vpc_peer + + # Complete example to create and accept a cross account peering connection. +- name: Create cross account EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + vpc_id: "vpc-12345678" + peer_vpc_id: "vpc-12345678" + peer_owner_id: "123456789012" + state: "present" + tags: + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" + register: vpc_peer + +- name: Accept EC2 VPC Peering Connection from remote account + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + peering_id: "{{ vpc_peer.peering_id }}" + profile: "bot03_profile_for_cross_account" + state: "accept" + register: vpc_peer + +# Complete example to create and accept an intra-region peering connection. +- name: Create intra-region EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + region: "us-east-1" + vpc_id: "vpc-12345678" + peer_vpc_id: "vpc-87654321" + peer_region: "us-west-2" + state: "present" + tags: + Name: "Peering connection for us-east-1 VPC to us-west-2 VPC" + CostCode: "CC1234" + Project: "phoenix" + register: vpc_peer + +- name: Accept EC2 VPC Peering Connection from peer region + amazon.aws.ec2_vpc_peer: + region: "us-west-2" + peering_id: "{{ vpc_peer.peering_id }}" + state: "accept" + register: vpc_peer + +# Complete example to create and reject a local peering connection. +- name: Create local account EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + vpc_id: "vpc-12345678" + peer_vpc_id: "vpc-87654321" + state: "present" + tags: + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" + register: vpc_peer + +- name: Reject a local EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + peering_id: "{{ vpc_peer.peering_id }}" + state: "reject" + +# Complete example to create and accept a cross account peering connection. +- name: Create cross account EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + vpc_id: "vpc-12345678" + peer_vpc_id: "vpc-12345678" + peer_owner_id: "123456789012" + state: "present" + tags: + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" + register: vpc_peer + +- name: Accept a cross account EC2 VPC Peering Connection request + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + peering_id: "{{ vpc_peer.peering_id }}" + profile: "bot03_profile_for_cross_account" + state: "accept" + tags: + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" + +# Complete example to create and reject a cross account peering connection. +- name: Create cross account EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + vpc_id: "vpc-12345678" + peer_vpc_id: "vpc-12345678" + peer_owner_id: "123456789012" + state: "present" + tags: + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" + register: vpc_peer + +- name: Reject a cross account EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + region: "ap-southeast-2" + peering_id: "{{ vpc_peer.peering_id }}" + profile: "bot03_profile_for_cross_account" + state: "reject" +""" + +RETURN = r""" +peering_id: + description: The id of the VPC peering connection created/deleted. + returned: always + type: str + sample: "pcx-034223d7c0aec3cde" +vpc_peering_connection: + description: The details of the VPC peering connection. + returned: success + type: dict + contains: + accepter_vpc_info: + description: Information about the VPC which accepted the connection. + returned: success + type: dict + contains: + cidr_block: + description: The primary CIDR for the VPC. + returned: when connection is in the accepted state. + type: str + sample: "10.10.10.0/23" + cidr_block_set: + description: A list of all CIDRs for the VPC. + returned: when connection is in the accepted state. + type: list + elements: dict + contains: + cidr_block: + description: A CIDR block used by the VPC. + returned: success + type: str + sample: "10.10.10.0/23" + owner_id: + description: The AWS account that owns the VPC. + returned: success + type: str + sample: "123456789012" + peering_options: + description: Additional peering configuration. + returned: when connection is in the accepted state. + type: dict + contains: + allow_dns_resolution_from_remote_vpc: + description: Indicates whether a VPC can resolve public DNS hostnames to private IP addresses when queried from instances in a peer VPC. + returned: success + type: bool + allow_egress_from_local_classic_link_to_remote_vpc: + description: Indicates whether a local ClassicLink connection can communicate with the peer VPC over the VPC peering connection. + returned: success + type: bool + allow_egress_from_local_vpc_to_remote_classic_link: + description: Indicates whether a local VPC can communicate with a ClassicLink connection in the peer VPC over the VPC peering connection. + returned: success + type: bool + region: + description: The AWS region that the VPC is in. + returned: success + type: str + sample: "us-east-1" + vpc_id: + description: The ID of the VPC + returned: success + type: str + sample: "vpc-0123456789abcdef0" + requester_vpc_info: + description: Information about the VPC which requested the connection. + returned: success + type: dict + contains: + cidr_block: + description: The primary CIDR for the VPC. + returned: when connection is not in the deleted state. + type: str + sample: "10.10.10.0/23" + cidr_block_set: + description: A list of all CIDRs for the VPC. + returned: when connection is not in the deleted state. + type: list + elements: dict + contains: + cidr_block: + description: A CIDR block used by the VPC + returned: success + type: str + sample: "10.10.10.0/23" + owner_id: + description: The AWS account that owns the VPC. + returned: success + type: str + sample: "123456789012" + peering_options: + description: Additional peering configuration. + returned: when connection is not in the deleted state. + type: dict + contains: + allow_dns_resolution_from_remote_vpc: + description: Indicates whether a VPC can resolve public DNS hostnames to private IP addresses when queried from instances in a peer VPC. + returned: success + type: bool + allow_egress_from_local_classic_link_to_remote_vpc: + description: Indicates whether a local ClassicLink connection can communicate with the peer VPC over the VPC peering connection. + returned: success + type: bool + allow_egress_from_local_vpc_to_remote_classic_link: + description: Indicates whether a local VPC can communicate with a ClassicLink connection in the peer VPC over the VPC peering connection. + returned: success + type: bool + region: + description: The AWS region that the VPC is in. + returned: success + type: str + sample: "us-east-1" + vpc_id: + description: The ID of the VPC + returned: success + type: str + sample: "vpc-0123456789abcdef0" + status: + description: Details of the current status of the connection. + returned: success + type: complex + contains: + code: + description: A short code describing the status of the connection. + returned: success + type: str + sample: "active" + message: + description: Additional information about the status of the connection. + returned: success + type: str + sample: "Pending Acceptance by 123456789012" + tags: + description: Tags applied to the connection. + returned: success + type: dict + expiration_time: + description: The time that an unaccepted VPC peering connection will expire. + type: str + sample: "2024-10-01T12:11:12+00:00" + vpc_peering_connection_id: + description: The ID of the VPC peering connection. + returned: success + type: str + sample: "pcx-0123456789abcdef0" +""" + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from typing import Any +from typing import Dict +from typing import NoReturn +from typing import Tuple + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import accept_vpc_peering_connection +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import create_vpc_peering_connection +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import delete_vpc_peering_connection +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import describe_vpc_peering_connections +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ensure_ec2_tags +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import reject_vpc_peering_connection +from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_list_to_ansible_dict +from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_specifications +from ansible_collections.amazon.aws.plugins.module_utils.transformation import ansible_dict_to_boto3_filter_list + + +def wait_for_state(client, module: AnsibleAWSModule, state: str, peering_id: str) -> NoReturn: + waiter = client.get_waiter("vpc_peering_connection_exists") + filters = { + "vpc-peering-connection-id": peering_id, + "status-code": state, + } + try: + waiter.wait(Filters=ansible_dict_to_boto3_filter_list(filters)) + except botocore.exceptions.WaiterError as e: + module.fail_json_aws(e, "Failed to wait for state change") + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Unable to describe Peering Connection while waiting for state to change") + + +def describe_peering_connections(client, module: AnsibleAWSModule, params) -> Dict[str, Any]: + peering_connections: Dict = {} + + filters = { + "requester-vpc-info.vpc-id": params["VpcId"], + "accepter-vpc-info.vpc-id": params["PeerVpcId"], + } + + peering_connections = describe_vpc_peering_connections(client, Filters=ansible_dict_to_boto3_filter_list(filters)) + if peering_connections == []: + # Try again with the VPC/Peer relationship reversed + filters = { + "requester-vpc-info.vpc-id": params["PeerVpcId"], + "accepter-vpc-info.vpc-id": params["VpcId"], + } + peering_connections = describe_vpc_peering_connections( + client, Filters=ansible_dict_to_boto3_filter_list(filters) + ) + + return peering_connections + + +def is_active(peering_connection: Dict[str, Any]) -> bool: + return peering_connection["Status"]["Code"] == "active" + + +def is_rejected(peering_connection: Dict[str, Any]) -> bool: + return peering_connection["Status"]["Code"] == "rejected" + + +def is_pending(peering_connection: Dict[str, Any]) -> bool: + return peering_connection["Status"]["Code"] == "pending-acceptance" + + +def is_deleted(peering_connection: Dict[str, Any]) -> bool: + return peering_connection["Status"]["Code"] == "deleted" + + +def create_peering_connection(client, module: AnsibleAWSModule) -> Tuple[bool, Dict[str, Any]]: + changed: bool = False + params: Dict = {} + + params["VpcId"] = module.params.get("vpc_id") + params["PeerVpcId"] = module.params.get("peer_vpc_id") + + if module.params.get("peer_region"): + params["PeerRegion"] = module.params["peer_region"] + + if module.params.get("peer_owner_id"): + params["PeerOwnerId"] = module.params["peer_owner_id"] + + peering_connections = describe_peering_connections(client, module, params) + for peering_connection in peering_connections: + changed |= ensure_ec2_tags( + client, + module, + peering_connection["VpcPeeringConnectionId"], + purge_tags=module.params.get("purge_tags"), + tags=module.params.get("tags"), + ) + + if is_active(peering_connection): + return (changed, peering_connection) + + if is_pending(peering_connection): + return (changed, peering_connection) + + if module.params.get("tags"): + params["TagSpecifications"] = boto3_tag_specifications(module.params["tags"], types="vpc-peering-connection") + + if module.check_mode: + return (True, {"VpcPeeringConnectionId": ""}) + + peering_connection = create_vpc_peering_connection(client, **params) + if module.params.get("wait"): + wait_for_state(client, module, "pending-acceptance", peering_connection["VpcPeeringConnectionId"]) + changed = True + return (changed, peering_connection) + + +def delete_peering_connection(client, module: AnsibleAWSModule) -> NoReturn: + peering_id = module.params.get("peering_id") + if peering_id: + peering_connection = get_peering_connection_by_id(client, module, peering_id) + else: + params: Dict = {} + params["VpcId"] = module.params.get("vpc_id") + params["PeerVpcId"] = module.params.get("peer_vpc_id") + params["PeerRegion"] = module.params.get("peer_region") + + if module.params.get("peer_owner_id"): + params["PeerOwnerId"] = module.params["peer_owner_id"] + + peering_connection = describe_peering_connections(client, module, params)[0] + + if not peering_connection: + module.exit_json(changed=False) + else: + peering_id = peering_id or peering_connection["VpcPeeringConnectionId"] + + if is_deleted(peering_connection): + module.exit_json(msg="Connection in deleted state.", changed=False, peering_id=peering_id) + + if is_rejected(peering_connection): + module.exit_json( + msg="Connection has been rejected. State cannot be changed and will be removed automatically by AWS", + changed=False, + peering_id=peering_id, + ) + + if not module.check_mode: + delete_vpc_peering_connection(client, peering_id) + if module.params.get("wait"): + wait_for_state(client, module, "deleted", peering_id) + + module.exit_json(changed=True, peering_id=peering_id) + + +def get_peering_connection_by_id(client, module: AnsibleAWSModule, peering_id: str) -> Dict[str, Any]: + filters: Dict = {} + filters["VpcPeeringConnectionIds"] = [peering_id] + + try: + result = describe_vpc_peering_connections(client, VpcPeeringConnectionIds=[peering_id]) + return result[0] + except is_boto3_error_code("InvalidVpcPeeringConnectionId.Malformed") as e: + module.fail_json_aws(e, msg="Malformed connection ID") + + +def accept_reject_peering_connection(client, module: AnsibleAWSModule, state: str) -> Tuple[bool, Dict[str, Any]]: + changed: bool = False + + peering_id = module.params.get("peering_id") + vpc_peering_connection = get_peering_connection_by_id(client, module, peering_id) + + if not (is_active(vpc_peering_connection) or is_rejected(vpc_peering_connection)): + if not module.check_mode: + if state == "accept": + changed |= accept_vpc_peering_connection(client, peering_id) + target_state = "active" + else: + changed |= reject_vpc_peering_connection(client, peering_id) + target_state = "rejected" + + if module.params.get("wait"): + wait_for_state(client, module, target_state, peering_id) + + changed = True + + changed |= ensure_ec2_tags( + client, + module, + peering_id, + purge_tags=module.params.get("purge_tags"), + tags=module.params.get("tags"), + ) + + # Reload peering conection info to return latest state/params + vpc_peering_connection = get_peering_connection_by_id(client, module, peering_id) + + return (changed, vpc_peering_connection) + + +def main(): + argument_spec = dict( + vpc_id=dict(type="str"), + peer_vpc_id=dict(type="str"), + peer_region=dict(type="str"), + peering_id=dict(type="str"), + peer_owner_id=dict(type="str"), + tags=dict(required=False, type="dict", aliases=["resource_tags"]), + purge_tags=dict(default=True, type="bool"), + state=dict(default="present", type="str", choices=["present", "absent", "accept", "reject"]), + wait=dict(default=False, type="bool"), + ) + required_if = [ + ("state", "present", ["vpc_id", "peer_vpc_id"]), + ("state", "accept", ["peering_id"]), + ("state", "reject", ["peering_id"]), + ] + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, required_if=required_if) + + state = module.params.get("state") + peering_id = module.params.get("peering_id") + vpc_id = module.params.get("vpc_id") + peer_vpc_id = module.params.get("peer_vpc_id") + + client = module.client("ec2") + + if state == "present": + (changed, results) = create_peering_connection(client, module) + elif state == "absent": + if not peering_id and (not vpc_id or not peer_vpc_id): + module.fail_json( + msg="state is absent but one of the following is missing: peering_id or [vpc_id, peer_vpc_id]" + ) + + delete_peering_connection(client, module) + else: + (changed, results) = accept_reject_peering_connection(client, module, state) + + formatted_results = camel_dict_to_snake_dict(results) + # Turn the resource tags from boto3 into an ansible friendly tag dictionary + formatted_results["tags"] = boto3_tag_list_to_ansible_dict(formatted_results.get("tags", [])) + + module.exit_json( + changed=changed, vpc_peering_connection=formatted_results, peering_id=results["VpcPeeringConnectionId"] + ) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ec2_vpc_peering_info.py b/plugins/modules/ec2_vpc_peering_info.py new file mode 100644 index 00000000000..82031e8013e --- /dev/null +++ b/plugins/modules/ec2_vpc_peering_info.py @@ -0,0 +1,388 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r""" +module: ec2_vpc_peering_info +short_description: Retrieves AWS VPC Peering details using AWS methods +version_added: 1.0.0 +version_added_collection: community.aws +description: + - Gets various details related to AWS VPC Peers +options: + peer_connection_ids: + description: + - List of specific VPC peer IDs to get details for. + type: list + elements: str + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcPeeringConnections.html) + for possible filters. + type: dict + default: {} +author: + - Karen Cheng (@Etherdaemon) + - Alina Buzachis (@alinabuzachis) +extends_documentation_fragment: + - amazon.aws.common.modules + - amazon.aws.region.modules + - amazon.aws.boto3 +""" + +EXAMPLES = r""" +- name: List all EC2 VPC Peering Connections + amazon.aws.ec2_vpc_peering_info: + region: ap-southeast-2 + register: all_vpc_peers + +- name: Debugging the result + ansible.builtin.debug: + msg: "{{ all_vpc_peers.result }}" + +- name: Get details on specific EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peering_info: + peer_connection_ids: + - "pcx-12345678" + - "pcx-87654321" + region: "ap-southeast-2" + register: all_vpc_peers + +- name: Get all EC2 VPC Peering Connections with specific filters + amazon.aws.ec2_vpc_peering_info: + region: "ap-southeast-2" + filters: + status-code: ["pending-acceptance"] + register: pending_vpc_peers +""" + +RETURN = r""" +vpc_peering_connections: + description: Details of the matching VPC peering connections. + returned: success + type: list + elements: dict + contains: + accepter_vpc_info: + description: Information about the VPC which accepted the connection. + returned: success + type: dict + contains: + cidr_block: + description: The primary CIDR for the VPC. + returned: when connection is in the accepted state. + type: str + sample: "10.10.10.0/23" + cidr_block_set: + description: A list of all CIDRs for the VPC. + returned: when connection is in the accepted state. + type: list + elements: dict + contains: + cidr_block: + description: A CIDR block used by the VPC. + returned: success + type: str + sample: "10.10.10.0/23" + owner_id: + description: The AWS account that owns the VPC. + returned: success + type: str + sample: "123456789012" + peering_options: + description: Additional peering configuration. + returned: when connection is in the accepted state. + type: dict + contains: + allow_dns_resolution_from_remote_vpc: + description: Indicates whether a VPC can resolve public DNS hostnames to private IP addresses when queried from instances in a peer VPC. + returned: success + type: bool + allow_egress_from_local_classic_link_to_remote_vpc: + description: Indicates whether a local ClassicLink connection can communicate with the peer VPC over the VPC peering connection. + returned: success + type: bool + allow_egress_from_local_vpc_to_remote_classic_link: + description: Indicates whether a local VPC can communicate with a ClassicLink connection in the peer VPC over the VPC peering connection. + returned: success + type: bool + region: + description: The AWS region that the VPC is in. + returned: success + type: str + sample: "us-east-1" + vpc_id: + description: The ID of the VPC + returned: success + type: str + sample: "vpc-0123456789abcdef0" + requester_vpc_info: + description: Information about the VPC which requested the connection. + returned: success + type: dict + contains: + cidr_block: + description: The primary CIDR for the VPC. + returned: when connection is not in the deleted state. + type: str + sample: "10.10.10.0/23" + cidr_block_set: + description: A list of all CIDRs for the VPC. + returned: when connection is not in the deleted state. + type: list + elements: dict + contains: + cidr_block: + description: A CIDR block used by the VPC + returned: success + type: str + sample: "10.10.10.0/23" + owner_id: + description: The AWS account that owns the VPC. + returned: success + type: str + sample: "123456789012" + peering_options: + description: Additional peering configuration. + returned: when connection is not in the deleted state. + type: dict + contains: + allow_dns_resolution_from_remote_vpc: + description: Indicates whether a VPC can resolve public DNS hostnames to private IP addresses when queried from instances in a peer VPC. + returned: success + type: bool + allow_egress_from_local_classic_link_to_remote_vpc: + description: Indicates whether a local ClassicLink connection can communicate with the peer VPC over the VPC peering connection. + returned: success + type: bool + allow_egress_from_local_vpc_to_remote_classic_link: + description: Indicates whether a local VPC can communicate with a ClassicLink connection in the peer VPC over the VPC peering connection. + returned: success + type: bool + region: + description: The AWS region that the VPC is in. + returned: success + type: str + sample: "us-east-1" + vpc_id: + description: The ID of the VPC + returned: success + type: str + sample: "vpc-0123456789abcdef0" + status: + description: Details of the current status of the connection. + returned: success + type: dict + contains: + code: + description: A short code describing the status of the connection. + returned: success + type: str + sample: "active" + message: + description: Additional information about the status of the connection. + returned: success + type: str + sample: "Pending Acceptance by 123456789012" + tags: + description: Tags applied to the connection. + returned: success + type: dict + vpc_peering_connection_id: + description: The ID of the VPC peering connection. + returned: success + type: str + sample: "pcx-0123456789abcdef0" + +result: + description: The result of the describe. + returned: success + type: list + elements: dict + contains: + accepter_vpc_info: + description: Information about the VPC which accepted the connection. + returned: success + type: dict + contains: + cidr_block: + description: The primary CIDR for the VPC. + returned: when connection is in the accepted state. + type: str + sample: "10.10.10.0/23" + cidr_block_set: + description: A list of all CIDRs for the VPC. + returned: when connection is in the accepted state. + type: list + elements: dict + contains: + cidr_block: + description: A CIDR block used by the VPC. + returned: success + type: str + sample: "10.10.10.0/23" + owner_id: + description: The AWS account that owns the VPC. + returned: success + type: str + sample: "123456789012" + peering_options: + description: Additional peering configuration. + returned: when connection is in the accepted state. + type: dict + contains: + allow_dns_resolution_from_remote_vpc: + description: Indicates whether a VPC can resolve public DNS hostnames to private IP addresses when queried from instances in a peer VPC. + returned: success + type: bool + allow_egress_from_local_classic_link_to_remote_vpc: + description: Indicates whether a local ClassicLink connection can communicate with the peer VPC over the VPC peering connection. + returned: success + type: bool + allow_egress_from_local_vpc_to_remote_classic_link: + description: Indicates whether a local VPC can communicate with a ClassicLink connection in the peer VPC over the VPC peering connection. + returned: success + type: bool + region: + description: The AWS region that the VPC is in. + returned: success + type: str + sample: "us-east-1" + vpc_id: + description: The ID of the VPC + returned: success + type: str + sample: "vpc-0123456789abcdef0" + requester_vpc_info: + description: Information about the VPC which requested the connection. + returned: success + type: dict + contains: + cidr_block: + description: The primary CIDR for the VPC. + returned: when connection is not in the deleted state. + type: str + sample: "10.10.10.0/23" + cidr_block_set: + description: A list of all CIDRs for the VPC. + returned: when connection is not in the deleted state. + type: list + elements: dict + contains: + cidr_block: + description: A CIDR block used by the VPC + returned: success + type: str + sample: "10.10.10.0/23" + owner_id: + description: The AWS account that owns the VPC. + returned: success + type: str + sample: "123456789012" + peering_options: + description: Additional peering configuration. + returned: when connection is not in the deleted state. + type: dict + contains: + allow_dns_resolution_from_remote_vpc: + description: Indicates whether a VPC can resolve public DNS hostnames to private IP addresses when queried from instances in a peer VPC. + returned: success + type: bool + allow_egress_from_local_classic_link_to_remote_vpc: + description: Indicates whether a local ClassicLink connection can communicate with the peer VPC over the VPC peering connection. + returned: success + type: bool + allow_egress_from_local_vpc_to_remote_classic_link: + description: Indicates whether a local VPC can communicate with a ClassicLink connection in the peer VPC over the VPC peering connection. + returned: success + type: bool + region: + description: The AWS region that the VPC is in. + returned: success + type: str + sample: "us-east-1" + vpc_id: + description: The ID of the VPC + returned: success + type: str + sample: "vpc-0123456789abcdef0" + status: + description: Details of the current status of the connection. + returned: success + type: dict + contains: + code: + description: A short code describing the status of the connection. + returned: success + type: str + sample: "active" + message: + description: Additional information about the status of the connection. + returned: success + type: str + sample: "Pending Acceptance by 123456789012" + tags: + description: Tags applied to the connection. + returned: success + type: dict + vpc_peering_connection_id: + description: The ID of the VPC peering connection. + returned: success + type: str + sample: "pcx-0123456789abcdef0" +""" + + +from typing import Any +from typing import Dict +from typing import List + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.botocore import normalize_boto3_result +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import describe_vpc_peering_connections +from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_list_to_ansible_dict +from ansible_collections.amazon.aws.plugins.module_utils.transformation import ansible_dict_to_boto3_filter_list + + +def get_vpc_peers(client, module: AnsibleAWSModule) -> List[Dict[str, Any]]: + params: Dict = {} + params["Filters"] = ansible_dict_to_boto3_filter_list(module.params.get("filters")) + + if module.params.get("peer_connection_ids"): + params["VpcPeeringConnectionIds"] = module.params.get("peer_connection_ids") + + result = describe_vpc_peering_connections(client, **params) + + return normalize_boto3_result(result) + + +def main(): + argument_spec = dict( + filters=dict(default=dict(), type="dict"), + peer_connection_ids=dict(default=None, type="list", elements="str"), + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + client = module.client("ec2") + + # Turn the boto3 result in to ansible friendly_snaked_names + results = [camel_dict_to_snake_dict(peer) for peer in get_vpc_peers(client, module)] + + # Turn the boto3 result in to ansible friendly tag dictionary + for peer in results: + peer["tags"] = boto3_tag_list_to_ansible_dict(peer.get("tags", [])) + + module.exit_json(result=results, vpc_peering_connections=results) + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/ec2_vpc_peer/aliases b/tests/integration/targets/ec2_vpc_peer/aliases new file mode 100644 index 00000000000..8807cb2514c --- /dev/null +++ b/tests/integration/targets/ec2_vpc_peer/aliases @@ -0,0 +1,3 @@ +cloud/aws + +ec2_vpc_peering_info diff --git a/tests/integration/targets/ec2_vpc_peer/defaults/main.yml b/tests/integration/targets/ec2_vpc_peer/defaults/main.yml new file mode 100644 index 00000000000..99698043b5e --- /dev/null +++ b/tests/integration/targets/ec2_vpc_peer/defaults/main.yml @@ -0,0 +1,5 @@ +vpc_seed: '{{ resource_prefix }}' +vpc_1_name: '{{ resource_prefix }}-vpc-1' +vpc_1_cidr: 10.{{ 256 | random(seed=vpc_seed) }}.0.0/23 +vpc_2_name: '{{ resource_prefix }}-vpc-1' +vpc_2_cidr: 10.{{ 256 | random(seed=vpc_seed) }}.2.0/23 diff --git a/tests/integration/targets/ec2_vpc_peer/meta/main.yml b/tests/integration/targets/ec2_vpc_peer/meta/main.yml new file mode 100644 index 00000000000..32cf5dda7ed --- /dev/null +++ b/tests/integration/targets/ec2_vpc_peer/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/tests/integration/targets/ec2_vpc_peer/tasks/main.yml b/tests/integration/targets/ec2_vpc_peer/tasks/main.yml new file mode 100644 index 00000000000..a915b5581fe --- /dev/null +++ b/tests/integration/targets/ec2_vpc_peer/tasks/main.yml @@ -0,0 +1,657 @@ +- name: EC2 VPC Peering Connection integration tests + module_defaults: + group/aws: + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + session_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + - name: Get ARN of calling user + aws_caller_info: + register: aws_caller_info + + - name: Store Account ID for later use + ansible.builtin.set_fact: + account_id: '{{ aws_caller_info.account }}' + + # ============================================================ + - name: Fetch EC2 VPC Peering Connections in check_mode + amazon.aws.ec2_vpc_peering_info: + register: peers_info + check_mode: true + + - name: Assert success + ansible.builtin.assert: + that: + - peers_info is successful + - '"result" in peers_info' + + # ============================================================ + - name: Create VPC 1 + amazon.aws.ec2_vpc_net: + name: '{{ vpc_1_name }}' + state: present + cidr_block: '{{ vpc_1_cidr }}' + tags: + Name: '{{ vpc_1_name }}' + TestPrefex: '{{ resource_prefix }}' + register: vpc_1_result + + - name: Assert success + ansible.builtin.assert: + that: + - vpc_1_result is successful + + - name: Create VPC 2 + amazon.aws.ec2_vpc_net: + name: '{{ vpc_2_name }}' + state: present + cidr_block: '{{ vpc_2_cidr }}' + tags: + Name: '{{ vpc_2_name }}' + TestPrefex: '{{ resource_prefix }}' + register: vpc_2_result + + - name: Assert success + ansible.builtin.assert: + that: + - vpc_2_result is successful + + - name: Store VPC IDs + set_fact: + vpc_1: '{{ vpc_1_result.vpc.id }}' + vpc_2: '{{ vpc_2_result.vpc.id }}' + + - name: Set a name to use with the connections + ansible.builtin.set_fact: + connection_name: Peering connection for VPC {{ vpc_1 }} to VPC {{ vpc_2 }} + + - name: Create local account EC2 VPC Peering Connection request (check_mode) + amazon.aws.ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + check_mode: true + register: vpc_peer + + - name: Assert success + ansible.builtin.assert: + that: + - vpc_peer is changed + + - name: Create local account EC2 VPC Peering Connection request + amazon.aws.ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + register: vpc_peer + + - name: Assert success + ansible.builtin.assert: + that: + - vpc_peer is changed + - vpc_peer is successful + - "'peering_id' in vpc_peer" + - vpc_peer.vpc_peering_connection.requester_vpc_info.cidr_block == vpc_1_cidr + - vpc_peer.peering_id.startswith('pcx-') + + - name: Store Connection ID + ansible.builtin.set_fact: + peer_id_1: '{{ vpc_peer.peering_id }}' + + - name: Re-create local account EC2 VPC Peering Connection request (idempotency + check_mode) + amazon.aws.ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + check_mode: true + register: vpc_peer + + - name: Assert success + ansible.builtin.assert: + that: + - vpc_peer is not changed + + - name: Re-create local account EC2 VPC Peering Connection request (idempotency) + amazon.aws.ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + register: vpc_peer + + - name: Assert success + ansible.builtin.assert: + that: + - vpc_peer is not changed + - vpc_peer is successful + - vpc_peer.peering_id == peer_id_1 + + - name: Create local account EC2 VPC Peering Connection request with accepter/requester + reversed (idempotency check_mode) + amazon.aws.ec2_vpc_peer: + vpc_id: '{{ vpc_2 }}' + peer_vpc_id: '{{ vpc_1 }}' + state: present + tags: + Name: '{{ connection_name }}' + check_mode: true + register: vpc_peer + + - name: Assert success + ansible.builtin.assert: + that: + - vpc_peer is not changed + + - name: Create local account EC2 VPC Peering Connection request with accepter/requester + reversed (idempotency) + amazon.aws.ec2_vpc_peer: + vpc_id: '{{ vpc_2 }}' + peer_vpc_id: '{{ vpc_1 }}' + state: present + tags: + Name: '{{ connection_name }}' + register: vpc_peer + + - name: Assert success + ansible.builtin.assert: + that: + - vpc_peer is not changed + - vpc_peer is successful + - vpc_peer.peering_id == peer_id_1 + + - name: Get details on specific EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peering_info: + peer_connection_ids: + - '{{ peer_id_1 }}' + register: peer_info + + - name: Assert expected values + ansible.builtin.assert: + that: + - peer_info is successful + - "'vpc_peering_connections' in peer_info" + - "'result' in peer_info" + - "'accepter_vpc_info' in peer_details" + - "'requester_vpc_info' in peer_details" + - "'status' in peer_details" + - "'code' in peer_details.status" + - peer_details.status.code == "pending-acceptance" + - "'message' in peer_details.status" + - "'tags' in peer_details" + - "'Name' in peer_details.tags" + - peer_details.tags.Name == connection_name + - "'vpc_peering_connection_id' in peer_details" + - peer_details.vpc_peering_connection_id == peer_id_1 + # Acceptor info isn't available until the connection has been accepted + - "'cidr_block' not in acceptor_details" + - "'cidr_block_set' not in acceptor_details" + - "'peering_options' not in acceptor_details" + - "'owner_id' in acceptor_details" + - acceptor_details.owner_id == account_id + - "'region' in acceptor_details" + - acceptor_details.region == aws_region + - "'vpc_id' in acceptor_details" + - acceptor_details.vpc_id == vpc_2 + # Information about the 'requesting' VPC + - "'cidr_block' in requester_details" + - requester_details.cidr_block == vpc_1_cidr + - "'cidr_block_set' in requester_details" + - requester_details.cidr_block_set | length == 1 + - "'cidr_block' in requester_details.cidr_block_set[0]" + - requester_details.cidr_block_set[0].cidr_block == vpc_1_cidr + - "'peering_options' in requester_details" + - "'owner_id' in requester_details" + - requester_details.owner_id == account_id + - "'region' in requester_details" + - requester_details.region == aws_region + - "'vpc_id' in requester_details" + - requester_details.vpc_id == vpc_1 + vars: + peer_details: '{{ peer_info.vpc_peering_connections[0] }}' + acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' + requester_details: '{{ peer_details["requester_vpc_info"] }}' + + - name: Get all EC2 VPC Peering Connections with specific filters + amazon.aws.ec2_vpc_peering_info: + filters: + status-code: [pending-acceptance] + register: pending_vpc_peers + + - name: Assert expected values + ansible.builtin.assert: + that: + # Not guaranteed to just be us, only assert the shape + - pending_vpc_peers is successful + - "'vpc_peering_connections' in peer_info" + - "'result' in peer_info" + - "'accepter_vpc_info' in peer_details" + - "'requester_vpc_info' in peer_details" + - "'status' in peer_details" + - "'code' in peer_details.status" + - peer_details.status.code == "pending-acceptance" + - "'message' in peer_details.status" + - "'tags' in peer_details" + - "'vpc_peering_connection_id' in peer_details" + # Acceptor info isn't available until the connection has been accepted + - "'cidr_block' not in acceptor_details" + - "'cidr_block_set' not in acceptor_details" + - "'peering_options' not in acceptor_details" + - "'owner_id' in acceptor_details" + - "'region' in acceptor_details" + - "'vpc_id' in acceptor_details" + # Information about the 'requesting' VPC + - "'cidr_block' in requester_details" + - "'cidr_block_set' in requester_details" + - "'cidr_block' in requester_details.cidr_block_set[0]" + - "'peering_options' in requester_details" + - "'owner_id' in requester_details" + - "'region' in requester_details" + - "'vpc_id' in requester_details" + vars: + peer_details: '{{ pending_vpc_peers.vpc_peering_connections[0] }}' + acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' + requester_details: '{{ peer_details["requester_vpc_info"] }}' + + - name: Update tags on the EC2 VPC Peering Connection (check_mode) + amazon.aws.ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + testPrefix: '{{ resource_prefix }}' + check_mode: true + register: tag_peer + + - name: Assert success + ansible.builtin.assert: + that: + - tag_peer is changed + + - name: Update tags on the EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + testPrefix: '{{ resource_prefix }}' + register: tag_peer + + - name: Assert success + ansible.builtin.assert: + that: + - tag_peer is changed + - tag_peer is successful + - tag_peer.peering_id == peer_id_1 + + - name: Update tags on the EC2 VPC Peering Connection (idempotency check_mode) + amazon.aws.ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + testPrefix: '{{ resource_prefix }}' + check_mode: true + register: tag_peer + + - name: Assert success + ansible.builtin.assert: + that: + - tag_peer is not changed + + - name: Update tags on the EC2 VPC Peering Connection (idempotency) + amazon.aws.ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: '{{ connection_name }}' + testPrefix: '{{ resource_prefix }}' + register: tag_peer + + - name: Assert success + ansible.builtin.assert: + that: + - tag_peer is not changed + - tag_peer is successful + - tag_peer.peering_id == peer_id_1 + + - name: Get details on specific EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peering_info: + peer_connection_ids: + - '{{ peer_id_1 }}' + register: peer_info + + - name: Assert expected tags + ansible.builtin.assert: + that: + - peer_info is successful + - "'tags' in peer_details" + - "'Name' in peer_details.tags" + - "'testPrefix' in peer_details.tags" + - peer_details.tags.Name == connection_name + - peer_details.tags.testPrefix == resource_prefix + vars: + peer_details: '{{ peer_info.vpc_peering_connections[0] }}' + + - name: Accept local EC2 VPC Peering request (check_mode) + amazon.aws.ec2_vpc_peer: + peering_id: '{{ vpc_peer.peering_id }}' + state: accept + wait: true + check_mode: true + register: action_peer + + - name: Assert success + ansible.builtin.assert: + that: + - action_peer is changed + + - name: Accept local EC2 VPC Peering request + amazon.aws.ec2_vpc_peer: + peering_id: '{{ vpc_peer.peering_id }}' + state: accept + wait: true + register: action_peer + + - name: Assert success + ansible.builtin.assert: + that: + - action_peer is changed + - action_peer is successful + - action_peer.peering_id == peer_id_1 + - action_peer.vpc_peering_connection.accepter_vpc_info.cidr_block == vpc_2_cidr + - action_peer.vpc_peering_connection.vpc_peering_connection_id == peer_id_1 + + - name: Get details on specific EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peering_info: + peer_connection_ids: + - '{{ peer_id_1 }}' + register: peer_info + + - name: Assert expected values + ansible.builtin.assert: + that: + - peer_info is successful + - "'vpc_peering_connections' in peer_info" + - "'result' in peer_info" + - "'accepter_vpc_info' in peer_details" + - "'requester_vpc_info' in peer_details" + - "'status' in peer_details" + - "'code' in peer_details.status" + - peer_details.status.code == "active" + - "'message' in peer_details.status" + - "'tags' in peer_details" + - "'Name' in peer_details.tags" + - peer_details.tags.Name == connection_name + - "'testPrefix' in peer_details.tags" + - peer_details.tags.testPrefix == resource_prefix + - "'vpc_peering_connection_id' in peer_details" + - peer_details.vpc_peering_connection_id == peer_id_1 + # Information about the 'accepting' VPC should be available now + - "'cidr_block' in acceptor_details" + - acceptor_details.cidr_block == vpc_2_cidr + - "'cidr_block_set' in acceptor_details" + - acceptor_details.cidr_block_set | length == 1 + - "'cidr_block' in acceptor_details.cidr_block_set[0]" + - acceptor_details.cidr_block_set[0].cidr_block == vpc_2_cidr + - "'peering_options' in acceptor_details" + - "'owner_id' in acceptor_details" + - acceptor_details.owner_id == account_id + - "'region' in acceptor_details" + - acceptor_details.region == aws_region + - "'vpc_id' in acceptor_details" + - acceptor_details.vpc_id == vpc_2 + # Information about the 'requesting' VPC + - "'cidr_block' in requester_details" + - requester_details.cidr_block == vpc_1_cidr + - "'cidr_block_set' in requester_details" + - requester_details.cidr_block_set | length == 1 + - "'cidr_block' in requester_details.cidr_block_set[0]" + - requester_details.cidr_block_set[0].cidr_block == vpc_1_cidr + - "'peering_options' in requester_details" + - "'owner_id' in requester_details" + - requester_details.owner_id == account_id + - "'region' in requester_details" + - requester_details.region == aws_region + - "'vpc_id' in requester_details" + - requester_details.vpc_id == vpc_1 + vars: + peer_details: '{{ peer_info.vpc_peering_connections[0] }}' + acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' + requester_details: '{{ peer_details["requester_vpc_info"] }}' + + - name: Accept local EC2 VPC Peering request (idempotency check_mode) + amazon.aws.ec2_vpc_peer: + peering_id: '{{ vpc_peer.peering_id }}' + state: accept + check_mode: true + register: action_peer + + - name: Assert success + ansible.builtin.assert: + that: + - action_peer is not changed + + - name: Accept local EC2 VPC Peering request (idempotency) + amazon.aws.ec2_vpc_peer: + peering_id: '{{ vpc_peer.peering_id }}' + state: accept + register: action_peer + + - name: Assert success + ansible.builtin.assert: + that: + - action_peer is not changed + - action_peer is successful + - action_peer.peering_id == peer_id_1 + - action_peer.vpc_peering_connection.vpc_peering_connection_id == peer_id_1 + + - name: Delete a local EC2 VPC Peering Connection (check_mode) + amazon.aws.ec2_vpc_peer: + peering_id: '{{ vpc_peer.peering_id }}' + state: absent + check_mode: true + register: delete_peer + + - name: Assert success + ansible.builtin.assert: + that: + - delete_peer is changed + + - name: Delete a local EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + peering_id: '{{ vpc_peer.peering_id }}' + state: absent + register: delete_peer + + - name: Assert success + ansible.builtin.assert: + that: + - delete_peer is changed + - delete_peer is successful + - "'peering_id' in delete_peer" + + - name: Get details on specific EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peering_info: + peer_connection_ids: + - '{{ peer_id_1}}' + register: peer_info + + - name: Assert expected values + ansible.builtin.assert: + that: + - peer_info is successful + - "'vpc_peering_connections' in peer_info" + - "'result' in peer_info" + - "'accepter_vpc_info' in peer_details" + - "'requester_vpc_info' in peer_details" + - "'status' in peer_details" + - "'code' in peer_details.status" + - peer_details.status.code == "deleted" + - "'message' in peer_details.status" + - "'tags' in peer_details" + - "'Name' in peer_details.tags" + - peer_details.tags.Name == connection_name + - "'testPrefix' in peer_details.tags" + - peer_details.tags.testPrefix == resource_prefix + - "'vpc_peering_connection_id' in peer_details" + - peer_details.vpc_peering_connection_id == peer_id_1 + # Information about the 'accepting' VPC is reduced again + - "'cidr_block' not in acceptor_details" + - "'cidr_block_set' not in acceptor_details" + - "'peering_options' not in acceptor_details" + - "'owner_id' in acceptor_details" + - acceptor_details.owner_id == account_id + - "'region' in acceptor_details" + - acceptor_details.region == aws_region + - "'vpc_id' in acceptor_details" + - acceptor_details.vpc_id == vpc_2 + # Information about the 'requesting' VPC is reduced once the VPC's deleted + - "'cidr_block' not in requester_details" + - "'cidr_block_set' not in requester_details" + - "'peering_options' not in requester_details" + - "'owner_id' in requester_details" + - requester_details.owner_id == account_id + - "'region' in requester_details" + - requester_details.region == aws_region + - "'vpc_id' in requester_details" + - requester_details.vpc_id == vpc_1 + vars: + peer_details: '{{ peer_info.vpc_peering_connections[0] }}' + acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' + requester_details: '{{ peer_details["requester_vpc_info"] }}' + + - name: Delete a local EC2 VPC Peering Connection (idempotency check_mode) + amazon.aws.ec2_vpc_peer: + peering_id: '{{ vpc_peer.peering_id }}' + state: absent + check_mode: true + register: delete_peer + + - name: Assert success + ansible.builtin.assert: + that: + - delete_peer is not changed + + - name: Delete a local EC2 VPC Peering Connection (idempotency) + amazon.aws.ec2_vpc_peer: + peering_id: '{{ vpc_peer.peering_id }}' + state: absent + register: delete_peer + + - name: Assert success + ansible.builtin.assert: + that: + - delete_peer is not changed + - delete_peer is successful + + - name: Create local account EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + vpc_id: '{{ vpc_1 }}' + peer_vpc_id: '{{ vpc_2 }}' + state: present + tags: + Name: Peering connection for VPC {{ vpc_1 }} to VPC {{ vpc_2 }} + register: vpc_peer2 + + - name: Assert success + ansible.builtin.assert: + that: + - vpc_peer2 is changed + - vpc_peer2 is successful + - "'peering_id' in vpc_peer2" + - vpc_peer2.peering_id.startswith('pcx-') + + - name: Store Connection ID + ansible.builtin.set_fact: + peer_id_2: '{{ vpc_peer2.peering_id }}' + + - name: Reject a local EC2 VPC Peering Connection + amazon.aws.ec2_vpc_peer: + peering_id: '{{ vpc_peer2.peering_id }}' + state: reject + wait: true + register: reject_peer + + - name: Assert success + ansible.builtin.assert: + that: + - reject_peer is changed + - reject_peer is successful + - reject_peer.peering_id == peer_id_2 + + - name: Reject a local EC2 VPC Peering Connection (idempotency) + amazon.aws.ec2_vpc_peer: + peering_id: '{{ vpc_peer2.peering_id }}' + state: reject + register: reject_peer + + - name: Assert success + ansible.builtin.assert: + that: + - reject_peer is not changed + - reject_peer is successful + - reject_peer.peering_id == peer_id_2 + - reject_peer.vpc_peering_connection.vpc_peering_connection_id == peer_id_2 + + - name: Delete a local EC2 VPC Peering Connections + amazon.aws.ec2_vpc_peer: + peering_id: '{{ vpc_peer2.peering_id }}' + state: absent + register: delete_peer + + - name: Assert success + ansible.builtin.assert: + that: + - delete_peer is not changed + - delete_peer is successful + + always: + + - name: Find all EC2 VPC Peering Connections for our VPCs + amazon.aws.ec2_vpc_peering_info: + filters: + accepter-vpc-info.vpc-id: '{{ item }}' + register: peering_info + loop: + - '{{ vpc_1 }}' + - '{{ vpc_2 }}' + + - ansible.builtin.set_fact: + vpc_peering_connection_ids: '{{ _vpc_peering_connections | map(attribute="vpc_peering_connection_id") + | list }}' + vars: + _vpc_peering_connections: '{{ peering_info.results | map(attribute="vpc_peering_connections") + | flatten }}' + ignore_errors: true + + # ============================================================ + + - name: Delete remaining EC2 VPC Peering Connections + amazon.aws.ec2_vpc_peer: + peering_id: '{{ item }}' + state: absent + ignore_errors: true + loop: '{{ vpc_peering_connection_ids }}' + + - name: Tidy up VPCs + amazon.aws.ec2_vpc_net: + name: '{{ item.name }}' + state: absent + cidr_block: '{{ item.cidr }}' + ignore_errors: true + loop: + - {name: '{{ vpc_2_name }}', cidr: '{{ vpc_2_cidr }}'} + - {name: '{{ vpc_1_name }}', cidr: '{{ vpc_1_cidr }}'}