From 91d0cbb268cf9c542b209b444ee891a4c3cede6b Mon Sep 17 00:00:00 2001 From: aparna-patil Date: Sun, 11 Oct 2020 20:44:30 +0530 Subject: [PATCH 1/6] Resolved issue #265 --- .../modules/azure_rm_privatednsrecordset.py | 469 ++++++++++++++++++ .../azure_rm_privatednsrecordset_info.py | 322 ++++++++++++ pr-pipelines.yml | 1 + .../azure_rm_privatednsrecordset/aliases | 3 + .../meta/main.yml | 2 + .../tasks/main.yml | 183 +++++++ tests/sanity/ignore-2.10.txt | 4 + tests/sanity/ignore-2.11.txt | 4 + 8 files changed, 988 insertions(+) create mode 100644 plugins/modules/azure_rm_privatednsrecordset.py create mode 100644 plugins/modules/azure_rm_privatednsrecordset_info.py create mode 100644 tests/integration/targets/azure_rm_privatednsrecordset/aliases create mode 100644 tests/integration/targets/azure_rm_privatednsrecordset/meta/main.yml create mode 100644 tests/integration/targets/azure_rm_privatednsrecordset/tasks/main.yml diff --git a/plugins/modules/azure_rm_privatednsrecordset.py b/plugins/modules/azure_rm_privatednsrecordset.py new file mode 100644 index 000000000..f5fd8e622 --- /dev/null +++ b/plugins/modules/azure_rm_privatednsrecordset.py @@ -0,0 +1,469 @@ +#!/usr/bin/python +# +# Copyright (c) 2020 Aparna Patil(@aparna-patil) +# +# 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 + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: azure_rm_privatednsrecordset + +version_added: "1.1.0" + +short_description: Create, delete and update Private DNS record sets and records + +description: + - Creates, deletes, and updates Private DNS records sets and records within an existing Azure Private DNS Zone. + +options: + resource_group: + description: + - Name of resource group. + required: true + zone_name: + description: + - Name of the existing Private DNS zone in which to manage the record set. + required: true + relative_name: + description: + - Relative name of the record set. + required: true + record_type: + description: + - The type of record set to create or delete. + choices: + - A + - AAAA + - CNAME + - MX + - PTR + - SOA + - SRV + - TXT + required: true + record_mode: + description: + - Whether existing record values not sent to the module should be purged. + default: purge + choices: + - append + - purge + state: + description: + - Assert the state of the record set. Use C(present) to create or update and C(absent) to delete. + default: present + choices: + - absent + - present + time_to_live: + description: + - Time to live of the record set in seconds. + default: 3600 + records: + description: + - List of records to be created depending on the type of record (set). + suboptions: + preference: + description: + - Used for creating an C(MX) record set/records. + priority: + description: + - Used for creating an C(SRV) record set/records. + weight: + description: + - Used for creating an C(SRV) record set/records. + port: + description: + - Used for creating an C(SRV) record set/records. + entry: + description: + - Primary data value for all record types. + +extends_documentation_fragment: + - azure.azcollection.azure + - azure.azcollection.azure_tags + +author: + - Aparna Patil (@aparna-patil) +''' + +EXAMPLES = ''' + +- name: ensure an "A" record set with multiple records + azure_rm_privatednsrecordset: + resource_group: myResourceGroup + relative_name: www + zone_name: testing.com + record_type: A + records: + - entry: 192.168.100.101 + - entry: 192.168.100.102 + - entry: 192.168.100.103 + +- name: delete a record set + azure_rm_privatednsrecordset: + resource_group: myResourceGroup + record_type: A + relative_name: www + zone_name: testing.com + state: absent + +- name: create multiple "A" record sets with multiple records + azure_rm_privatednsrecordset: + resource_group: myResourceGroup + zone_name: testing.com + relative_name: "{{ item.name }}" + record_type: "{{ item.type }}" + records: "{{ item.records }}" + with_items: + - { name: 'servera', type: 'A', records: [ { entry: '10.10.10.20' }, { entry: '10.10.10.21' }] } + - { name: 'serverb', type: 'A', records: [ { entry: '10.10.10.30' }, { entry: '10.10.10.41' }] } + - { name: 'serverc', type: 'A', records: [ { entry: '10.10.10.40' }, { entry: '10.10.10.41' }] } + +- name: create SRV records in a new record set + azure_rm_privatednsrecordset: + resource_group: myResourceGroup + relative_name: _sip._tcp.testing.com + zone_name: testing.com + time_to_live: 7200 + record_type: SRV + records: + - entry: sip.testing.com + priority: 20 + weight: 10 + port: 5060 + +- name: create PTR record in a new record set + azure_rm_privatednsrecordset: + resource_group: myResourceGroup + relative_name: 192.168.100.101.in-addr.arpa + zone_name: testing.com + record_type: PTR + records: + - entry: servera.testing.com + +- name: create TXT record in a new record set + azure_rm_privatednsrecordset: + resource_group: myResourceGroup + relative_name: mail.testing.com + zone_name: testing.com + record_type: TXT + records: + - entry: 'v=spf1 a -all' + +''' + +RETURN = ''' +state: + description: + - Current state of the DNS record set. + returned: always + type: complex + contains: + id: + description: + - The DNS record set ID. + returned: always + type: str + sample: "/subscriptions/xxxx......xxx/resourceGroups/v-xisuRG/providers/Microsoft.Network/privateDnsZones/ + b57dc95985712e4523282.com/A/www" + name: + description: + - Relate name of the record set. + returned: always + type: str + sample: 'www' + fqdn: + description: + - Fully qualified domain name of the record set. + return: always + type: str + sample: www.b57dc95985712e4523282.com + etag: + description: + - The etag of the record set. + return: always + type: str + sample: 692c3e92-a618-46fc-aecd-8f888807cd6c + is_auto_registered: + description: + - Is the record set auto-registered in the Private DNS zone through a virtual network link. + return: always + type: bool + sample: false + ttl: + description: + - The TTL(time-to-live) of the records in the records set. + return: always + type: int + sample: 3600 + type: + description: + - The type of DNS record in this record set. + return: always + type: str + sample: A + a_records: + description: + - A list of records in the record set. + return: always + type: list + sample: [ + { + "ipv4_address": "192.0.2.2" + }, + { + "ipv4_address": "192.0.2.4" + }, + { + "ipv4_address": "192.0.2.8" + } + ] +''' + +from ansible.module_utils.basic import _load_params +from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase, HAS_AZURE + +try: + from msrestazure.azure_exceptions import CloudError +except ImportError: + # This is handled in azure_rm_common + pass + + +RECORD_ARGSPECS = dict( + A=dict( + ipv4_address=dict(type='str', required=True, aliases=['entry']) + ), + AAAA=dict( + ipv6_address=dict(type='str', required=True, aliases=['entry']) + ), + CNAME=dict( + cname=dict(type='str', required=True, aliases=['entry']) + ), + MX=dict( + preference=dict(type='int', required=True), + exchange=dict(type='str', required=True, aliases=['entry']) + ), + PTR=dict( + ptrdname=dict(type='str', required=True, aliases=['entry']) + ), + SRV=dict( + priority=dict(type='int', required=True), + port=dict(type='int', required=True), + weight=dict(type='int', required=True), + target=dict(type='str', required=True, aliases=['entry']) + ), + TXT=dict( + value=dict(type='list', required=True, aliases=['entry']) + ), + SOA=dict( + host=dict(type='str', aliases=['entry']), + email=dict(type='str'), + serial_number=dict(type='long'), + refresh_time=dict(type='long'), + retry_time=dict(type='long'), + expire_time=dict(type='long'), + minimum_ttl=dict(type='long') + ) +) + +RECORDSET_VALUE_MAP = dict( + A=dict(attrname='a_records', classobj='ARecord', is_list=True), + AAAA=dict(attrname='aaaa_records', classobj='AaaaRecord', is_list=True), + CNAME=dict(attrname='cname_record', classobj='CnameRecord', is_list=False), + MX=dict(attrname='mx_records', classobj='MxRecord', is_list=True), + NS=dict(attrname='ns_records', classobj='NsRecord', is_list=True), + PTR=dict(attrname='ptr_records', classobj='PtrRecord', is_list=True), + SRV=dict(attrname='srv_records', classobj='SrvRecord', is_list=True), + TXT=dict(attrname='txt_records', classobj='TxtRecord', is_list=True), + SOA=dict(attrname='soa_record', classobj='SoaRecord', is_list=False), + CAA=dict(attrname='caa_records', classobj='CaaRecord', is_list=True) +) if HAS_AZURE else {} + + +class AzureRMPrivateDNSRecordSet(AzureRMModuleBase): + + def __init__(self): + + _load_params() + # define user inputs from playbook + self.module_arg_spec = dict( + resource_group=dict(type='str', required=True), + relative_name=dict(type='str', required=True), + zone_name=dict(type='str', required=True), + record_type=dict(choices=RECORD_ARGSPECS.keys(), required=True, type='str'), + record_mode=dict(choices=['append', 'purge'], default='purge'), + state=dict(choices=['present', 'absent'], default='present', type='str'), + time_to_live=dict(type='int', default=3600), + records=dict(type='list', elements='dict') + ) + + required_if = [ + ('state', 'present', ['records']) + ] + + self.results = dict( + changed=False + ) + + # Argument validation + super(AzureRMPrivateDNSRecordSet, self).__init__(self.module_arg_spec, + required_if=required_if, + supports_check_mode=True, + skip_exec=True) + + # check the subspec and metadata + record_subspec = RECORD_ARGSPECS.get(self.module.params['record_type']) + + # patch the right record shape onto the argspec + self.module_arg_spec['records']['options'] = record_subspec + + self.resource_group = None + self.relative_name = None + self.zone_name = None + self.record_type = None + self.record_mode = None + self.state = None + self.time_to_live = None + self.records = None + + # rerun validation and module + super(AzureRMPrivateDNSRecordSet, self).__init__(self.module_arg_spec, + required_if=required_if, + supports_check_mode=True) + + def exec_module(self, **kwargs): + for key in self.module_arg_spec.keys(): + setattr(self, key, kwargs[key]) + + self.log('Fetching private DNS zone {0}'.format(self.zone_name)) + zone = self.private_dns_client.private_zones.get(self.resource_group, self.zone_name) + if not zone: + self.fail('The zone {0} does not exist in the resource group {1}'.format(self.zone_name, + self.resource_group)) + + try: + self.log('Fetching Private DNS Record Set {0}'.format(self.relative_name)) + record_set = self.private_dns_client.record_sets.get(self.resource_group, + self.zone_name, + self.record_type, + self.relative_name) + self.results['state'] = self.recordset_to_dict(record_set) + except CloudError: + record_set = None + + record_type_metadata = RECORDSET_VALUE_MAP.get(self.record_type) + + if self.state == 'present': + # convert the input records to SDK objects + self.input_sdk_records = self.create_sdk_records(self.records, self.record_type) + + if not record_set: + changed = True + else: + # use recordset to get the type-specific records + server_records = getattr(record_set, record_type_metadata.get('attrname')) + + # Compare the input records to the server records + self.input_sdk_records, changed = self.records_changed(self.input_sdk_records, server_records) + + # check the top-level recordset properties + changed |= record_set.ttl != self.time_to_live + + self.results['changed'] |= changed + + elif self.state == 'absent': + if record_set: + self.results['changed'] = True + + if self.check_mode: + return self.results + + if self.results['changed']: + if self.state == 'present': + record_set_args = dict( + ttl=self.time_to_live + ) + + record_set_args[record_type_metadata['attrname']] = \ + self.input_sdk_records if record_type_metadata['is_list'] else self.input_sdk_records[0] + + record_set = self.private_dns_models.RecordSet(**record_set_args) + # create record set + self.results['state'] = self.create_or_update(record_set) + + elif self.state == 'absent': + # delete record set + self.delete_record_set() + + return self.results + + def create_or_update(self, record_set): + try: + # create the record set + record_set = \ + self.private_dns_client.record_sets.create_or_update(resource_group_name=self.resource_group, + private_zone_name=self.zone_name, + record_type=self.record_type, + relative_record_set_name=self.relative_name, + parameters=record_set) + return self.recordset_to_dict(record_set) + except Exception as exc: + self.fail("Error creating or updating dns record {0} - {1}".format(self.relative_name, + exc.message or str(exc))) + + def delete_record_set(self): + try: + # delete the record set + self.private_dns_client.record_sets.delete(resource_group_name=self.resource_group, + private_zone_name=self.zone_name, + relative_record_set_name=self.relative_name, + record_type=self.record_type) + except Exception as exc: + self.fail("Error deleting record set {0} - {1}".format(self.relative_name, exc.message or str(exc))) + return None + + def create_sdk_records(self, input_records, record_type): + record = RECORDSET_VALUE_MAP.get(record_type) + if not record: + self.fail('record type {0} is not supported now'.format(record_type)) + record_sdk_class = getattr(self.private_dns_models, record.get('classobj')) + return [record_sdk_class(**x) for x in input_records] + + def records_changed(self, input_records, server_records): + # ensure we're always comparing a list, even for the single-valued types + if not isinstance(server_records, list): + server_records = [server_records] + + input_set = set([self.module.jsonify(x.as_dict()) for x in input_records]) + server_set = set([self.module.jsonify(x.as_dict()) for x in server_records]) + + if self.record_mode == 'append': # only a difference if the server set is missing something from the input set + input_set = server_set.union(input_set) + + # non-append mode; any difference in the sets is a change + changed = input_set != server_set + + records = [self.module.from_json(x) for x in input_set] + return self.create_sdk_records(records, self.record_type), changed + + def recordset_to_dict(self, recordset): + result = recordset.as_dict() + result['type'] = result['type'].strip('Microsoft.Network/privateDnsZones/') + return result + + +def main(): + AzureRMPrivateDNSRecordSet() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/azure_rm_privatednsrecordset_info.py b/plugins/modules/azure_rm_privatednsrecordset_info.py new file mode 100644 index 000000000..f66d2a433 --- /dev/null +++ b/plugins/modules/azure_rm_privatednsrecordset_info.py @@ -0,0 +1,322 @@ +#!/usr/bin/python +# +# Copyright (c) 2020 Aparna Patil(@aparna-patil) +# +# 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 + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: azure_rm_privatednsrecordset_info + +version_added: "1.1.0" + +short_description: Get Private DNS Record Set facts + +description: + - Get facts for a specific DNS Record Set in a Private DNS Zone, or a specific type of DNS record in all zones or + one zone etc. + +options: + relative_name: + description: + - Only show results for a Record Set. + resource_group: + description: + - Limit results by resource group. Required when filtering by name or type. + zone_name: + description: + - Limit results by zones. Required when filtering by name or type. + record_type: + description: + - Limit record sets by record type. + top: + description: + - Limit the maximum number of record sets to return. + type: int + +extends_documentation_fragment: + - azure.azcollection.azure + - azure.azcollection.azure_tags + +author: + - Aparna Patil (@aparna-patil) + +''' + +EXAMPLES = ''' +- name: Get facts for one record set in one Private DNS Zone + azure_rm_privatednsrecordset_info: + resource_group: myResourceGroup + zone_name: newzone.com + relative_name: servera + record_type: A +- name: Get facts for all Type A record sets in a Private DNS Zone + azure_rm_privatednsrecordset_info: + resource_group: myResourceGroup + zone_name: newzone.com + record_type: A +- name: Get all record sets in a Private DNS Zone + azure_rm_privatednsrecordset_info: + resource_group: myResourceGroup + zone_name: newzone.com +''' + +RETURN = ''' +dnsrecordsets: + description: + - Gets a list of record set dict in a Private DNS zone. + returned: always + type: list + sample: [ + { + "fqdn": "servera.newzone.com", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/ + Microsoft.Network/privateDnsZones/newzone.com/A/servera", + "record_type": "A", + "records": [ + { + "ipv4_address": "10.10.10.10" + } + ], + "relative_name": "servera", + "time_to_live": 3600 + } + ] +dnsrecordsets: + description: + - Lists recordsets dict of a specified type in a Private DNS zone. + returned: always + type: list + sample: [ + { + "fqdn": "servera.newzone.com.", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/ + Microsoft.Network/privateDnsZones/newzone.com/A/servera", + "record_type": "A", + "records": [ + { + "ipv4_address": "10.10.10.10" + } + ], + "relative_name": "servera", + "time_to_live": 3600 + }, + { + "fqdn": "serverb.newzone.com.", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/ + Microsoft.Network/privateDnsZones/newzone.com/A/serverb", + "record_type": "A", + "records": [ + { + "ipv4_address": "10.10.10.11" + } + ], + "relative_name": "serverb", + "time_to_live": 3600 + } + ] +dnsrecordsets: + description: + - Lists all record sets in a Private DNS zone. + returned: always + type: list + sample: [ + { + "fqdn": "recordcname.newzone.com.", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/ + Microsoft.Network/privateDnsZones/newzone.com/CNAME/recordcname", + "record_type": "CNAME", + "records": [ + { + "cname": "newzone.com" + } + ], + "relative_name": "recordcname", + "time_to_live": 3600 + }, + { + "fqdn": "servera.newzone.com.", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/ + Microsoft.Network/privateDnsZones/newzone.com/A/servera", + "record_type": "A", + "records": [ + { + "ipv4_address": "10.10.10.10" + } + ], + "relative_name": "servera", + "time_to_live": 3600 + }, + { + "fqdn": "serverb.newzone.com.", + "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/ + Microsoft.Network/privateDnsZones/newzone.com/A/serverb", + "record_type": "A", + "records": [ + { + "ipv4_address": "10.10.10.11" + } + ], + "relative_name": "serverb", + "time_to_live": 3600 + } + ] +''' + +from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase + +try: + from msrestazure.azure_exceptions import CloudError + from azure.common import AzureMissingResourceHttpError, AzureHttpError +except Exception: + # This is handled in azure_rm_common + pass + +AZURE_OBJECT_CLASS = 'RecordSet' + + +RECORDSET_VALUE_MAP = dict( + A='a_records', + AAAA='aaaa_records', + CNAME='cname_record', + MX='mx_records', + PTR='ptr_records', + SRV='srv_records', + TXT='txt_records', + SOA='soa_record' +) + + +class AzureRMPrivateDNSRecordSetInfo(AzureRMModuleBase): + + def __init__(self): + + # define user inputs into argument + self.module_arg_spec = dict( + relative_name=dict(type='str'), + resource_group=dict(type='str'), + zone_name=dict(type='str'), + record_type=dict(type='str'), + top=dict(type='int') + ) + + # store the results of the module operation + self.results = dict( + changed=False, + ) + + self.relative_name = None + self.resource_group = None + self.zone_name = None + self.record_type = None + self.top = None + + super(AzureRMPrivateDNSRecordSetInfo, self).__init__(self.module_arg_spec) + + def exec_module(self, **kwargs): + + for key in self.module_arg_spec: + setattr(self, key, kwargs[key]) + + if not self.top or self.top <= 0: + self.top = None + + # create conditionals to catch errors when calling record facts + if self.relative_name and not self.resource_group: + self.fail("Parameter error: resource group required when filtering by name or record type.") + if self.relative_name and not self.zone_name: + self.fail("Parameter error: DNS Zone required when filtering by name or record type.") + + results = [] + # list the conditions for what to return based on input + if self.relative_name is not None: + # if there is a name listed, they want only facts about that specific Record Set itself + results = self.get_item() + elif self.record_type: + # else, they just want all the record sets of a specific type + results = self.list_type() + elif self.zone_name: + # if there is a zone name listed, then they want all the record sets in a zone + results = self.list_zone() + + self.results['dnsrecordsets'] = self.curated_list(results) + return self.results + + def get_item(self): + self.log('Get properties for {0}'.format(self.relative_name)) + item = None + results = [] + + # try to get information for specific Record Set + try: + item = self.private_dns_client.record_sets.get(self.resource_group, + self.zone_name, + self.record_type, + self.relative_name) + except CloudError: + pass + + results = [item] + return results + + def list_type(self): + self.log('Lists the record sets of a specified type in a Private DNS zone') + try: + response = self.private_dns_client.record_sets.list_by_type(self.resource_group, + self.zone_name, + self.record_type, + top=self.top) + except AzureHttpError as exc: + self.fail("Failed to list for record type {0} - {1}".format(self.record_type, str(exc))) + + results = [] + for item in response: + results.append(item) + return results + + def list_zone(self): + self.log('Lists all record sets in a Private DNS zone') + try: + response = self.private_dns_client.record_sets.list(self.resource_group, self.zone_name, top=self.top) + except AzureHttpError as exc: + self.fail("Failed to list for zone {0} - {1}".format(self.zone_name, str(exc))) + + results = [] + for item in response: + results.append(item) + return results + + def curated_list(self, raws): + return [self.record_to_dict(item) for item in raws] if raws else [] + + def record_to_dict(self, record): + record_type = record.type[len('Microsoft.Network/privateDnsZones/'):] + records = getattr(record, RECORDSET_VALUE_MAP.get(record_type)) + if records: + if not isinstance(records, list): + records = [records] + else: + records = [] + return dict( + id=record.id, + relative_name=record.name, + record_type=record_type, + records=[x.as_dict() for x in records], + time_to_live=record.ttl, + fqdn=record.fqdn + ) + + +def main(): + AzureRMPrivateDNSRecordSetInfo() + + +if __name__ == '__main__': + main() diff --git a/pr-pipelines.yml b/pr-pipelines.yml index 01b1dffea..e3ae45631 100644 --- a/pr-pipelines.yml +++ b/pr-pipelines.yml @@ -54,6 +54,7 @@ parameters: - "azure_rm_networkinterface" - "azure_rm_openshiftmanagedcluster" - "azure_rm_postgresqlserver" + - "azure_rm_privatednsrecordset" - "azure_rm_privatednszone" - "azure_rm_publicipaddress" - "azure_rm_rediscache" diff --git a/tests/integration/targets/azure_rm_privatednsrecordset/aliases b/tests/integration/targets/azure_rm_privatednsrecordset/aliases new file mode 100644 index 000000000..8f7a9a2e5 --- /dev/null +++ b/tests/integration/targets/azure_rm_privatednsrecordset/aliases @@ -0,0 +1,3 @@ +cloud/azure +shippable/azure/group1 +destructive diff --git a/tests/integration/targets/azure_rm_privatednsrecordset/meta/main.yml b/tests/integration/targets/azure_rm_privatednsrecordset/meta/main.yml new file mode 100644 index 000000000..95e1952f9 --- /dev/null +++ b/tests/integration/targets/azure_rm_privatednsrecordset/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_azure diff --git a/tests/integration/targets/azure_rm_privatednsrecordset/tasks/main.yml b/tests/integration/targets/azure_rm_privatednsrecordset/tasks/main.yml new file mode 100644 index 000000000..b5b9fe5f1 --- /dev/null +++ b/tests/integration/targets/azure_rm_privatednsrecordset/tasks/main.yml @@ -0,0 +1,183 @@ +- name: Create random domain name + set_fact: + domain_name: "{{ resource_group | hash('md5') | truncate(16, True, '') + (65535 | random | string) }}" + +- name: Create a Private DNS zone + azure_rm_privatednszone: + resource_group: "{{ resource_group }}" + name: "{{ domain_name }}.com" + state: present + register: results + +- name: Assert that Private DNS zone was created + assert: + that: results.changed + +- name: create "A" record set with multiple records + azure_rm_privatednsrecordset: + resource_group: "{{ resource_group }}" + relative_name: www + zone_name: "{{ domain_name }}.com" + record_type: A + records: + - entry: 192.168.100.101 + - entry: 192.168.100.102 + - entry: 192.168.100.103 + register: results + +- name: Assert that A record set was created + assert: + that: results.changed + +- name: re-run "A" record with same values + azure_rm_privatednsrecordset: + resource_group: "{{ resource_group }}" + relative_name: www + zone_name: "{{ domain_name }}.com" + record_type: A + records: + - entry: 192.168.100.101 + - entry: 192.168.100.102 + - entry: 192.168.100.103 + register: results + +- name: Assert that A record set was not changed + assert: + that: not results.changed + +- name: Update "A" record set with additional record + azure_rm_privatednsrecordset: + resource_group: "{{ resource_group }}" + relative_name: www + zone_name: "{{ domain_name }}.com" + record_type: A + record_mode: append + records: + - entry: 192.168.100.104 + register: results + +- name: Assert that new record was appended + assert: + that: + - results.changed + +- name: re-update "A" record set with additional record + azure_rm_privatednsrecordset: + resource_group: "{{ resource_group }}" + relative_name: www + zone_name: "{{ domain_name }}.com" + record_type: A + record_mode: append + records: + - entry: 192.168.100.104 + register: results + +- name: Assert that A record set was not changed + assert: + that: + - not results.changed + +- name: Remove 1 record from record set + azure_rm_privatednsrecordset: + resource_group: "{{ resource_group }}" + relative_name: www + zone_name: "{{ domain_name }}.com" + record_type: A + records: + - entry: 192.168.100.101 + - entry: 192.168.100.102 + - entry: 192.168.100.103 + register: results + +- name: Assert that record was deleted + assert: + that: + - results.changed + +- name: Check_mode test + azure_rm_privatednsrecordset: + resource_group: "{{ resource_group }}" + relative_name: www + zone_name: "{{ domain_name }}.com" + record_type: A + records: + - entry: 192.168.100.105 + check_mode: yes + register: results + +- name: Assert that check_mode returns new state + assert: + that: + - results.changed + +- name: delete a record set + azure_rm_privatednsrecordset: + resource_group: "{{ resource_group }}" + relative_name: www + zone_name: "{{ domain_name }}.com" + record_type: A + state: absent + register: results + +- name: Assert that record set deleted + assert: + that: results.changed + +- name: (idempotence test) re-run record set absent + azure_rm_privatednsrecordset: + resource_group: "{{ resource_group }}" + relative_name: www + zone_name: "{{ domain_name }}.com" + record_type: A + state: absent + register: results + +- name: + assert: + that: not results.changed + +- name: create SRV records in a new record set + azure_rm_privatednsrecordset: + resource_group: "{{ resource_group }}" + relative_name: "_sip._tcp.{{ domain_name }}.com" + zone_name: "{{ domain_name }}.com" + time_to_live: 7200 + record_type: SRV + state: present + records: + - entry: sip.{{ domain_name }}.com + priority: 20 + weight: 10 + port: 5060 + register: results + +- name: Assert that SRV record set was created + assert: + that: + - results.changed + +- name: create TXT records in a new record set + azure_rm_privatednsrecordset: + resource_group: "{{ resource_group }}" + relative_name: "_txt.{{ domain_name }}.com" + zone_name: "{{ domain_name }}.com" + record_type: TXT + state: present + records: + - entry: "v=spf1 a -all" + - entry: "foo" + - entry: + - "bar" + - "baz" + register: results + +- name: Assert that TXT record set was created + assert: + that: + - results.changed + +- name: Delete DNS zone + azure_rm_privatednszone: + resource_group: "{{ resource_group }}" + name: "{{ domain_name }}.com" + state: absent diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index 51af3e420..c76576162 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -727,5 +727,9 @@ plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:invalid-ans plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:required_if-requirements-unknown plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:parameter-list-no-elements plugins/modules/azure_rm_vmbackuppolicy.py validate-modules:parameter-list-no-elements +plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-requirements-unknown +plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-unknown-key +plugins/modules/azure_rm_privatednsrecordset_info.py validate-modules:required_if-requirements-unknown +plugins/modules/azure_rm_privatednsrecordset_info.py validate-modules:required_if-unknown-key tests/utils/shippable/check_matrix.py replace-urlopen tests/utils/shippable/timing.py shebang diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index 12918ea80..7a6111252 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -727,5 +727,9 @@ plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:invalid-ans plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:required_if-requirements-unknown plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:parameter-list-no-elements plugins/modules/azure_rm_vmbackuppolicy.py validate-modules:parameter-list-no-elements +plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-requirements-unknown +plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-unknown-key +plugins/modules/azure_rm_privatednsrecordset_info.py validate-modules:required_if-requirements-unknown +plugins/modules/azure_rm_privatednsrecordset_info.py validate-modules:required_if-unknown-key tests/utils/shippable/check_matrix.py replace-urlopen tests/utils/shippable/timing.py shebang From 8be78c24fde0a91146bc9411b58079cb14d87284 Mon Sep 17 00:00:00 2001 From: aparna-patil Date: Tue, 13 Oct 2020 12:57:54 +0530 Subject: [PATCH 2/6] Incorporated all the review comments --- .../modules/azure_rm_privatednsrecordset.py | 27 +++++-- .../azure_rm_privatednsrecordset_info.py | 73 ++----------------- .../tasks/main.yml | 54 ++++++++++++++ tests/sanity/ignore-2.10.txt | 2 + tests/sanity/ignore-2.11.txt | 2 + 5 files changed, 85 insertions(+), 73 deletions(-) diff --git a/plugins/modules/azure_rm_privatednsrecordset.py b/plugins/modules/azure_rm_privatednsrecordset.py index f5fd8e622..18eac36c0 100644 --- a/plugins/modules/azure_rm_privatednsrecordset.py +++ b/plugins/modules/azure_rm_privatednsrecordset.py @@ -27,14 +27,17 @@ description: - Name of resource group. required: true + type: str zone_name: description: - Name of the existing Private DNS zone in which to manage the record set. required: true + type: str relative_name: description: - Relative name of the record set. required: true + type: str record_type: description: - The type of record set to create or delete. @@ -48,10 +51,12 @@ - SRV - TXT required: true + type: str record_mode: description: - Whether existing record values not sent to the module should be purged. default: purge + type: str choices: - append - purge @@ -59,6 +64,7 @@ description: - Assert the state of the record set. Use C(present) to create or update and C(absent) to delete. default: present + type: str choices: - absent - present @@ -66,25 +72,33 @@ description: - Time to live of the record set in seconds. default: 3600 + type: int records: description: - List of records to be created depending on the type of record (set). + type: list + elements: dict suboptions: preference: description: - Used for creating an C(MX) record set/records. + type: int priority: description: - Used for creating an C(SRV) record set/records. + type: int weight: description: - Used for creating an C(SRV) record set/records. + type: int port: description: - Used for creating an C(SRV) record set/records. + type: int entry: description: - Primary data value for all record types. + type: str extends_documentation_fragment: - azure.azcollection.azure @@ -183,38 +197,39 @@ fqdn: description: - Fully qualified domain name of the record set. - return: always + returned: always type: str sample: www.b57dc95985712e4523282.com etag: description: - The etag of the record set. - return: always + returned: always type: str sample: 692c3e92-a618-46fc-aecd-8f888807cd6c is_auto_registered: description: - Is the record set auto-registered in the Private DNS zone through a virtual network link. - return: always + returned: always type: bool sample: false ttl: description: - The TTL(time-to-live) of the records in the records set. - return: always + returned: always type: int sample: 3600 type: description: - The type of DNS record in this record set. - return: always + returned: always type: str sample: A a_records: description: - A list of records in the record set. - return: always + returned: always type: list + elements: dict sample: [ { "ipv4_address": "192.0.2.2" diff --git a/plugins/modules/azure_rm_privatednsrecordset_info.py b/plugins/modules/azure_rm_privatednsrecordset_info.py index f66d2a433..12d46dca5 100644 --- a/plugins/modules/azure_rm_privatednsrecordset_info.py +++ b/plugins/modules/azure_rm_privatednsrecordset_info.py @@ -27,15 +27,19 @@ relative_name: description: - Only show results for a Record Set. + type: str resource_group: description: - Limit results by resource group. Required when filtering by name or type. + type: str zone_name: description: - Limit results by zones. Required when filtering by name or type. + type: str record_type: description: - Limit record sets by record type. + type:str top: description: - Limit the maximum number of record sets to return. @@ -71,76 +75,11 @@ RETURN = ''' dnsrecordsets: description: - - Gets a list of record set dict in a Private DNS zone. + - Gets a list of recordsets dict in a Private DNS zone. returned: always type: list + elements: dict sample: [ - { - "fqdn": "servera.newzone.com", - "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/ - Microsoft.Network/privateDnsZones/newzone.com/A/servera", - "record_type": "A", - "records": [ - { - "ipv4_address": "10.10.10.10" - } - ], - "relative_name": "servera", - "time_to_live": 3600 - } - ] -dnsrecordsets: - description: - - Lists recordsets dict of a specified type in a Private DNS zone. - returned: always - type: list - sample: [ - { - "fqdn": "servera.newzone.com.", - "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/ - Microsoft.Network/privateDnsZones/newzone.com/A/servera", - "record_type": "A", - "records": [ - { - "ipv4_address": "10.10.10.10" - } - ], - "relative_name": "servera", - "time_to_live": 3600 - }, - { - "fqdn": "serverb.newzone.com.", - "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/ - Microsoft.Network/privateDnsZones/newzone.com/A/serverb", - "record_type": "A", - "records": [ - { - "ipv4_address": "10.10.10.11" - } - ], - "relative_name": "serverb", - "time_to_live": 3600 - } - ] -dnsrecordsets: - description: - - Lists all record sets in a Private DNS zone. - returned: always - type: list - sample: [ - { - "fqdn": "recordcname.newzone.com.", - "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/ - Microsoft.Network/privateDnsZones/newzone.com/CNAME/recordcname", - "record_type": "CNAME", - "records": [ - { - "cname": "newzone.com" - } - ], - "relative_name": "recordcname", - "time_to_live": 3600 - }, { "fqdn": "servera.newzone.com.", "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/ diff --git a/tests/integration/targets/azure_rm_privatednsrecordset/tasks/main.yml b/tests/integration/targets/azure_rm_privatednsrecordset/tasks/main.yml index b5b9fe5f1..3df8cb625 100644 --- a/tests/integration/targets/azure_rm_privatednsrecordset/tasks/main.yml +++ b/tests/integration/targets/azure_rm_privatednsrecordset/tasks/main.yml @@ -110,6 +110,24 @@ that: - results.changed +- name: Get information for A DNS recordset from Private DNS zone + azure_rm_privatednsrecordset_info: + resource_group: "{{ resource_group }}" + zone_name: "{{ domain_name }}.com" + relative_name: www + record_type: A + register: results + +- assert: + that: + - not results.changed + - results.dnsrecordsets[0].id != None + - results.dnsrecordsets[0].fqdn != None + - results.dnsrecordsets[0].record_type == 'A' + - results.dnsrecordsets[0].time_to_live != None + - results.dnsrecordsets[0].relative_name == 'www' + - results.dnsrecordsets[0].records | length > 0 + - name: delete a record set azure_rm_privatednsrecordset: resource_group: "{{ resource_group }}" @@ -156,6 +174,24 @@ that: - results.changed +- name: Get information for SRV DNS recordset from Private DNS zone + azure_rm_privatednsrecordset_info: + resource_group: "{{ resource_group }}" + zone_name: "{{ domain_name }}.com" + relative_name: "_sip._tcp.{{ domain_name }}.com" + record_type: SRV + register: results + +- assert: + that: + - not results.changed + - results.dnsrecordsets[0].id != None + - results.dnsrecordsets[0].fqdn != None + - results.dnsrecordsets[0].record_type == 'SRV' + - results.dnsrecordsets[0].time_to_live == 7200 + - results.dnsrecordsets[0].relative_name == "_sip._tcp.{{ domain_name }}.com" + - results.dnsrecordsets[0].records | length > 0 + - name: create TXT records in a new record set azure_rm_privatednsrecordset: resource_group: "{{ resource_group }}" @@ -176,6 +212,24 @@ that: - results.changed +- name: Get information for TXT DNS recordset from Private DNS zone + azure_rm_privatednsrecordset_info: + resource_group: "{{ resource_group }}" + zone_name: "{{ domain_name }}.com" + relative_name: "_txt.{{ domain_name }}.com" + record_type: TXT + register: results + +- assert: + that: + - not results.changed + - results.dnsrecordsets[0].id != None + - results.dnsrecordsets[0].fqdn != None + - results.dnsrecordsets[0].record_type == 'TXT' + - results.dnsrecordsets[0].time_to_live == 3600 + - results.dnsrecordsets[0].relative_name == "_txt.{{ domain_name }}.com" + - results.dnsrecordsets[0].records | length > 0 + - name: Delete DNS zone azure_rm_privatednszone: resource_group: "{{ resource_group }}" diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index c76576162..c7d52b55e 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -727,6 +727,8 @@ plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:invalid-ans plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:required_if-requirements-unknown plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:parameter-list-no-elements plugins/modules/azure_rm_vmbackuppolicy.py validate-modules:parameter-list-no-elements +plugins/modules/azure_rm_privatednsrecordset.py validate-modules:doc-elements-mismatch +plugins/modules/azure_rm_privatednsrecordset.py validate-modules:invalid-ansiblemodule-schema plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-requirements-unknown plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-unknown-key plugins/modules/azure_rm_privatednsrecordset_info.py validate-modules:required_if-requirements-unknown diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index 7a6111252..9cb75e811 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -727,6 +727,8 @@ plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:invalid-ans plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:required_if-requirements-unknown plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:parameter-list-no-elements plugins/modules/azure_rm_vmbackuppolicy.py validate-modules:parameter-list-no-elements +plugins/modules/azure_rm_privatednsrecordset.py validate-modules:doc-elements-mismatch +plugins/modules/azure_rm_privatednsrecordset.py validate-modules:invalid-ansiblemodule-schema plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-requirements-unknown plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-unknown-key plugins/modules/azure_rm_privatednsrecordset_info.py validate-modules:required_if-requirements-unknown From 9c1a0db31bfd8b1aac3df95d13a6670be9f380ac Mon Sep 17 00:00:00 2001 From: aparna-patil <71707721+aparna-patil@users.noreply.github.com> Date: Thu, 22 Oct 2020 09:01:14 +0530 Subject: [PATCH 3/6] Update plugins/modules/azure_rm_privatednsrecordset_info.py Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> --- plugins/modules/azure_rm_privatednsrecordset_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/azure_rm_privatednsrecordset_info.py b/plugins/modules/azure_rm_privatednsrecordset_info.py index 12d46dca5..9570a64b1 100644 --- a/plugins/modules/azure_rm_privatednsrecordset_info.py +++ b/plugins/modules/azure_rm_privatednsrecordset_info.py @@ -20,7 +20,7 @@ short_description: Get Private DNS Record Set facts description: - - Get facts for a specific DNS Record Set in a Private DNS Zone, or a specific type of DNS record in all zones or + - Get facts for a specific DNS Record Set in a Private DNS Zone, or a specific type of DNS record in all zones or one zone etc. options: From 77921ac1fcaa8eba793161e0cc380ffd5db4e5dc Mon Sep 17 00:00:00 2001 From: aparna-patil Date: Mon, 26 Oct 2020 17:28:59 +0530 Subject: [PATCH 4/6] Corrected documentation --- plugins/modules/azure_rm_privatednsrecordset_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/azure_rm_privatednsrecordset_info.py b/plugins/modules/azure_rm_privatednsrecordset_info.py index 9570a64b1..21f9bb5cd 100644 --- a/plugins/modules/azure_rm_privatednsrecordset_info.py +++ b/plugins/modules/azure_rm_privatednsrecordset_info.py @@ -39,7 +39,7 @@ record_type: description: - Limit record sets by record type. - type:str + type: str top: description: - Limit the maximum number of record sets to return. From 4db72700922cde5f9a387053eac6e5c459178c66 Mon Sep 17 00:00:00 2001 From: aparna-patil <71707721+aparna-patil@users.noreply.github.com> Date: Mon, 26 Oct 2020 18:07:35 +0530 Subject: [PATCH 5/6] Update tests/sanity/ignore-2.10.txt Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> --- tests/sanity/ignore-2.10.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index c7d52b55e..0296763a4 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -727,7 +727,6 @@ plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:invalid-ans plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:required_if-requirements-unknown plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:parameter-list-no-elements plugins/modules/azure_rm_vmbackuppolicy.py validate-modules:parameter-list-no-elements -plugins/modules/azure_rm_privatednsrecordset.py validate-modules:doc-elements-mismatch plugins/modules/azure_rm_privatednsrecordset.py validate-modules:invalid-ansiblemodule-schema plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-requirements-unknown plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-unknown-key From 2d32d3657eb2d483834e9f240c0de1d4814a7e26 Mon Sep 17 00:00:00 2001 From: aparna-patil <71707721+aparna-patil@users.noreply.github.com> Date: Mon, 26 Oct 2020 18:11:42 +0530 Subject: [PATCH 6/6] Update tests/sanity/ignore-2.11.txt Co-authored-by: Fred-sun <37327967+Fred-sun@users.noreply.github.com> --- tests/sanity/ignore-2.11.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index 9cb75e811..40a8e4bd6 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -727,7 +727,6 @@ plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:invalid-ans plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:required_if-requirements-unknown plugins/modules/azure_rm_openshiftmanagedcluster.py validate-modules:parameter-list-no-elements plugins/modules/azure_rm_vmbackuppolicy.py validate-modules:parameter-list-no-elements -plugins/modules/azure_rm_privatednsrecordset.py validate-modules:doc-elements-mismatch plugins/modules/azure_rm_privatednsrecordset.py validate-modules:invalid-ansiblemodule-schema plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-requirements-unknown plugins/modules/azure_rm_privatednsrecordset.py validate-modules:required_if-unknown-key