From 106b64f01858b9e45be07c263a7d372d8f1bb08c Mon Sep 17 00:00:00 2001 From: Mandar Kulkarni Date: Tue, 10 May 2022 13:22:25 -0700 Subject: [PATCH] route53: add support for GeoLocation parameter (#1117) route53: add support for GeoLocation parameter SUMMARY Added support for GeoLocation parameter to community.aws.route53 Fixes #89. ISSUE TYPE Feature Pull Request COMPONENT NAME route53 ADDITIONAL INFORMATION Uses https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53.html#Route53.Client.change_resource_record_sets Reviewed-by: Joseph Torcasso Reviewed-by: Mandar Kulkarni Reviewed-by: Sloane Hertel Reviewed-by: Alina Buzachis (cherry picked from commit 29e51f9ef07f331f451b2b66368afdbc274b25cc) --- .../1117-route53-add_geo_location_support.yml | 2 + plugins/modules/route53.py | 91 ++++- .../targets/route53/tasks/main.yml | 311 +++++++++++++++++- 3 files changed, 399 insertions(+), 5 deletions(-) create mode 100644 changelogs/fragments/1117-route53-add_geo_location_support.yml diff --git a/changelogs/fragments/1117-route53-add_geo_location_support.yml b/changelogs/fragments/1117-route53-add_geo_location_support.yml new file mode 100644 index 00000000000..8dfa4399f7d --- /dev/null +++ b/changelogs/fragments/1117-route53-add_geo_location_support.yml @@ -0,0 +1,2 @@ +minor_changes: + - route53 - add support for GeoLocation param (https://github.com/ansible-collections/amazon.aws/pull/1117). diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 4275d65b684..849194d5aff 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -108,6 +108,30 @@ latency-based routing - Mutually exclusive with I(weight) and I(failover). type: str + geo_location: + description: + - Allows to control how Amazon Route 53 responds to DNS queries based on the geographic origin of the query. + - Two geolocation resource record sets that specify same geographic location cannot be created. + - Non-geolocation resource record sets that have the same values for the Name and Type elements as geolocation + resource record sets cannot be created. + suboptions: + continent_code: + description: + - The two-letter code for the continent. + - Specifying I(continent_code) with either I(country_code) or I(subdivision_code) returns an InvalidInput error. + type: str + country_code: + description: + - The two-letter code for a country. + - Amazon Route 53 uses the two-letter country codes that are specified in ISO standard 3166-1 alpha-2 . + type: str + subdivision_code: + description: + - The two-letter code for a state of the United States. + - To specify I(subdivision_code), I(country_code) must be set to C(US). + type: str + type: dict + version_added: 3.3.0 health_check: description: - Health check to associate with this record @@ -166,6 +190,12 @@ returned: always type: str sample: PRIMARY + geo_location: + description: geograpic location based on which Route53 resonds to DNS queries. + returned: when configured + type: dict + sample: { continent_code: "NA", country_code: "US", subdivision_code: "CA" } + version_added: 3.3.0 health_check: description: health_check associated with this record. returned: always @@ -350,6 +380,29 @@ - 0 issue "ca.example.net" - 0 issuewild ";" - 0 iodef "mailto:security@example.com" +- name: Create a record with geo_location - country_code + community.aws.route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test.{{ zone_one }}' + identifier: "geohost@www" + type: A + value: 1.1.1.1 + ttl: 30 + geo_location: + country_code: US +- name: Create a record with geo_location - subdivision code + community.aws.route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test.{{ zone_one }}' + identifier: "geohost@www" + type: A + value: 1.1.1.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX ''' from operator import itemgetter @@ -495,6 +548,12 @@ def main(): identifier=dict(type='str'), weight=dict(type='int'), region=dict(type='str'), + geo_location=dict(type='dict', + options=dict( + continent_code=dict(type="str"), + country_code=dict(type="str"), + subdivision_code=dict(type="str")), + required=False), health_check=dict(type='str'), failover=dict(type='str', choices=['PRIMARY', 'SECONDARY']), vpc_id=dict(type='str'), @@ -518,11 +577,12 @@ def main(): ('failover', 'region', 'weight'), ('alias', 'ttl'), ], - # failover, region and weight require identifier + # failover, region, weight and geo_location require identifier required_by=dict( failover=('identifier',), region=('identifier',), weight=('identifier',), + geo_location=('identifier'), ), ) @@ -557,6 +617,7 @@ def main(): vpc_id_in = module.params.get('vpc_id') wait_in = module.params.get('wait') wait_timeout_in = module.params.get('wait_timeout') + geo_location = module.params.get('geo_location') if zone_in[-1:] != '.': zone_in += "." @@ -567,8 +628,8 @@ def main(): if command_in == 'create' or command_in == 'delete': if alias_in and len(value_in) != 1: module.fail_json(msg="parameter 'value' must contain a single dns name for alias records") - if (weight_in is None and region_in is None and failover_in is None) and identifier_in is not None: - module.fail_json(msg="You have specified identifier which makes sense only if you specify one of: weight, region or failover.") + if not any([weight_in, region_in, failover_in, geo_location]) and identifier_in is not None: + module.fail_json(msg="You have specified identifier which makes sense only if you specify one of: weight, region, geo_location or failover.") retry_decorator = AWSRetry.jittered_backoff( retries=MAX_AWS_RETRIES, @@ -604,6 +665,30 @@ def main(): 'HealthCheckId': health_check_in, 'SetIdentifier': identifier_in, }) + + if geo_location: + continent_code = geo_location.get('continent_code') + country_code = geo_location.get('country_code') + subdivision_code = geo_location.get('subdivision_code') + + if continent_code and (country_code or subdivision_code): + module.fail_json(changed=False, msg='While using geo_location, continent_code is mutually exclusive with country_code and subdivision_code.') + + if not any([continent_code, country_code, subdivision_code]): + module.fail_json(changed=False, msg='To use geo_location please specify either continent_code, country_code, or subdivision_code.') + + if geo_location.get('subdivision_code') and geo_location.get('country_code').lower() != 'us': + module.fail_json(changed=False, msg='To use subdivision_code, you must specify country_code as US.') + + # Build geo_location suboptions specification + resource_record_set['GeoLocation'] = {} + if continent_code: + resource_record_set['GeoLocation']['ContinentCode'] = continent_code + if country_code: + resource_record_set['GeoLocation']['CountryCode'] = country_code + if subdivision_code: + resource_record_set['GeoLocation']['SubdivisionCode'] = subdivision_code + if command_in == 'delete' and aws_record is not None: resource_record_set['TTL'] = aws_record.get('TTL') if not resource_record_set['ResourceRecords']: diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index 92f8601e64a..063e279f1cf 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -635,7 +635,314 @@ - weighted_record is not failed - weighted_record is not changed +#Test Geo Location - Continent Code + - name: Create a record with geo_location - continent_code (check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-1.{{ zone_one }}' + identifier: "geohost1@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + continent_code: NA + check_mode: true + register: create_geo_continent_check_mode + - assert: + that: + - create_geo_continent_check_mode is changed + - create_geo_continent_check_mode is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_continent_check_mode.resource_actions' + + - name: Create a record with geo_location - continent_code + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-1.{{ zone_one }}' + identifier: "geohost1@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + continent_code: NA + register: create_geo_continent + # Get resulting A record and geo_location parameters are applied + - name: 'get Route53 A record information' + route53_info: + type: A + query: record_sets + hosted_zone_id: '{{ z1.zone_id }}' + start_record_name: 'geo-test-1.{{ zone_one }}' + max_items: 1 + register: result + + - assert: + that: + - create_geo_continent is changed + - create_geo_continent is not failed + - '"route53:ChangeResourceRecordSets" in create_geo_continent.resource_actions' + - result.ResourceRecordSets[0].GeoLocation.ContinentCode == "NA" + + - name: Create a record with geo_location - continent_code (idempotency) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-1.{{ zone_one }}' + identifier: "geohost1@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + continent_code: NA + register: create_geo_continent_idem + - assert: + that: + - create_geo_continent_idem is not changed + - create_geo_continent_idem is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_continent_idem.resource_actions' + + - name: Create a record with geo_location - continent_code (idempotency - check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-1.{{ zone_one }}' + identifier: "geohost1@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + continent_code: NA + check_mode: true + register: create_geo_continent_idem_check + + - assert: + that: + - create_geo_continent_idem_check is not changed + - create_geo_continent_idem_check is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_continent_idem_check.resource_actions' + +#Test Geo Location - Country Code + - name: Create a record with geo_location - country_code (check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-2.{{ zone_one }}' + identifier: "geohost2@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + check_mode: true + register: create_geo_country_check_mode + - assert: + that: + - create_geo_country_check_mode is changed + - create_geo_country_check_mode is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_country_check_mode.resource_actions' + + - name: Create a record with geo_location - country_code + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-2.{{ zone_one }}' + identifier: "geohost2@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + register: create_geo_country + # Get resulting A record and geo_location parameters are applied + - name: 'get Route53 A record information' + route53_info: + type: A + query: record_sets + hosted_zone_id: '{{ z1.zone_id }}' + start_record_name: 'geo-test-2.{{ zone_one }}' + max_items: 1 + register: result + - assert: + that: + - create_geo_country is changed + - create_geo_country is not failed + - '"route53:ChangeResourceRecordSets" in create_geo_country.resource_actions' + - result.ResourceRecordSets[0].GeoLocation.CountryCode == "US" + + - name: Create a record with geo_location - country_code (idempotency) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-2.{{ zone_one }}' + identifier: "geohost2@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + register: create_geo_country_idem + - assert: + that: + - create_geo_country_idem is not changed + - create_geo_country_idem is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_country_idem.resource_actions' + + - name: Create a record with geo_location - country_code (idempotency - check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-2.{{ zone_one }}' + identifier: "geohost2@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + check_mode: true + register: create_geo_country_idem_check + + - assert: + that: + - create_geo_country_idem_check is not changed + - create_geo_country_idem_check is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_country_idem_check.resource_actions' + +#Test Geo Location - Subdivision Code + - name: Create a record with geo_location - subdivision_code (check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-3.{{ zone_one }}' + identifier: "geohost3@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX + check_mode: true + register: create_geo_subdivision_check_mode + - assert: + that: + - create_geo_subdivision_check_mode is changed + - create_geo_subdivision_check_mode is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_check_mode.resource_actions' + + - name: Create a record with geo_location - subdivision_code + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-3.{{ zone_one }}' + identifier: "geohost3@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX + register: create_geo_subdivision + # Get resulting A record and geo_location parameters are applied + - name: 'get Route53 A record information' + route53_info: + type: A + query: record_sets + hosted_zone_id: '{{ z1.zone_id }}' + start_record_name: 'geo-test-3.{{ zone_one }}' + max_items: 1 + register: result + - assert: + that: + - create_geo_subdivision is changed + - create_geo_subdivision is not failed + - '"route53:ChangeResourceRecordSets" in create_geo_subdivision.resource_actions' + - result.ResourceRecordSets[0].GeoLocation.CountryCode == "US" + - result.ResourceRecordSets[0].GeoLocation.SubdivisionCode == "TX" + + - name: Create a record with geo_location - subdivision_code (idempotency) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-3.{{ zone_one }}' + identifier: "geohost3@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX + register: create_geo_subdivision_idem + - assert: + that: + - create_geo_subdivision_idem is not changed + - create_geo_subdivision_idem is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_idem.resource_actions' + + - name: Create a record with geo_location - subdivision_code (idempotency - check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-3.{{ zone_one }}' + identifier: "geohost3@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX + check_mode: true + register: create_geo_subdivision_idem_check + + - assert: + that: + - create_geo_subdivision_idem_check is not changed + - create_geo_subdivision_idem_check is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_idem_check.resource_actions' + +#Cleanup------------------------------------------------------ + always: + + - name: delete a record with geo_location - continent_code + route53: + state: absent + zone: '{{ zone_one }}' + record: 'geo-test-1.{{ zone_one }}' + identifier: "geohost1@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + continent_code: NA + ignore_errors: true + + - name: delete a record with geo_location - country_code + route53: + state: absent + zone: '{{ zone_one }}' + record: 'geo-test-2.{{ zone_one }}' + identifier: "geohost2@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + ignore_errors: true + + - name: delete a record with geo_location - subdivision_code + route53: + state: absent + zone: '{{ zone_one }}' + record: 'geo-test-3.{{ zone_one }}' + identifier: "geohost3@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX + ignore_errors: true + - route53_info: query: record_sets hosted_zone_id: '{{ z1.zone_id }}' @@ -737,7 +1044,7 @@ state: absent zone: '{{ zone_one }}' register: delete_one - ignore_errors: yes + ignore_errors: true retries: 10 until: delete_one is not failed @@ -746,7 +1053,7 @@ state: absent zone: '{{ zone_two }}' register: delete_two - ignore_errors: yes + ignore_errors: True retries: 10 until: delete_two is not failed