Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return all infos of a VPC peering connection in ec2_vpc_peer module #355

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/355-ec2_vpc_peer_improvements.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- ec2_vpc_peer - More return info added, also simplified module code a bit and extended tests (https://github.com/ansible-collections/community.aws/pull/355)
221 changes: 182 additions & 39 deletions plugins/modules/ec2_vpc_peer.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,144 @@

'''
RETURN = '''
task:
description: The result of the create, accept, reject or delete action.
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 as returned by Boto3 (snake cased).
returned: success
type: dict
type: complex
contains:
accepter_vpc_info:
description: Information about the VPC which accepted the connection.
returned: success
type: complex
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'
cidr_block_set:
description: A list of all CIDRs for the VPC.
returned: when connection is in the accepted state.
type: complex
contains:
cidr_block:
description: A CIDR block used by the VPC.
returned: success
type: str
example: '10.10.10.0/23'
owner_id:
description: The AWS account that owns the VPC.
returned: success
type: str
example: 012345678901
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
example: us-east-1
vpc_id:
description: The ID of the VPC
returned: success
type: str
example: vpc-0123456789abcdef0
requester_vpc_info:
description: Information about the VPC which requested the connection.
returned: success
type: complex
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'
cidr_block_set:
description: A list of all CIDRs for the VPC.
returned: when connection is not in the deleted state.
type: complex
contains:
cidr_block:
description: A CIDR block used by the VPC
returned: success
type: str
example: '10.10.10.0/23'
owner_id:
description: The AWS account that owns the VPC.
returned: success
type: str
example: 012345678901
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
example: us-east-1
vpc_id:
description: The ID of the VPC
returned: success
type: str
example: 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
example: active
message:
description: Additional information about the status of the connection.
returned: success
type: str
example: Pending Acceptance by 012345678901
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
example: "pcx-0123456789abcdef0"
'''

try:
Expand All @@ -231,6 +365,8 @@
from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict


def wait_for_state(client, module, state, pcx_id):
Expand All @@ -254,9 +390,9 @@ def tags_changed(pcx_id, client, module):
tags = dict()
if module.params.get('tags'):
tags = module.params.get('tags')
pcx = find_pcx_by_id(pcx_id, client, module)
if pcx['VpcPeeringConnections']:
pcx_values = [t.values() for t in pcx['VpcPeeringConnections'][0]['Tags']]
peering_connection = get_peering_connection_by_id(pcx_id, client, module)
if peering_connection['Tags']:
pcx_values = [t.values() for t in peering_connection['Tags']]
pcx_tags = [item for sublist in pcx_values for item in sublist]
tag_values = [[key, str(value)] for key, value in tags.items()]
tags = [item for sublist in tag_values for item in sublist]
Expand All @@ -283,6 +419,7 @@ def describe_peering_connections(params, client):
aws_retry=True,
Filters=ansible_dict_to_boto3_filter_list(peer_filter),
)

return result


Expand Down Expand Up @@ -311,9 +448,9 @@ def create_peer_connection(client, module):
if tags_changed(pcx_id, client, module):
changed = True
if is_active(peering_conn):
return (changed, peering_conn['VpcPeeringConnectionId'])
return (changed, peering_conn)
if is_pending(peering_conn):
return (changed, peering_conn['VpcPeeringConnectionId'])
return (changed, peering_conn)
try:
peering_conn = client.create_vpc_peering_connection(aws_retry=True, **params)
pcx_id = peering_conn['VpcPeeringConnection']['VpcPeeringConnectionId']
Expand All @@ -322,51 +459,55 @@ def create_peer_connection(client, module):
if module.params.get('tags'):
create_tags(pcx_id, client, module)
changed = True
return (changed, peering_conn['VpcPeeringConnection']['VpcPeeringConnectionId'])
return (changed, peering_conn['VpcPeeringConnection'])
except botocore.exceptions.ClientError as e:
module.fail_json(msg=str(e))


def remove_peer_connection(client, module):
pcx_id = module.params.get('peering_id')
if pcx_id:
peering_conns = client.describe_vpc_peering_connections(aws_retry=True, VpcPeeringConnectionIds=[pcx_id])
peering_conn = get_peering_connection_by_id(pcx_id, client, module)
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'] = str(module.params.get('peer_owner_id'))
peering_conns = describe_peering_connections(params, client)
peering_conn = describe_peering_connections(params, client)['VpcPeeringConnections'][0]

if not peering_conns:
if not peering_conn:
module.exit_json(changed=False)
else:
pcx_id = pcx_id or peering_conns['VpcPeeringConnections'][0]['VpcPeeringConnectionId']

if peering_conns['VpcPeeringConnections'][0]['Status']['Code'] == 'deleted':
module.exit_json(msg='Connection in deleted state.', changed=False)
if peering_conns['VpcPeeringConnections'][0]['Status']['Code'] == 'rejected':
module.exit_json(msg='Connection has been rejected. State cannot be changed and will be removed automatically by AWS', changed=False)
pcx_id = pcx_id or peering_conn['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':
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
)

try:
params = dict()
params['VpcPeeringConnectionId'] = pcx_id
client.delete_vpc_peering_connection(aws_retry=True, **params)
if module.params.get('wait'):
wait_for_state(client, module, 'deleted', pcx_id)
module.exit_json(changed=True)
module.exit_json(changed=True, peering_id=pcx_id)
except botocore.exceptions.ClientError as e:
module.fail_json(msg=str(e))


def peer_status(client, module):
def get_peering_connection_by_id(peering_id, client, module):
params = dict()
params['VpcPeeringConnectionIds'] = [module.params.get('peering_id')]
params['VpcPeeringConnectionIds'] = [peering_id]
try:
vpc_peering_connection = client.describe_vpc_peering_connections(aws_retry=True, **params)
return vpc_peering_connection['VpcPeeringConnections'][0]['Status']['Code']
return vpc_peering_connection['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
Expand All @@ -376,10 +517,12 @@ def peer_status(client, module):
def accept_reject(state, client, module):
changed = False
params = dict()
pcx_id = module.params.get('peering_id')
params['VpcPeeringConnectionId'] = pcx_id
current_state = peer_status(client, module)
if current_state not in ['active', 'rejected']:
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']

if peering_status not in ['active', 'rejected']:
try:
if state == 'accept':
client.accept_vpc_peering_connection(aws_retry=True, **params)
Expand All @@ -388,15 +531,18 @@ def accept_reject(state, client, module):
client.reject_vpc_peering_connection(aws_retry=True, **params)
target_state = 'rejected'
if module.params.get('tags'):
create_tags(params['VpcPeeringConnectionId'], client, module)
create_tags(peering_id, client, module)
changed = True
if module.params.get('wait'):
wait_for_state(client, module, target_state, pcx_id)
wait_for_state(client, module, target_state, peering_id)
except botocore.exceptions.ClientError as e:
module.fail_json(msg=str(e))
if tags_changed(params['VpcPeeringConnectionId'], client, module):
if tags_changed(peering_id, client, module):
changed = True
return changed, params['VpcPeeringConnectionId']

# 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 load_tags(module):
Expand All @@ -422,13 +568,6 @@ def delete_tags(pcx_id, client, module):
module.fail_json(msg=str(e))


def find_pcx_by_id(pcx_id, client, module):
try:
return client.describe_vpc_peering_connections(aws_retry=True, VpcPeeringConnectionIds=[pcx_id])
except botocore.exceptions.ClientError as e:
module.fail_json(msg=str(e))


def main():
argument_spec = dict(
vpc_id=dict(),
Expand Down Expand Up @@ -460,15 +599,19 @@ def main():

if state == 'present':
(changed, results) = create_peer_connection(client, module)
module.exit_json(changed=changed, peering_id=results)
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)
else:
(changed, results) = accept_reject(state, client, module)
module.exit_json(changed=changed, peering_id=results)

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__':
Expand Down
Loading