#!/usr/bin/python # # Copyright (c) 2021 Ross Bender (@l3ender) # # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = ''' --- module: azure_rm_privateendpointdnszonegroup version_added: "1.10.0" short_description: Create, update, or manage private endpoint DNS zone groups. description: - Create, update, or manage private endpoint DNS zone groups. options: name: description: - The name of the private endpoint DNS zone group. type: str required: true private_endpoint: description: - Name of private endpoint. type: str required: true resource_group: description: - Resource group of the private endpoint. type: str required: true private_dns_zone_configs: description: - The Private DNS zones configurations. type: list elements: dict suboptions: name: description: - The name of the private dns zone configs. type: str private_dns_zone: description: - The name of the Private DNS zone. - If set, the Private DNS Zone under the current resource group is obtained. type: str private_dns_zone_id: description: - The ID of the private dns zone. - If set, gets the value of the specified connection Private DNS Zone. type: str state: description: - State of the private endpoint DNS zone group. Use C(present) to create or update and C(absent) to delete. default: present type: str choices: - absent - present extends_documentation_fragment: - azure.azcollection.azure author: - Ross Bender (@l3ender) ''' EXAMPLES = ''' - name: Create zone group for private endpoint azure_rm_privateendpointdnszonegroup: name: "my-zone-group" private_endpoint: "my-private-endpoint" resource_group: "my-resource-group" private_dns_zone_configs: - name: "default" private_dns_zone: "privatelink.postgres.database.azure.com" - name: Create zone group for private endpoint azure_rm_privateendpointdnszonegroup: name: "my-zone-group" private_endpoint: "my-private-endpoint" resource_group: "my-resource-group" state: "absent" ''' RETURN = ''' state: description: - Current state of the private endpoint zone group. returned: always type: complex contains: id: description: - Resource ID of the private endpoint zone group. sample: >- /subscriptions/xxx/resourceGroups/myResourceGroup/providers/Microsoft.Network/privateEndpoints/ myPrivateEndpoint/privateDnsZoneGroups/myZoneGroup returned: always type: str name: description: - Name of the private endpoint zone group. returned: always type: str sample: myZoneGroup private_dns_zone_configs: description: - List of zone configuration within the zone group. returned: always type: list contains: name: description: - Name of the zone config. returned: always type: str sample: default private_dns_zone_id: description: - ID of the private DNS zone. returned: always type: str sample: >- /subscriptions/xxx/resourceGroups/myResourceGroup/providers/Microsoft.Network/ privateDnsZones/privatelink.postgres.database.azure.com record_sets: description: - List of DNS records for zone. returned: always type: list contains: fqdn: description: - Fully qualified domain name of the record. returned: always type: str sample: myPostgreSqlSrv-123.privatelink.postgres.database.azure.com ip_addresses: description: - IP addresses for the record. returned: always type: list sample: ['10.1.0.4'] provisioning_state: description: - Provisioning state of the resource. returned: always type: str sample: Succeeded record_set_name: description: - Name of the record. returned: always type: str sample: myPostgreSqlSrv-123 record_type: description: - Type of record. returned: always type: str sample: A ttl: description: - Time to live value of the record. returned: always type: int sample: 10 provisioning_state: description: - Provisioning state of the resource. returned: always type: str sample: Succeeded ''' try: from azure.mgmt.core.tools import resource_id from azure.core.exceptions import ResourceNotFoundError from azure.core.polling import LROPoller except ImportError: # This is handled in azure_rm_common pass from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBaseExt private_dns_zone_configs_spec = dict( name=dict(type="str"), private_dns_zone=dict(type="str"), private_dns_zone_id=dict(type="str") ) class Actions: NoAction, Create, Update, Delete = range(4) class AzureRMPrivateEndpointDnsZoneGroup(AzureRMModuleBaseExt): def __init__(self): self.module_arg_spec = dict( name=dict(type="str", required=True), private_endpoint=dict(type="str", required=True), resource_group=dict(type="str", required=True), private_dns_zone_configs=dict(type="list", elements="dict", options=private_dns_zone_configs_spec), state=dict(type="str", default="present", choices=["present", "absent"]), ) self.name = None self.private_endpoint = None self.resource_group = None self.state = None self.parameters = dict() self.results = dict( changed=False, state=dict() ) self.to_do = Actions.NoAction super(AzureRMPrivateEndpointDnsZoneGroup, self).__init__(self.module_arg_spec, supports_tags=False, supports_check_mode=True) def exec_module(self, **kwargs): for key in self.module_arg_spec: if hasattr(self, key): setattr(self, key, kwargs[key]) elif kwargs[key] is not None: self.parameters[key] = kwargs[key] for zone_config in self.parameters.get("private_dns_zone_configs", []): if zone_config.get("private_dns_zone_id") is not None: self.log("The private_dns_zone_id exist, do nothing") else: zone_name = zone_config.pop("private_dns_zone") zone_config["private_dns_zone_id"] = self.private_dns_zone_id(zone_name) self.log("Fetching private endpoint {0}".format(self.name)) old_response = self.get_zone() if old_response is None or not old_response: if self.state == "present": self.to_do = Actions.Create self.ensure_private_endpoint() else: if self.state == "absent": self.to_do = Actions.Delete else: self.results["compare"] = [] if not self.idempotency_check(old_response, self.parameters): self.to_do = Actions.Update if (self.to_do == Actions.Create) or (self.to_do == Actions.Update): self.results["changed"] = True if self.check_mode: return self.results response = self.create_update_zone() elif self.to_do == Actions.Delete: self.results["changed"] = True if self.check_mode: return self.results response = self.delete_zone() else: self.results["changed"] = False response = old_response if response is not None: self.results["state"] = response return self.results def get_zone(self): try: item = self.network_client.private_dns_zone_groups.get(resource_group_name=self.resource_group, private_endpoint_name=self.private_endpoint, private_dns_zone_group_name=self.name) return self.zone_to_dict(item) except ResourceNotFoundError: self.log("Did not find the private endpoint resource") return None def create_update_zone(self): try: self.parameters["name"] = self.name response = self.network_client.private_dns_zone_groups.begin_create_or_update(resource_group_name=self.resource_group, private_endpoint_name=self.private_endpoint, private_dns_zone_group_name=self.name, parameters=self.parameters) if isinstance(response, LROPoller): response = self.get_poller_result(response) return self.zone_to_dict(response) except Exception as exc: self.fail("Error creating or updating DNS zone group {0} for private endpoint {1}: {2}".format(self.name, self.private_endpoint, str(exc))) def ensure_private_endpoint(self): try: self.network_client.private_endpoints.get(resource_group_name=self.resource_group, private_endpoint_name=self.private_endpoint) except ResourceNotFoundError: self.fail("Could not load the private endpoint {0}.".format(self.private_endpoint)) def delete_zone(self): try: response = self.network_client.private_dns_zone_groups.begin_delete(resource_group_name=self.resource_group, private_endpoint_name=self.private_endpoint, private_dns_zone_group_name=self.name) if isinstance(response, LROPoller): response = self.get_poller_result(response) return response except Exception as exc: self.fail("Error deleting private endpoint {0}: {1}".format(self.name, str(exc))) def zone_to_dict(self, zone): if zone is not None: zone_dict = zone.as_dict() else: return None return dict( id=zone_dict.get("id"), name=zone_dict.get("name"), private_dns_zone_configs=[self.zone_config_to_dict(zone_config) for zone_config in zone_dict.get("private_dns_zone_configs", [])], provisioning_state=zone_dict.get("provisioning_state"), ) def zone_config_to_dict(self, zone_config): return dict( name=zone_config.get("name"), private_dns_zone_id=zone_config.get("private_dns_zone_id"), record_sets=[self.record_set_to_dict(record_set) for record_set in zone_config.get("record_sets", [])], ) def record_set_to_dict(self, record_set): return dict( fqdn=record_set.get("fqdn"), ip_addresses=record_set.get("ip_addresses"), provisioning_state=record_set.get("provisioning_state"), record_set_name=record_set.get("record_set_name"), record_type=record_set.get("record_type"), ttl=record_set.get("ttl"), ) def private_dns_zone_id(self, name): return resource_id(subscription=self.subscription_id, resource_group=self.resource_group, namespace='Microsoft.Network', type='privateDnsZones', name=name) def main(): AzureRMPrivateEndpointDnsZoneGroup() if __name__ == "__main__": main()