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

Support on_premises_extension_attributes to azure_rm_aduser #1518

78 changes: 66 additions & 12 deletions plugins/modules/azure_rm_aduser.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@
- 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
on_premises_extension_attributes:
description:
- Contains extensionAttributes1-15 for the user.
- These extension attributes are also known as Exchange custom attributes 1-15.
- For an onPremisesSyncEnabled user, the source of authority for this set of properties is the on-premises and is read-only.
- For a cloud-only user (where onPremisesSyncEnabled is false), these properties can be set during the creation or update of a user object.
- For a cloud-only user previously synced from on-premises Active Directory, these properties are read-only in Microsoft Graph\
but can be fully managed through the Exchange Admin Center or the Exchange Online V2 module in PowerShell.
type: dict
aliases:
- extension_attributes

extends_documentation_fragment:
- azure.azcollection.azure

Expand All @@ -143,6 +155,10 @@
usage_location: "US"
mail: "{{ user_principal_name }}@contoso.com"
company_name: 'Test Company'
on_premises_extension_attributes:
extension_attribute1: "test_extension_attribute1"
extension_attribute2: "test_extension_attribute2"
extension_attribute11: "test_extension_attribute11"

- name: Update user with new value for account_enabled
azure_rm_aduser:
Expand Down Expand Up @@ -205,13 +221,25 @@
type: str
returned: always
sample: 'Test Company'
on_premises_extension_attributes:
description:
- Contains extensionAttributes1-15 for the user.
- These extension attributes are also known as Exchange custom attributes 1-15.
- For an onPremisesSyncEnabled user, the source of authority for this set of properties is the on-premises and is read-only.
- For a cloud-only user (where onPremisesSyncEnabled is false), these properties can be set during the creation or update of a user object.
- For a cloud-only user previously synced from on-premises Active Directory, these properties are read-only in Microsoft Graph\
but can be fully managed through the Exchange Admin Center or the Exchange Online V2 module in PowerShell.
type: dict
returned: always
sample: {}
'''

from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBase

try:
import asyncio
from msgraph.generated.models.password_profile import PasswordProfile
from msgraph.generated.models.on_premises_extension_attributes import OnPremisesExtensionAttributes
from msgraph.generated.models.user import User
from msgraph.generated.users.users_request_builder import UsersRequestBuilder
except ImportError:
Expand Down Expand Up @@ -239,7 +267,8 @@ def __init__(self):
surname=dict(type='str'),
user_type=dict(type='str'),
mail=dict(type='str'),
company_name=dict(type='str')
company_name=dict(type='str'),
on_premises_extension_attributes=dict(type='dict', aliases=['extension_attributes'])
)

self.user_principal_name = None
Expand All @@ -259,6 +288,7 @@ def __init__(self):
self.user_type = None
self.mail = None
self.company_name = None
self.on_premises_extension_attributes = None
self.log_path = None
self.log_mode = None

Expand Down Expand Up @@ -288,6 +318,13 @@ def exec_module(self, **kwargs):

if self.state == 'present':

extension_attributes = None

if self.on_premises_extension_attributes:
extension_attributes = OnPremisesExtensionAttributes(
**self.on_premises_extension_attributes
)

if ad_user: # Update, changed

password = None
Expand All @@ -298,7 +335,6 @@ def exec_module(self, **kwargs):
)

should_update = False

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:
Expand All @@ -321,9 +357,12 @@ def exec_module(self, **kwargs):
should_update = True
if should_update or self.company_name and ad_user.company_name != self.company_name:
should_update = True

if should_update or (
self.on_premises_extension_attributes and
self.on_premises_extension_attributes_to_dict(ad_user.on_premises_extension_attributes) != self.on_premises_extension_attributes):
should_update = True
if should_update:
asyncio.get_event_loop().run_until_complete(self.update_user(ad_user, password))
asyncio.get_event_loop().run_until_complete(self.update_user(ad_user, password, extension_attributes))

self.results['changed'] = True

Expand All @@ -335,7 +374,7 @@ def exec_module(self, **kwargs):
self.results['changed'] = False

else: # Create, changed
asyncio.get_event_loop().run_until_complete(self.create_user())
asyncio.get_event_loop().run_until_complete(self.create_user(extension_attributes))
self.results['changed'] = True
ad_user = self.get_exisiting_user()

Expand Down Expand Up @@ -391,6 +430,16 @@ def get_exisiting_user(self):
raise
return ad_user

def on_premises_extension_attributes_to_dict(self, on_premises_extension_attributes):
extension_attributes = {}
for index in range(1, 16 + 1):
attribute_name = f'extension_attribute{index}'
if hasattr(on_premises_extension_attributes, attribute_name):
attr_value = getattr(on_premises_extension_attributes, attribute_name)
if attr_value is not None:
extension_attributes[attribute_name] = attr_value
return extension_attributes

def to_dict(self, object):
return dict(
object_id=object.id,
Expand All @@ -400,10 +449,11 @@ def to_dict(self, object):
mail=object.mail,
account_enabled=object.account_enabled,
user_type=object.user_type,
company_name=object.company_name
company_name=object.company_name,
on_premises_extension_attributes=self.on_premises_extension_attributes_to_dict(object.on_premises_extension_attributes)
)

async def update_user(self, ad_user, password):
async def update_user(self, ad_user, password, extension_attributes):
request_body = User(
on_premises_immutable_id=self.on_premises_immutable_id,
usage_location=self.usage_location,
Expand All @@ -415,11 +465,12 @@ async def update_user(self, ad_user, password):
password_profile=password,
user_principal_name=self.user_principal_name,
mail_nickname=self.mail_nickname,
company_name=self.company_name
company_name=self.company_name,
on_premises_extension_attributes=extension_attributes
)
return await self._client.users.by_user_id(ad_user.id).patch(body=request_body)

async def create_user(self):
async def create_user(self, extension_attributes):
password = PasswordProfile(
password=self.password_profile
)
Expand All @@ -435,7 +486,8 @@ async def create_user(self):
surname=self.surname,
user_type=self.user_type,
mail=self.mail,
company_name=self.company_name
company_name=self.company_name,
on_premises_extension_attributes=extension_attributes
)
return await self._client.users.post(body=request_body)

Expand All @@ -446,7 +498,8 @@ 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"]
"onPremisesImmutableId", "usageLocation", "givenName", "surname", "companyName",
"OnPremisesExtensionAttributes"]
),
)
return await self._client.users.by_user_id(object).get(request_configuration=request_configuration)
Expand All @@ -457,7 +510,8 @@ async def get_users_by_filter(self, filter):
query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
filter=filter,
select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName",
"userType", "onPremisesImmutableId", "usageLocation", "givenName", "surname", "companyName"],
"userType", "onPremisesImmutableId", "usageLocation", "givenName", "surname", "companyName",
"OnPremisesExtensionAttributes"],
count=True
),
headers={'ConsistencyLevel': "eventual", }
Expand Down
33 changes: 28 additions & 5 deletions plugins/modules/azure_rm_aduser_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,17 @@
type: str
returned: always
sample: "Test Company"
on_premises_extension_attributes:
description:
- Contains extensionAttributes1-15 for the user.
- These extension attributes are also known as Exchange custom attributes 1-15.
- For an onPremisesSyncEnabled user, the source of authority for this set of properties is the on-premises and is read-only.
- For a cloud-only user (where onPremisesSyncEnabled is false), these properties can be set during the creation or update of a user object.
- For a cloud-only user previously synced from on-premises Active Directory, these properties are read-only in Microsoft Graph/
but can be fully managed through the Exchange Admin Center or the Exchange Online V2 module in PowerShell.
type: dict
returned: always
sample: {}
'''

from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBase
Expand Down Expand Up @@ -224,14 +235,23 @@ def exec_module(self, **kwargs):
elif self.all:
# this returns as a list, since we parse multiple pages
ad_users = asyncio.get_event_loop().run_until_complete(self.get_users())

self.results['ad_users'] = [self.to_dict(user) for user in ad_users]

except Exception as e:
self.fail("failed to get ad user info {0}".format(str(e)))

return self.results

def on_premises_extension_attributes_to_dict(self, on_premises_extension_attributes):
extension_attributes = {}
for index in range(1, 16 + 1):
attribute_name = f'extension_attribute{index}'
if hasattr(on_premises_extension_attributes, attribute_name):
attr_value = getattr(on_premises_extension_attributes, attribute_name)
if attr_value is not None:
extension_attributes[attribute_name] = attr_value
return extension_attributes
therapac marked this conversation as resolved.
Show resolved Hide resolved

def to_dict(self, object):
return dict(
object_id=object.id,
Expand All @@ -241,21 +261,24 @@ def to_dict(self, object):
mail=object.mail,
account_enabled=object.account_enabled,
user_type=object.user_type,
company_name=object.company_name
company_name=object.company_name,
on_premises_extension_attributes=self.on_premises_extension_attributes_to_dict(object.on_premises_extension_attributes)
)

async def get_user(self, object):
request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName", "userType", "companyName"]
select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName",
"userType", "companyName", "onPremisesExtensionAttributes"]
),
)
return await self._client.users.by_user_id(object).get(request_configuration=request_configuration)

async def get_users(self):
request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName", "userType", "companyName"]
select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName",
"userType", "companyName", "onPremisesExtensionAttributes"]
),
)
users = []
Expand All @@ -276,7 +299,7 @@ async def get_users_by_filter(self, filter):
query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
filter=filter,
select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName",
"userType", "companyName"],
"userType", "companyName", "onPremisesExtensionAttributes"],
count=True
),
))
Expand Down
Loading