From 7f67600691a1cc598b317c8bad3c09391ce49223 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Tue, 24 Sep 2024 14:37:55 +0200 Subject: [PATCH] Refactor ec2_vpc_peer* modules Signed-off-by: Alina Buzachis --- .../20240924-ec2_vpc_peer-refactor.yml | 4 + plugins/modules/ec2_vpc_peer.py | 477 +++++++++--------- plugins/modules/ec2_vpc_peering_info.py | 221 ++++++-- .../targets/ec2_vpc_peer/tasks/main.yml | 143 +++--- 4 files changed, 509 insertions(+), 336 deletions(-) create mode 100644 changelogs/fragments/20240924-ec2_vpc_peer-refactor.yml diff --git a/changelogs/fragments/20240924-ec2_vpc_peer-refactor.yml b/changelogs/fragments/20240924-ec2_vpc_peer-refactor.yml new file mode 100644 index 00000000000..6cc663334fd --- /dev/null +++ b/changelogs/fragments/20240924-ec2_vpc_peer-refactor.yml @@ -0,0 +1,4 @@ +--- +minor_changes: + - ec2_vpc_peer - Refactor module to use shared code from ``amazon.aws.plugins.module_utils.ec2``. + - ec2_vpc_peering_info - Refactor module to use shared code from ``amazon.aws.plugins.module_utils.ec2``. diff --git a/plugins/modules/ec2_vpc_peer.py b/plugins/modules/ec2_vpc_peer.py index 2a731bf23e4..f53c52ecbba 100644 --- a/plugins/modules/ec2_vpc_peer.py +++ b/plugins/modules/ec2_vpc_peer.py @@ -51,7 +51,7 @@ default: false type: bool notes: - - Support for I(purge_tags) was added in release 2.0.0. + - Support for O(purge_tags) was added in release 2.0.0. author: - Mike Mochan (@mmochan) extends_documentation_fragment: @@ -63,152 +63,152 @@ EXAMPLES = r""" # Complete example to create and accept a local peering connection. -- name: Create local account VPC peering Connection +- name: Create local account EC2 VPC Peering Connection community.aws.ec2_vpc_peer: - region: ap-southeast-2 - vpc_id: vpc-12345678 - peer_vpc_id: vpc-87654321 - state: present + 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 + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" register: vpc_peer -- name: Accept local VPC peering request +- name: Accept local EC2 VPC Peering request community.aws.ec2_vpc_peer: - region: ap-southeast-2 + region: "ap-southeast-2" peering_id: "{{ vpc_peer.peering_id }}" - state: accept + state: "accept" register: action_peer # Complete example to delete a local peering connection. -- name: Create local account VPC peering Connection +- name: Create local account EC2 VPC Peering Connection community.aws.ec2_vpc_peer: - region: ap-southeast-2 - vpc_id: vpc-12345678 - peer_vpc_id: vpc-87654321 - state: present + 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 + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" register: vpc_peer -- name: delete a local VPC peering Connection +- name: Delete a local EC2 VPC Peering Connection community.aws.ec2_vpc_peer: - region: ap-southeast-2 + region: "ap-southeast-2" peering_id: "{{ vpc_peer.peering_id }}" - state: absent + state: "absent" register: vpc_peer # Complete example to create and accept a cross account peering connection. -- name: Create cross account VPC peering Connection +- name: Create cross account EC2 VPC Peering Connection community.aws.ec2_vpc_peer: - region: ap-southeast-2 - vpc_id: vpc-12345678 - peer_vpc_id: vpc-12345678 - peer_owner_id: 123456789012 - state: present + 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 + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" register: vpc_peer -- name: Accept peering connection from remote account +- name: Accept EC2 VPC Peering Connection from remote account community.aws.ec2_vpc_peer: - region: ap-southeast-2 + region: "ap-southeast-2" peering_id: "{{ vpc_peer.peering_id }}" - profile: bot03_profile_for_cross_account - state: accept + 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 VPC peering Connection +- name: Create intra-region EC2 VPC Peering Connection community.aws.ec2_vpc_peer: - region: us-east-1 - vpc_id: vpc-12345678 - peer_vpc_id: vpc-87654321 - peer_region: us-west-2 - state: present + 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 + Name: "Peering connection for us-east-1 VPC to us-west-2 VPC" + CostCode: "CC1234" + Project: "phoenix" register: vpc_peer -- name: Accept peering connection from peer region +- name: Accept EC2 VPC Peering Connection from peer region community.aws.ec2_vpc_peer: - region: us-west-2 + region: "us-west-2" peering_id: "{{ vpc_peer.peering_id }}" - state: accept + state: "accept" register: vpc_peer # Complete example to create and reject a local peering connection. -- name: Create local account VPC peering Connection +- name: Create local account EC2 VPC Peering Connection community.aws.ec2_vpc_peer: - region: ap-southeast-2 - vpc_id: vpc-12345678 - peer_vpc_id: vpc-87654321 - state: present + 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 + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" register: vpc_peer -- name: Reject a local VPC peering Connection +- name: Reject a local EC2 VPC Peering Connection community.aws.ec2_vpc_peer: - region: ap-southeast-2 + region: "ap-southeast-2" peering_id: "{{ vpc_peer.peering_id }}" - state: reject + state: "reject" # Complete example to create and accept a cross account peering connection. -- name: Create cross account VPC peering Connection +- name: Create cross account EC2 VPC Peering Connection community.aws.ec2_vpc_peer: - region: ap-southeast-2 - vpc_id: vpc-12345678 - peer_vpc_id: vpc-12345678 - peer_owner_id: 123456789012 - state: present + 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 + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" register: vpc_peer -- name: Accept a cross account VPC peering connection request +- name: Accept a cross account EC2 VPC Peering Connection request community.aws.ec2_vpc_peer: - region: ap-southeast-2 + region: "ap-southeast-2" peering_id: "{{ vpc_peer.peering_id }}" - profile: bot03_profile_for_cross_account - state: accept + profile: "bot03_profile_for_cross_account" + state: "accept" tags: - Name: Peering connection for VPC 21 to VPC 22 - CostCode: CC1234 - Project: phoenix + 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 VPC peering Connection +- name: Create cross account EC2 VPC Peering Connection community.aws.ec2_vpc_peer: - region: ap-southeast-2 - vpc_id: vpc-12345678 - peer_vpc_id: vpc-12345678 - peer_owner_id: 123456789012 - state: present + 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 + Name: "Peering connection for VPC 21 to VPC 22" + CostCode: "CC1234" + Project: "phoenix" register: vpc_peer -- name: Reject a cross account VPC peering Connection +- name: Reject a cross account EC2 VPC Peering Connection community.aws.ec2_vpc_peer: - region: ap-southeast-2 + region: "ap-southeast-2" peering_id: "{{ vpc_peer.peering_id }}" - profile: bot03_profile_for_cross_account - state: reject + profile: "bot03_profile_for_cross_account" + state: "reject" """ RETURN = r""" @@ -216,37 +216,38 @@ description: The id of the VPC peering connection created/deleted. returned: always type: str - sample: pcx-034223d7c0aec3cde + sample: "pcx-034223d7c0aec3cde" vpc_peering_connection: - description: The details of the VPC peering connection as returned by Boto3 (snake cased). + description: The details of the VPC peering connection. returned: success - type: complex + type: dict contains: accepter_vpc_info: description: Information about the VPC which accepted the connection. returned: success - type: complex + type: dict contains: cidr_block: description: The primary CIDR for the VPC. returned: when connection is in the accepted state. type: str - example: '10.10.10.0/23' + 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: complex + type: list + elements: dict contains: cidr_block: description: A CIDR block used by the VPC. returned: success type: str - example: '10.10.10.0/23' + sample: "10.10.10.0/23" owner_id: description: The AWS account that owns the VPC. returned: success type: str - example: 123456789012 + sample: "123456789012" peering_options: description: Additional peering configuration. returned: when connection is in the accepted state. @@ -268,37 +269,38 @@ description: The AWS region that the VPC is in. returned: success type: str - example: us-east-1 + sample: "us-east-1" vpc_id: description: The ID of the VPC returned: success type: str - example: vpc-0123456789abcdef0 + sample: "vpc-0123456789abcdef0" requester_vpc_info: description: Information about the VPC which requested the connection. returned: success - type: complex + type: dict contains: cidr_block: description: The primary CIDR for the VPC. returned: when connection is not in the deleted state. type: str - example: '10.10.10.0/23' + 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: complex + type: list + elements: dict contains: cidr_block: description: A CIDR block used by the VPC returned: success type: str - example: '10.10.10.0/23' + sample: "10.10.10.0/23" owner_id: description: The AWS account that owns the VPC. returned: success type: str - example: 123456789012 + sample: "123456789012" peering_options: description: Additional peering configuration. returned: when connection is not in the deleted state. @@ -320,12 +322,12 @@ description: The AWS region that the VPC is in. returned: success type: str - example: us-east-1 + sample: "us-east-1" vpc_id: description: The ID of the VPC returned: success type: str - example: vpc-0123456789abcdef0 + sample: "vpc-0123456789abcdef0" status: description: Details of the current status of the connection. returned: success @@ -335,21 +337,25 @@ description: A short code describing the status of the connection. returned: success type: str - example: active + sample: "active" message: description: Additional information about the status of the connection. returned: success type: str - example: Pending Acceptance by 123456789012 + 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 - example: "pcx-0123456789abcdef0" + sample: "pcx-0123456789abcdef0" """ try: @@ -357,215 +363,233 @@ 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 AnsibleEC2Error +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import accept_vpc_peering_connection from ansible_collections.amazon.aws.plugins.module_utils.ec2 import add_ec2_tags +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.retries import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import reject_vpc_peering_connection 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 from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule -def wait_for_state(client, module, state, pcx_id): +def wait_for_state(client, module: AnsibleAWSModule, state: str, peering_id: str) -> NoReturn: waiter = client.get_waiter("vpc_peering_connection_exists") - peer_filter = { - "vpc-peering-connection-id": pcx_id, + filters = { + "vpc-peering-connection-id": peering_id, "status-code": state, } try: - waiter.wait(Filters=ansible_dict_to_boto3_filter_list(peer_filter)) + 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, "Enable to describe Peerig Connection while waiting for state to change") -def describe_peering_connections(params, client): - peer_filter = { +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"], } - result = client.describe_vpc_peering_connections( - aws_retry=True, - Filters=ansible_dict_to_boto3_filter_list(peer_filter), - ) - if result["VpcPeeringConnections"] == []: - # Try again with the VPC/Peer relationship reversed - peer_filter = { - "requester-vpc-info.vpc-id": params["PeerVpcId"], - "accepter-vpc-info.vpc-id": params["VpcId"], - } - result = client.describe_vpc_peering_connections( - aws_retry=True, - Filters=ansible_dict_to_boto3_filter_list(peer_filter), + + try: + peering_connections = describe_vpc_peering_connections( + client, **{"Filters": ansible_dict_to_boto3_filter_list(filters)} ) + if peering_connections["VpcPeeringConnections"] == []: + # 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)} + ) + except AnsibleEC2Error as e: + module.fail_json_aws_error(e) - return result + return peering_connections["VpcPeeringConnections"] -def is_active(peering_conn): - return peering_conn["Status"]["Code"] == "active" +def is_active(peering_connection: Dict[str, Any]) -> bool: + return peering_connection["Status"]["Code"] == "active" -def is_pending(peering_conn): - return peering_conn["Status"]["Code"] == "pending-acceptance" +def is_rejected(peering_connection: Dict[str, Any]) -> bool: + return peering_connection["Status"]["Code"] == "rejected" -def create_peer_connection(client, module): - changed = False - params = dict() +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.get("peer_region") + params["PeerRegion"] = module.params["peer_region"] + if module.params.get("peer_owner_id"): - params["PeerOwnerId"] = str(module.params.get("peer_owner_id")) - peering_conns = describe_peering_connections(params, client) - for peering_conn in peering_conns["VpcPeeringConnections"]: - pcx_id = peering_conn["VpcPeeringConnectionId"] - if ensure_ec2_tags( + 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, - pcx_id, + peering_connection["VpcPeeringConnectionId"], purge_tags=module.params.get("purge_tags"), tags=module.params.get("tags"), - ): - changed = True - if is_active(peering_conn): - return (changed, peering_conn) - if is_pending(peering_conn): - return (changed, peering_conn) + ) + + 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") + try: - peering_conn = client.create_vpc_peering_connection(aws_retry=True, **params) - pcx_id = peering_conn["VpcPeeringConnection"]["VpcPeeringConnectionId"] - if module.params.get("tags"): - # Once the minimum botocore version is bumped to > 1.17.24 - # (hopefully community.aws 3.0.0) we can add the tags to the - # creation parameters - add_ec2_tags( - client, - module, - pcx_id, - module.params.get("tags"), - retry_codes=["InvalidVpcPeeringConnectionID.NotFound"], - ) + peering_connection = create_vpc_peering_connection(client, **params) if module.params.get("wait"): - wait_for_state(client, module, "pending-acceptance", pcx_id) + wait_for_state(client, module, "pending-acceptance", peering_connection["VpcPeeringConnectionId"]) changed = True - return (changed, peering_conn["VpcPeeringConnection"]) - except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + return (changed, peering_connection) + except AnsibleEC2Error as e: + module.fail_json_aws_error(e) -def remove_peer_connection(client, module): - pcx_id = module.params.get("peering_id") - if pcx_id: - peering_conn = get_peering_connection_by_id(pcx_id, client, module) +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: 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"] = str(module.params.get("peer_owner_id")) - peering_conn = describe_peering_connections(params, client)["VpcPeeringConnections"][0] + params["PeerOwnerId"] = module.params["peer_owner_id"] + + peering_connection = describe_peering_connections(client, module, params)[0] - if not peering_conn: + if not peering_connection: module.exit_json(changed=False) else: - pcx_id = pcx_id or peering_conn["VpcPeeringConnectionId"] + peering_id = peering_id or peering_connection["VpcPeeringConnectionId"] - if peering_conn["Status"]["Code"] == "deleted": - module.exit_json(msg="Connection in deleted state.", changed=False, peering_id=pcx_id) - if peering_conn["Status"]["Code"] == "rejected": + 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=pcx_id, + peering_id=peering_id, ) try: - params = dict() - params["VpcPeeringConnectionId"] = pcx_id - client.delete_vpc_peering_connection(aws_retry=True, **params) + delete_vpc_peering_connection(client, peering_id) if module.params.get("wait"): - wait_for_state(client, module, "deleted", pcx_id) - module.exit_json(changed=True, peering_id=pcx_id) - except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) + wait_for_state(client, module, "deleted", peering_id) + except AnsibleEC2Error as e: + module.fail_json_aws_error(e) + + module.exit_json(changed=True, peering_id=peering_id) -def get_peering_connection_by_id(peering_id, client, module): - params = dict() - params["VpcPeeringConnectionIds"] = [peering_id] +def get_peering_connection_by_id(client, module: AnsibleAWSModule, peering_id: str) -> Dict[str, Any]: + filters: Dict = {} + filters["VpcPeeringConnectionIds"] = [peering_id] + try: - vpc_peering_connection = client.describe_vpc_peering_connections(aws_retry=True, **params) - return vpc_peering_connection["VpcPeeringConnections"][0] + result = describe_vpc_peering_connections(client, **filters) + return result["VpcPeeringConnections"][0] except is_boto3_error_code("InvalidVpcPeeringConnectionId.Malformed") as e: module.fail_json_aws(e, msg="Malformed connection ID") - except ( - botocore.exceptions.ClientError, - botocore.exceptions.BotoCoreError, - ) as e: # pylint: disable=duplicate-except + except AnsibleEC2Error as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Error while describing peering connection by peering_id") -def accept_reject(state, client, module): - changed = False - params = dict() +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") - params["VpcPeeringConnectionId"] = peering_id - vpc_peering_connection = get_peering_connection_by_id(peering_id, client, module) - peering_status = vpc_peering_connection["Status"]["Code"] + vpc_peering_connection = get_peering_connection_by_id(client, module, peering_id) - if peering_status not in ["active", "rejected"]: + if not (is_active(vpc_peering_connection) or is_rejected(vpc_peering_connection)): try: if state == "accept": - client.accept_vpc_peering_connection(aws_retry=True, **params) + changed |= accept_vpc_peering_connection(client, peering_id) target_state = "active" else: - client.reject_vpc_peering_connection(aws_retry=True, **params) + changed |= reject_vpc_peering_connection(client, peering_id) target_state = "rejected" + if module.params.get("tags"): - add_ec2_tags( + changed |= add_ec2_tags( client, module, peering_id, - module.params.get("tags"), - retry_codes=["InvalidVpcPeeringConnectionID.NotFound"], + module.params["tags"], ) - changed = True + if module.params.get("wait"): wait_for_state(client, module, target_state, peering_id) - except botocore.exceptions.ClientError as e: - module.fail_json(msg=str(e)) - if ensure_ec2_tags( + except AnsibleEC2Error as e: + module.fail_json_aws_error(e) + + changed |= ensure_ec2_tags( client, module, peering_id, purge_tags=module.params.get("purge_tags"), tags=module.params.get("tags"), - ): - changed = True + ) + + # Reload peering conection info to return latest state/params + vpc_peering_connection = get_peering_connection_by_id(client, module, peering_id) - # Relaod peering conection infos to return latest state/params - vpc_peering_connection = get_peering_connection_by_id(peering_id, client, module) return (changed, vpc_peering_connection) def main(): argument_spec = dict( - vpc_id=dict(), - peer_vpc_id=dict(), - peer_region=dict(), - peering_id=dict(), - peer_owner_id=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", choices=["present", "absent", "accept", "reject"]), + state=dict(default="present", type="str", choices=["present", "absent", "accept", "reject"]), wait=dict(default=False, type="bool"), ) required_if = [ @@ -581,22 +605,19 @@ def main(): vpc_id = module.params.get("vpc_id") peer_vpc_id = module.params.get("peer_vpc_id") - try: - client = module.client("ec2", retry_decorator=AWSRetry.jittered_backoff()) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to connect to AWS") + client = module.client("ec2") if state == "present": - (changed, results) = create_peer_connection(client, module) + (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]" ) - remove_peer_connection(client, module) + delete_peering_connection(client, module) else: - (changed, results) = accept_reject(state, client, module) + (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 diff --git a/plugins/modules/ec2_vpc_peering_info.py b/plugins/modules/ec2_vpc_peering_info.py index badc9f8fd80..b9190ef4138 100644 --- a/plugins/modules/ec2_vpc_peering_info.py +++ b/plugins/modules/ec2_vpc_peering_info.py @@ -6,7 +6,7 @@ DOCUMENTATION = r""" module: ec2_vpc_peering_info -short_description: Retrieves AWS VPC Peering details using AWS methods. +short_description: Retrieves AWS VPC Peering details using AWS methods version_added: 1.0.0 description: - Gets various details related to AWS VPC Peers @@ -32,8 +32,7 @@ """ EXAMPLES = r""" -# Simple example of listing all VPC Peers -- name: List all vpc peers +- name: List all EC2 VPC Peering Connections community.aws.ec2_vpc_peering_info: region: ap-southeast-2 register: all_vpc_peers @@ -42,19 +41,19 @@ ansible.builtin.debug: msg: "{{ all_vpc_peers.result }}" -- name: Get details on specific VPC peer +- name: Get details on specific EC2 VPC Peering Connection community.aws.ec2_vpc_peering_info: peer_connection_ids: - - pcx-12345678 - - pcx-87654321 - region: ap-southeast-2 + - "pcx-12345678" + - "pcx-87654321" + region: "ap-southeast-2" register: all_vpc_peers -- name: Get all vpc peers with specific filters +- name: Get all EC2 VPC Peering Connections with specific filters community.aws.ec2_vpc_peering_info: - region: ap-southeast-2 + region: "ap-southeast-2" filters: - status-code: ['pending-acceptance'] + status-code: ["pending-acceptance"] register: pending_vpc_peers """ @@ -63,32 +62,34 @@ 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: complex + type: dict contains: cidr_block: description: The primary CIDR for the VPC. returned: when connection is in the accepted state. type: str - example: '10.10.10.0/23' + 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: complex + type: list + elements: dict contains: cidr_block: description: A CIDR block used by the VPC. returned: success type: str - example: '10.10.10.0/23' + sample: "10.10.10.0/23" owner_id: description: The AWS account that owns the VPC. returned: success type: str - example: 123456789012 + sample: "123456789012" peering_options: description: Additional peering configuration. returned: when connection is in the accepted state. @@ -110,37 +111,38 @@ description: The AWS region that the VPC is in. returned: success type: str - example: us-east-1 + sample: "us-east-1" vpc_id: description: The ID of the VPC returned: success type: str - example: vpc-0123456789abcdef0 + sample: "vpc-0123456789abcdef0" requester_vpc_info: description: Information about the VPC which requested the connection. returned: success - type: complex + type: dict contains: cidr_block: description: The primary CIDR for the VPC. returned: when connection is not in the deleted state. type: str - example: '10.10.10.0/23' + 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: complex + type: list + elements: dict contains: cidr_block: description: A CIDR block used by the VPC returned: success type: str - example: '10.10.10.0/23' + sample: "10.10.10.0/23" owner_id: description: The AWS account that owns the VPC. returned: success type: str - example: 123456789012 + sample: "123456789012" peering_options: description: Additional peering configuration. returned: when connection is not in the deleted state. @@ -162,27 +164,27 @@ description: The AWS region that the VPC is in. returned: success type: str - example: us-east-1 + sample: "us-east-1" vpc_id: description: The ID of the VPC returned: success type: str - example: vpc-0123456789abcdef0 + sample: "vpc-0123456789abcdef0" status: description: Details of the current status of the connection. returned: success - type: complex + type: dict contains: code: description: A short code describing the status of the connection. returned: success type: str - example: active + sample: "active" message: description: Additional information about the status of the connection. returned: success type: str - example: Pending Acceptance by 123456789012 + sample: "Pending Acceptance by 123456789012" tags: description: Tags applied to the connection. returned: success @@ -191,41 +193,175 @@ description: The ID of the VPC peering connection. returned: success type: str - example: "pcx-0123456789abcdef0" + 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" """ -try: - import botocore -except ImportError: - pass # Handled by AnsibleAWSModule + +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.retries import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AnsibleEC2Error +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import describe_vpc_peering_connections 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 from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule -def get_vpc_peers(client, module): - params = dict() +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") + try: - result = client.describe_vpc_peering_connections(aws_retry=True, **params) - result = normalize_boto3_result(result) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + result = describe_vpc_peering_connections(client, **params)["VpcPeeringConnections"] + except AnsibleEC2Error as e: module.fail_json_aws(e, msg="Failed to describe peering connections") - return result["VpcPeeringConnections"] + return normalize_boto3_result(result) def main(): @@ -239,13 +375,10 @@ def main(): supports_check_mode=True, ) - try: - ec2 = module.client("ec2", retry_decorator=AWSRetry.jittered_backoff()) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to connect to AWS") + 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(ec2, module)] + 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: diff --git a/tests/integration/targets/ec2_vpc_peer/tasks/main.yml b/tests/integration/targets/ec2_vpc_peer/tasks/main.yml index b39b69b74b0..9bf7b84038e 100644 --- a/tests/integration/targets/ec2_vpc_peer/tasks/main.yml +++ b/tests/integration/targets/ec2_vpc_peer/tasks/main.yml @@ -1,5 +1,5 @@ --- -- name: ec2_vpc_igw tests +- name: EC2 VPC Peering Connection integration tests collections: - amazon.aws module_defaults: @@ -9,18 +9,20 @@ session_token: "{{ security_token | default(omit) }}" region: "{{ aws_region }}" block: - - name: get ARN of calling user + - name: Get ARN of calling user aws_caller_info: register: aws_caller_info + - name: Store Account ID for later use - set_fact: + ansible.builtin.set_fact: account_id: '{{ aws_caller_info.account }}' # ============================================================ - - name: Fetch Peers in check_mode - ec2_vpc_peering_info: + - name: Fetch EC2 VPC Peering Connections in check_mode + community.aws.ec2_vpc_peering_info: register: peers_info check_mode: True + - name: Assert success assert: that: @@ -28,8 +30,8 @@ - '"result" in peers_info' # ============================================================ - - name: create VPC 1 - ec2_vpc_net: + - name: Create VPC 1 + amazon.aws.ec2_vpc_net: name: "{{ vpc_1_name }}" state: present cidr_block: "{{ vpc_1_cidr }}" @@ -37,13 +39,14 @@ Name: "{{ vpc_1_name }}" TestPrefex: "{{ resource_prefix }}" register: vpc_1_result + - name: Assert success assert: that: - vpc_1_result is successful - - name: create VPC 2 - ec2_vpc_net: + - name: Create VPC 2 + amazon.aws.ec2_vpc_net: name: "{{ vpc_2_name }}" state: present cidr_block: "{{ vpc_2_cidr }}" @@ -51,6 +54,7 @@ Name: "{{ vpc_2_name }}" TestPrefex: "{{ resource_prefix }}" register: vpc_2_result + - name: Assert success assert: that: @@ -62,11 +66,11 @@ vpc_2: '{{ vpc_2_result.vpc.id }}' - name: Set a name to use with the connections - set_fact: + ansible.builtin.set_fact: connection_name: 'Peering connection for VPC {{ vpc_1 }} to VPC {{ vpc_2 }}' - - name: Create local account VPC peering Connection request - ec2_vpc_peer: + - name: Create local account EC2 VPC Peering Connection request + community.aws.ec2_vpc_peer: vpc_id: '{{ vpc_1 }}' peer_vpc_id: '{{ vpc_2 }}' state: present @@ -84,11 +88,11 @@ - vpc_peer.peering_id.startswith('pcx-') - name: Store Connection ID - set_fact: + ansible.builtin.set_fact: peer_id_1: '{{ vpc_peer.peering_id }}' - - name: (re-) Create local account VPC peering Connection request (idempotency) - ec2_vpc_peer: + - name: Re-create local account EC2 VPC Peering Connection request (idempotency) + community.aws.ec2_vpc_peer: vpc_id: '{{ vpc_1 }}' peer_vpc_id: '{{ vpc_2 }}' state: present @@ -103,8 +107,8 @@ - vpc_peer is successful - vpc_peer.peering_id == peer_id_1 - - name: (re-) Create local account VPC peering Connection request with accepter/requester reversed (idempotency) - ec2_vpc_peer: + - name: Create local account EC2 VPC Peering Connection request with accepter/requester reversed (idempotency) + community.aws.ec2_vpc_peer: vpc_id: '{{ vpc_2 }}' peer_vpc_id: '{{ vpc_1 }}' state: present @@ -119,11 +123,12 @@ - vpc_peer is successful - vpc_peer.peering_id == peer_id_1 - - name: Get details on specific VPC peer - ec2_vpc_peering_info: + - name: Get details on specific EC2 VPC Peering Connection + community.aws.ec2_vpc_peering_info: peer_connection_ids: - '{{ peer_id_1 }}' register: peer_info + - name: Assert expected values assert: that: @@ -170,11 +175,12 @@ acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' requester_details: '{{ peer_details["requester_vpc_info"] }}' - - name: Get all vpc peers with specific filters - ec2_vpc_peering_info: + - name: Get all EC2 VPC Peering Connections with specific filters + community.aws.ec2_vpc_peering_info: filters: status-code: ['pending-acceptance'] register: pending_vpc_peers + - name: Assert expected values assert: that: @@ -210,8 +216,8 @@ acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' requester_details: '{{ peer_details["requester_vpc_info"] }}' - - name: Update tags on the VPC Peering Connection - ec2_vpc_peer: + - name: Update tags on the EC2 VPC Peering Connection + community.aws.ec2_vpc_peer: vpc_id: '{{ vpc_1 }}' peer_vpc_id: '{{ vpc_2 }}' state: present @@ -219,6 +225,7 @@ Name: '{{ connection_name }}' testPrefix: '{{ resource_prefix }}' register: tag_peer + - name: Assert success assert: that: @@ -226,8 +233,8 @@ - tag_peer is successful - tag_peer.peering_id == peer_id_1 - - name: (re-) Update tags on the VPC Peering Connection (idempotency) - ec2_vpc_peer: + - name: Update tags on the EC2 VPC Peering Connection (idempotency) + community.aws.ec2_vpc_peer: vpc_id: '{{ vpc_1 }}' peer_vpc_id: '{{ vpc_2 }}' state: present @@ -235,6 +242,7 @@ Name: '{{ connection_name }}' testPrefix: '{{ resource_prefix }}' register: tag_peer + - name: Assert success assert: that: @@ -242,11 +250,12 @@ - tag_peer is successful - tag_peer.peering_id == peer_id_1 - - name: Get details on specific VPC peer - ec2_vpc_peering_info: + - name: Get details on specific EC2 VPC Peering Connection + community.aws.ec2_vpc_peering_info: peer_connection_ids: - '{{ peer_id_1 }}' register: peer_info + - name: Assert expected tags assert: that: @@ -259,12 +268,13 @@ vars: peer_details: '{{ peer_info.vpc_peering_connections[0] }}' - - name: Accept local VPC peering request - ec2_vpc_peer: + - name: Accept local EC2 VPC Peering request + community.aws.ec2_vpc_peer: peering_id: "{{ vpc_peer.peering_id }}" state: accept wait: True register: action_peer + - name: Assert success assert: that: @@ -274,11 +284,12 @@ - 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 VPC peer - ec2_vpc_peering_info: + - name: Get details on specific EC2 VPC Peering Connection + community.aws.ec2_vpc_peering_info: peer_connection_ids: - '{{ peer_id_1 }}' register: peer_info + - name: Assert expected values assert: that: @@ -331,11 +342,12 @@ acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' requester_details: '{{ peer_details["requester_vpc_info"] }}' - - name: (re-) Accept local VPC peering request (idempotency) - ec2_vpc_peer: + - name: Accept local EC2 VPC Peering request (idempotency) + community.aws.ec2_vpc_peer: peering_id: "{{ vpc_peer.peering_id }}" state: accept register: action_peer + - name: Assert success assert: that: @@ -344,11 +356,12 @@ - action_peer.peering_id == peer_id_1 - action_peer.vpc_peering_connection.vpc_peering_connection_id == peer_id_1 - - name: delete a local VPC peering Connection - ec2_vpc_peer: + - name: Delete a local EC2 VPC Peering Connection + community.aws.ec2_vpc_peer: peering_id: "{{ vpc_peer.peering_id }}" state: absent register: delete_peer + - name: Assert success assert: that: @@ -356,11 +369,12 @@ - delete_peer is successful - "'peering_id' in delete_peer" - - name: Get details on specific VPC peer - ec2_vpc_peering_info: + - name: Get details on specific EC2 VPC Peering Connection + community.aws.ec2_vpc_peering_info: peer_connection_ids: - '{{ peer_id_1}}' register: peer_info + - name: Assert expected values assert: that: @@ -405,25 +419,27 @@ acceptor_details: '{{ peer_details["accepter_vpc_info"] }}' requester_details: '{{ peer_details["requester_vpc_info"] }}' - - name: (re-) delete a local VPC peering Connection (idempotency) - ec2_vpc_peer: + - name: Delete a local EC2 VPC Peering Connection (idempotency) + community.aws.ec2_vpc_peer: peering_id: "{{ vpc_peer.peering_id }}" state: absent register: delete_peer + - name: Assert success assert: that: - delete_peer is not changed - delete_peer is successful - - name: Create local account VPC peering Connection - ec2_vpc_peer: + - name: Create local account EC2 VPC Peering Connection + community.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 assert: that: @@ -433,15 +449,16 @@ - vpc_peer2.peering_id.startswith('pcx-') - name: Store Connection ID - set_fact: + ansible.builtin.set_fact: peer_id_2: '{{ vpc_peer2.peering_id }}' - - name: reject a local VPC peering Connection - ec2_vpc_peer: + - name: Reject a local EC2 VPC Peering Connection + community.aws.ec2_vpc_peer: peering_id: "{{ vpc_peer2.peering_id }}" state: reject wait: True register: reject_peer + - name: Assert success assert: that: @@ -449,11 +466,12 @@ - reject_peer is successful - reject_peer.peering_id == peer_id_2 - - name: (re-) reject a local VPC peering Connection - ec2_vpc_peer: + - name: Reject a local EC2 VPC Peering Connection (idempotency) + community.aws.ec2_vpc_peer: peering_id: "{{ vpc_peer2.peering_id }}" state: reject register: reject_peer + - name: Assert success assert: that: @@ -462,11 +480,12 @@ - reject_peer.peering_id == peer_id_2 - reject_peer.vpc_peering_connection.vpc_peering_connection_id == peer_id_2 - - name: delete a local VPC peering Connection - ec2_vpc_peer: + - name: Delete a local EC2 VPC Peering Connections + community.aws.ec2_vpc_peer: peering_id: "{{ vpc_peer2.peering_id }}" state: absent register: delete_peer + - name: Assert success assert: that: @@ -475,8 +494,8 @@ always: - - name: Find all VPC Peering connections for our VPCs - ec2_vpc_peering_info: + - name: Find all EC2 VPC Peering Connections for our VPCs + community.aws.ec2_vpc_peering_info: filters: accepter-vpc-info.vpc-id: '{{ item }}' register: peering_info @@ -484,7 +503,7 @@ - '{{ vpc_1 }}' - '{{ vpc_2 }}' - - set_fact: + - 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 }}' @@ -492,23 +511,19 @@ # ============================================================ - - name: Delete remaining Peering connections - ec2_vpc_peer: + - name: Delete remaining EC2 VPC Peering Connections + community.aws.ec2_vpc_peer: peering_id: "{{ item }}" state: absent ignore_errors: True loop: '{{ vpc_peering_connection_ids }}' - - name: tidy up VPC 2 - ec2_vpc_net: - name: "{{ vpc_2_name }}" + - name: Tidy up VPCs + amazon.aws.ec2_vpc_net: + name: "{{ item.name }}" state: absent - cidr_block: "{{ vpc_2_cidr }}" - ignore_errors: true - - - name: tidy up VPC 1 - ec2_vpc_net: - name: "{{ vpc_1_name }}" - state: absent - cidr_block: "{{ vpc_1_cidr }}" + cidr_block: "{{ item.cidr }}" ignore_errors: true + loop: + - { name: "{{ vpc_2_name }}", cidr: "{{ vpc_2_cidr }}"} + - { name: "{{ vpc_1_name }}", cidr: "{{ vpc_1_cidr }}"}