diff --git a/plugins/modules/ec2_vpc_endpoint_info.py b/plugins/modules/ec2_vpc_endpoint_info.py index 7e259c6ca8e..7706a00c915 100644 --- a/plugins/modules/ec2_vpc_endpoint_info.py +++ b/plugins/modules/ec2_vpc_endpoint_info.py @@ -10,22 +10,26 @@ short_description: Retrieves AWS VPC endpoints details using AWS methods. version_added: 1.0.0 description: - - Gets various details related to AWS VPC Endpoints. + - Gets various details related to AWS VPC endpoints. - This module was called C(ec2_vpc_endpoint_facts) before Ansible 2.9. The usage did not change. requirements: [ boto3 ] options: query: description: - - Specifies the query action to take. Services returns the supported - AWS services that can be specified when creating an endpoint. - required: True + - Defaults to C(endpoints). + - Specifies the query action to take. + - I(query=endpoints) returns information about AWS VPC endpoints. + - Retrieving information about services using I(query=services) has been + deprecated in favour of the M(ec2_vpc_endpoint_service_info) module. + - The I(query) option has been deprecated and will be removed after 2022-12-01. + required: False choices: - services - endpoints type: str vpc_endpoint_ids: description: - - Get details of specific endpoint IDs + - The IDs of specific endpoints to retrieve the details of. type: list elements: str filters: @@ -161,7 +165,7 @@ def get_endpoints(client, module): def main(): argument_spec = dict( - query=dict(choices=['services', 'endpoints'], required=True), + query=dict(choices=['services', 'endpoints'], required=False), filters=dict(default={}, type='dict'), vpc_endpoint_ids=dict(type='list', elements='str'), ) @@ -176,11 +180,29 @@ def main(): except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Failed to connect to AWS') + query = module.params.get('query') + if query == 'endpoints': + module.deprecate('The query option has been deprecated and' + ' will be removed after 2022-12-01. Searching for' + ' `endpoints` is now the default and after' + ' 2022-12-01 this module will only support fetching' + ' endpoints.', + date='2022-12-01', collection_name='community.aws') + elif query == 'services': + module.deprecate('Support for fetching service information with this ' + 'module has been deprecated and will be removed after' + ' 2022-12-01. ' + 'Please use the ec2_vpc_endpoint_service_info module ' + 'instead.', date='2022-12-01', + collection_name='community.aws') + else: + query = 'endpoints' + invocations = { 'services': get_supported_services, 'endpoints': get_endpoints, } - results = invocations[module.params.get('query')](connection, module) + results = invocations[query](connection, module) module.exit_json(**results) diff --git a/plugins/modules/ec2_vpc_endpoint_service_info.py b/plugins/modules/ec2_vpc_endpoint_service_info.py new file mode 100644 index 00000000000..2afd0e5e906 --- /dev/null +++ b/plugins/modules/ec2_vpc_endpoint_service_info.py @@ -0,0 +1,180 @@ +#!/usr/bin/python +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +module: ec2_vpc_endpoint_service_info +short_description: retrieves AWS VPC endpoint service details +version_added: 1.5.0 +description: + - Gets details related to AWS VPC Endpoint Services. +requirements: [ boto3 ] +options: + 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_DescribeVpcEndpointServices.html) + for possible filters. + type: dict + service_names: + description: + - A list of service names which can be used to narrow the search results. + type: list + elements: str +author: + - Mark Chappell (@tremble) +extends_documentation_fragment: + - amazon.aws.aws + - amazon.aws.ec2 + +''' + +EXAMPLES = r''' +# Simple example of listing all supported AWS services for VPC endpoints +- name: List supported AWS endpoint services + community.aws.ec2_vpc_endpoint_service_info: + region: ap-southeast-2 + register: supported_endpoint_services +''' + +RETURN = r''' +service_names: + description: List of supported AWS VPC endpoint service names. + returned: success + type: list + sample: + service_names: + - com.amazonaws.ap-southeast-2.s3 +service_details: + description: Detailed information about the AWS VPC endpoint services. + returned: success + type: complex + contains: + service_name: + returned: success + description: The ARN of the endpoint service. + type: str + service_id: + returned: success + description: The ID of the endpoint service. + type: str + service_type: + returned: success + description: The type of the service + type: list + availability_zones: + returned: success + description: The Availability Zones in which the service is available. + type: list + owner: + returned: success + description: The AWS account ID of the service owner. + type: str + base_endpoint_dns_names: + returned: success + description: The DNS names for the service. + type: list + private_dns_name: + returned: success + description: The private DNS name for the service. + type: str + private_dns_names: + returned: success + description: The private DNS names assigned to the VPC endpoint service. + type: list + vpc_endpoint_policy_supported: + returned: success + description: Whether the service supports endpoint policies. + type: bool + acceptance_required: + returned: success + description: + Whether VPC endpoint connection requests to the service must be + accepted by the service owner. + type: bool + manages_vpc_endpoints: + returned: success + description: Whether the service manages its VPC endpoints. + type: bool + tags: + returned: success + description: A dict of tags associated with the service + type: dict + private_dns_name_verification_state: + returned: success + description: + - The verification state of the VPC endpoint service. + - Consumers of an endpoint service cannot use the private name when the state is not C(verified). + type: str +''' + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +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 AWSRetry + + +# We're using a paginator so we can't use the client decorators +@AWSRetry.jittered_backoff() +def get_services(client, module): + paginator = client.get_paginator('describe_vpc_endpoint_services') + params = {} + if module.params.get("filters"): + params['Filters'] = ansible_dict_to_boto3_filter_list(module.params.get("filters")) + + if module.params.get("service_names"): + params['ServiceNames'] = module.params.get("service_names") + + results = paginator.paginate(**params).build_full_result() + return results + + +def normalize_service(service): + normalized = camel_dict_to_snake_dict(service, ignore_list=['Tags']) + normalized["tags"] = boto3_tag_list_to_ansible_dict(service.get('Tags')) + return normalized + + +def normalize_result(result): + normalized = {} + normalized['service_details'] = [normalize_service(service) for service in result.get('ServiceDetails')] + normalized['service_names'] = result.get('ServiceNames', []) + return normalized + + +def main(): + argument_spec = dict( + filters=dict(default={}, type='dict'), + service_names=dict(type='list', elements='str'), + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + + # Validate Requirements + try: + client = module.client('ec2') + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to connect to AWS') + + try: + results = get_services(client, module) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to connect to retrieve service details') + normalized_result = normalize_result(results) + + module.exit_json(changed=False, **normalized_result) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/ec2_vpc_endpoint_service_info/aliases b/tests/integration/targets/ec2_vpc_endpoint_service_info/aliases new file mode 100644 index 00000000000..bf2988bbc2e --- /dev/null +++ b/tests/integration/targets/ec2_vpc_endpoint_service_info/aliases @@ -0,0 +1,3 @@ +cloud/aws +shippable/aws/group2 +ec2_vpc_endpoint_service_info diff --git a/tests/integration/targets/ec2_vpc_endpoint_service_info/defaults/main.yml b/tests/integration/targets/ec2_vpc_endpoint_service_info/defaults/main.yml new file mode 100644 index 00000000000..445cc7f3c52 --- /dev/null +++ b/tests/integration/targets/ec2_vpc_endpoint_service_info/defaults/main.yml @@ -0,0 +1,3 @@ +search_service_names: +- 'com.amazonaws.{{ aws_region }}.s3' +- 'com.amazonaws.{{ aws_region }}.ec2' diff --git a/tests/integration/targets/ec2_vpc_endpoint_service_info/meta/main.yml b/tests/integration/targets/ec2_vpc_endpoint_service_info/meta/main.yml new file mode 100644 index 00000000000..1810d4bec98 --- /dev/null +++ b/tests/integration/targets/ec2_vpc_endpoint_service_info/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_remote_tmp_dir diff --git a/tests/integration/targets/ec2_vpc_endpoint_service_info/tasks/main.yml b/tests/integration/targets/ec2_vpc_endpoint_service_info/tasks/main.yml new file mode 100644 index 00000000000..40eb0a88924 --- /dev/null +++ b/tests/integration/targets/ec2_vpc_endpoint_service_info/tasks/main.yml @@ -0,0 +1,139 @@ +--- +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + # Needed until added to the group in Ansible 2.9 + ec2_vpc_endpoint_service_info: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + + block: + + - name: 'List all available services (Check Mode)' + ec2_vpc_endpoint_service_info: + check_mode: True + register: services_check + + - name: 'Verify services (Check Mode)' + vars: + first_service: '{{ services_check.service_details[0] }}' + assert: + that: + - services_check is successful + - services_check is not changed + - '"service_names" in services_check' + - '"service_details" in services_check' + - '"acceptance_required" in first_service' + - '"availability_zones" in first_service' + - '"base_endpoint_dns_names" in first_service' + - '"manages_vpc_endpoints" in first_service' + - '"owner" in first_service' + - '"private_dns_name" in first_service' + - '"private_dns_name_verification_state" in first_service' + - '"service_id" in first_service' + - '"service_name" in first_service' + - '"service_type" in first_service' + - '"tags" in first_service' + - '"vpc_endpoint_policy_supported" in first_service' + + - name: 'List all available services' + ec2_vpc_endpoint_service_info: + register: services_info + + - name: 'Verify services' + vars: + first_service: '{{ services_info.service_details[0] }}' + assert: + that: + - services_info is successful + - services_info is not changed + - '"service_names" in services_info' + - '"service_details" in services_info' + - '"acceptance_required" in first_service' + - '"availability_zones" in first_service' + - '"base_endpoint_dns_names" in first_service' + - '"manages_vpc_endpoints" in first_service' + - '"owner" in first_service' + - '"private_dns_name" in first_service' + - '"private_dns_name_verification_state" in first_service' + - '"service_id" in first_service' + - '"service_name" in first_service' + - '"service_type" in first_service' + - '"tags" in first_service' + - '"vpc_endpoint_policy_supported" in first_service' + + - name: 'Limit services by name' + ec2_vpc_endpoint_service_info: + service_names: '{{ search_service_names }}' + register: services_info + + - name: 'Verify services' + vars: + first_service: '{{ services_info.service_details[0] }}' + # The same service sometimes pop up twice. s3 for example has + # s3.us-east-1.amazonaws.com and s3.us-east-1.vpce.amazonaws.com which are + # part of com.amazonaws.us-east-1.s3 so we need to run the results through + # the unique filter to know if we've got what we think we have + unique_names: '{{ services_info.service_names | unique | list }}' + unique_detail_names: '{{ services_info.service_details | map(attribute="service_name") | unique | list }}' + assert: + that: + - services_info is successful + - services_info is not changed + - '"service_names" in services_info' + - (unique_names | length) == (search_service_names | length) + - (unique_detail_names | length ) == (search_service_names | length) + - (unique_names | difference(search_service_names) | length) == 0 + - (unique_detail_names | difference(search_service_names) | length) == 0 + - '"service_details" in services_info' + - '"acceptance_required" in first_service' + - '"availability_zones" in first_service' + - '"base_endpoint_dns_names" in first_service' + - '"manages_vpc_endpoints" in first_service' + - '"owner" in first_service' + - '"private_dns_name" in first_service' + - '"private_dns_name_verification_state" in first_service' + - '"service_id" in first_service' + - '"service_name" in first_service' + - '"service_type" in first_service' + - '"tags" in first_service' + - '"vpc_endpoint_policy_supported" in first_service' + + - name: 'Grab single service details to test filters' + set_fact: + example_service: '{{ services_info.service_details[0] }}' + + - name: 'Limit services by filter' + ec2_vpc_endpoint_service_info: + filters: + service-name: '{{ example_service.service_name }}' + register: filtered_service + + - name: 'Verify services' + vars: + first_service: '{{ filtered_service.service_details[0] }}' + assert: + that: + - filtered_service is successful + - filtered_service is not changed + - '"service_names" in filtered_service' + - filtered_service.service_names | length == 1 + - '"service_details" in filtered_service' + - filtered_service.service_details | length == 1 + - '"acceptance_required" in first_service' + - '"availability_zones" in first_service' + - '"base_endpoint_dns_names" in first_service' + - '"manages_vpc_endpoints" in first_service' + - '"owner" in first_service' + - '"private_dns_name" in first_service' + - '"private_dns_name_verification_state" in first_service' + - '"service_id" in first_service' + - '"service_name" in first_service' + - '"service_type" in first_service' + - '"tags" in first_service' + - '"vpc_endpoint_policy_supported" in first_service'