From 394eac4c7e6989dc623712c2193bfa6f4ca66de2 Mon Sep 17 00:00:00 2001 From: Bill Peck Date: Tue, 17 Sep 2024 23:04:36 -0400 Subject: [PATCH] Add Identity Management to log_analytics (#1683) Added Identity Management support to log_analytics module. --- .../modules/azure_rm_loganalyticsworkspace.py | 63 ++++++++++++++++--- requirements.txt | 2 +- .../tasks/main.yml | 45 +++++++++++++ 3 files changed, 100 insertions(+), 10 deletions(-) diff --git a/plugins/modules/azure_rm_loganalyticsworkspace.py b/plugins/modules/azure_rm_loganalyticsworkspace.py index b323d9292..1110fefe9 100644 --- a/plugins/modules/azure_rm_loganalyticsworkspace.py +++ b/plugins/modules/azure_rm_loganalyticsworkspace.py @@ -38,6 +38,26 @@ description: - Resource location. type: str + identity: + description: + - Identity for Azure Recovery Service Vault. + type: dict + version_added: '3.0.0' + suboptions: + type: + description: + - Type of the managed identity + choices: + - SystemAssigned + - UserAssigned + - None + default: None + type: str + user_assigned_identity: + description: + - User Assigned Managed Identity associated to this resource + required: false + type: str sku: description: - The SKU of the workspace. @@ -160,14 +180,15 @@ from ansible.module_utils.common.dict_transformations import _snake_to_camel, _camel_to_snake try: - from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase + from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBaseExt from azure.core.exceptions import ResourceNotFoundError + from azure.mgmt.loganalytics.models import (Identity, UserIdentityProperties) except ImportError: # This is handled in azure_rm_common pass -class AzureRMLogAnalyticsWorkspace(AzureRMModuleBase): +class AzureRMLogAnalyticsWorkspace(AzureRMModuleBaseExt): def __init__(self): @@ -175,6 +196,10 @@ def __init__(self): resource_group=dict(type='str', required=True), name=dict(type='str', required=True), state=dict(type='str', default='present', choices=['present', 'absent']), + identity=dict( + type='dict', + options=self.managed_identity_single_spec + ), location=dict(type='str'), sku=dict(type='str', default='per_gb2018', choices=['free', 'standard', 'premium', 'unlimited', 'per_node', 'per_gb2018', 'standalone']), retention_in_days=dict(type='int'), @@ -191,13 +216,23 @@ def __init__(self): self.name = None self.state = None self.location = None + self.identity = None self.sku = None self.retention_in_days = None self.intelligence_packs = None self.force = None + self._managed_identity = None super(AzureRMLogAnalyticsWorkspace, self).__init__(self.module_arg_spec, supports_check_mode=True) + @property + def managed_identity(self): + if not self._managed_identity: + self._managed_identity = {"identity": Identity, + "user_assigned": UserIdentityProperties + } + return self._managed_identity + def exec_module(self, **kwargs): for key in list(self.module_arg_spec.keys()) + ['tags']: @@ -215,11 +250,18 @@ def exec_module(self, **kwargs): else: self.sku = _snake_to_camel(self.sku) workspace = self.get_workspace() + update_identity = False + identity = None + if self.identity: + old_identity = workspace and workspace.identity.as_dict() or None + update_identity, identity = self.update_single_managed_identity(curr_identity=old_identity, + new_identity=self.identity) if not workspace and self.state == 'present': changed = True workspace = self.log_analytics_models.Workspace(sku=self.log_analytics_models.WorkspaceSku(name=self.sku), retention_in_days=self.retention_in_days, location=self.location, + identity=identity, tags=self.tags) if not self.check_mode: workspace = self.create_workspace(workspace) @@ -230,10 +272,13 @@ def exec_module(self, **kwargs): update_tags, results['tags'] = self.update_tags(workspace.tags) if update_tags: changed = True + if update_identity: + changed = True if not self.check_mode and changed: workspace = self.log_analytics_models.Workspace(sku=self.log_analytics_models.WorkspaceSku(name=self.sku), retention_in_days=self.retention_in_days, location=self.location, + identity=identity, tags=results['tags']) workspace = self.create_workspace(workspace) elif workspace and self.state == 'absent': @@ -268,7 +313,7 @@ def create_workspace(self, workspace): poller = self.log_analytics_client.workspaces.begin_create_or_update(self.resource_group, self.name, workspace) return self.get_poller_result(poller) except Exception as exc: - self.fail('Error when creating workspace {0} - {1}'.format(self.name, exc.message or str(exc))) + self.fail('Error when creating workspace {0} - {1}'.format(self.name, str(exc))) def get_workspace(self): try: @@ -280,7 +325,7 @@ def delete_workspace(self): try: self.log_analytics_client.workspaces.begin_delete(self.resource_group, self.name, force=self.force) except Exception as exc: - self.fail('Error when deleting workspace {0} - {1}'.format(self.name, exc.message or str(exc))) + self.fail('Error when deleting workspace {0} - {1}'.format(self.name, str(exc))) def to_dict(self, workspace): result = workspace.as_dict() @@ -292,7 +337,7 @@ def list_intelligence_packs(self): response = self.log_analytics_client.intelligence_packs.list(self.resource_group, self.name) return [x.as_dict() for x in response] except Exception as exc: - self.fail('Error when listing intelligence packs {0}'.format(exc.message or str(exc))) + self.fail('Error when listing intelligence packs {0}'.format(str(exc))) def change_intelligence(self, key, value): try: @@ -301,7 +346,7 @@ def change_intelligence(self, key, value): else: self.log_analytics_client.intelligence_packs.disable(self.resource_group, self.name, key) except Exception as exc: - self.fail('Error when changing intelligence pack {0} - {1}'.format(key, exc.message or str(exc))) + self.fail('Error when changing intelligence pack {0} - {1}'.format(key, str(exc))) def list_management_groups(self): result = [] @@ -312,7 +357,7 @@ def list_management_groups(self): except StopIteration: pass except Exception as exc: - self.fail('Error when listing management groups {0}'.format(exc.message or str(exc))) + self.fail('Error when listing management groups {0}'.format(str(exc))) return result def list_usages(self): @@ -324,14 +369,14 @@ def list_usages(self): except StopIteration: pass except Exception as exc: - self.fail('Error when listing usages {0}'.format(exc.message or str(exc))) + self.fail('Error when listing usages {0}'.format(str(exc))) return result def get_shared_keys(self): try: return self.log_analytics_client.shared_keys.get_shared_keys(self.resource_group, self.name).as_dict() except Exception as exc: - self.fail('Error when getting shared key {0}'.format(exc.message or str(exc))) + self.fail('Error when getting shared key {0}'.format(str(exc))) def main(): diff --git a/requirements.txt b/requirements.txt index b0e3a8468..bdecf425e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -41,7 +41,7 @@ azure-mgmt-keyvault==10.0.0 azure-mgmt-cosmosdb==10.0.0b3 azure-mgmt-hdinsight==9.0.0 azure-mgmt-devtestlabs==9.0.0 -azure-mgmt-loganalytics==12.0.0 +azure-mgmt-loganalytics==13.0.0b6 azure-mgmt-automation==1.0.0 azure-mgmt-iothub==3.0.0 azure-iot-hub==2.6.1;platform_machine=="x86_64" diff --git a/tests/integration/targets/azure_rm_loganalyticsworkspace/tasks/main.yml b/tests/integration/targets/azure_rm_loganalyticsworkspace/tasks/main.yml index be5e45572..744157a4f 100644 --- a/tests/integration/targets/azure_rm_loganalyticsworkspace/tasks/main.yml +++ b/tests/integration/targets/azure_rm_loganalyticsworkspace/tasks/main.yml @@ -1,7 +1,30 @@ +- name: Gather Resource Group info + azure.azcollection.azure_rm_resourcegroup_info: + name: "{{ resource_group }}" + register: __rg_info + - name: Prepare random number ansible.builtin.set_fact: name_rpfx: "loganalytics{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" retention_days: 60 + location: "{{ __rg_info.resourcegroups.0.location }}" + +- name: Create User Managed Identities + azure_rm_resource: + resource_group: "{{ resource_group }}" + provider: ManagedIdentity + resource_type: userAssignedIdentities + resource_name: "{{ item }}" + api_version: "2023-01-31" + body: + location: "{{ location }}" + state: present + loop: + - "ansible-test-loganalyze-identity" + +- name: Set identities IDs to test. Identities ansible-test-loganalyze-identity have to be created previously + ansible.builtin.set_fact: + user_identity_1: "/subscriptions/{{ azure_subscription_id }}/resourcegroups/{{ resource_group }}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/ansible-test-loganalyze-identity" - name: Create Log Analytics Workspace (Check Mode On) azure_rm_loganalyticsworkspace: @@ -11,6 +34,8 @@ Containers: true retention_in_days: "{{ retention_days }}" resource_group: "{{ resource_group }}" + identity: + type: SystemAssigned check_mode: true register: output @@ -44,6 +69,8 @@ resource_group: "{{ resource_group }}" tags: key1: value1 + identity: + type: SystemAssigned register: output - name: Assert the log analytics workspace created @@ -55,6 +82,7 @@ # - output.intelligence_packs | json_query('[?name == `Containers`].enabled') | first == true - output.sku == 'per_gb2018' - output.tags.key1 == 'value1' + - output.identity.type == 'SystemAssigned' - name: Update Log Analytics Workspace azure_rm_loganalyticsworkspace: @@ -67,6 +95,9 @@ tags: key1: value1 key2: value2 + identity: + type: UserAssigned + user_assigned_identity: "{{ user_identity_1 }}" register: output - name: Assert the log analytics workspace updated @@ -75,6 +106,8 @@ - output.changed - output.retention_in_days == retention_days - output.tags.key2 == 'value2' + - output.identity.type == 'UserAssigned' + - output.identity.user_assigned_identities | length == 1 - name: Get Log Analytics workspace information (Show full information) azure_rm_loganalyticsworkspace_info: @@ -102,6 +135,7 @@ - facts.workspaces[0].usages | length > 0 - facts.workspaces[0].management_groups is defined - facts.workspaces[0].sku == 'per_gb2018' + - user_identity_1 in facts.workspaces[0].identity.user_assigned_identities - name: Get Log Analytics workspace information (Show default information) azure_rm_loganalyticsworkspace_info: @@ -193,3 +227,14 @@ ansible.builtin.assert: that: - not output.changed + +- name: Destroy User Managed Identities + azure_rm_resource: + resource_group: "{{ resource_group }}" + provider: ManagedIdentity + resource_type: userAssignedIdentities + resource_name: "{{ item }}" + api_version: "2023-01-31" + state: absent + loop: + - "ansible-test-loganalyze-identity"