dns zone enhancement and return curated value (#50740)
This commit is contained in:
parent
9487815a26
commit
0f6252baf3
8 changed files with 445 additions and 118 deletions
|
@ -220,7 +220,7 @@ AZURE_PKG_VERSIONS = {
|
|||
},
|
||||
'DnsManagementClient': {
|
||||
'package_name': 'dns',
|
||||
'expected_version': '1.2.0'
|
||||
'expected_version': '2.1.0'
|
||||
},
|
||||
'WebSiteManagementClient': {
|
||||
'package_name': 'web',
|
||||
|
@ -839,9 +839,15 @@ class AzureRMModuleBase(object):
|
|||
self.log('Getting dns client')
|
||||
if not self._dns_client:
|
||||
self._dns_client = self.get_mgmt_svc_client(DnsManagementClient,
|
||||
base_url=self._cloud_environment.endpoints.resource_manager)
|
||||
base_url=self._cloud_environment.endpoints.resource_manager,
|
||||
api_version='2018-05-01')
|
||||
return self._dns_client
|
||||
|
||||
@property
|
||||
def dns_models(self):
|
||||
self.log("Getting dns models...")
|
||||
return DnsManagementClient.models('2018-05-01')
|
||||
|
||||
@property
|
||||
def web_client(self):
|
||||
self.log('Getting web client')
|
||||
|
|
|
@ -48,6 +48,8 @@ options:
|
|||
- SRV
|
||||
- TXT
|
||||
- PTR
|
||||
- CAA
|
||||
- SOA
|
||||
required: true
|
||||
record_mode:
|
||||
description:
|
||||
|
@ -179,7 +181,6 @@ from ansible.module_utils.azure_rm_common import AzureRMModuleBase, HAS_AZURE
|
|||
|
||||
try:
|
||||
from msrestazure.azure_exceptions import CloudError
|
||||
from azure.mgmt.dns.models import Zone, RecordSet, ARecord, AaaaRecord, MxRecord, NsRecord, PtrRecord, SrvRecord, TxtRecord, CnameRecord, SoaRecord
|
||||
except ImportError:
|
||||
# This is handled in azure_rm_common
|
||||
pass
|
||||
|
@ -214,18 +215,34 @@ RECORD_ARGSPECS = dict(
|
|||
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')
|
||||
),
|
||||
CAA=dict(
|
||||
value=dict(type='str', aliases=['entry']),
|
||||
flags=dict(type='int'),
|
||||
tag=dict(type='str')
|
||||
)
|
||||
# FUTURE: ensure all record types are supported (see https://github.com/Azure/azure-sdk-for-python/tree/master/azure-mgmt-dns/azure/mgmt/dns/models)
|
||||
)
|
||||
|
||||
RECORDSET_VALUE_MAP = dict(
|
||||
A=dict(attrname='arecords', 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),
|
||||
A=dict(attrname='arecords', 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)
|
||||
# FUTURE: add missing record types from https://github.com/Azure/azure-sdk-for-python/blob/master/azure-mgmt-dns/azure/mgmt/dns/models/record_set.py
|
||||
) if HAS_AZURE else {}
|
||||
|
||||
|
@ -261,14 +278,18 @@ class AzureRMRecordSet(AzureRMModuleBase):
|
|||
|
||||
# look up the right subspec and metadata
|
||||
record_subspec = RECORD_ARGSPECS.get(self.module.params['record_type'])
|
||||
self.record_type_metadata = RECORDSET_VALUE_MAP.get(self.module.params['record_type'])
|
||||
|
||||
# patch the right record shape onto the argspec
|
||||
self.module_arg_spec['records']['options'] = record_subspec
|
||||
|
||||
# monkeypatch __hash__ on SDK model objects so we can safely use them in sets
|
||||
for rvm in RECORDSET_VALUE_MAP.values():
|
||||
rvm['classobj'].__hash__ = gethash
|
||||
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 actually run the module this time
|
||||
super(AzureRMRecordSet, self).__init__(self.module_arg_spec, required_if=required_if, supports_check_mode=True)
|
||||
|
@ -286,24 +307,27 @@ class AzureRMRecordSet(AzureRMModuleBase):
|
|||
try:
|
||||
self.log('Fetching Record Set {0}'.format(self.relative_name))
|
||||
record_set = self.dns_client.record_sets.get(self.resource_group, self.zone_name, self.relative_name, self.record_type)
|
||||
except CloudError as ce:
|
||||
self.results['state'] = self.recordset_to_dict(record_set)
|
||||
except CloudError:
|
||||
record_set = None
|
||||
# FUTURE: fail on anything other than ResourceNotFound
|
||||
|
||||
record_type_metadata = RECORDSET_VALUE_MAP.get(self.record_type)
|
||||
|
||||
# FUTURE: implement diff mode
|
||||
|
||||
if self.state == 'present':
|
||||
# convert the input records to SDK objects
|
||||
self.input_sdk_records = self.create_sdk_records(self.records)
|
||||
self.input_sdk_records = self.create_sdk_records(self.records, self.record_type)
|
||||
|
||||
if not record_set:
|
||||
changed = True
|
||||
else:
|
||||
# and use it to get the type-specific records
|
||||
server_records = getattr(record_set, self.record_type_metadata['attrname'])
|
||||
server_records = getattr(record_set, record_type_metadata.get('attrname'))
|
||||
|
||||
# compare the input records to the server records
|
||||
changed = self.records_changed(self.input_sdk_records, server_records)
|
||||
self.input_sdk_records, changed = self.records_changed(self.input_sdk_records, server_records)
|
||||
|
||||
# also check top-level recordset properties
|
||||
changed |= record_set.ttl != self.time_to_live
|
||||
|
@ -325,18 +349,11 @@ class AzureRMRecordSet(AzureRMModuleBase):
|
|||
ttl=self.time_to_live
|
||||
)
|
||||
|
||||
if not self.record_type_metadata['is_list']:
|
||||
records_to_create_or_update = self.input_sdk_records[0]
|
||||
elif self.record_mode == 'append' and record_set: # append mode, merge with existing values before update
|
||||
records_to_create_or_update = set(self.input_sdk_records).union(set(server_records))
|
||||
else:
|
||||
records_to_create_or_update = self.input_sdk_records
|
||||
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_args[self.record_type_metadata['attrname']] = records_to_create_or_update
|
||||
record_set = self.dns_models.RecordSet(**record_set_args)
|
||||
|
||||
record_set = RecordSet(**record_set_args)
|
||||
|
||||
rsout = self.dns_client.record_sets.create_or_update(self.resource_group, self.zone_name, self.relative_name, self.record_type, record_set)
|
||||
self.results['state'] = self.create_or_update(record_set)
|
||||
|
||||
elif self.state == 'absent':
|
||||
# delete record set
|
||||
|
@ -344,45 +361,56 @@ class AzureRMRecordSet(AzureRMModuleBase):
|
|||
|
||||
return self.results
|
||||
|
||||
def create_or_update(self, record_set):
|
||||
try:
|
||||
record_set = self.dns_client.record_sets.create_or_update(resource_group_name=self.resource_group,
|
||||
zone_name=self.zone_name,
|
||||
relative_record_set_name=self.relative_name,
|
||||
record_type=self.record_type,
|
||||
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.dns_client.record_sets.delete(self.resource_group, self.zone_name, self.relative_name, self.record_type)
|
||||
self.dns_client.record_sets.delete(resource_group_name=self.resource_group,
|
||||
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, str(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_sdk_class = self.record_type_metadata['classobj']
|
||||
record_argspec = inspect.getargspec(record_sdk_class.__init__)
|
||||
return [record_sdk_class(**dict([(k, v) for k, v in iteritems(x) if k in record_argspec.args])) for x in input_records]
|
||||
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.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(input_records)
|
||||
server_set = set(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
|
||||
return len(input_set.difference(server_set)) > 0
|
||||
input_set = server_set.union(input_set)
|
||||
|
||||
# non-append mode; any difference in the sets is a change
|
||||
return input_set != server_set
|
||||
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
|
||||
|
||||
# Quick 'n dirty hash impl suitable for monkeypatching onto SDK model objects (so we can use set comparisons)
|
||||
def gethash(self):
|
||||
if not getattr(self, '_cachedhash', None):
|
||||
spec = inspect.getargspec(self.__init__)
|
||||
valuetuple = tuple(
|
||||
map(lambda v: v if not isinstance(v, list) else str(v), [
|
||||
getattr(self, x, None) for x in spec.args if x != 'self'
|
||||
])
|
||||
)
|
||||
self._cachedhash = hash(valuetuple)
|
||||
return self._cachedhash
|
||||
def recordset_to_dict(self, recordset):
|
||||
result = recordset.as_dict()
|
||||
result['type'] = result['type'].strip('Microsoft.Network/dnszones/')
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
@ -38,7 +38,7 @@ options:
|
|||
top:
|
||||
description:
|
||||
- Limit the maximum number of record sets to return
|
||||
default: 100
|
||||
type: int
|
||||
|
||||
extends_documentation_fragment:
|
||||
- azure
|
||||
|
@ -91,6 +91,41 @@ azure_dnsrecordset:
|
|||
"type": "Microsoft.Network/dnszones/A"
|
||||
}
|
||||
]
|
||||
dnsrecordsets:
|
||||
description: List of record set dicts, which shares the same hierarchy as azure_rm_dnsrecordset module's parameter.
|
||||
returned: always
|
||||
type: list
|
||||
contains:
|
||||
id:
|
||||
description: ID of the dns recordset.
|
||||
sample: "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/testing/providers/Microsoft.Network/dnszones/newzone.com/A/servera"
|
||||
relative_name:
|
||||
description: Name of the dns recordset.
|
||||
sample: servera
|
||||
record_type:
|
||||
description:
|
||||
- The type of the record set.
|
||||
- Can be C(A), C(AAAA), C(CNAME), C(MX), C(NS), C(SRV), C(TXT), C(PTR).
|
||||
sample: A
|
||||
time_to_live:
|
||||
description: Time to live of the record set in seconds.
|
||||
sample: 12900
|
||||
records:
|
||||
description: List of records depending on the type of recordset.
|
||||
sample: [
|
||||
{
|
||||
"ipv4Address": "10.4.5.7"
|
||||
},
|
||||
{
|
||||
"ipv4Address": "2.4.5.8"
|
||||
}
|
||||
]
|
||||
provisioning_state:
|
||||
description: Provision state of the resource.
|
||||
sample: Successed
|
||||
fqdn:
|
||||
description: Fully qualified domain name of the record set.
|
||||
sample: www.newzone.com
|
||||
'''
|
||||
|
||||
from ansible.module_utils.azure_rm_common import AzureRMModuleBase
|
||||
|
@ -105,6 +140,21 @@ except Exception:
|
|||
AZURE_OBJECT_CLASS = 'RecordSet'
|
||||
|
||||
|
||||
RECORDSET_VALUE_MAP = dict(
|
||||
A='arecords',
|
||||
AAAA='aaaa_records',
|
||||
CNAME='cname_record',
|
||||
MX='mx_records',
|
||||
NS='ns_records',
|
||||
PTR='ptr_records',
|
||||
SRV='srv_records',
|
||||
TXT='txt_records',
|
||||
SOA='soa_record',
|
||||
CAA='caa_records'
|
||||
# FUTURE: add missing record types from https://github.com/Azure/azure-sdk-for-python/blob/master/azure-mgmt-dns/azure/mgmt/dns/models/record_set.py
|
||||
)
|
||||
|
||||
|
||||
class AzureRMRecordSetFacts(AzureRMModuleBase):
|
||||
|
||||
def __init__(self):
|
||||
|
@ -115,7 +165,7 @@ class AzureRMRecordSetFacts(AzureRMModuleBase):
|
|||
resource_group=dict(type='str'),
|
||||
zone_name=dict(type='str'),
|
||||
record_type=dict(type='str'),
|
||||
top=dict(type='str', default='100')
|
||||
top=dict(type='int')
|
||||
)
|
||||
|
||||
# store the results of the module operation
|
||||
|
@ -137,22 +187,29 @@ class AzureRMRecordSetFacts(AzureRMModuleBase):
|
|||
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
|
||||
self.results['ansible_facts']['azure_dnsrecordset'] = self.get_item()
|
||||
results = self.get_item()
|
||||
elif self.record_type:
|
||||
# else, they just want all the record sets of a specific type
|
||||
self.results['ansible_facts']['azure_dnsrecordset'] = self.list_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
|
||||
self.results['ansible_facts']['azure_dnsrecordset'] = self.list_zone()
|
||||
results = self.list_zone()
|
||||
|
||||
self.results['ansible_facts']['azure_dnsrecordset'] = self.serialize_list(results)
|
||||
self.results['dnsrecordsets'] = self.curated_list(results)
|
||||
return self.results
|
||||
|
||||
def get_item(self):
|
||||
|
@ -166,33 +223,54 @@ class AzureRMRecordSetFacts(AzureRMModuleBase):
|
|||
except CloudError:
|
||||
pass
|
||||
|
||||
results = [self.serialize_obj(item, AZURE_OBJECT_CLASS)]
|
||||
results = [item]
|
||||
return results
|
||||
|
||||
def list_type(self):
|
||||
self.log('Lists the record sets of a specified type in a DNS zone')
|
||||
try:
|
||||
response = self.dns_client.record_sets.list_by_type(self.resource_group, self.zone_name, self.record_type, top=int(self.top))
|
||||
response = self.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(self.serialize_obj(item, AZURE_OBJECT_CLASS))
|
||||
results.append(item)
|
||||
return results
|
||||
|
||||
def list_zone(self):
|
||||
self.log('Lists all record sets in a DNS zone')
|
||||
try:
|
||||
response = self.dns_client.record_sets.list_by_dns_zone(self.resource_group, self.zone_name, top=int(self.top))
|
||||
response = self.dns_client.record_sets.list_by_dns_zone(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(self.serialize_obj(item, AZURE_OBJECT_CLASS))
|
||||
results.append(item)
|
||||
return results
|
||||
|
||||
def serialize_list(self, raws):
|
||||
return [self.serialize_obj(item, AZURE_OBJECT_CLASS) for item in raws] if raws else []
|
||||
|
||||
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/dnszones/'):]
|
||||
records = getattr(record, RECORDSET_VALUE_MAP.get(record_type))
|
||||
if not isinstance(records, list):
|
||||
records = [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,
|
||||
provisioning_state=record.provisioning_state
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
AzureRMRecordSetFacts()
|
||||
|
|
|
@ -53,6 +53,27 @@ options:
|
|||
choices:
|
||||
- absent
|
||||
- present
|
||||
type:
|
||||
description:
|
||||
- The type of this DNS zone (public or private)
|
||||
choices:
|
||||
- public
|
||||
- private
|
||||
version_added: 2.8
|
||||
registration_virtual_networks:
|
||||
description:
|
||||
- A list of references to virtual networks that register hostnames in this DNS zone.
|
||||
- This is a only when I(type) is C(private).
|
||||
- Each element can be the name or resource id, or a dict contains C(name), C(resource_group) information of the virtual network.
|
||||
version_added: 2.8
|
||||
type: list
|
||||
resolution_virtual_networks:
|
||||
description:
|
||||
- A list of references to virtual networks that resolve records in this DNS zone.
|
||||
- This is a only when I(type) is C(private).
|
||||
- Each element can be the name or resource id, or a dict contains C(name), C(resource_group) information of the virtual network.
|
||||
version_added: 2.8
|
||||
type: list
|
||||
|
||||
extends_documentation_fragment:
|
||||
- azure
|
||||
|
@ -92,16 +113,18 @@ state:
|
|||
"ns3-07.azure-dns.org.",
|
||||
"ns4-07.azure-dns.info."
|
||||
],
|
||||
"number_of_record_sets": 2
|
||||
"number_of_record_sets": 2,
|
||||
"type": "private",
|
||||
"resolution_virtual_networks": ["/subscriptions/XXXX/resourceGroups/Testing/providers/Microsoft.Network/virtualNetworks/foo"]
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
from ansible.module_utils.azure_rm_common import AzureRMModuleBase
|
||||
from ansible.module_utils.azure_rm_common import AzureRMModuleBase, format_resource_id
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
from msrestazure.azure_exceptions import CloudError
|
||||
from azure.mgmt.dns.models import Zone
|
||||
except ImportError:
|
||||
# This is handled in azure_rm_common
|
||||
pass
|
||||
|
@ -115,7 +138,10 @@ class AzureRMDNSZone(AzureRMModuleBase):
|
|||
self.module_arg_spec = dict(
|
||||
resource_group=dict(type='str', required=True),
|
||||
name=dict(type='str', required=True),
|
||||
state=dict(choices=['present', 'absent'], default='present', type='str')
|
||||
state=dict(choices=['present', 'absent'], default='present', type='str'),
|
||||
type=dict(type='str', choices=['private', 'public']),
|
||||
registration_virtual_networks=dict(type='list', elements='raw'),
|
||||
resolution_virtual_networks=dict(type='list', elements='raw')
|
||||
)
|
||||
|
||||
# store the results of the module operation
|
||||
|
@ -128,6 +154,9 @@ class AzureRMDNSZone(AzureRMModuleBase):
|
|||
self.name = None
|
||||
self.state = None
|
||||
self.tags = None
|
||||
self.type = None
|
||||
self.registration_virtual_networks = None
|
||||
self.resolution_virtual_networks = None
|
||||
|
||||
super(AzureRMDNSZone, self).__init__(self.module_arg_spec,
|
||||
supports_check_mode=True,
|
||||
|
@ -140,6 +169,9 @@ class AzureRMDNSZone(AzureRMModuleBase):
|
|||
for key in list(self.module_arg_spec.keys()) + ['tags']:
|
||||
setattr(self, key, kwargs[key])
|
||||
|
||||
self.registration_virtual_networks = self.preprocess_vn_list(self.registration_virtual_networks)
|
||||
self.resolution_virtual_networks = self.preprocess_vn_list(self.resolution_virtual_networks)
|
||||
|
||||
self.results['check_mode'] = self.check_mode
|
||||
|
||||
# retrieve resource group to make sure it exists
|
||||
|
@ -162,7 +194,22 @@ class AzureRMDNSZone(AzureRMModuleBase):
|
|||
update_tags, results['tags'] = self.update_tags(results['tags'])
|
||||
if update_tags:
|
||||
changed = True
|
||||
|
||||
if self.type and results['type'] != self.type:
|
||||
changed = True
|
||||
results['type'] = self.type
|
||||
if self.resolution_virtual_networks:
|
||||
if set(self.resolution_virtual_networks) != set(results['resolution_virtual_networks'] or []):
|
||||
changed = True
|
||||
results['resolution_virtual_networks'] = self.resolution_virtual_networks
|
||||
else:
|
||||
# this property should not be changed
|
||||
self.resolution_virtual_networks = results['resolution_virtual_networks']
|
||||
if self.registration_virtual_networks:
|
||||
if set(self.registration_virtual_networks) != set(results['registration_virtual_networks'] or []):
|
||||
changed = True
|
||||
results['registration_virtual_networks'] = self.registration_virtual_networks
|
||||
else:
|
||||
self.registration_virtual_networks = results['registration_virtual_networks']
|
||||
elif self.state == 'absent':
|
||||
changed = True
|
||||
|
||||
|
@ -183,16 +230,13 @@ class AzureRMDNSZone(AzureRMModuleBase):
|
|||
|
||||
if changed:
|
||||
if self.state == 'present':
|
||||
if not zone:
|
||||
# create new zone
|
||||
self.log('Creating zone {0}'.format(self.name))
|
||||
zone = Zone(location='global', tags=self.tags)
|
||||
else:
|
||||
# update zone
|
||||
zone = Zone(
|
||||
location='global',
|
||||
tags=results['tags']
|
||||
)
|
||||
zone = self.dns_models.Zone(zone_type=str.capitalize(self.type) if self.type else None,
|
||||
tags=self.tags,
|
||||
location='global')
|
||||
if self.resolution_virtual_networks:
|
||||
zone.resolution_virtual_networks = self.construct_subresource_list(self.resolution_virtual_networks)
|
||||
if self.registration_virtual_networks:
|
||||
zone.registration_virtual_networks = self.construct_subresource_list(self.registration_virtual_networks)
|
||||
self.results['state'] = self.create_or_update_zone(zone)
|
||||
elif self.state == 'absent':
|
||||
# delete zone
|
||||
|
@ -208,7 +252,7 @@ class AzureRMDNSZone(AzureRMModuleBase):
|
|||
# create or update the new Zone object we created
|
||||
new_zone = self.dns_client.zones.create_or_update(self.resource_group, self.name, zone)
|
||||
except Exception as exc:
|
||||
self.fail("Error creating or updating zone {0} - {1}".format(self.name, str(exc)))
|
||||
self.fail("Error creating or updating zone {0} - {1}".format(self.name, exc.message or str(exc)))
|
||||
return zone_to_dict(new_zone)
|
||||
|
||||
def delete_zone(self):
|
||||
|
@ -217,9 +261,23 @@ class AzureRMDNSZone(AzureRMModuleBase):
|
|||
poller = self.dns_client.zones.delete(self.resource_group, self.name)
|
||||
result = self.get_poller_result(poller)
|
||||
except Exception as exc:
|
||||
self.fail("Error deleting zone {0} - {1}".format(self.name, str(exc)))
|
||||
self.fail("Error deleting zone {0} - {1}".format(self.name, exc.message or str(exc)))
|
||||
return result
|
||||
|
||||
def preprocess_vn_list(self, vn_list):
|
||||
return [self.parse_vn_id(x) for x in vn_list] if vn_list else None
|
||||
|
||||
def parse_vn_id(self, vn):
|
||||
vn_dict = self.parse_resource_to_dict(vn) if not isinstance(vn, dict) else vn
|
||||
return format_resource_id(val=vn_dict['name'],
|
||||
subscription_id=vn_dict.get('subscription') or self.subscription_id,
|
||||
namespace='Microsoft.Network',
|
||||
types='virtualNetworks',
|
||||
resource_group=vn_dict.get('resource_group') or self.resource_group)
|
||||
|
||||
def construct_subresource_list(self, raw):
|
||||
return [self.dns_models.SubResource(id=x) for x in raw] if raw else None
|
||||
|
||||
|
||||
def zone_to_dict(zone):
|
||||
# turn Zone object into a dictionary (serialization)
|
||||
|
@ -228,7 +286,10 @@ def zone_to_dict(zone):
|
|||
name=zone.name,
|
||||
number_of_record_sets=zone.number_of_record_sets,
|
||||
name_servers=zone.name_servers,
|
||||
tags=zone.tags
|
||||
tags=zone.tags,
|
||||
type=zone.zone_type.value.lower(),
|
||||
registration_virtual_networks=[to_native(x.id) for x in zone.registration_virtual_networks] if zone.registration_virtual_networks else None,
|
||||
resolution_virtual_networks=[to_native(x.id) for x in zone.resolution_virtual_networks] if zone.resolution_virtual_networks else None
|
||||
)
|
||||
return result
|
||||
|
||||
|
|
|
@ -73,9 +73,52 @@ azure_dnszones:
|
|||
},
|
||||
"tags": {}
|
||||
}]
|
||||
dnszones:
|
||||
description: List of zone dicts, which share the same layout as azure_rm_dnszone module parameter.
|
||||
returned: always
|
||||
type: list
|
||||
contains:
|
||||
id:
|
||||
description:
|
||||
- id of the DNS Zone.
|
||||
sample: "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/foo/providers/Microsoft.Network/dnszones/azure.com"
|
||||
name:
|
||||
description:
|
||||
- name of the DNS Zone.
|
||||
sample: azure.com
|
||||
type:
|
||||
description:
|
||||
- The type of this DNS zone (public or private)
|
||||
sample: private
|
||||
registration_virtual_networks:
|
||||
description:
|
||||
- A list of references to virtual networks that register hostnames in this DNS zone.
|
||||
sample: ["/subscriptions/XXXXXXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/foo/providers/Microsoft.Network/virtualNetworks/bar"]
|
||||
resolution_virtual_networks:
|
||||
description:
|
||||
- A list of references to virtual networks that resolve records in this DNS zone.
|
||||
sample: ["/subscriptions/XXXXXXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/foo/providers/Microsoft.Network/virtualNetworks/deadbeef"]
|
||||
number_of_record_sets:
|
||||
description:
|
||||
- The current number of record sets in this DNS zone.
|
||||
sample: 2
|
||||
max_number_of_record_sets:
|
||||
description:
|
||||
- The maximum number of record sets that can be created in this DNS zone.
|
||||
sample: 5000
|
||||
name_servers:
|
||||
description:
|
||||
- The name servers for this DNS zone.
|
||||
sample: [
|
||||
"ns1-03.azure-dns.com.",
|
||||
"ns2-03.azure-dns.net.",
|
||||
"ns3-03.azure-dns.org.",
|
||||
"ns4-03.azure-dns.info."
|
||||
]
|
||||
'''
|
||||
|
||||
from ansible.module_utils.azure_rm_common import AzureRMModuleBase
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
from msrestazure.azure_exceptions import CloudError
|
||||
|
@ -118,17 +161,20 @@ class AzureRMDNSZoneFacts(AzureRMModuleBase):
|
|||
if self.name and not self.resource_group:
|
||||
self.fail("Parameter error: resource group required when filtering by name.")
|
||||
|
||||
results = []
|
||||
# list the conditions and what to return based on user input
|
||||
|
||||
if self.name is not None:
|
||||
# if there is a name, facts about that specific zone
|
||||
self.results['ansible_facts']['azure_dnszones'] = self.get_item()
|
||||
results = self.get_item()
|
||||
elif self.resource_group:
|
||||
# all the zones listed in that specific resource group
|
||||
self.results['ansible_facts']['azure_dnszones'] = self.list_resource_group()
|
||||
results = self.list_resource_group()
|
||||
else:
|
||||
# all the zones in a subscription
|
||||
self.results['ansible_facts']['azure_dnszones'] = self.list_items()
|
||||
results = self.list_items()
|
||||
|
||||
self.results['ansible_facts']['azure_dnszones'] = self.serialize_items(results)
|
||||
self.results['dnszones'] = self.curated_items(results)
|
||||
|
||||
return self.results
|
||||
|
||||
|
@ -144,7 +190,7 @@ class AzureRMDNSZoneFacts(AzureRMModuleBase):
|
|||
|
||||
# serialize result
|
||||
if item and self.has_tags(item.tags, self.tags):
|
||||
results = [self.serialize_obj(item, AZURE_OBJECT_CLASS)]
|
||||
results = [item]
|
||||
return results
|
||||
|
||||
def list_resource_group(self):
|
||||
|
@ -157,7 +203,7 @@ class AzureRMDNSZoneFacts(AzureRMModuleBase):
|
|||
results = []
|
||||
for item in response:
|
||||
if self.has_tags(item.tags, self.tags):
|
||||
results.append(self.serialize_obj(item, AZURE_OBJECT_CLASS))
|
||||
results.append(item)
|
||||
return results
|
||||
|
||||
def list_items(self):
|
||||
|
@ -170,9 +216,28 @@ class AzureRMDNSZoneFacts(AzureRMModuleBase):
|
|||
results = []
|
||||
for item in response:
|
||||
if self.has_tags(item.tags, self.tags):
|
||||
results.append(self.serialize_obj(item, AZURE_OBJECT_CLASS))
|
||||
results.append(item)
|
||||
return results
|
||||
|
||||
def serialize_items(self, raws):
|
||||
return [self.serialize_obj(item, AZURE_OBJECT_CLASS) for item in raws] if raws else []
|
||||
|
||||
def curated_items(self, raws):
|
||||
return [self.zone_to_dict(item) for item in raws] if raws else []
|
||||
|
||||
def zone_to_dict(self, zone):
|
||||
return dict(
|
||||
id=zone.id,
|
||||
name=zone.name,
|
||||
number_of_record_sets=zone.number_of_record_sets,
|
||||
max_number_of_record_sets=zone.max_number_of_record_sets,
|
||||
name_servers=zone.name_servers,
|
||||
tags=zone.tags,
|
||||
type=zone.zone_type.value.lower(),
|
||||
registration_virtual_networks=[to_native(x.id) for x in zone.registration_virtual_networks] if zone.registration_virtual_networks else None,
|
||||
resolution_virtual_networks=[to_native(x.id) for x in zone.resolution_virtual_networks] if zone.resolution_virtual_networks else None
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
AzureRMDNSZoneFacts()
|
||||
|
|
|
@ -9,7 +9,7 @@ azure-mgmt-compute==4.3.1
|
|||
azure-mgmt-containerinstance==0.4.0
|
||||
azure-mgmt-containerregistry==2.0.0
|
||||
azure-mgmt-containerservice==4.2.2
|
||||
azure-mgmt-dns==1.2.0
|
||||
azure-mgmt-dns==2.1.0
|
||||
azure-mgmt-keyvault==0.40.0
|
||||
azure-mgmt-marketplaceordering==0.1.0
|
||||
azure-mgmt-monitor==0.5.2
|
||||
|
|
|
@ -2,11 +2,20 @@
|
|||
set_fact:
|
||||
domain_name: "{{ resource_group | hash('md5') | truncate(16, True, '') + (65535 | random | string) }}"
|
||||
|
||||
- name: Create a DNS zone (check mode)
|
||||
azure_rm_dnszone:
|
||||
resource_group: "{{ resource_group }}"
|
||||
name: "{{ domain_name }}.com"
|
||||
register: results
|
||||
check_mode: yes
|
||||
|
||||
- assert:
|
||||
that: results.changed
|
||||
|
||||
- name: Create a DNS zone
|
||||
azure_rm_dnszone:
|
||||
resource_group: "{{ resource_group }}"
|
||||
name: "{{ domain_name }}.com"
|
||||
state: present
|
||||
register: results
|
||||
|
||||
- assert:
|
||||
|
@ -16,7 +25,6 @@
|
|||
azure_rm_dnszone:
|
||||
resource_group: "{{ resource_group }}"
|
||||
name: "{{ domain_name }}.com"
|
||||
state: present
|
||||
tags:
|
||||
test: modified
|
||||
register: results
|
||||
|
@ -26,33 +34,79 @@
|
|||
- results.changed
|
||||
- results.state.tags.test == 'modified'
|
||||
|
||||
- name: Test check_mode
|
||||
azure_rm_dnszone:
|
||||
resource_group: "{{ resource_group }}"
|
||||
name: "{{ domain_name }}.com"
|
||||
state: present
|
||||
tags:
|
||||
test: new_modified
|
||||
check_mode: yes
|
||||
register: results
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- results.changed
|
||||
- results.state.tags.test == 'new_modified'
|
||||
- results.check_mode == true
|
||||
|
||||
- name: Retrieve DNS Zone Facts
|
||||
azure_rm_dnszone_facts:
|
||||
resource_group: "{{ resource_group }}"
|
||||
name: "{{ domain_name }}.com"
|
||||
register: results
|
||||
register: zones
|
||||
|
||||
- name: Assert that facts module returned result
|
||||
assert:
|
||||
that:
|
||||
- not results.changed
|
||||
- results.ansible_facts.azure_dnszones[0].tags.test == 'modified'
|
||||
- azure_dnszones[0].tags.test == 'modified'
|
||||
- zones.dnszones[0].type == 'public'
|
||||
|
||||
- name: Create virtual network
|
||||
azure_rm_virtualnetwork:
|
||||
resource_group: "{{ resource_group }}"
|
||||
name: "{{ item }}"
|
||||
address_prefixes_cidr:
|
||||
- 10.1.0.0/16
|
||||
- 172.100.0.0/16
|
||||
with_items:
|
||||
- "{{ domain_name }}registration1"
|
||||
- "{{ domain_name }}resolution1"
|
||||
- "{{ domain_name }}registration2"
|
||||
- "{{ domain_name }}resolution2"
|
||||
|
||||
- name: Create private dns zone
|
||||
azure_rm_dnszone:
|
||||
name: "{{ domain_name }}.private"
|
||||
resource_group: "{{ resource_group }}"
|
||||
type: private
|
||||
registration_virtual_networks:
|
||||
- name: "{{ domain_name }}registration1"
|
||||
resolution_virtual_networks:
|
||||
- name: "{{ domain_name }}resolution1"
|
||||
- name: "{{ domain_name }}resolution2"
|
||||
register: results
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "results.state.registration_virtual_networks | length == 1"
|
||||
- "results.state.resolution_virtual_networks | length == 2"
|
||||
- results.state.type == 'private'
|
||||
|
||||
- name: Update private dns zone
|
||||
azure_rm_dnszone:
|
||||
name: "{{ domain_name }}.private"
|
||||
resource_group: "{{ resource_group }}"
|
||||
type: private
|
||||
registration_virtual_networks:
|
||||
- name: "{{ domain_name }}registration1"
|
||||
resolution_virtual_networks:
|
||||
- name: "{{ domain_name }}resolution1"
|
||||
register: results
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "results.state.registration_virtual_networks | length == 1"
|
||||
- "results.state.resolution_virtual_networks | length == 1"
|
||||
- results.state.type == 'private'
|
||||
|
||||
- name: Test idempotent
|
||||
azure_rm_dnszone:
|
||||
name: "{{ item }}"
|
||||
resource_group: "{{ resource_group }}"
|
||||
with_items:
|
||||
- "{{ domain_name }}.com"
|
||||
- "{{ domain_name }}.private"
|
||||
register: results
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "not {{ item.changed }}"
|
||||
with_items: "{{ results.results }}"
|
||||
|
||||
#
|
||||
# azure_rm_dnsrecordset test
|
||||
|
@ -72,7 +126,9 @@
|
|||
|
||||
- name: Assert that A record set was created
|
||||
assert:
|
||||
that: results.changed
|
||||
that:
|
||||
- results.changed
|
||||
- 'results.state.arecords | length == 3'
|
||||
|
||||
- name: re-run "A" record with same values
|
||||
azure_rm_dnsrecordset:
|
||||
|
@ -105,6 +161,7 @@
|
|||
assert:
|
||||
that:
|
||||
- results.changed
|
||||
- 'results.state.arecords | length == 4'
|
||||
|
||||
- name: re-update "A" record set with additional record
|
||||
azure_rm_dnsrecordset:
|
||||
|
@ -138,6 +195,7 @@
|
|||
assert:
|
||||
that:
|
||||
- results.changed
|
||||
- 'results.state.arecords | length == 3'
|
||||
|
||||
- name: Check_mode test
|
||||
azure_rm_dnsrecordset:
|
||||
|
@ -213,38 +271,56 @@
|
|||
assert:
|
||||
that:
|
||||
- not results.changed
|
||||
- results.ansible_facts.azure_dnsrecordset[0].name == 'www'
|
||||
- azure_dnsrecordset[0].name == 'www'
|
||||
- results.dnsrecordsets[0].relative_name == 'www'
|
||||
- 'results.dnsrecordsets[0].records | length == 3'
|
||||
- results.dnsrecordsets[0].record_type == 'A'
|
||||
|
||||
- name: Retrieve DNS Record Set Facts for all Record Sets
|
||||
azure_rm_dnsrecordset_facts:
|
||||
resource_group: "{{ resource_group }}"
|
||||
zone_name: "{{ domain_name }}.com"
|
||||
register: results
|
||||
register: facts
|
||||
|
||||
- name: Assert that facts module returned result for all Record Sets
|
||||
assert:
|
||||
that:
|
||||
- not results.changed
|
||||
- results.ansible_facts.azure_dnsrecordset[0].name == '@'
|
||||
- results.ansible_facts.azure_dnsrecordset[1].name == '@'
|
||||
- results.ansible_facts.azure_dnsrecordset[4].name == 'www'
|
||||
- not facts.changed
|
||||
- facts.ansible_facts.azure_dnsrecordset[0].name == '@'
|
||||
- facts.ansible_facts.azure_dnsrecordset[1].name == '@'
|
||||
- facts.ansible_facts.azure_dnsrecordset[4].name == 'www'
|
||||
|
||||
#
|
||||
# azure_rm_dnsrecordset cleanup
|
||||
#
|
||||
- name: delete a record set
|
||||
- name: delete all record sets except for @
|
||||
azure_rm_dnsrecordset:
|
||||
resource_group: "{{ resource_group }}"
|
||||
relative_name: www
|
||||
relative_name: "{{ item.relative_name }}"
|
||||
zone_name: "{{ domain_name }}.com"
|
||||
record_type: A
|
||||
record_type: "{{ item.record_type }}"
|
||||
state: absent
|
||||
with_items: "{{ facts.dnsrecordsets }}"
|
||||
when:
|
||||
- item.relative_name != '@'
|
||||
register: results
|
||||
|
||||
- name: Assert that record set deleted
|
||||
assert:
|
||||
that: results.changed
|
||||
|
||||
- name: Retrieve DNS Record Set Facts for all Record Sets
|
||||
azure_rm_dnsrecordset_facts:
|
||||
resource_group: "{{ resource_group }}"
|
||||
zone_name: "{{ domain_name }}.com"
|
||||
register: facts
|
||||
|
||||
- name: Assert all record set deleted
|
||||
assert:
|
||||
that:
|
||||
- item.relative_name == '@'
|
||||
with_items: "{{ facts.dnsrecordsets }}"
|
||||
|
||||
- name: (idempotence test) re-run record set absent
|
||||
azure_rm_dnsrecordset:
|
||||
resource_group: "{{ resource_group }}"
|
||||
|
@ -262,7 +338,20 @@
|
|||
# azure_rm_dnszone cleanup
|
||||
#
|
||||
- name: Delete DNS zone
|
||||
azure_rm_dnszone:
|
||||
resource_group: "{{ resource_group }}"
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- "{{ domain_name }}.com"
|
||||
- "{{ domain_name }}.private"
|
||||
|
||||
- name: Delete DNS zone (idempotent)
|
||||
azure_rm_dnszone:
|
||||
resource_group: "{{ resource_group }}"
|
||||
name: "{{ domain_name }}.com"
|
||||
state: absent
|
||||
register: results
|
||||
|
||||
- assert:
|
||||
that: not results.changed
|
|
@ -9,7 +9,7 @@ azure-mgmt-compute==4.3.1
|
|||
azure-mgmt-containerinstance==0.4.0
|
||||
azure-mgmt-containerregistry==2.0.0
|
||||
azure-mgmt-containerservice==4.2.2
|
||||
azure-mgmt-dns==1.2.0
|
||||
azure-mgmt-dns==2.1.0
|
||||
azure-mgmt-keyvault==0.40.0
|
||||
azure-mgmt-marketplaceordering==0.1.0
|
||||
azure-mgmt-monitor==0.5.2
|
||||
|
|
Loading…
Reference in a new issue