Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add azure_rm_afdendpoint module to support Azure Front Door Standard and Premium #1589

Merged
merged 12 commits into from
Jul 1, 2024
Merged
351 changes: 351 additions & 0 deletions plugins/modules/azure_rm_afdendpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,351 @@
#!/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.afdendpointsoperations?view=azure-python
#
# TODO: Name check the URL
#
jartoo marked this conversation as resolved.
Show resolved Hide resolved
from __future__ import absolute_import, division, print_function
__metaclass__ = type

DOCUMENTATION = '''
---
module: azure_rm_afdendpoint

version_added: ""
jartoo marked this conversation as resolved.
Show resolved Hide resolved

short_description: Manage an Azure Front Door Endpoint to be used with Standard or Premium Frontdoor

description:
- Create, update and delete an Azure Front Door (AFD) Endpoint to be used by a Front Door Service Profile created using azure_rm_cdnprofile. This differs from the Front Door classic service and only is intended to be used by the Standard or Premium service offering.
jartoo marked this conversation as resolved.
Show resolved Hide resolved

options:
auto_generated_domain_name_label_scope:
description:
- Indicates the endpoint name reuse scope. Known values are: "TenantReuse", "SubscriptionReuse", "ResourceGroupReuse", and "NoReuse". Cannot be used to update an existing Endpoint at this time.
jartoo marked this conversation as resolved.
Show resolved Hide resolved
default: TenantReuse
type: str
enabled_state:
description:
- Whether to enable use of this rule.
default: Enabled
type: str
location:
description:
- Valid Azure location. Defaults to location of the resource group. Cannot be used to update an existing Endpoint at this time.
type: str
name:
description:
- Name of the AFD Endpoint.
required: true
type: str
profile_name:
description:
- Name of the AFD Profile where the Endpoint will be attached to.
required: true
type: str
resource_group:
description:
- Name of a resource group where the Azure Front Door Endpoint exists or will be created.
required: true
type: str
state:
description:
- Assert the state of the AFD Endpoint. Use C(present) to create or update an AFD Endpoint and C(absent) to delete it.
default: present
type: str
choices:
- absent
- present

extends_documentation_fragment:
- azure.azcollection.azure
- azure.azcollection.azure_tags

author:
- Jarret Tooley (@jartoo)
'''

EXAMPLES = '''
- name: Create an AFD Endpoint
azure_rm_afdendpoint:
name: myEndpoint
profile_name: myProfile
resource_group: myResourceGroup
state: present
tags:
testing: testing

- name: Delete the AFD Endpoint
azure_rm_afdendpoint:
name: myEndPoint
profile_name: myProfile
resource_group: myResourceGroup
state: absent
'''
RETURN = '''
id:
description:
- ID of the AFD Endpoint.
returned: always
type: str
sample: "id: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroup/providers/Microsoft.Cdn/profiles/myProfile/endpoints/myEndpoint"
jartoo marked this conversation as resolved.
Show resolved Hide resolved
host_name:
description:
- Host name of the AFD Endpoint.
returned: always
type: str
sample: "myendpoint.azurefd.net"

'''
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase

try:
from azure.mgmt.cdn.models import AFDEndpoint, AFDEndpointUpdateParameters
from azure.mgmt.cdn import CdnManagementClient
except ImportError as ec:
# This is handled in azure_rm_common
pass


def endpoint_to_dict(endpoint):
return dict(
deployment_status = endpoint.deployment_status,
jartoo marked this conversation as resolved.
Show resolved Hide resolved
jartoo marked this conversation as resolved.
Show resolved Hide resolved
enabled_state = endpoint.enabled_state,
jartoo marked this conversation as resolved.
Show resolved Hide resolved
host_name = endpoint.host_name,
id = endpoint.id,
location=endpoint.location,
name=endpoint.name,
provisioning_state=endpoint.provisioning_state,
tags=endpoint.tags,
type=endpoint.type
)


class AzureRMEndpoint(AzureRMModuleBase):

def __init__(self):
self.module_arg_spec = dict(
auto_generated_domain_name_label_scope=dict(
type='str',
default = 'TenantReuse',
choices=["TenantReuse", "SubscriptionReuse", "ResourceGroupReuse", "NoReuse"]
),
enabled_state=dict(
type='str',
default = 'Enabled',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
default = 'Enabled',
default='Enabled',

choices=['Enabled', 'Disabled']
),
location=dict(
type='str'
),
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']
)
)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

self.auto_generated_domain_name_label_scope = None
self.enabled_state = None
self.location = None
self.tags = None

self.name = None
self.profile_name = None
self.resource_group = None
self.state = None

self.endpoint_client = None

self.results = dict(changed=False)

super(AzureRMEndpoint, self).__init__(derived_arg_spec=self.module_arg_spec,
supports_check_mode=True,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

177, 178 continuation line over-indented for visual indent

supports_tags=True)

def exec_module(self, **kwargs):
"""Main module execution method"""

for key in list(self.module_arg_spec.keys()) + ['tags']:
setattr(self, key, kwargs[key])

self.endpoint_client = self.get_endpoint_client()

to_be_updated = False

if not self.location:
resource_group = self.get_resource_group(self.resource_group)
self.location = resource_group.location

response = self.get_endpoint()

if self.state == 'present':

if not response:
self.log("Need to create the AFD Endpoint")

if not self.check_mode:
new_response = self.create_endpoint()
self.results['id'] = new_response['id']
self.results['host_name'] = new_response['host_name']
self.log("AFD Endpoint creation done")

self.results['changed'] = True
return self.results

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

else:
self.log('Results : {0}'.format(response))
self.results['id'] = response['id']
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.results['id'] = response['id']
self.results['id'] = response['id']

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand this one, as there is no response in this section of code, so setting self.results['id'] = response['id'] would be blank. I set it to new_response['id'] as the return result from the create_endpoint() function. I could honestly be missing something though.. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or are you referring to having a value when in check mode?

self.results['host_name'] = response['host_name']

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

update_tags, self.tags = self.update_tags(response['tags'])

if update_tags:
to_be_updated = True

if response['provisioning_state'] == "Succeeded":
if response['enabled_state'] != self.enabled_state:
to_be_updated = True

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

if self.location.lower() != response['location'].lower() and self.location:
# Location is not currently implemented in begin_update()
self.log("AFD Endpoint locations changes are not idempotent, please delete and recreate the Endpoint.")

if to_be_updated:
self.log("Need to update the AFD Endpoint")
self.results['changed'] = True

if not self.check_mode:
result = self.update_endpoint()
self.results['host_name'] = result['host_name']
self.log("AFD Endpoint update done")
jartoo marked this conversation as resolved.
Show resolved Hide resolved

elif self.state == 'absent':
if not response:
self.log("AFD Endpoint {0} does not exist.".format(self.name))
self.results['id'] = ""
else:
self.log("Need to delete the AFD Endpoint")
self.results['changed'] = True
self.results['id'] = response['id']

if not self.check_mode:
self.delete_endpoint()
self.log("Azure AFD Endpoint deleted")

return self.results

def create_endpoint(self):
'''
Creates an AFD Endpoint.

:return: deserialized AFD Endpoint instance state dictionary
'''
self.log("Creating the AFD Endpoint instance {0}".format(self.name))

parameters = AFDEndpoint(
auto_generated_domain_name_label_scope=self.auto_generated_domain_name_label_scope,
enabled_state=self.enabled_state,
location=self.location,
tags=self.tags
)

try:
poller = self.endpoint_client.afd_endpoints.begin_create(
endpoint_name=self.name,
profile_name=self.profile_name,
resource_group_name=self.resource_group,
endpoint=parameters)
response = self.get_poller_result(poller)
return endpoint_to_dict(response)
except Exception as exc:
self.log('Error attempting to create AFD Endpoint instance.')
self.fail("Error Creating AFD Endpoint instance: {0}".format(str(exc)))

def update_endpoint(self):
'''
Updates an AFD Endpoint.

:return: deserialized AFD Endpoint instance state dictionary
'''
self.log("Updating the AFD Endpoint instance {0}".format(self.name))

parameters = AFDEndpointUpdateParameters(
tags=self.tags,
enabled_state=self.enabled_state
)

jartoo marked this conversation as resolved.
Show resolved Hide resolved
try:
poller = self.endpoint_client.afd_endpoints.begin_update(
endpoint_name=self.name,
profile_name=self.profile_name,
resource_group_name=self.resource_group,
endpoint_update_properties=parameters)
response = self.get_poller_result(poller)
return endpoint_to_dict(response)
except Exception as exc:
self.log('Error attempting to update AFD Endpoint instance.')
self.fail("Error updating AFD Endpoint instance: {0}".format(str(exc)))

def delete_endpoint(self):
'''
Deletes the specified AFD Endpoint in the specified subscription and resource group.

:return: True
'''
self.log("Deleting the AFD Endpoint {0}".format(self.name))
try:
poller = self.endpoint_client.afd_endpoints.begin_delete(
resource_group_name=self.resource_group, profile_name=self.profile_name, endpoint_name=self.name)
self.get_poller_result(poller)
return True
except Exception as exc:
self.log('Error attempting to delete the AFD Endpoint.')
self.fail("Error deleting the AFD Endpoint: {0}".format(str(exc)))
return False

def get_endpoint(self):
'''
Gets the properties of the specified AFD Endpoint.

:return: deserialized AFD Endpoint state dictionary
'''
self.log(
"Checking if the AFD Endpoint {0} is present".format(self.name))
try:
response = self.endpoint_client.afd_endpoints.get(resource_group_name=self.resource_group, profile_name=self.profile_name, endpoint_name=self.name)
self.log("Response : {0}".format(response))
self.log("AFD Endpoint : {0} found".format(response.name))
return endpoint_to_dict(response)
except Exception as err:
self.log('Did not find the AFD Endpoint.')
return False

def get_endpoint_client(self):
if not self.endpoint_client:
self.endpoint_client = self.get_mgmt_svc_client(CdnManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
jartoo marked this conversation as resolved.
Show resolved Hide resolved
api_version='2023-05-01')
return self.endpoint_client


def main():
"""Main execution"""
AzureRMEndpoint()

jartoo marked this conversation as resolved.
Show resolved Hide resolved
if __name__ == '__main__':
main()
Loading