diff --git a/plugins/modules/azure_rm_sqlmanagedinstance.py b/plugins/modules/azure_rm_sqlmanagedinstance.py index 3cb6e1138b..df0e1f7529 100644 --- a/plugins/modules/azure_rm_sqlmanagedinstance.py +++ b/plugins/modules/azure_rm_sqlmanagedinstance.py @@ -65,23 +65,30 @@ - Azure Active Directory identity configuration for a resource. type: dict suboptions: - user_assigned_identities: - description: - - The resource ids of the user assigned identities to use. - type: str - principal_id: - description: - - The Azure Active Directory principal ID. - type: str type: description: - - The identity type. - - Set this to C(SystemAssigned) in order to automatically create and assign an Azure Active Directory principal for the resource. + - Type of the managed identity + choices: + - SystemAssigned + - UserAssigned + - SystemAssigned, UserAssigned + - None + default: None type: str - tenant_id: + user_assigned_identities: description: - - The Azure Active Directory tenant id. - type: str + - User Assigned Managed Identities and its options + required: false + type: dict + default: {} + suboptions: + id: + description: + - List of the user assigned identities IDs associated to the WebApp + required: false + type: list + elements: str + default: [] managed_instance_create_mode: description: - Specifies the mode of database creation. @@ -513,6 +520,7 @@ try: from azure.core.exceptions import ResourceNotFoundError + from azure.mgmt.sql.models import (ResourceIdentity, UserIdentity) except ImportError: pass @@ -526,11 +534,28 @@ ) -identity_spec = dict( - user_assigned_identities=dict(type='str'), - principal_id=dict(type='str'), - type=dict(type='str'), - tenant_id=dict(type='str') +user_assigned_identities_spec = dict( + id=dict( + type='list', + default=[], + elements='str' + ) +) + +managed_identity_spec = dict( + type=dict( + type='str', + choices=['SystemAssigned', + 'UserAssigned', + 'SystemAssigned, UserAssigned', + 'None'], + default='None' + ), + user_assigned_identities=dict( + type='dict', + options=user_assigned_identities_spec, + default={} + ), ) @@ -555,7 +580,7 @@ def __init__(self): ), identity=dict( type='dict', - options=identity_spec + options=managed_identity_spec ), sku=dict( type='dict', @@ -648,10 +673,20 @@ def __init__(self): self.name = None self.location = None self.state = None + self.identity = None self.body = dict() + self._managed_identity = None super(AzureRMSqlManagedInstance, self).__init__(self.module_arg_spec, supports_check_mode=True, supports_tags=True) + @property + def managed_identity(self): + if not self._managed_identity: + self._managed_identity = {"identity": ResourceIdentity, + "user_assigned": UserIdentity + } + return self._managed_identity + def exec_module(self, **kwargs): for key in list(self.module_arg_spec) + ['tags']: @@ -668,6 +703,13 @@ def exec_module(self, **kwargs): self.body['location'] = self.location sql_managed_instance = self.get() + + update_identity, identity = self.update_managed_identity(sql_managed_instance and + sql_managed_instance.get('identity'), + self.identity) + if update_identity: + self.body["identity"] = identity.as_dict() + changed = False if self.state == 'present': if sql_managed_instance: @@ -678,7 +720,7 @@ def exec_module(self, **kwargs): if not self.default_compare(modifiers, self.body, sql_managed_instance, '', self.results): changed = True - if changed: + if changed or update_identity: if not self.check_mode: # sql_managed_instance = self.update_sql_managed_instance(self.body) sql_managed_instance = self.create_or_update(self.body) diff --git a/tests/integration/targets/azure_rm_sqlmanagedinstance/tasks/main.yml b/tests/integration/targets/azure_rm_sqlmanagedinstance/tasks/main.yml index c635a5614c..a0addc7fe6 100644 --- a/tests/integration/targets/azure_rm_sqlmanagedinstance/tasks/main.yml +++ b/tests/integration/targets/azure_rm_sqlmanagedinstance/tasks/main.yml @@ -1,7 +1,35 @@ -- name: Prepare random number +- name: Gather Resource Group info + azure.azcollection.azure_rm_resourcegroup_info: + name: "{{ resource_group }}" + register: __rg_info + +- name: Prepare random number and set location based on resource group ansible.builtin.set_fact: random_postfix: "sqlmi{{ 1000 | random }}{{ resource_group | hash('md5') | truncate(7, True, '') }}" password: "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_letters', 'digits', 'punctuation'], length=25) }}" + location: "{{ __rg_info.resourcegroups.0.location }}" + +- name: Delete sql managed instance + azure_rm_sqlmanagedinstance: + resource_group: "{{ resource_group }}" + name: "{{ random_postfix }}" + state: absent + +- name: Create identity and policy ids arrays + ansible.builtin.set_fact: + managed_identity_ids: [] + access_policies_object_ids: [] + +- name: Create user managed identities + ansible.builtin.include_tasks: managedidentity.yml + vars: + managed_identity_test_unique: 'sqlmanagedinstance' + managed_identity_unique: "{{ item }}" + managed_identity_action: 'create' + managed_identity_location: "{{ location }}" + with_items: + - '1' + - '2' - name: Create virtual network azure_rm_virtualnetwork: @@ -69,7 +97,11 @@ name: "{{ random_postfix }}" subnet_id: "{{ subnet_output.state.id }}" identity: - type: SystemAssigned + type: UserAssigned + user_assigned_identities: + id: + - "{{ managed_identity_ids[0] }}" + primary_user_assigned_identity_id: "{{ managed_identity_ids[0] }}" sku: name: GP_Gen5 tier: GeneralPurpose @@ -81,6 +113,7 @@ v_cores: 8 tags: key0: value0 + check_mode: true register: output - name: Assert the resource instance is not exist @@ -94,7 +127,11 @@ name: "{{ random_postfix }}" subnet_id: "{{ subnet_output.state.id }}" identity: - type: SystemAssigned + type: UserAssigned + user_assigned_identities: + id: + - "{{ managed_identity_ids[0] }}" + primary_user_assigned_identity_id: "{{ managed_identity_ids[0] }}" sku: name: GP_Gen5 tier: GeneralPurpose @@ -113,13 +150,26 @@ that: - output.changed +- name: Wait for sql managed instance provisioning to complete + azure_rm_sqlmanagedinstance_info: + resource_group: "{{ resource_group }}" + name: "{{ random_postfix }}" + register: facts + until: facts.sql_managed_instance[0]['state'] == 'Ready' + retries: 40 + delay: 60 + - name: Create sql managed instance (Idempotent test) azure_rm_sqlmanagedinstance: resource_group: "{{ resource_group }}" name: "{{ random_postfix }}" subnet_id: "{{ subnet_output.state.id }}" identity: - type: SystemAssigned + type: UserAssigned + user_assigned_identities: + id: + - "{{ managed_identity_ids[0] }}" + primary_user_assigned_identity_id: "{{ managed_identity_ids[0] }}" sku: name: GP_Gen5 tier: GeneralPurpose @@ -137,13 +187,17 @@ that: - not output.changed -- name: Upgarde sql managed instance with tags +- name: Upgrade sql managed instance with tags azure_rm_sqlmanagedinstance: resource_group: "{{ resource_group }}" name: "{{ random_postfix }}" subnet_id: "{{ subnet_output.state.id }}" identity: - type: SystemAssigned + type: UserAssigned + user_assigned_identities: + id: + - "{{ managed_identity_ids[0] }}" + primary_user_assigned_identity_id: "{{ managed_identity_ids[0] }}" sku: name: GP_Gen5 tier: GeneralPurpose @@ -175,9 +229,32 @@ - output.sql_managed_instance[0].tags | length == 1 - output.sql_managed_instance[0].storage_size_in_gb == 256 - output.sql_managed_instance[0].sku.name == 'GP_Gen5' + - output.sql_managed_instance[0].identity.type == 'UserAssigned' + - output.sql_managed_instance[0].identity.user_assigned_identities | length == 1 + - output.sql_managed_instance[0].identity.user_assigned_identities[managed_identity_ids[0]] is defined + +- name: Wait for sql managed instance provisioning to complete + azure_rm_sqlmanagedinstance_info: + resource_group: "{{ resource_group }}" + name: "{{ random_postfix }}" + register: facts + until: facts.sql_managed_instance[0]['state'] == 'Ready' + retries: 40 + delay: 60 - name: Delete sql managed instance azure_rm_sqlmanagedinstance: resource_group: "{{ resource_group }}" name: "{{ random_postfix }}" state: absent + +- name: Delete user managed identities + ansible.builtin.include_tasks: managedidentity.yml + vars: + managed_identity_test_unique: 'sqlmanagedinstance' + managed_identity_unique: "{{ item }}" + managed_identity_action: 'delete' + managed_identity_location: "{{ location }}" + with_items: + - '1' + - '2' diff --git a/tests/integration/targets/azure_rm_sqlmanagedinstance/tasks/managedidentity.yml b/tests/integration/targets/azure_rm_sqlmanagedinstance/tasks/managedidentity.yml new file mode 100644 index 0000000000..f0389056b1 --- /dev/null +++ b/tests/integration/targets/azure_rm_sqlmanagedinstance/tasks/managedidentity.yml @@ -0,0 +1,57 @@ +- name: Set user managed identity name + ansible.builtin.set_fact: + identity_name: "ansible-test-{{ managed_identity_test_unique }}-identity-{{ managed_identity_unique }}" + +- name: Set user managed identity ID base path + ansible.builtin.set_fact: + base_path: "/subscriptions/{{ azure_subscription_id }}/resourcegroups/{{ resource_group }}" + +- name: Set user managed identity ID + ansible.builtin.set_fact: + identity_id: "{{ base_path }}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{{ identity_name }}" + +- name: Set managed identity ID into the ids list. + ansible.builtin.set_fact: + managed_identity_ids: "{{ managed_identity_ids + [identity_id] }}" + when: managed_identity_action == 'create' + +- name: Create the user managed identity - {{ identity_name }} + azure_rm_resource: + resource_group: "{{ resource_group }}" + provider: ManagedIdentity + resource_type: userAssignedIdentities + resource_name: "{{ identity_name }}" + api_version: "2023-01-31" + body: + location: "{{ managed_identity_location }}" + state: present + when: managed_identity_action == 'create' + +- name: Lookup service principal object id for identity {{ identity_name }} + azure_rm_resource_info: + api_version: "2023-01-31" + resource_group: "{{ resource_group }}" + provider: ManagedIdentity + resource_type: userAssignedIdentities + resource_name: "{{ identity_name }}" + register: output + when: + - access_policies_object_ids is defined + - managed_identity_action == 'create' + +- name: Set object ID for identity {{ identity_name }} + ansible.builtin.set_fact: + access_policies_object_ids: "{{ access_policies_object_ids + [output.response[0].properties.principalId] }}" + when: + - access_policies_object_ids is defined + - managed_identity_action == 'create' + +- name: Destroy the user managed identity - {{ identity_name }} + azure_rm_resource: + resource_group: "{{ resource_group }}" + provider: ManagedIdentity + resource_type: userAssignedIdentities + resource_name: "{{ identity_name }}" + api_version: "2023-01-31" + state: absent + when: managed_identity_action == 'delete'