From 7bd078f93f1c5b502605637f371c45541d5d678c Mon Sep 17 00:00:00 2001 From: jartoo Date: Thu, 6 Jun 2024 13:51:41 -0500 Subject: [PATCH 1/5] Add new module azure_rm_afdorigin to support Azure Frontdoor Standard and Premium --- plugins/modules/azure_rm_afdorigin.py | 488 ++++++++++++++++++ plugins/modules/azure_rm_afdorigin_info.py | 326 ++++++++++++ .../targets/azure_rm_afdorigin/aliases | 3 + .../targets/azure_rm_afdorigin/meta/main.yml | 2 + .../targets/azure_rm_afdorigin/tasks/main.yml | 228 ++++++++ 5 files changed, 1047 insertions(+) create mode 100644 plugins/modules/azure_rm_afdorigin.py create mode 100644 plugins/modules/azure_rm_afdorigin_info.py create mode 100644 tests/integration/targets/azure_rm_afdorigin/aliases create mode 100644 tests/integration/targets/azure_rm_afdorigin/meta/main.yml create mode 100644 tests/integration/targets/azure_rm_afdorigin/tasks/main.yml diff --git a/plugins/modules/azure_rm_afdorigin.py b/plugins/modules/azure_rm_afdorigin.py new file mode 100644 index 000000000..d12425391 --- /dev/null +++ b/plugins/modules/azure_rm_afdorigin.py @@ -0,0 +1,488 @@ +#!/usr/bin/python +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# Python SDK Reference: https://learn.microsoft.com/en-us/python/api/azure-mgmt-cdn/azure.mgmt.cdn.operations.afdoriginsoperations?view=azure-python +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: azure_rm_afdorigin +version_added: "" +short_description: Manage an Azure Front Door Origin to be used with Standard or Premium Frontdoor. +description: + - Create, update and delete an Azure Front Door (AFD) Origin to be used by a Front Door Service Profile created using azure_rm_cdnprofile. + +options: + name: + description: + - Name of the origin that is unique within the AFD Profile. + required: true + type: str + origin: + description: + - AFD Origin properties + type: dict + suboptions: + azure_origin: + description: + - Resource reference to the AFD origin resource. + type: str + enabled_state: + description: + - Whether to enable health probes to be made against backends defined under backend pools. Health probes can only be disabled if there is a single enabled backend in single enabled backend pool. + type: str + choices: + - Enabled + - Disabled + host_name: + description: + - The address of the origin. Domain names, IPv4 addresses, and IPv6 addresses are supported. This should be unique across all origins in an endpoint. + type: str + http_port: + description: + - The value of the HTTP port. Must be between 1 and 65535. + default: 80 + type: int + https_port: + description: + - The value of the HTTPS port. Must be between 1 and 65535. + default: 443 + type: int + origin_host_header: + description: + - The host header value sent to the origin with each request. If you leave this blank, the request hostname determines this value. Azure Front Door origins, such as Web Apps, Blob Storage, and Cloud Services require this host header value to match the origin hostname by default. This overrides the host header defined at the AFD Endpoint. + type: str + priority: + description: + - Priority of origin in given origin group for load balancing. Higher priorities will not be used for load balancing if any lower priority origin is healthy. Must be between 1 and 5. + type: int + shared_private_link_resource: + description: + - The properties of the private link resource for private origin. + type: dict + suboptions: + group_id: + description: + - The group id from the provider of resource the shared private link resource is for. + type: str + private_link: + description: + - The resource id of the resource the shared private link resource is for. + type: str + private_link_location: + description: + - The location of the shared private link resource. + type: str + request_message: + description: + - The request message for requesting approval of the shared private link resource. + type: str + status: + description: + - Status of the shared private link resource. Can be Pending, Approved, Rejected, Disconnected, or Timeout. + type: str + choices: + - Approved + - Disconnected + - Pending + - Rejected + - Timeout + weight: + description: + - Weight of the origin in given origin group for load balancing. Must be between 1 and 1000. + type: int + origin_group_name: + description: + - Name of the origin group which is unique within the profile. + required: true + type: str + profile_name: + description: + - Name of the AFD Profile. + required: true + type: str + resource_group: + description: + - Name of a resource group where the AFD Origin exists or will be created. + required: true + type: str + state: + description: + - Assert the state of the AFD Profile. Use C(present) to create or update an AFD profile and C(absent) to delete it. + default: present + type: str + choices: + - absent + - present + +extends_documentation_fragment: + - azure.azcollection.azure + +author: + - Jarret Tooley (@jartoo) +''' + +EXAMPLES = ''' +- name: Create an AFD Origin + azure_rm_afdorigin: + name: myOrigin + origin_group_name: myOriginGroup + profile_name: myProfile + resource_group: myResourceGroup + state: present + origin: + host_name: "10.0.0.1" + origin_host_header: "10.0.0.1" + http_port: 80 + https_port: 443 + priority: 1 + weight: 1000 + +- name: Delete an AFD Origin + azure_rm_afdorigin: + name: myOrigin + origin_group_name: myOriginGroup + profile_name: myProfile + resource_group: myResourceGroup + state: absent + +''' +RETURN = ''' +id: + description: + - ID of the AFD Origin. + returned: always + type: str + sample: "id: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroup/providers/Microsoft.Cdn/profiles/myProfile/origingroups/myOriginGroup/origins/myOrigin" +host_name: + description: + - Host name of the AFD Origin. + returned: always + type: str + sample: "myorigin.azurefd.net" + +''' +from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase + +import re + +try: + from azure.mgmt.cdn.models import AFDOrigin, AFDOriginUpdateParameters, SharedPrivateLinkResourceProperties + from azure.mgmt.cdn import CdnManagementClient +except ImportError as ec: + # This is handled in azure_rm_common + pass + +def origin_to_dict(origin): + return dict( + azure_origin=origin.azure_origin, + deployment_status=origin.deployment_status, + enabled_state = origin.enabled_state, + # enforce_certificate_check = origin.enforce_certificate_check, # Not fully implemented yet + host_name = origin.host_name, + http_port = origin.http_port, + https_port = origin.https_port, + id = origin.id, + name=origin.name, + origin_group_name=re.sub('\\/.*', '', re.sub('.*origingroups\\/', '', origin.id)), + origin_host_header=origin.origin_host_header, + priority=origin.priority, + provisioning_state=origin.provisioning_state, + shared_private_link_resource=origin.shared_private_link_resource, + type=origin.type, + weight=origin.weight + ) + + +class AzureRMOrigin(AzureRMModuleBase): + + def __init__(self): + self.module_arg_spec = dict( + name=dict( + type='str', + required=True + ), + origin=dict( + type='dict', + options=dict( + azure_origin=dict(type='str'), + enabled_state=dict(type='str'), + # enforce_certification_name_check=dict(type='bool'), + host_name=dict(type='str'), + http_port=dict(type='int',default=80), + https_port=dict(type='int',default=443), + origin_host_header=dict(type='str'), + priority=dict(type='int'), + shared_private_link_resource=dict( + type='dict', + options=dict( + group_id=dict(type='str'), + private_link=dict(type='str'), + private_link_location=dict(type='str'), + request_message=dict(type='str'), + status=dict(type='str',default='Approved',choices=["Pending", "Approved", "Rejected", "Disconnected", "Timeout"]) + ) + ), + weight=dict(type='int') + ) + ), + origin_group_name=dict( + type='str', + required=True + ), + profile_name=dict( + type='str', + required=True + ), + resource_group=dict( + type='str', + required=True + ), + state=dict( + type='str', + default='present', + choices=['present', 'absent'] + ) + ) + self.origin = None + + self.origin_group_name = None + self.name = None + self.profile_name = None + self.resource_group = None + self.state = None + + self.origin_client = None + + required_if = [ + # ('state', 'present', ['host_name']) # TODO: Flesh these out + ] + + self.results = dict(changed=False) + + super(AzureRMOrigin, self).__init__(derived_arg_spec=self.module_arg_spec, + supports_check_mode=True, + supports_tags=False, + required_if=required_if) + + def exec_module(self, **kwargs): + """Main module execution method""" + + for key in list(self.module_arg_spec.keys()): + setattr(self, key, kwargs[key]) + + self.origin_client = self.get_origin_client() + + to_be_updated = False + + response = self.get_origin() + + if self.state == 'present': + + if not response: + self.log("Need to create the Origin") + + if not self.check_mode: + new_response = self.create_origin() + self.results['id'] = new_response['id'] + self.results['host_name'] = new_response['host_name'] + + self.results['changed'] = True + + else: + self.log('Results : {0}'.format(response)) + self.results['id'] = response['id'] + self.results['host_name'] = response['host_name'] + + if response['host_name'] != self.origin['host_name'] and self.origin['host_name']: + to_be_updated = True + if response['http_port'] != self.origin['http_port'] and self.origin['http_port']: + to_be_updated = True + if response['https_port'] != self.origin['https_port'] and self.origin['https_port']: + to_be_updated = True + if response['origin_host_header'] != self.origin['origin_host_header'] and self.origin['origin_host_header']: + to_be_updated = True + if response['priority'] != self.origin['priority'] and self.origin['priority']: + to_be_updated = True + if response['weight'] != self.origin['weight'] and self.origin['weight']: + to_be_updated = True + if response['enabled_state'] != self.origin['enabled_state'] and self.origin['enabled_state']: + to_be_updated = True + # if response['enforce_certificate_name_check'] != self.origin['enforce_certificate_name_check'] and self.origin['enforce_certificate_name_check']: + # to_be_updated = True + if response['shared_private_link_resource']: + if response['shared_private_link_resource']['group_id'] != self.origin['shared_private_link_resource']['group_id'] and self.origin['shared_private_link_resource']['group_id']: + to_be_updated = True + if response['shared_private_link_resource']['private_link'] != self.origin['shared_private_link_resource']['private_link'] and self.origin['shared_private_link_resource']['private_link']: + to_be_updated = True + if response['shared_private_link_resource']['private_link_location'] != self.origin['shared_private_link_resource']['private_link_location'] and self.origin['shared_private_link_resource']['private_link_location']: + to_be_updated = True + if response['shared_private_link_resource']['request_message'] != self.origin['shared_private_link_resource']['request_message'] and self.origin['shared_private_link_resource']['request_message']: + to_be_updated = True + if response['shared_private_link_resource']['status'] != self.origin['shared_private_link_resource']['status'] and self.origin['shared_private_link_resource']['status']: + to_be_updated = True + + if to_be_updated: + self.log("Need to update the Origin") + + if not self.check_mode: + new_response = self.update_origin() + self.results['id'] = new_response['id'] + self.results['host_name'] = new_response['host_name'] + + self.results['changed'] = True + + elif self.state == 'absent': + if not response: + self.log("Origin {0} does not exist.".format(self.name)) + self.results['id'] = "" + self.results['host_name'] = "" + else: + self.log("Need to delete the Origin") + self.results['changed'] = True + self.results['id'] = response['id'] + self.results['host_name'] = response['host_name'] + + if not self.check_mode: + self.delete_origin() + + return self.results + + def create_origin(self): + ''' + Creates a Azure Origin. + + :return: deserialized Azure Origin instance state dictionary + ''' + self.log("Creating the Azure Origin instance {0}".format(self.name)) + + shared_private_link_resource = None + if self.origin['shared_private_link_resource']: + shared_private_link_resource = SharedPrivateLinkResourceProperties( + group_id=self.origin['shared_private_link_resource']['group_id'], + private_link=self.origin['shared_private_link_resource']['private_link'], + private_link_location=self.origin['shared_private_link_resource']['private_link_location'], + request_message=self.origin['shared_private_link_resource']['request_message'], + status=self.origin['shared_private_link_resource']['status'] + ) + + parameters = AFDOrigin( + azure_origin=self.origin['azure_origin'], + host_name=self.origin['host_name'], + http_port=self.origin['http_port'], + https_port=self.origin['https_port'], + origin_host_header=self.origin['origin_host_header'], + priority=self.origin['priority'], + weight=self.origin['weight'], + enabled_state=self.origin['enabled_state'], + shared_private_link_resource=shared_private_link_resource + ) + + try: + poller = self.origin_client.afd_origins.begin_create(resource_group_name=self.resource_group, + profile_name=self.profile_name, + origin_group_name=self.origin_group_name, + origin_name=self.name, + origin=parameters) + response = self.get_poller_result(poller) + return origin_to_dict(response) + except Exception as exc: + self.log('Error attempting to create Azure Origin instance.') + self.fail("Error Creating Azure Origin instance: {0}".format(str(exc))) + + def update_origin(self): + ''' + Updates an Azure Origin. + + :return: deserialized Azure Origin instance state dictionary + ''' + self.log("Updating the Azure Origin instance {0}".format(self.name)) + + shared_private_link_resource = None + if self.origin['shared_private_link_resource']: + shared_private_link_resource = SharedPrivateLinkResourceProperties( + group_id=self.origin['shared_private_link_resource']['group_id'], + private_link=self.origin['shared_private_link_resource']['private_link'], + private_link_location=self.origin['shared_private_link_resource']['private_link_location'], + request_message=self.origin['shared_private_link_resource']['request_message'], + status=self.origin['shared_private_link_resource']['status'] + ) + + parameters = AFDOriginUpdateParameters( + azure_origin=self.origin['azure_origin'], + host_name=self.origin['host_name'], + http_port=self.origin['http_port'], + https_port=self.origin['https_port'], + origin_host_header=self.origin['origin_host_header'], + priority=self.origin['priority'], + weight=self.origin['weight'], + enabled_state=self.origin['enabled_state'], + shared_private_link_resource=shared_private_link_resource + ) + + try: + poller = self.origin_client.afd_origins.begin_update(resource_group_name=self.resource_group, + profile_name=self.profile_name, + origin_group_name=self.origin_group_name, + origin_name=self.name, + origin_update_properties=parameters) + response = self.get_poller_result(poller) + return origin_to_dict(response) + except Exception as exc: + self.log('Error attempting to update Azure Origin instance.') + self.fail("Error updating Azure Origin instance: {0}".format(str(exc))) + + def delete_origin(self): + ''' + Deletes the specified Azure Origin in the specified subscription and resource group. + + :return: True + ''' + self.log("Deleting the Origin {0}".format(self.name)) + try: + poller = self.origin_client.afd_origins.begin_delete(resource_group_name=self.resource_group, + profile_name=self.profile_name, + origin_group_name=self.origin_group_name, + origin_name=self.name) + self.get_poller_result(poller) + return True + except Exception as exc: + self.log('Error attempting to delete the Origin.') + self.fail("Error deleting the Origin: {0}".format(str(exc))) + return False + + def get_origin(self): + ''' + Gets the properties of the specified Origin. + + :return: deserialized Origin state dictionary + ''' + self.log( + "Checking if the Origin {0} is present".format(self.name)) + try: + response = self.origin_client.afd_origins.get(resource_group_name=self.resource_group, + profile_name=self.profile_name, + origin_group_name=self.origin_group_name, + origin_name=self.name) + self.log("Response : {0}".format(response)) + self.log("Origin : {0} found".format(response.name)) + return origin_to_dict(response) + except Exception as exc: + self.log('Did not find the Origin. {0}'.format(str(exc))) + return False + + def get_origin_client(self): + if not self.origin_client: + self.origin_client = self.get_mgmt_svc_client(CdnManagementClient, + base_url=self._cloud_environment.endpoints.resource_manager, + api_version='2023-05-01') + return self.origin_client + + +def main(): + """Main execution""" + AzureRMOrigin() + +if __name__ == '__main__': + main() diff --git a/plugins/modules/azure_rm_afdorigin_info.py b/plugins/modules/azure_rm_afdorigin_info.py new file mode 100644 index 000000000..eed978076 --- /dev/null +++ b/plugins/modules/azure_rm_afdorigin_info.py @@ -0,0 +1,326 @@ +#!/usr/bin/python +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +# Python SDK Reference: https://learn.microsoft.com/en-us/python/api/azure-mgmt-cdn/azure.mgmt.cdn.operations.afdoriginsoperations?view=azure-python +# + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: azure_rm_afdorigin_info + +version_added: "" + +short_description: Get Azure Front Door Origin facts to be used with Standard or Premium Frontdoor Service + +description: + - Get facts for a specific Azure Front Door (AFD) Origin or all AFD Origins. This differs from the Front Door classic service and only is intended to be used by the Standard or Premium service offering. + +options: + resource_group: + description: + - Name of the resource group where this AFD Profile belongs. + required: true + type: str + profile_name: + description: + - Name of the AFD profile. + required: true + type: str + name: + description: + - Limit results to a specific AFD Origin. + type: str + +extends_documentation_fragment: + - azure.azcollection.azure + +author: + - Jarret Tooley (@jartoo) +''' + +EXAMPLES = ''' +- name: Get facts for all Origins in AFD Profile + azure_rm_afdorigin_info: + resource_group: myResourceGroup + profile_name: myCDNProfile + +- name: Get facts of specific AFD Origin + azure_rm_afdorigin_info: + name: myOrigin1 + profile_name: myCDNProfile + resource_group: myResourceGroup +''' + +RETURN = ''' +afdorigins: + description: List of AFD Origins. + returned: always + type: complex + contains: + azure_origin: + description: + - Resource reference to the AFD origin resource. + type: str + deployment_status: + description: + - Current state of the resource. + type: str + sample: NotStarted + enabled_state: + description: + - Whether to enable health probes to be made against backends defined under backend pools. Health probes can only be disabled if there is a single enabled backend in single enabled backend pool. + type: str + choices: + - Enabled + - Disabled + host_name: + description: + - The address of the origin. Domain names, IPv4 addresses, and IPv6 addresses are supported. This should be unique across all origins in an endpoint. + type: str + http_port: + description: + - The value of the HTTP port. Must be between 1 and 65535. + default: 80 + type: int + https_port: + description: + - The value of the HTTPS port. Must be between 1 and 65535. + default: 443 + type: int + id: + description: + - ID of the AFD Origin. + type: str + sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myCDN/providers/Microsoft.Cdn/profiles/myProfile/origingroups/myOriginGroup1/origins/myOrigin1" + name: + description: + - Name of the AFD Origin. + required: true + type: str + origin_group_name: + description: + - Name of the origin group which is unique within the profile. + type: str + origin_host_header: + description: + - The host header value sent to the origin with each request. If you leave this blank, the request hostname determines this value. Azure Front Door origins, such as Web Apps, Blob Storage, and Cloud Services require this host header value to match the origin hostname by default. This overrides the host header defined at the AFD Endpoint. + type: str + priority: + description: + - Priority of origin in given origin group for load balancing. Higher priorities will not be used for load balancing if any lower priority origin is healthy. Must be between 1 and 5. + type: int + profile_name: + description: + - Name of the AFD Profile where the Origin will be added. + required: true + type: str + provisioning_state: + description: + - Provisioning status of the AFD Origin. + type: str + sample: Succeeded + resource_group: + description: + - Name of a resource group where the AFD Origin exists or will be created. + required: true + type: str + shared_private_link_resource: + description: + - The properties of the private link resource for private origin. + type: dict + suboptions: + group_id: + description: + - The group id from the provider of resource the shared private link resource is for. + type: str + private_link: + description: + - The resource id of the resource the shared private link resource is for. + type: str + private_link_location: + description: + - The location of the shared private link resource. + type: str + request_message: + description: + - The request message for requesting approval of the shared private link resource. + type: str + status: + description: + - Status of the shared private link resource. Can be Pending, Approved, Rejected, Disconnected, or Timeout. + type: str + choices: + - Approved + - Disconnected + - Pending + - Rejected + - Timeout + type: + description: + - Resource type. + type: str + sample: Microsoft.Cdn/profiles/origingroups/origins + weight: + description: + - Weight of the origin in given origin group for load balancing. Must be between 1 and 1000. + type: int +''' + +from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase + +try: + from azure.mgmt.cdn import CdnManagementClient +except ImportError: + # handled in azure_rm_common + pass + +import re + +AZURE_OBJECT_CLASS = 'AFDOrigin' + + +class AzureRMAFDOriginInfo(AzureRMModuleBase): + """Utility class to get Azure AFD Origin facts""" + + def __init__(self): + + self.module_args = dict( + name=dict(type='str'), + origin_group_name=dict( + type='str', + required=True + ), + profile_name=dict( + type='str', + required=True + ), + resource_group=dict( + type='str', + required=True + ) + ) + + self.results = dict( + changed=False, + afdorigins=[] + ) + + self.name = None + self.origin_group_name = None + self.resource_group = None + self.profile_name = None + + super(AzureRMAFDOriginInfo, self).__init__( + supports_check_mode=True, + derived_arg_spec=self.module_args, + supports_tags=False, + facts_module=True + ) + + def exec_module(self, **kwargs): + + for key in self.module_args: + setattr(self, key, kwargs[key]) + + self.endpoint_client = self.get_mgmt_svc_client(CdnManagementClient, + base_url=self._cloud_environment.endpoints.resource_manager, + api_version='2023-05-01') + + if self.name: + self.results['afdorigins'] = self.get_item() + else: + self.results['afdorigins'] = self.list_by_profile() + + return self.results + + def get_item(self): + """Get a single Azure AFD Origin""" + + self.log('Get properties for {0}'.format(self.name)) + + item = None + result = [] + + try: + item = self.endpoint_client.afd_origins.get( + resource_group_name=self.resource_group, profile_name=self.profile_name, origin_group_name=self.origin_group_name, origin_name=self.name) + except Exception as exc: + self.log("Did not find resource.".format(str(exc))) + pass + + if item: + result = [self.serialize_afdorigin(item)] + + return result + + def list_by_profile(self): + """Get all Azure AFD Origins within an AFD profile""" + + self.log('List all AFD Origins within an AFD profile') + + try: + response = self.endpoint_client.afd_origins.list_by_origin_group( + resource_group_name=self.resource_group, profile_name=self.profile_name, origin_group_name=self.origin_group_name) + except Exception as exc: + self.fail('Failed to list all origins. {0}'.format(str(exc))) + + results = [] + for item in response: + results.append(self.serialize_afdorigin(item)) + + return results + + def serialize_afdorigin(self, afdorigin): + ''' + Convert a AFD Origin object to dict. + :param afdorigin: AFD Origin object + :return: dict + ''' + result = self.serialize_obj(afdorigin, AZURE_OBJECT_CLASS) + + new_result = {} + new_result['azure_origin'] = afdorigin.azure_origin + new_result['deployment_status'] = afdorigin.deployment_status + new_result['enabled_state'] = afdorigin.enabled_state + new_result['host_name'] = afdorigin.host_name + new_result['http_port'] = afdorigin.http_port + new_result['https_port'] = afdorigin.https_port + new_result['https_port'] = afdorigin.https_port + new_result['id'] = afdorigin.id + new_result['name'] = afdorigin.name + new_result['origin_group_name'] = re.sub('\\/.*', '', re.sub('.*origingroups\\/', '', result['id'])) + new_result['origin_host_header'] = afdorigin.origin_host_header + new_result['priority'] = afdorigin.priority + new_result['provisioning_state'] = afdorigin.provisioning_state + new_result['priority'] = afdorigin.priority + new_result['profile_name'] = re.sub('\\/.*', '', re.sub('.*profiles\\/', '', result['id'])) + new_result['resource_group'] = re.sub('\\/.*', '', re.sub('.*resourcegroups\\/', '', result['id'])) + new_result['shared_private_link_resource'] = dict() + if afdorigin.shared_private_link_resource: + new_result['shared_private_link_resource']['group_id'] = afdorigin.shared_private_link_resource.group_id + new_result['shared_private_link_resource']['private_link'] = afdorigin.shared_private_link_resource.private_link + new_result['shared_private_link_resource']['private_link_location'] = afdorigin.shared_private_link_resource.private_link_location + new_result['shared_private_link_resource']['request_message'] = afdorigin.shared_private_link_resource.request_message + new_result['shared_private_link_resource']['status'] = afdorigin.shared_private_link_resource.status + else: + new_result['shared_private_link_resource']['group_id'] = None + new_result['shared_private_link_resource']['private_link'] = None + new_result['shared_private_link_resource']['private_link_location'] = None + new_result['shared_private_link_resource']['request_message'] = None + new_result['shared_private_link_resource']['status'] = None + new_result['type'] = afdorigin.type + new_result['weight'] = afdorigin.weight + return new_result + + +def main(): + """Main module execution code path""" + AzureRMAFDOriginInfo() + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/azure_rm_afdorigin/aliases b/tests/integration/targets/azure_rm_afdorigin/aliases new file mode 100644 index 000000000..3b050cbc1 --- /dev/null +++ b/tests/integration/targets/azure_rm_afdorigin/aliases @@ -0,0 +1,3 @@ +cloud/azure +destructive +shippable/azure/group6 diff --git a/tests/integration/targets/azure_rm_afdorigin/meta/main.yml b/tests/integration/targets/azure_rm_afdorigin/meta/main.yml new file mode 100644 index 000000000..95e1952f9 --- /dev/null +++ b/tests/integration/targets/azure_rm_afdorigin/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_azure diff --git a/tests/integration/targets/azure_rm_afdorigin/tasks/main.yml b/tests/integration/targets/azure_rm_afdorigin/tasks/main.yml new file mode 100644 index 000000000..e4618af83 --- /dev/null +++ b/tests/integration/targets/azure_rm_afdorigin/tasks/main.yml @@ -0,0 +1,228 @@ +- name: Prepare random name for Profile & EndPoint & Origin Group & Origin + ansible.builtin.set_fact: + profile: "prof-{{ resource_group | hash('md5') | truncate(4, True, '') }}{{ 100000 | random }}" + origin_group: "origin-group-{{ resource_group | hash('md5') | truncate(4, True, '') }}{{ 100000 | random }}" + origin: "origin-{{ resource_group | hash('md5') | truncate(4, True, '') }}{{ 100000 | random }}" + origin2: "origin2-{{ resource_group | hash('md5') | truncate(4, True, '') }}{{ 100000 | random }}" + +- name: Create Standard Frontdoor Profile + azure_rm_cdnprofile: + name: "{{ profile }}" + location: "Global" + resource_group: "{{ resource_group }}" + sku: "standard_azurefrontdoor" + state: "present" + +- name: Create Origin Group + azure_rm_afdorigingroup: + name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + state: "present" + health_probe_settings: + probe_interval_in_seconds: 100 + probe_path: "/" + probe_protocol: "Https" + probe_request_type: "HEAD" + load_balancing_settings: + additional_latency_in_milliseconds: 50 + sample_size: 4 + successful_samples_required: 3 + register: output + +- name: Create Origin + azure_rm_afdorigin: + name: "{{ origin }}" + origin_group_name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + state: "present" + origin: + host_name: "10.0.0.1" + origin_host_header: "10.0.0.1" + http_port: 80 + https_port: 443 + priority: 1 + weight: 123 + register: output + +- name: Assert the resource was created + ansible.builtin.assert: + that: + - output.changed + - output.id + +- name: Update Origin (idempotent test) + azure_rm_afdorigin: + name: "{{ origin }}" + origin_group_name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + state: "present" + origin: + host_name: "10.0.0.1" + origin_host_header: "10.0.0.1" + http_port: 80 + https_port: 443 + priority: 1 + weight: 123 + register: output + +- name: Assert the resource was not changed + ansible.builtin.assert: + that: + - not output.changed + +- name: Load Origin info + azure_rm_afdorigin_info: + name: "{{ origin }}" + origin_group_name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + register: output + +- name: Assert the resource has the proper values set + ansible.builtin.assert: + that: + - output.afdorigins | length == 1 + - output.afdorigins[0].enabled_state == 'Enabled' + - output.afdorigins[0].host_name == '10.0.0.1' + - output.afdorigins[0].http_port == 80 + - output.afdorigins[0].https_port == 443 + - output.afdorigins[0].id + - output.afdorigins[0].origin_host_header == '10.0.0.1' + - output.afdorigins[0].priority == 1 + - output.afdorigins[0].weight == 123 + - not output.changed + +- name: Create Origin 2 + azure_rm_afdorigin: + name: "{{ origin2 }}" + origin_group_name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + state: "present" + origin: + host_name: "10.0.1.2" + origin_host_header: "10.0.1.2" + http_port: 80 + https_port: 443 + priority: 2 + weight: 234 + register: output + +- name: Assert the resource was created + ansible.builtin.assert: + that: + - output.changed + - output.id + +- name: Load Origin info for both + azure_rm_afdorigin_info: + origin_group_name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + register: output + +- name: Assert the resource has the proper values set + ansible.builtin.assert: + that: + - output.afdorigins | length == 2 + - not output.changed + +- name: Update Origin (with different values) + azure_rm_afdorigin: + name: "{{ origin }}" + origin_group_name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + state: "present" + origin: + host_name: "10.0.0.2" + origin_host_header: "10.0.0.2" + http_port: 8080 + priority: 1 + weight: 111 + register: output + +- name: Assert the resource was changed + ansible.builtin.assert: + that: + - output.changed + +- name: Update Origin Group (idempotent test) + azure_rm_afdorigin: + name: "{{ origin }}" + origin_group_name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + state: "present" + origin: + host_name: "10.0.0.2" + origin_host_header: "10.0.0.2" + http_port: 8080 + priority: 1 + weight: 111 + register: output + +- name: Assert the resource was not changed + ansible.builtin.assert: + that: + - not output.changed + +- name: Delete Origin + azure_rm_afdorigin: + name: "{{ origin }}" + origin_group_name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + state: "absent" + register: output + +- name: Assert the resource was deleted + ansible.builtin.assert: + that: + - output.changed + +- name: Delete Origin 2 + azure_rm_afdorigin: + name: "{{ origin2 }}" + origin_group_name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + state: "absent" + register: output + +- name: Assert the resource was delete + ansible.builtin.assert: + that: + - output.changed + +- name: Delete Origin (idempotent test) + azure_rm_afdorigin: + name: "{{ origin }}" + origin_group_name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + state: "absent" + register: output + +- name: Assert the resource was not changed + ansible.builtin.assert: + that: + - not output.changed + +- name: Delete Origin Group + azure_rm_afdorigingroup: + name: "{{ origin_group }}" + profile_name: "{{ profile }}" + resource_group: "{{ resource_group }}" + state: "absent" + register: output + +- name: Delete Frontdoor Profile + azure_rm_cdnprofile: + name: "{{ profile }}" + location: "Global" + resource_group: "{{ resource_group }}" + state: "absent" From 61df8e9ca9fdbb6c25288b16ee122b09b555533b Mon Sep 17 00:00:00 2001 From: jartoo Date: Fri, 7 Jun 2024 15:21:03 -0500 Subject: [PATCH 2/5] Fixed all Sanity Check issues --- plugins/modules/azure_rm_afdorigin.py | 104 ++++++++++++--------- plugins/modules/azure_rm_afdorigin_info.py | 60 ++++++------ 2 files changed, 92 insertions(+), 72 deletions(-) diff --git a/plugins/modules/azure_rm_afdorigin.py b/plugins/modules/azure_rm_afdorigin.py index d12425391..08ddeb8e0 100644 --- a/plugins/modules/azure_rm_afdorigin.py +++ b/plugins/modules/azure_rm_afdorigin.py @@ -8,7 +8,7 @@ DOCUMENTATION = ''' --- module: azure_rm_afdorigin -version_added: "" +version_added: "2.4.0" short_description: Manage an Azure Front Door Origin to be used with Standard or Premium Frontdoor. description: - Create, update and delete an Azure Front Door (AFD) Origin to be used by a Front Door Service Profile created using azure_rm_cdnprofile. @@ -30,14 +30,16 @@ type: str enabled_state: description: - - Whether to enable health probes to be made against backends defined under backend pools. Health probes can only be disabled if there is a single enabled backend in single enabled backend pool. + - Whether to enable health probes to be made against backends defined under backend pools. + - Health probes can only be disabled if there is a single enabled backend in single enabled backend pool. type: str choices: - Enabled - Disabled host_name: description: - - The address of the origin. Domain names, IPv4 addresses, and IPv6 addresses are supported. This should be unique across all origins in an endpoint. + - The address of the origin. Domain names, IPv4 addresses, and IPv6 addresses are supported. + - This should be unique across all origins in an endpoint. type: str http_port: description: @@ -51,11 +53,15 @@ type: int origin_host_header: description: - - The host header value sent to the origin with each request. If you leave this blank, the request hostname determines this value. Azure Front Door origins, such as Web Apps, Blob Storage, and Cloud Services require this host header value to match the origin hostname by default. This overrides the host header defined at the AFD Endpoint. + - The host header value sent to the origin with each request. If you leave this blank, the request hostname determines this value. + - Azure Front Door origins, such as Web Apps, Blob Storage, + - and Cloud Services require this host header value to match the origin hostname by default. + - This overrides the host header defined at the AFD Endpoint. type: str priority: description: - - Priority of origin in given origin group for load balancing. Higher priorities will not be used for load balancing if any lower priority origin is healthy. Must be between 1 and 5. + - Priority of origin in given origin group for load balancing. + - Higher priorities will not be used for load balancing if any lower priority origin is healthy. Must be between 1 and 5. type: int shared_private_link_resource: description: @@ -82,6 +88,7 @@ description: - Status of the shared private link resource. Can be Pending, Approved, Rejected, Disconnected, or Timeout. type: str + default: Approved choices: - Approved - Disconnected @@ -132,12 +139,12 @@ resource_group: myResourceGroup state: present origin: - host_name: "10.0.0.1" - origin_host_header: "10.0.0.1" - http_port: 80 - https_port: 443 - priority: 1 - weight: 1000 + host_name: "10.0.0.1" + origin_host_header: "10.0.0.1" + http_port: 80 + https_port: 443 + priority: 1 + weight: 1000 - name: Delete an AFD Origin azure_rm_afdorigin: @@ -146,7 +153,6 @@ profile_name: myProfile resource_group: myResourceGroup state: absent - ''' RETURN = ''' id: @@ -154,7 +160,7 @@ - ID of the AFD Origin. returned: always type: str - sample: "id: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroup/providers/Microsoft.Cdn/profiles/myProfile/origingroups/myOriginGroup/origins/myOrigin" + sample: "id: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx/resourcegroups/myRG/providers/Microsoft.Cdn/profiles/myProf/origingroups/myOG/origins/myO" host_name: description: - Host name of the AFD Origin. @@ -174,16 +180,17 @@ # This is handled in azure_rm_common pass + def origin_to_dict(origin): return dict( azure_origin=origin.azure_origin, deployment_status=origin.deployment_status, - enabled_state = origin.enabled_state, + enabled_state=origin.enabled_state, # enforce_certificate_check = origin.enforce_certificate_check, # Not fully implemented yet - host_name = origin.host_name, - http_port = origin.http_port, - https_port = origin.https_port, - id = origin.id, + host_name=origin.host_name, + http_port=origin.http_port, + https_port=origin.https_port, + id=origin.id, name=origin.name, origin_group_name=re.sub('\\/.*', '', re.sub('.*origingroups\\/', '', origin.id)), origin_host_header=origin.origin_host_header, @@ -207,11 +214,11 @@ def __init__(self): type='dict', options=dict( azure_origin=dict(type='str'), - enabled_state=dict(type='str'), + enabled_state=dict(type='str', choices=['Enabled', 'Disabled']), # enforce_certification_name_check=dict(type='bool'), host_name=dict(type='str'), - http_port=dict(type='int',default=80), - https_port=dict(type='int',default=443), + http_port=dict(type='int', default=80), + https_port=dict(type='int', default=443), origin_host_header=dict(type='str'), priority=dict(type='int'), shared_private_link_resource=dict( @@ -221,7 +228,7 @@ def __init__(self): private_link=dict(type='str'), private_link_location=dict(type='str'), request_message=dict(type='str'), - status=dict(type='str',default='Approved',choices=["Pending", "Approved", "Rejected", "Disconnected", "Timeout"]) + status=dict(type='str', default='Approved', choices=["Pending", "Approved", "Rejected", "Disconnected", "Timeout"]) ) ), weight=dict(type='int') @@ -261,10 +268,11 @@ def __init__(self): self.results = dict(changed=False) - super(AzureRMOrigin, self).__init__(derived_arg_spec=self.module_arg_spec, - supports_check_mode=True, - supports_tags=False, - required_if=required_if) + super(AzureRMOrigin, self).__init__( + derived_arg_spec=self.module_arg_spec, + supports_check_mode=True, + supports_tags=False, + required_if=required_if) def exec_module(self, **kwargs): """Main module execution method""" @@ -309,20 +317,26 @@ def exec_module(self, **kwargs): to_be_updated = True if response['enabled_state'] != self.origin['enabled_state'] and self.origin['enabled_state']: to_be_updated = True - # if response['enforce_certificate_name_check'] != self.origin['enforce_certificate_name_check'] and self.origin['enforce_certificate_name_check']: - # to_be_updated = True if response['shared_private_link_resource']: - if response['shared_private_link_resource']['group_id'] != self.origin['shared_private_link_resource']['group_id'] and self.origin['shared_private_link_resource']['group_id']: + if response['shared_private_link_resource']['group_id'] != \ + self.origin['shared_private_link_resource']['group_id'] and \ + self.origin['shared_private_link_resource']['group_id']: to_be_updated = True - if response['shared_private_link_resource']['private_link'] != self.origin['shared_private_link_resource']['private_link'] and self.origin['shared_private_link_resource']['private_link']: + if response['shared_private_link_resource']['private_link'] != \ + self.origin['shared_private_link_resource']['private_link'] and \ + self.origin['shared_private_link_resource']['private_link']: to_be_updated = True - if response['shared_private_link_resource']['private_link_location'] != self.origin['shared_private_link_resource']['private_link_location'] and self.origin['shared_private_link_resource']['private_link_location']: + if response['shared_private_link_resource']['private_link_location'] != \ + self.origin['shared_private_link_resource']['private_link_location'] and \ + self.origin['shared_private_link_resource']['private_link_location']: to_be_updated = True - if response['shared_private_link_resource']['request_message'] != self.origin['shared_private_link_resource']['request_message'] and self.origin['shared_private_link_resource']['request_message']: + if response['shared_private_link_resource']['request_message'] != self.origin['shared_private_link_resource']['request_message'] and \ + self.origin['shared_private_link_resource']['request_message']: to_be_updated = True - if response['shared_private_link_resource']['status'] != self.origin['shared_private_link_resource']['status'] and self.origin['shared_private_link_resource']['status']: + if response['shared_private_link_resource']['status'] != self.origin['shared_private_link_resource']['status'] and \ + self.origin['shared_private_link_resource']['status']: to_be_updated = True - + if to_be_updated: self.log("Need to update the Origin") @@ -380,7 +394,8 @@ def create_origin(self): ) try: - poller = self.origin_client.afd_origins.begin_create(resource_group_name=self.resource_group, + poller = self.origin_client.afd_origins.begin_create( + resource_group_name=self.resource_group, profile_name=self.profile_name, origin_group_name=self.origin_group_name, origin_name=self.name, @@ -420,9 +435,10 @@ def update_origin(self): enabled_state=self.origin['enabled_state'], shared_private_link_resource=shared_private_link_resource ) - + try: - poller = self.origin_client.afd_origins.begin_update(resource_group_name=self.resource_group, + poller = self.origin_client.afd_origins.begin_update( + resource_group_name=self.resource_group, profile_name=self.profile_name, origin_group_name=self.origin_group_name, origin_name=self.name, @@ -441,7 +457,8 @@ def delete_origin(self): ''' self.log("Deleting the Origin {0}".format(self.name)) try: - poller = self.origin_client.afd_origins.begin_delete(resource_group_name=self.resource_group, + poller = self.origin_client.afd_origins.begin_delete( + resource_group_name=self.resource_group, profile_name=self.profile_name, origin_group_name=self.origin_group_name, origin_name=self.name) @@ -461,7 +478,8 @@ def get_origin(self): self.log( "Checking if the Origin {0} is present".format(self.name)) try: - response = self.origin_client.afd_origins.get(resource_group_name=self.resource_group, + response = self.origin_client.afd_origins.get( + resource_group_name=self.resource_group, profile_name=self.profile_name, origin_group_name=self.origin_group_name, origin_name=self.name) @@ -474,9 +492,10 @@ def get_origin(self): def get_origin_client(self): if not self.origin_client: - self.origin_client = self.get_mgmt_svc_client(CdnManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2023-05-01') + self.origin_client = self.get_mgmt_svc_client( + CdnManagementClient, + base_url=self._cloud_environment.endpoints.resource_manager, + api_version='2023-05-01') return self.origin_client @@ -484,5 +503,6 @@ def main(): """Main execution""" AzureRMOrigin() + if __name__ == '__main__': main() diff --git a/plugins/modules/azure_rm_afdorigin_info.py b/plugins/modules/azure_rm_afdorigin_info.py index eed978076..5bb7d3e89 100644 --- a/plugins/modules/azure_rm_afdorigin_info.py +++ b/plugins/modules/azure_rm_afdorigin_info.py @@ -12,13 +12,11 @@ DOCUMENTATION = ''' --- module: azure_rm_afdorigin_info - -version_added: "" - +version_added: "2.4.0" short_description: Get Azure Front Door Origin facts to be used with Standard or Premium Frontdoor Service - description: - - Get facts for a specific Azure Front Door (AFD) Origin or all AFD Origins. This differs from the Front Door classic service and only is intended to be used by the Standard or Premium service offering. + - Get facts for a specific Azure Front Door (AFD) Origin or all AFD Origins. + - This differs from the Front Door classic service and only is intended to be used by the Standard or Premium service offering. options: resource_group: @@ -26,6 +24,11 @@ - Name of the resource group where this AFD Profile belongs. required: true type: str + origin_group_name: + description: + - Name of the origin group which is unique within the profile. + required: true + type: str profile_name: description: - Name of the AFD profile. @@ -61,7 +64,7 @@ description: List of AFD Origins. returned: always type: complex - contains: + contains: azure_origin: description: - Resource reference to the AFD origin resource. @@ -73,34 +76,33 @@ sample: NotStarted enabled_state: description: - - Whether to enable health probes to be made against backends defined under backend pools. Health probes can only be disabled if there is a single enabled backend in single enabled backend pool. + - Whether to enable health probes to be made against backends defined under backend pools. + - Health probes can only be disabled if there is a single enabled backend in single enabled backend pool. type: str - choices: - - Enabled - - Disabled + sample: Enabled host_name: description: - - The address of the origin. Domain names, IPv4 addresses, and IPv6 addresses are supported. This should be unique across all origins in an endpoint. + - The address of the origin. Domain names, IPv4 addresses, and IPv6 addresses are supported. + - This should be unique across all origins in an endpoint. type: str http_port: description: - The value of the HTTP port. Must be between 1 and 65535. - default: 80 type: int + sample: 80 https_port: description: - The value of the HTTPS port. Must be between 1 and 65535. - default: 443 type: int + sample: 443 id: description: - ID of the AFD Origin. type: str - sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myCDN/providers/Microsoft.Cdn/profiles/myProfile/origingroups/myOriginGroup1/origins/myOrigin1" + sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxx/resourcegroups/myRG/providers/Microsoft.Cdn/profiles/myProf/origingroups/myOG/origins/myO" name: description: - Name of the AFD Origin. - required: true type: str origin_group_name: description: @@ -108,16 +110,18 @@ type: str origin_host_header: description: - - The host header value sent to the origin with each request. If you leave this blank, the request hostname determines this value. Azure Front Door origins, such as Web Apps, Blob Storage, and Cloud Services require this host header value to match the origin hostname by default. This overrides the host header defined at the AFD Endpoint. + - The host header value sent to the origin with each request. If you leave this blank, the request hostname determines this value. + - Azure Front Door origins, such as Web Apps, Blob Storage, and Cloud Services require this host header value to match the origin + - hostname by default. This overrides the host header defined at the AFD Endpoint. type: str priority: description: - - Priority of origin in given origin group for load balancing. Higher priorities will not be used for load balancing if any lower priority origin is healthy. Must be between 1 and 5. + - Priority of origin in given origin group for load balancing. Higher priorities will not be used for load balancing if any + - lower priority origin is healthy. Must be between 1 and 5. type: int profile_name: description: - Name of the AFD Profile where the Origin will be added. - required: true type: str provisioning_state: description: @@ -127,13 +131,12 @@ resource_group: description: - Name of a resource group where the AFD Origin exists or will be created. - required: true type: str shared_private_link_resource: description: - The properties of the private link resource for private origin. type: dict - suboptions: + contains: group_id: description: - The group id from the provider of resource the shared private link resource is for. @@ -154,12 +157,7 @@ description: - Status of the shared private link resource. Can be Pending, Approved, Rejected, Disconnected, or Timeout. type: str - choices: - - Approved - - Disconnected - - Pending - - Rejected - - Timeout + sample: Approved type: description: - Resource type. @@ -227,9 +225,10 @@ def exec_module(self, **kwargs): for key in self.module_args: setattr(self, key, kwargs[key]) - self.endpoint_client = self.get_mgmt_svc_client(CdnManagementClient, - base_url=self._cloud_environment.endpoints.resource_manager, - api_version='2023-05-01') + self.endpoint_client = self.get_mgmt_svc_client( + CdnManagementClient, + base_url=self._cloud_environment.endpoints.resource_manager, + api_version='2023-05-01') if self.name: self.results['afdorigins'] = self.get_item() @@ -250,7 +249,7 @@ def get_item(self): item = self.endpoint_client.afd_origins.get( resource_group_name=self.resource_group, profile_name=self.profile_name, origin_group_name=self.origin_group_name, origin_name=self.name) except Exception as exc: - self.log("Did not find resource.".format(str(exc))) + self.log("Did not find resource. {0}".format(str(exc))) pass if item: @@ -322,5 +321,6 @@ def main(): """Main module execution code path""" AzureRMAFDOriginInfo() + if __name__ == '__main__': main() From 7133298003f8de79d82db5c5c57a528e6dbc1972 Mon Sep 17 00:00:00 2001 From: jartoo Date: Thu, 13 Jun 2024 08:27:59 -0500 Subject: [PATCH 3/5] Updated pipeline and runtime yml files --- meta/runtime.yml | 2 ++ pr-pipelines.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/meta/runtime.yml b/meta/runtime.yml index 2da8ce1a3..4784dbec6 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -14,6 +14,8 @@ action_groups: - azure.azcollection.azure_rm_adserviceprincipal_info - azure.azcollection.azure_rm_aduser - azure.azcollection.azure_rm_aduser_info + - azure.azcollection.azure_rm_afdorigin + - azure.azcollection.azure_rm_afdorigin_info - azure.azcollection.azure_rm_aks - azure.azcollection.azure_rm_aks_info - azure.azcollection.azure_rm_aksagentpool diff --git a/pr-pipelines.yml b/pr-pipelines.yml index 744860d8d..d1546e866 100644 --- a/pr-pipelines.yml +++ b/pr-pipelines.yml @@ -35,6 +35,7 @@ parameters: - "azure_rm_acs" - "azure_rm_adgroup" - "azure_rm_aduser" + - "azure_rm_afdorigin" - "azure_rm_aks" - "azure_rm_aksagentpool" - "azure_rm_apimanagement" From 01a7da70cd778ae16abddef5c621da816e8cda92 Mon Sep 17 00:00:00 2001 From: jartoo Date: Mon, 17 Jun 2024 10:23:05 -0500 Subject: [PATCH 4/5] Flattened the input parameters --- plugins/modules/azure_rm_afdorigin.py | 335 +++++++++--------- plugins/modules/azure_rm_afdorigin_info.py | 3 +- .../targets/azure_rm_afdorigin/tasks/main.yml | 61 ++-- 3 files changed, 199 insertions(+), 200 deletions(-) diff --git a/plugins/modules/azure_rm_afdorigin.py b/plugins/modules/azure_rm_afdorigin.py index 08ddeb8e0..64306875d 100644 --- a/plugins/modules/azure_rm_afdorigin.py +++ b/plugins/modules/azure_rm_afdorigin.py @@ -14,96 +14,55 @@ - Create, update and delete an Azure Front Door (AFD) Origin to be used by a Front Door Service Profile created using azure_rm_cdnprofile. options: + azure_origin: + description: + - Resource reference to the AFD origin resource. + type: str + enabled_state: + description: + - Whether to enable health probes to be made against backends defined under backend pools. + - Health probes can only be disabled if there is a single enabled backend in single enabled backend pool. + type: str + choices: + - Enabled + - Disabled + host_name: + description: + - The address of the origin. Domain names, IPv4 addresses, and IPv6 addresses are supported. + - This should be unique across all origins in an endpoint. + type: str + http_port: + description: + - The value of the HTTP port. Must be between 1 and 65535. + default: 80 + type: int + https_port: + description: + - The value of the HTTPS port. Must be between 1 and 65535. + default: 443 + type: int name: description: - Name of the origin that is unique within the AFD Profile. required: true type: str - origin: + origin_host_header: description: - - AFD Origin properties - type: dict - suboptions: - azure_origin: - description: - - Resource reference to the AFD origin resource. - type: str - enabled_state: - description: - - Whether to enable health probes to be made against backends defined under backend pools. - - Health probes can only be disabled if there is a single enabled backend in single enabled backend pool. - type: str - choices: - - Enabled - - Disabled - host_name: - description: - - The address of the origin. Domain names, IPv4 addresses, and IPv6 addresses are supported. - - This should be unique across all origins in an endpoint. - type: str - http_port: - description: - - The value of the HTTP port. Must be between 1 and 65535. - default: 80 - type: int - https_port: - description: - - The value of the HTTPS port. Must be between 1 and 65535. - default: 443 - type: int - origin_host_header: - description: - - The host header value sent to the origin with each request. If you leave this blank, the request hostname determines this value. - - Azure Front Door origins, such as Web Apps, Blob Storage, - - and Cloud Services require this host header value to match the origin hostname by default. - - This overrides the host header defined at the AFD Endpoint. - type: str - priority: - description: - - Priority of origin in given origin group for load balancing. - - Higher priorities will not be used for load balancing if any lower priority origin is healthy. Must be between 1 and 5. - type: int - shared_private_link_resource: - description: - - The properties of the private link resource for private origin. - type: dict - suboptions: - group_id: - description: - - The group id from the provider of resource the shared private link resource is for. - type: str - private_link: - description: - - The resource id of the resource the shared private link resource is for. - type: str - private_link_location: - description: - - The location of the shared private link resource. - type: str - request_message: - description: - - The request message for requesting approval of the shared private link resource. - type: str - status: - description: - - Status of the shared private link resource. Can be Pending, Approved, Rejected, Disconnected, or Timeout. - type: str - default: Approved - choices: - - Approved - - Disconnected - - Pending - - Rejected - - Timeout - weight: - description: - - Weight of the origin in given origin group for load balancing. Must be between 1 and 1000. - type: int + - The host header value sent to the origin with each request. If you leave this blank, the request hostname determines this value. + - Azure Front Door origins, such as Web Apps, Blob Storage, + - and Cloud Services require this host header value to match the origin hostname by default. + - This overrides the host header defined at the AFD Endpoint. + type: str origin_group_name: description: - Name of the origin group which is unique within the profile. required: true type: str + priority: + description: + - Priority of origin in given origin group for load balancing. + - Higher priorities will not be used for load balancing if any lower priority origin is healthy. Must be between 1 and 5. + type: int profile_name: description: - Name of the AFD Profile. @@ -114,6 +73,38 @@ - Name of a resource group where the AFD Origin exists or will be created. required: true type: str + shared_private_link_resource: + description: + - The properties of the private link resource for private origin. + type: dict + suboptions: + group_id: + description: + - The group id from the provider of resource the shared private link resource is for. + type: str + private_link: + description: + - The resource id of the resource the shared private link resource is for. + type: str + private_link_location: + description: + - The location of the shared private link resource. + type: str + request_message: + description: + - The request message for requesting approval of the shared private link resource. + type: str + status: + description: + - Status of the shared private link resource. Can be Pending, Approved, Rejected, Disconnected, or Timeout. + type: str + default: Approved + choices: + - Approved + - Disconnected + - Pending + - Rejected + - Timeout state: description: - Assert the state of the AFD Profile. Use C(present) to create or update an AFD profile and C(absent) to delete it. @@ -122,6 +113,10 @@ choices: - absent - present + weight: + description: + - Weight of the origin in given origin group for load balancing. Must be between 1 and 1000. + type: int extends_documentation_fragment: - azure.azcollection.azure @@ -138,13 +133,12 @@ profile_name: myProfile resource_group: myResourceGroup state: present - origin: - host_name: "10.0.0.1" - origin_host_header: "10.0.0.1" - http_port: 80 - https_port: 443 - priority: 1 - weight: 1000 + host_name: "10.0.0.1" + origin_host_header: "10.0.0.1" + http_port: 80 + https_port: 443 + priority: 1 + weight: 1000 - name: Delete an AFD Origin azure_rm_afdorigin: @@ -169,9 +163,9 @@ sample: "myorigin.azurefd.net" ''' +import re from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase -import re try: from azure.mgmt.cdn.models import AFDOrigin, AFDOriginUpdateParameters, SharedPrivateLinkResourceProperties @@ -182,6 +176,7 @@ def origin_to_dict(origin): + ''' Convert object to dict ''' return dict( azure_origin=origin.azure_origin, deployment_status=origin.deployment_status, @@ -203,41 +198,25 @@ def origin_to_dict(origin): class AzureRMOrigin(AzureRMModuleBase): - + ''' Class for managing Origin ''' def __init__(self): self.module_arg_spec = dict( + azure_origin=dict(type='str'), + enabled_state=dict(type='str', choices=['Enabled', 'Disabled']), + # enforce_certification_name_check=dict(type='bool'), + host_name=dict(type='str'), + http_port=dict(type='int', default=80), + https_port=dict(type='int', default=443), name=dict( type='str', required=True ), - origin=dict( - type='dict', - options=dict( - azure_origin=dict(type='str'), - enabled_state=dict(type='str', choices=['Enabled', 'Disabled']), - # enforce_certification_name_check=dict(type='bool'), - host_name=dict(type='str'), - http_port=dict(type='int', default=80), - https_port=dict(type='int', default=443), - origin_host_header=dict(type='str'), - priority=dict(type='int'), - shared_private_link_resource=dict( - type='dict', - options=dict( - group_id=dict(type='str'), - private_link=dict(type='str'), - private_link_location=dict(type='str'), - request_message=dict(type='str'), - status=dict(type='str', default='Approved', choices=["Pending", "Approved", "Rejected", "Disconnected", "Timeout"]) - ) - ), - weight=dict(type='int') - ) - ), origin_group_name=dict( type='str', required=True ), + origin_host_header=dict(type='str'), + priority=dict(type='int'), profile_name=dict( type='str', required=True @@ -246,33 +225,58 @@ def __init__(self): type='str', required=True ), + shared_private_link_resource=dict( + type='dict', + options=dict( + group_id=dict(type='str'), + private_link=dict(type='str'), + private_link_location=dict(type='str'), + request_message=dict(type='str'), + status=dict( + type='str', + default='Approved', + choices=[ + "Pending", + "Approved", + "Rejected", + "Disconnected", + "Timeout" + ] + ) + ) + ), state=dict( type='str', default='present', choices=['present', 'absent'] - ) + ), + weight=dict(type='int') ) - self.origin = None - self.origin_group_name = None + self.azure_origin = None + self.enabled_state = None + self.host_name = None + self.http_port = None + self.https_port = None + self.origin_host_header = None + self.priority = None + self.shared_private_link_resource = None + self.weight = None + self.name = None + self.origin_group_name = None self.profile_name = None self.resource_group = None self.state = None self.origin_client = None - required_if = [ - # ('state', 'present', ['host_name']) # TODO: Flesh these out - ] - self.results = dict(changed=False) super(AzureRMOrigin, self).__init__( derived_arg_spec=self.module_arg_spec, supports_check_mode=True, - supports_tags=False, - required_if=required_if) + supports_tags=False) def exec_module(self, **kwargs): """Main module execution method""" @@ -303,38 +307,38 @@ def exec_module(self, **kwargs): self.results['id'] = response['id'] self.results['host_name'] = response['host_name'] - if response['host_name'] != self.origin['host_name'] and self.origin['host_name']: + if response['host_name'] != self.host_name and self.host_name: to_be_updated = True - if response['http_port'] != self.origin['http_port'] and self.origin['http_port']: + if response['http_port'] != self.http_port and self.http_port: to_be_updated = True - if response['https_port'] != self.origin['https_port'] and self.origin['https_port']: + if response['https_port'] != self.https_port and self.https_port: to_be_updated = True - if response['origin_host_header'] != self.origin['origin_host_header'] and self.origin['origin_host_header']: + if response['origin_host_header'] != self.origin_host_header and self.origin_host_header: to_be_updated = True - if response['priority'] != self.origin['priority'] and self.origin['priority']: + if response['priority'] != self.priority and self.priority: to_be_updated = True - if response['weight'] != self.origin['weight'] and self.origin['weight']: + if response['weight'] != self.weight and self.weight: to_be_updated = True - if response['enabled_state'] != self.origin['enabled_state'] and self.origin['enabled_state']: + if response['enabled_state'] != self.enabled_state and self.enabled_state: to_be_updated = True if response['shared_private_link_resource']: if response['shared_private_link_resource']['group_id'] != \ - self.origin['shared_private_link_resource']['group_id'] and \ - self.origin['shared_private_link_resource']['group_id']: + self.shared_private_link_resource.get('group_id') and \ + self.shared_private_link_resource.get('group_id'): to_be_updated = True if response['shared_private_link_resource']['private_link'] != \ - self.origin['shared_private_link_resource']['private_link'] and \ - self.origin['shared_private_link_resource']['private_link']: + self.shared_private_link_resource.get('private_link') and \ + self.shared_private_link_resource.get('private_link'): to_be_updated = True if response['shared_private_link_resource']['private_link_location'] != \ - self.origin['shared_private_link_resource']['private_link_location'] and \ - self.origin['shared_private_link_resource']['private_link_location']: + self.shared_private_link_resource.get('private_link_location') and \ + self.shared_private_link_resource.get('private_link_location'): to_be_updated = True if response['shared_private_link_resource']['request_message'] != self.origin['shared_private_link_resource']['request_message'] and \ - self.origin['shared_private_link_resource']['request_message']: + self.shared_private_link_resource.get('request_message'): to_be_updated = True - if response['shared_private_link_resource']['status'] != self.origin['shared_private_link_resource']['status'] and \ - self.origin['shared_private_link_resource']['status']: + if response['shared_private_link_resource']['status'] != self.shared_private_link_resource.get('status') and \ + self.shared_private_link_resource.get('status'): to_be_updated = True if to_be_updated: @@ -372,24 +376,24 @@ def create_origin(self): self.log("Creating the Azure Origin instance {0}".format(self.name)) shared_private_link_resource = None - if self.origin['shared_private_link_resource']: + if self.shared_private_link_resource: shared_private_link_resource = SharedPrivateLinkResourceProperties( - group_id=self.origin['shared_private_link_resource']['group_id'], - private_link=self.origin['shared_private_link_resource']['private_link'], - private_link_location=self.origin['shared_private_link_resource']['private_link_location'], - request_message=self.origin['shared_private_link_resource']['request_message'], - status=self.origin['shared_private_link_resource']['status'] + group_id=self.shared_private_link_resource.get('group_id'), + private_link=self.shared_private_link_resource.get('private_link'), + private_link_location=self.shared_private_link_resource.get('private_link_location'), + request_message=self.shared_private_link_resource.get('request_message'), + status=self.shared_private_link_resource.get('status') ) parameters = AFDOrigin( - azure_origin=self.origin['azure_origin'], - host_name=self.origin['host_name'], - http_port=self.origin['http_port'], - https_port=self.origin['https_port'], - origin_host_header=self.origin['origin_host_header'], - priority=self.origin['priority'], - weight=self.origin['weight'], - enabled_state=self.origin['enabled_state'], + azure_origin=self.azure_origin, + host_name=self.host_name, + http_port=self.http_port, + https_port=self.https_port, + origin_host_header=self.origin_host_header, + priority=self.priority, + weight=self.weight, + enabled_state=self.enabled_state, shared_private_link_resource=shared_private_link_resource ) @@ -415,24 +419,24 @@ def update_origin(self): self.log("Updating the Azure Origin instance {0}".format(self.name)) shared_private_link_resource = None - if self.origin['shared_private_link_resource']: + if self.shared_private_link_resource: shared_private_link_resource = SharedPrivateLinkResourceProperties( - group_id=self.origin['shared_private_link_resource']['group_id'], - private_link=self.origin['shared_private_link_resource']['private_link'], - private_link_location=self.origin['shared_private_link_resource']['private_link_location'], - request_message=self.origin['shared_private_link_resource']['request_message'], - status=self.origin['shared_private_link_resource']['status'] + group_id=self.shared_private_link_resource.get('group_id'), + private_link=self.shared_private_link_resource.get('private_link'), + private_link_location=self.shared_private_link_resource.get('private_link_location'), + request_message=self.shared_private_link_resource.get('request_message'), + status=self.shared_private_link_resource.get('status') ) parameters = AFDOriginUpdateParameters( - azure_origin=self.origin['azure_origin'], - host_name=self.origin['host_name'], - http_port=self.origin['http_port'], - https_port=self.origin['https_port'], - origin_host_header=self.origin['origin_host_header'], - priority=self.origin['priority'], - weight=self.origin['weight'], - enabled_state=self.origin['enabled_state'], + azure_origin=self.azure_origin, + host_name=self.host_name, + http_port=self.http_port, + https_port=self.https_port, + origin_host_header=self.origin_host_header, + priority=self.priority, + weight=self.weight, + enabled_state=self.enabled_state, shared_private_link_resource=shared_private_link_resource ) @@ -491,6 +495,7 @@ def get_origin(self): return False def get_origin_client(self): + ''' Set a reference to the Client ''' if not self.origin_client: self.origin_client = self.get_mgmt_svc_client( CdnManagementClient, diff --git a/plugins/modules/azure_rm_afdorigin_info.py b/plugins/modules/azure_rm_afdorigin_info.py index 5bb7d3e89..c2bce5425 100644 --- a/plugins/modules/azure_rm_afdorigin_info.py +++ b/plugins/modules/azure_rm_afdorigin_info.py @@ -169,6 +169,7 @@ type: int ''' +import re from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase try: @@ -177,7 +178,6 @@ # handled in azure_rm_common pass -import re AZURE_OBJECT_CLASS = 'AFDOrigin' @@ -250,7 +250,6 @@ def get_item(self): resource_group_name=self.resource_group, profile_name=self.profile_name, origin_group_name=self.origin_group_name, origin_name=self.name) except Exception as exc: self.log("Did not find resource. {0}".format(str(exc))) - pass if item: result = [self.serialize_afdorigin(item)] diff --git a/tests/integration/targets/azure_rm_afdorigin/tasks/main.yml b/tests/integration/targets/azure_rm_afdorigin/tasks/main.yml index e4618af83..d60536c41 100644 --- a/tests/integration/targets/azure_rm_afdorigin/tasks/main.yml +++ b/tests/integration/targets/azure_rm_afdorigin/tasks/main.yml @@ -37,13 +37,12 @@ profile_name: "{{ profile }}" resource_group: "{{ resource_group }}" state: "present" - origin: - host_name: "10.0.0.1" - origin_host_header: "10.0.0.1" - http_port: 80 - https_port: 443 - priority: 1 - weight: 123 + host_name: "10.0.0.1" + origin_host_header: "10.0.0.1" + http_port: 80 + https_port: 443 + priority: 1 + weight: 123 register: output - name: Assert the resource was created @@ -59,13 +58,12 @@ profile_name: "{{ profile }}" resource_group: "{{ resource_group }}" state: "present" - origin: - host_name: "10.0.0.1" - origin_host_header: "10.0.0.1" - http_port: 80 - https_port: 443 - priority: 1 - weight: 123 + host_name: "10.0.0.1" + origin_host_header: "10.0.0.1" + http_port: 80 + https_port: 443 + priority: 1 + weight: 123 register: output - name: Assert the resource was not changed @@ -102,13 +100,12 @@ profile_name: "{{ profile }}" resource_group: "{{ resource_group }}" state: "present" - origin: - host_name: "10.0.1.2" - origin_host_header: "10.0.1.2" - http_port: 80 - https_port: 443 - priority: 2 - weight: 234 + host_name: "10.0.1.2" + origin_host_header: "10.0.1.2" + http_port: 80 + https_port: 443 + priority: 2 + weight: 234 register: output - name: Assert the resource was created @@ -137,12 +134,11 @@ profile_name: "{{ profile }}" resource_group: "{{ resource_group }}" state: "present" - origin: - host_name: "10.0.0.2" - origin_host_header: "10.0.0.2" - http_port: 8080 - priority: 1 - weight: 111 + host_name: "10.0.0.2" + origin_host_header: "10.0.0.2" + http_port: 8080 + priority: 1 + weight: 111 register: output - name: Assert the resource was changed @@ -157,12 +153,11 @@ profile_name: "{{ profile }}" resource_group: "{{ resource_group }}" state: "present" - origin: - host_name: "10.0.0.2" - origin_host_header: "10.0.0.2" - http_port: 8080 - priority: 1 - weight: 111 + host_name: "10.0.0.2" + origin_host_header: "10.0.0.2" + http_port: 8080 + priority: 1 + weight: 111 register: output - name: Assert the resource was not changed From e278d17f26e57876790f24fb8e972472460b476f Mon Sep 17 00:00:00 2001 From: jartoo Date: Thu, 27 Jun 2024 12:22:49 -0500 Subject: [PATCH 5/5] Corrected for when private_link is None --- plugins/modules/azure_rm_afdorigin.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/modules/azure_rm_afdorigin.py b/plugins/modules/azure_rm_afdorigin.py index 64306875d..59c0f7f4d 100644 --- a/plugins/modules/azure_rm_afdorigin.py +++ b/plugins/modules/azure_rm_afdorigin.py @@ -168,7 +168,7 @@ try: - from azure.mgmt.cdn.models import AFDOrigin, AFDOriginUpdateParameters, SharedPrivateLinkResourceProperties + from azure.mgmt.cdn.models import AFDOrigin, AFDOriginUpdateParameters, SharedPrivateLinkResourceProperties, ResourceReference from azure.mgmt.cdn import CdnManagementClient except ImportError as ec: # This is handled in azure_rm_common @@ -321,7 +321,9 @@ def exec_module(self, **kwargs): to_be_updated = True if response['enabled_state'] != self.enabled_state and self.enabled_state: to_be_updated = True - if response['shared_private_link_resource']: + if response['shared_private_link_resource'] != self.shared_private_link_resource and self.shared_private_link_resource: + to_be_updated = True + elif response['shared_private_link_resource']: if response['shared_private_link_resource']['group_id'] != \ self.shared_private_link_resource.get('group_id') and \ self.shared_private_link_resource.get('group_id'): @@ -379,7 +381,7 @@ def create_origin(self): if self.shared_private_link_resource: shared_private_link_resource = SharedPrivateLinkResourceProperties( group_id=self.shared_private_link_resource.get('group_id'), - private_link=self.shared_private_link_resource.get('private_link'), + private_link=ResourceReference(id=self.shared_private_link_resource.get('private_link')), private_link_location=self.shared_private_link_resource.get('private_link_location'), request_message=self.shared_private_link_resource.get('request_message'), status=self.shared_private_link_resource.get('status') @@ -422,7 +424,7 @@ def update_origin(self): if self.shared_private_link_resource: shared_private_link_resource = SharedPrivateLinkResourceProperties( group_id=self.shared_private_link_resource.get('group_id'), - private_link=self.shared_private_link_resource.get('private_link'), + private_link=ResourceReference(id=self.shared_private_link_resource.get('private_link')), private_link_location=self.shared_private_link_resource.get('private_link_location'), request_message=self.shared_private_link_resource.get('request_message'), status=self.shared_private_link_resource.get('status')