Repository: ansible-collections/amazon.aws

Issue:
aws_service_ip_ranges suppport for ipv6
### Summary

We are using amazon.aws collection and we noticed that the aws_service_ip_ranges does not have an option to return IPv6 ranges.

### Issue Type

Feature Idea

### Component Name

`{ lookup('aws_service_ip_ranges', region='us-west-2', service='ROUTE53_HEALTHCHECKS', ipv6_prefix=True, wantlist=True) }`
Should return a list of IPv6 addresses that correspond to the Route53 health check. 


### Pull Request
#430

### Additional Information

<!--- Paste example playbooks or commands between quotes below -->
```
vars:
  rt53_ranges: "{{ lookup('aws_service_ip_ranges', region='us-west-2', service='ROUTE53_HEALTHCHECKS', ipv6_prefix=True, wantlist=True) }}"
tasks:

- name: "use list return option and iterate as a loop"
  debug: msg="{% for x in rt53_ranges %}{{ x }} {% endfor %}"
#    ###"2600:1f14:7ff:f800::/56,2600:1f14:fff:f800::/56"
```



### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:

Resolving patch:
diff --git a/plugins/lookup/aws_service_ip_ranges.py b/plugins/lookup/aws_service_ip_ranges.py
--- a/plugins/lookup/aws_service_ip_ranges.py
+++ b/plugins/lookup/aws_service_ip_ranges.py
@@ -19,6 +19,9 @@
     description: 'The service to filter ranges by. Options: EC2, S3, CLOUDFRONT, CODEbUILD, ROUTE53, ROUTE53_HEALTHCHECKS'
   region:
     description: 'The AWS region to narrow the ranges to. Examples: us-east-1, eu-west-2, ap-southeast-1'
+  ipv6_prefixes:
+    description: 'When I(ipv6_prefixes=True) the lookup will return ipv6 addresses instead of ipv4 addresses'
+    version_added: 2.1.0
 '''
 
 EXAMPLES = """
@@ -40,7 +43,6 @@
   description: comma-separated list of CIDR ranges
 """
 
-
 import json
 
 from ansible.errors import AnsibleError
@@ -55,9 +57,16 @@
 
 class LookupModule(LookupBase):
     def run(self, terms, variables, **kwargs):
+        if "ipv6_prefixes" in kwargs and kwargs["ipv6_prefixes"]:
+            prefixes_label = "ipv6_prefixes"
+            ip_prefix_label = "ipv6_prefix"
+        else:
+            prefixes_label = "prefixes"
+            ip_prefix_label = "ip_prefix"
+
         try:
             resp = open_url('https://ip-ranges.amazonaws.com/ip-ranges.json')
-            amazon_response = json.load(resp)['prefixes']
+            amazon_response = json.load(resp)[prefixes_label]
         except getattr(json.decoder, 'JSONDecodeError', ValueError) as e:
             # on Python 3+, json.decoder.JSONDecodeError is raised for bad
             # JSON. On 2.x it's a ValueError
@@ -77,5 +86,5 @@ def run(self, terms, variables, **kwargs):
         if 'service' in kwargs:
             service = str.upper(kwargs['service'])
             amazon_response = (item for item in amazon_response if item['service'] == service)
-
-        return [item['ip_prefix'] for item in amazon_response]
+        iprange = [item[ip_prefix_label] for item in amazon_response]
+        return iprange


Test patch:
diff --git a/tests/integration/targets/lookup_aws_service_ip_ranges/tasks/main.yaml b/tests/integration/targets/lookup_aws_service_ip_ranges/tasks/main.yaml
--- a/tests/integration/targets/lookup_aws_service_ip_ranges/tasks/main.yaml
+++ b/tests/integration/targets/lookup_aws_service_ip_ranges/tasks/main.yaml
@@ -11,6 +11,7 @@
 - name: lookup range with wantlist
   set_fact:
     want_list: "{{ lookup('amazon.aws.aws_service_ip_ranges', wantlist=True) }}"
+    want_ipv6_list: "{{ lookup('amazon.aws.aws_service_ip_ranges', wantlist=True, ipv6_prefixes=True) }}"
 
 - name: assert that we're returned a list
   assert:
@@ -20,10 +21,17 @@
       - want_list is not string
       - want_list | length > 1
       - want_list[0] | ansible.netcommon.ipv4
+      - want_ipv6_list is defined
+      - want_ipv6_list is iterable
+      - want_ipv6_list is not string
+      - want_ipv6_list | length > 1
+      - want_ipv6_list[0] | ansible.netcommon.ipv6
+
 
 - name: lookup range with service
   set_fact:
     s3_ips: "{{ lookup('amazon.aws.aws_service_ip_ranges', service='S3', wantlist=True) }}"
+    s3_ipv6s: "{{ lookup('amazon.aws.aws_service_ip_ranges', service='S3', wantlist=True, ipv6_prefixes=True) }}"
 
 - name: assert that we're returned a list
   assert:
@@ -33,10 +41,16 @@
       - s3_ips is not string
       - s3_ips | length > 1
       - s3_ips[0] | ansible.netcommon.ipv4
+      - s3_ipv6s is defined
+      - s3_ipv6s is iterable
+      - s3_ipv6s is not string
+      - s3_ipv6s | length > 1
+      - s3_ipv6s[0] | ansible.netcommon.ipv6
 
 - name: lookup range with a different service
   set_fact:
     route53_ips: "{{ lookup('amazon.aws.aws_service_ip_ranges', service='ROUTE53_HEALTHCHECKS', wantlist=True) }}"
+    route53_ipv6s: "{{ lookup('amazon.aws.aws_service_ip_ranges', service='ROUTE53_HEALTHCHECKS', wantlist=True, ipv6_prefixes=True) }}"
 
 - name: assert that we're returned a list
   assert:
@@ -46,16 +60,27 @@
       - route53_ips is not string
       - route53_ips | length > 1
       - route53_ips[0] | ansible.netcommon.ipv4
+      - route53_ipv6s is defined
+      - route53_ipv6s is iterable
+      - route53_ipv6s is not string
+      - route53_ipv6s | length > 1
+      - route53_ipv6s[0] | ansible.netcommon.ipv6
+
 
-- name: assert that service IPs don't overlap
+- name: assert that service IPV4s and IPV6s do not overlap
   assert:
     that:
       - route53_ips | intersect(s3_ips) | length == 0
+      - route53_ipv6s | intersect(s3_ipv6s) | length == 0
 
 - name: lookup range with region
   set_fact:
     us_east_1_ips: "{{ lookup('amazon.aws.aws_service_ip_ranges', region='us-east-1', wantlist=True) }}"
 
+- name: lookup IPV6 range with region
+  set_fact:
+    us_east_1_ipv6s: "{{ lookup('amazon.aws.aws_service_ip_ranges', region='us-east-1', wantlist=True, ipv6_prefixes=True) }}"
+
 - name: assert that we're returned a list
   assert:
     that:
@@ -64,10 +89,16 @@
       - us_east_1_ips is not string
       - us_east_1_ips | length > 1
       - us_east_1_ips[0] | ansible.netcommon.ipv4
+      - us_east_1_ipv6s is defined
+      - us_east_1_ipv6s is iterable
+      - us_east_1_ipv6s is not string
+      - us_east_1_ipv6s | length > 1
+      - us_east_1_ipv6s[0] | ansible.netcommon.ipv6
 
 - name: lookup range with a different region
   set_fact:
     eu_central_1_ips: "{{ lookup('amazon.aws.aws_service_ip_ranges', region='eu-central-1', wantlist=True) }}"
+    eu_central_1_ipv6s: "{{ lookup('amazon.aws.aws_service_ip_ranges', region='eu-central-1', wantlist=True, ipv6_prefixes=True) }}"
 
 - name: assert that we're returned a list
   assert:
@@ -77,15 +108,22 @@
       - eu_central_1_ips is not string
       - eu_central_1_ips | length > 1
       - eu_central_1_ips[0] | ansible.netcommon.ipv4
+      - eu_central_1_ipv6s is defined
+      - eu_central_1_ipv6s is iterable
+      - eu_central_1_ipv6s is not string
+      - eu_central_1_ipv6s | length > 1
+      - eu_central_1_ipv6s[0] | ansible.netcommon.ipv6
 
 - name: assert that regional IPs don't overlap
   assert:
     that:
       - eu_central_1_ips | intersect(us_east_1_ips) | length == 0
+      - eu_central_1_ipv6s | intersect(us_east_1_ipv6s) | length == 0
 
 - name: lookup range with service and region
   set_fact:
     s3_us_ips: "{{ lookup('amazon.aws.aws_service_ip_ranges', region='us-east-1', service='S3', wantlist=True) }}"
+    s3_us_ipv6s: "{{ lookup('amazon.aws.aws_service_ip_ranges', region='us-east-1', service='S3', wantlist=True, ipv6_prefixes=True) }}"
 
 - name: assert that we're returned a list
   assert:
@@ -95,9 +133,16 @@
       - s3_us_ips is not string
       - s3_us_ips | length > 1
       - s3_us_ips[0] | ansible.netcommon.ipv4
+      - s3_us_ipv6s is defined
+      - s3_us_ipv6s is iterable
+      - s3_us_ipv6s is not string
+      - s3_us_ipv6s | length > 1
+      - s3_us_ipv6s[0] | ansible.netcommon.ipv6
 
 - name: assert that the regional service IPs are a subset of the regional IPs and service IPs.
   assert:
     that:
       - ( s3_us_ips | intersect(us_east_1_ips) | length ) == ( s3_us_ips | length )
       - ( s3_us_ips | intersect(s3_ips) | length ) == ( s3_us_ips | length )
+      - ( s3_us_ipv6s | intersect(us_east_1_ipv6s) | length ) == ( s3_us_ipv6s | length )
+      - ( s3_us_ipv6s | intersect(s3_ipv6s) | length ) == ( s3_us_ipv6s | length )