diff options
Diffstat (limited to 'ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser.py')
-rw-r--r-- | ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser.py | 188 |
1 files changed, 125 insertions, 63 deletions
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser.py index b41c5ae7e..1e0a238c0 100644 --- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser.py +++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser.py @@ -5,6 +5,7 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function + __metaclass__ = type DOCUMENTATION = ''' @@ -18,11 +19,6 @@ description: - Create, delete, and update an Azure Active Directory user. options: - tenant: - description: - - The tenant ID. - type: str - required: True state: description: - State of the ad user. Use C(present) to create or update an ad user and C(absent) to delete an ad user. @@ -57,11 +53,13 @@ options: - The surname for the user. - Used when either creating or updating a user account. type: str - immutable_id: + on_premises_immutable_id: description: - - The immutable_id of the user. + - The on_premises_immutable_id of the user. - Used when either creating or updating a user account. type: str + aliases: + - immutable_id mail: description: - The primary email address of the user. @@ -114,6 +112,13 @@ options: - Filter that can be used to specify a user to update or delete. - Mutually exclusive with I(object_id), I(attribute_name), and I(user_principal_name). type: str + company_name: + description: + - The name of the company that the user is associated with. + - This property can be useful for describing the company that an external user comes from. + - The maximum length is 64 characters.Returned only on $select. + - Supports $filter (eq, ne, not, ge, le, in, startsWith, and eq on null values). + type: str extends_documentation_fragment: - azure.azcollection.azure @@ -126,30 +131,28 @@ EXAMPLES = ''' - name: Create user azure_rm_aduser: user_principal_name: "{{ user_id }}" - tenant: "{{ tenant_id }}" state: "present" account_enabled: "True" display_name: "Test_{{ user_principal_name }}_Display_Name" password_profile: "password" mail_nickname: "Test_{{ user_principal_name }}_mail_nickname" - immutable_id: "{{ object_id }}" + on_premises_immutable_id: "{{ object_id }}" given_name: "First" surname: "Last" user_type: "Member" usage_location: "US" mail: "{{ user_principal_name }}@contoso.com" + company_name: 'Test Company' - name: Update user with new value for account_enabled azure_rm_aduser: user_principal_name: "{{ user_id }}" - tenant: "{{ tenant_id }}" state: "present" account_enabled: "False" - name: Delete user azure_rm_aduser: user_principal_name: "{{ user_id }}" - tenant: "{{ tenant_id }}" state: "absent" ''' @@ -196,15 +199,21 @@ user_type: returned: always type: str sample: Member +company_name: + description: + - The name of the company that the user is associated with. + type: str + returned: always + sample: 'Test Company' ''' from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBase try: - from azure.graphrbac.models import UserUpdateParameters - from azure.graphrbac.models import UserCreateParameters - from azure.graphrbac.models import PasswordProfile - from azure.graphrbac.models import GraphErrorException + import asyncio + from msgraph.generated.models.password_profile import PasswordProfile + from msgraph.generated.models.user import User + from msgraph.generated.users.users_request_builder import UsersRequestBuilder except ImportError: # This is handled in azure_rm_common pass @@ -224,16 +233,15 @@ class AzureRMADUser(AzureRMModuleBase): display_name=dict(type='str'), password_profile=dict(type='str', no_log=True), mail_nickname=dict(type='str'), - immutable_id=dict(type='str'), + on_premises_immutable_id=dict(type='str', aliases=['immutable_id']), usage_location=dict(type='str'), given_name=dict(type='str'), surname=dict(type='str'), user_type=dict(type='str'), mail=dict(type='str'), - tenant=dict(type='str', required=True), + company_name=dict(type='str') ) - self.tenant = None self.user_principal_name = None self.state = None self.object_id = None @@ -244,12 +252,13 @@ class AzureRMADUser(AzureRMModuleBase): self.display_name = None self.password_profile = None self.mail_nickname = None - self.immutable_id = None + self.on_premises_immutable_id = None self.usage_location = None self.given_name = None self.surname = None self.user_type = None self.mail = None + self.company_name = None self.log_path = None self.log_mode = None @@ -273,9 +282,9 @@ class AzureRMADUser(AzureRMModuleBase): setattr(self, key, kwargs[key]) try: - client = self.get_graphrbac_client(self.tenant) + self._client = self.get_msgraph_client() - ad_user = self.get_exisiting_user(client) + ad_user = self.get_exisiting_user() if self.state == 'present': @@ -284,11 +293,13 @@ class AzureRMADUser(AzureRMModuleBase): password = None if self.password_profile: - password = PasswordProfile(password=self.password_profile) + password = PasswordProfile( + password=self.password_profile, + ) should_update = False - if self.immutable_id and ad_user.immutable_id != self.immutable_id: + if self.on_premises_immutable_id and ad_user.on_premises_immutable_id != self.on_premises_immutable_id: should_update = True if should_update or self.usage_location and ad_user.usage_location != self.usage_location: should_update = True @@ -308,84 +319,73 @@ class AzureRMADUser(AzureRMModuleBase): should_update = True if should_update or self.mail_nickname and ad_user.mail_nickname != self.mail_nickname: should_update = True + if should_update or self.company_name and ad_user.company_name != self.company_name: + should_update = True if should_update: - parameters = UserUpdateParameters(immutable_id=self.immutable_id, - usage_location=self.usage_location, - given_name=self.given_name, - surname=self.surname, - user_type=self.user_type, - account_enabled=self.account_enabled, - display_name=self.display_name, - password_profile=password, - user_principal_name=self.user_principal_name, - mail_nickname=self.mail_nickname) - - client.users.update(upn_or_object_id=ad_user.object_id, parameters=parameters) + asyncio.get_event_loop().run_until_complete(self.update_user(ad_user, password)) self.results['changed'] = True # Get the updated versions of the users to return # the update method, has no return value so it needs to be explicitely returned in a call - ad_user = self.get_exisiting_user(client) + ad_user = self.get_exisiting_user() else: self.results['changed'] = False else: # Create, changed - password = PasswordProfile(password=self.password_profile) - parameters = UserCreateParameters(account_enabled=self.account_enabled, - display_name=self.display_name, - password_profile=password, - user_principal_name=self.user_principal_name, - mail_nickname=self.mail_nickname, - immutable_id=self.immutable_id, - usage_location=self.usage_location, - given_name=self.given_name, - surname=self.surname, - user_type=self.user_type, - mail=self.mail) - ad_user = client.users.create(parameters=parameters) + asyncio.get_event_loop().run_until_complete(self.create_user()) self.results['changed'] = True + ad_user = self.get_exisiting_user() self.results['ad_user'] = self.to_dict(ad_user) elif self.state == 'absent': if ad_user: # Delete, changed - client.users.delete(ad_user.object_id) + asyncio.get_event_loop().run_until_complete(self.delete_user(ad_user)) self.results['changed'] = True else: # Do nothing unchanged self.results['changed'] = False - except GraphErrorException as e: + except Exception as e: self.fail("failed to get ad user info {0}".format(str(e))) return self.results - def get_exisiting_user(self, client): + def get_exisiting_user(self): ad_user = None try: if self.user_principal_name is not None: - ad_user = client.users.get(self.user_principal_name) + ad_user = asyncio.get_event_loop().run_until_complete(self.get_user(self.user_principal_name)) elif self.object_id is not None: - ad_user = client.users.get(self.object_id) + ad_user = asyncio.get_event_loop().run_until_complete(self.get_user(self.object_id)) elif self.attribute_name is not None and self.attribute_value is not None: try: - ad_user = list(client.users.list(filter="{0} eq '{1}'".format(self.attribute_name, self.attribute_value)))[0] - except GraphErrorException as e: + users = asyncio.get_event_loop().run_until_complete( + self.get_users_by_filter("{0} eq '{1}'".format(self.attribute_name, self.attribute_value))) + ad_users = list(users.value) + ad_user = ad_users[0] + except Exception as e: # the type doesn't get more specific. Could check the error message but no guarantees that message doesn't change in the future # more stable to try again assuming the first error came from the attribute being a list try: - ad_user = list(client.users.list(filter="{0}/any(c:c eq '{1}')".format(self.attribute_name, self.attribute_value)))[0] - except GraphErrorException as sub_e: + users = asyncio.get_event_loop().run_until_complete(self.get_users_by_filter( + "{0}/any(c:c eq '{1}')".format(self.attribute_name, self.attribute_value))) + ad_users = list(users.value) + ad_user = ad_users[0] + except Exception as sub_e: raise elif self.odata_filter is not None: # run a filter based on user input to return based on any given attribute/query - ad_user = list(client.users.list(filter=self.odata_filter))[0] - except GraphErrorException as e: + users = asyncio.get_event_loop().run_until_complete(self.get_users_by_filter(self.odata_filter)) + ad_users = list(users.value) + ad_user = ad_users[0] + except Exception as e: # User was not found err_msg = str(e) - if err_msg == "Resource '{0}' does not exist or one of its queried reference-property objects are not present.".format(self.user_principal_name): + if "Resource '{0}' does not exist or one of its queried reference-property objects are not present.".format( + self.user_principal_name) in err_msg: ad_user = None else: raise @@ -393,13 +393,75 @@ class AzureRMADUser(AzureRMModuleBase): def to_dict(self, object): return dict( - object_id=object.object_id, + object_id=object.id, display_name=object.display_name, user_principal_name=object.user_principal_name, mail_nickname=object.mail_nickname, mail=object.mail, account_enabled=object.account_enabled, - user_type=object.user_type + user_type=object.user_type, + company_name=object.company_name + ) + + async def update_user(self, ad_user, password): + request_body = User( + on_premises_immutable_id=self.on_premises_immutable_id, + usage_location=self.usage_location, + given_name=self.given_name, + surname=self.surname, + user_type=self.user_type, + account_enabled=self.account_enabled, + display_name=self.display_name, + password_profile=password, + user_principal_name=self.user_principal_name, + mail_nickname=self.mail_nickname, + company_name=self.company_name + ) + return await self._client.users.by_user_id(ad_user.id).patch(body=request_body) + + async def create_user(self): + password = PasswordProfile( + password=self.password_profile + ) + request_body = User( + account_enabled=self.account_enabled, + display_name=self.display_name, + password_profile=password, + user_principal_name=self.user_principal_name, + mail_nickname=self.mail_nickname, + on_premises_immutable_id=self.on_premises_immutable_id, + usage_location=self.usage_location, + given_name=self.given_name, + surname=self.surname, + user_type=self.user_type, + mail=self.mail, + company_name=self.company_name + ) + return await self._client.users.post(body=request_body) + + async def delete_user(self, ad_user): + return await self._client.users.by_user_id(ad_user.id).delete() + + async def get_user(self, object): + request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration( + query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters( + select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName", "userType", + "onPremisesImmutableId", "usageLocation", "givenName", "surname", "companyName"] + ), + ) + return await self._client.users.by_user_id(object).get(request_configuration=request_configuration) + + async def get_users_by_filter(self, filter): + return await self._client.users.get( + request_configuration=UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration( + query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters( + filter=filter, + select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName", + "userType", "onPremisesImmutableId", "usageLocation", "givenName", "surname", "companyName"], + count=True + ), + headers={'ConsistencyLevel': "eventual", } + ) ) |