For example I have an attribute in IDM that I need to map to extensionAttribute12 in user object
Cybersecurity
DevOps Cloud
IT Operations Cloud
For example I have an attribute in IDM that I need to map to extensionAttribute12 in user object
Hi Narayanan
Did you try to map any attribute from Identity Vault to extensionAttribute12 from AAD and just set any value?
learn.microsoft.com/.../extensibility-overview
Microsoft Entra ID offers a set of 15 extension attributes with predefined names on the user and device resources. These properties were initially custom attributes provided in on-premises Active Directory (AD) and Microsoft Exchange. However, they can now be used for more than syncing on-premises AD and Microsoft Exchange data to Microsoft Entra ID through Microsoft Graph.
For more information about these attributes in Microsoft Exchange, see Custom attributes in Exchange Server.
You can use the 15 extension attributes to store String values on user or device resource instances, through the onPremisesExtensionAttributes and extensionAttributes properties respectively. You can assign the values while creating a new resource instance or while updating an existing resource instance. You can also filter by the values.
In theory, AAD Driver SHIM is supposed to cover these attributes.
MS GRAPH API:
PATCH graph.microsoft.com/.../071cc716-8147-4397-a5ba-b2105951cc0b
{
"onPremisesExtensionAttributes": {
"extensionAttribute1": "skypeId.adeleVance",
"extensionAttribute13": null
}
}
Yes that is what I hoped for but it is not.
Not sure why it is doing a GET instead of a PATCH
Submitting document to subscriber shim:
<modify-attr attr-name="extensionAttribute12">
<remove-value>
<value timestamp="1730314543#7" type="string">testvalue1</value>
</remove-value>
<add-value>
<value timestamp="1730315129#42" type="string">testvalue2</value>
</add-value>
</modify-attr>
<modify-attr attr-name="extensionAttribute14">
<remove-value>
<value timestamp="1730314543#5" type="string">testvalue3</value>
</remove-value>
<add-value>
<value timestamp="1730315129#40" type="string">testvalue4</value>
</add-value>
</modify-attr>
</modify>
ST:AAD: AZSubscriber.execute()
ST:AAD_Azure: RESTSubscriptionShim.execute() :
ST:AAD_Azure: Calling document modifier class com.novell.nds.dirxml.driver.azure.apiext.GraphAPIExtension.modifySubscriberRequest()
ST:AAD_Azure: queryHandler
ST:AAD_Azure: queryHandler: class-name == 'users'
ST:AAD_Azure: Query: preparing GET to graph.microsoft.com/.../a0
ST:AAD_Azure: Response code and message: 200 OK
ST:AAD_Azure: Calling document modifier class com.novell.nds.dirxml.driver.azure.apiext.GraphAPIExtension.modifySubscriberResponse()
ST:AAD: ModifyHandler: Filtering out attribute 'extensionAttribute12' for class user.
ST:AAD: ModifyHandler: Filtering out attribute 'extensionAttribute14' for class user.
ST:SubscriptionShim.execute() returned:
More investigation will be required to determine if the driver supports extension attributes.
It also can be the cases in which it is not supported for specific tenants.
I can recommend to validate, which attributes are available for the user class in your case.
Thus, if app attributes extensionAttribute1-15 is "invisible", you can't use it in schema mapping and I have doubt, that SHIM covers these attributes correctly. (I didn't see anything in the documentation)
Let's start from the schema and maybe will find more information.
It also can be the case, when driver SHIM doesn't parse AAD schema correctly.
MS user directoryObject metadata (schema definition) for GRAPH v1.0 from my test tenant.
<EntityType Name="user" BaseType="graph.directoryObject" OpenType="true">
<Property Name="signInActivity" Type="graph.signInActivity"/>
<Property Name="accountEnabled" Type="Edm.Boolean"/>
<Property Name="ageGroup" Type="Edm.String"/>
<Property Name="assignedLicenses" Type="Collection(graph.assignedLicense)" Nullable="false"/>
<Property Name="assignedPlans" Type="Collection(graph.assignedPlan)" Nullable="false"/>
<Property Name="authorizationInfo" Type="graph.authorizationInfo"/>
<Property Name="businessPhones" Type="Collection(Edm.String)" Nullable="false"/>
<Property Name="city" Type="Edm.String"/>
<Property Name="companyName" Type="Edm.String"/>
<Property Name="consentProvidedForMinor" Type="Edm.String"/>
<Property Name="country" Type="Edm.String"/>
<Property Name="createdDateTime" Type="Edm.DateTimeOffset"/>
<Property Name="creationType" Type="Edm.String"/>
<Property Name="customSecurityAttributes" Type="graph.customSecurityAttributeValue"/>
<Property Name="department" Type="Edm.String"/>
<Property Name="displayName" Type="Edm.String"/>
<Property Name="employeeHireDate" Type="Edm.DateTimeOffset"/>
<Property Name="employeeId" Type="Edm.String"/>
<Property Name="employeeLeaveDateTime" Type="Edm.DateTimeOffset"/>
<Property Name="employeeOrgData" Type="graph.employeeOrgData"/>
<Property Name="employeeType" Type="Edm.String"/>
<Property Name="externalUserState" Type="Edm.String"/>
<Property Name="externalUserStateChangeDateTime" Type="Edm.DateTimeOffset"/>
<Property Name="faxNumber" Type="Edm.String"/>
<Property Name="givenName" Type="Edm.String"/>
<Property Name="identities" Type="Collection(graph.objectIdentity)"/>
<Property Name="imAddresses" Type="Collection(Edm.String)"/>
<Property Name="isManagementRestricted" Type="Edm.Boolean"/>
<Property Name="isResourceAccount" Type="Edm.Boolean"/>
<Property Name="jobTitle" Type="Edm.String"/>
<Property Name="lastPasswordChangeDateTime" Type="Edm.DateTimeOffset"/>
<Property Name="legalAgeGroupClassification" Type="Edm.String"/>
<Property Name="licenseAssignmentStates" Type="Collection(graph.licenseAssignmentState)"/>
<Property Name="mail" Type="Edm.String"/>
<Property Name="mailNickname" Type="Edm.String"/>
<Property Name="mobilePhone" Type="Edm.String"/>
<Property Name="officeLocation" Type="Edm.String"/>
<Property Name="onPremisesDistinguishedName" Type="Edm.String"/>
<Property Name="onPremisesDomainName" Type="Edm.String"/>
<Property Name="onPremisesExtensionAttributes" Type="graph.onPremisesExtensionAttributes"/>
<Property Name="onPremisesImmutableId" Type="Edm.String"/>
<Property Name="onPremisesLastSyncDateTime" Type="Edm.DateTimeOffset"/>
<Property Name="onPremisesProvisioningErrors" Type="Collection(graph.onPremisesProvisioningError)"/>
<Property Name="onPremisesSamAccountName" Type="Edm.String"/>
<Property Name="onPremisesSecurityIdentifier" Type="Edm.String"/>
<Property Name="onPremisesSyncEnabled" Type="Edm.Boolean"/>
<Property Name="onPremisesUserPrincipalName" Type="Edm.String"/>
<Property Name="otherMails" Type="Collection(Edm.String)" Nullable="false"/>
<Property Name="passwordPolicies" Type="Edm.String"/>
<Property Name="passwordProfile" Type="graph.passwordProfile"/>
<Property Name="postalCode" Type="Edm.String"/>
<Property Name="preferredDataLocation" Type="Edm.String"/>
<Property Name="preferredLanguage" Type="Edm.String"/>
<Property Name="provisionedPlans" Type="Collection(graph.provisionedPlan)" Nullable="false"/>
<Property Name="proxyAddresses" Type="Collection(Edm.String)" Nullable="false"/>
<Property Name="securityIdentifier" Type="Edm.String"/>
<Property Name="serviceProvisioningErrors" Type="Collection(graph.serviceProvisioningError)"/>
<Property Name="showInAddressList" Type="Edm.Boolean"/>
<Property Name="signInSessionsValidFromDateTime" Type="Edm.DateTimeOffset"/>
<Property Name="state" Type="Edm.String"/>
<Property Name="streetAddress" Type="Edm.String"/>
<Property Name="surname" Type="Edm.String"/>
<Property Name="usageLocation" Type="Edm.String"/>
<Property Name="userPrincipalName" Type="Edm.String"/>
<Property Name="userType" Type="Edm.String"/>
<Property Name="mailboxSettings" Type="graph.mailboxSettings"/>
<Property Name="deviceEnrollmentLimit" Type="Edm.Int32" Nullable="false"/>
<Property Name="print" Type="graph.userPrint"/>
<Property Name="aboutMe" Type="Edm.String"/>
<Property Name="birthday" Type="Edm.DateTimeOffset" Nullable="false"/>
<Property Name="hireDate" Type="Edm.DateTimeOffset" Nullable="false"/>
<Property Name="interests" Type="Collection(Edm.String)"/>
<Property Name="mySite" Type="Edm.String"/>
<Property Name="pastProjects" Type="Collection(Edm.String)"/>
<Property Name="preferredName" Type="Edm.String"/>
<Property Name="responsibilities" Type="Collection(Edm.String)"/>
<Property Name="schools" Type="Collection(Edm.String)"/>
<Property Name="skills" Type="Collection(Edm.String)"/>
<NavigationProperty Name="appRoleAssignments" Type="Collection(graph.appRoleAssignment)" ContainsTarget="true"/>
<NavigationProperty Name="createdObjects" Type="Collection(graph.directoryObject)"/>
<NavigationProperty Name="directReports" Type="Collection(graph.directoryObject)"/>
<NavigationProperty Name="licenseDetails" Type="Collection(graph.licenseDetails)" ContainsTarget="true"/>
<NavigationProperty Name="manager" Type="graph.directoryObject"/>
<NavigationProperty Name="memberOf" Type="Collection(graph.directoryObject)"/>
<NavigationProperty Name="oauth2PermissionGrants" Type="Collection(graph.oAuth2PermissionGrant)"/>
<NavigationProperty Name="ownedDevices" Type="Collection(graph.directoryObject)"/>
<NavigationProperty Name="ownedObjects" Type="Collection(graph.directoryObject)"/>
<NavigationProperty Name="registeredDevices" Type="Collection(graph.directoryObject)"/>
<NavigationProperty Name="scopedRoleMemberOf" Type="Collection(graph.scopedRoleMembership)" ContainsTarget="true"/>
<NavigationProperty Name="sponsors" Type="Collection(graph.directoryObject)"/>
<NavigationProperty Name="transitiveMemberOf" Type="Collection(graph.directoryObject)"/>
<NavigationProperty Name="calendar" Type="graph.calendar" ContainsTarget="true"/>
<NavigationProperty Name="calendarGroups" Type="Collection(graph.calendarGroup)" ContainsTarget="true"/>
<NavigationProperty Name="calendars" Type="Collection(graph.calendar)" ContainsTarget="true"/>
<NavigationProperty Name="calendarView" Type="Collection(graph.event)" ContainsTarget="true"/>
<NavigationProperty Name="contactFolders" Type="Collection(graph.contactFolder)" ContainsTarget="true"/>
<NavigationProperty Name="contacts" Type="Collection(graph.contact)" ContainsTarget="true"/>
<NavigationProperty Name="events" Type="Collection(graph.event)" ContainsTarget="true"/>
<NavigationProperty Name="inferenceClassification" Type="graph.inferenceClassification" ContainsTarget="true"/>
<NavigationProperty Name="mailFolders" Type="Collection(graph.mailFolder)" ContainsTarget="true"/>
<NavigationProperty Name="messages" Type="Collection(graph.message)" ContainsTarget="true"/>
<NavigationProperty Name="outlook" Type="graph.outlookUser" ContainsTarget="true"/>
<NavigationProperty Name="people" Type="Collection(graph.person)" ContainsTarget="true"/>
<NavigationProperty Name="drive" Type="graph.drive" ContainsTarget="true"/>
<NavigationProperty Name="drives" Type="Collection(graph.drive)" ContainsTarget="true"/>
<NavigationProperty Name="followedSites" Type="Collection(graph.site)"/>
<NavigationProperty Name="extensions" Type="Collection(graph.extension)" ContainsTarget="true"/>
<NavigationProperty Name="agreementAcceptances" Type="Collection(graph.agreementAcceptance)"/>
<NavigationProperty Name="managedDevices" Type="Collection(graph.managedDevice)" ContainsTarget="true"/>
<NavigationProperty Name="managedAppRegistrations" Type="Collection(graph.managedAppRegistration)"/>
<NavigationProperty Name="deviceManagementTroubleshootingEvents" Type="Collection(graph.deviceManagementTroubleshootingEvent)" ContainsTarget="true"/>
<NavigationProperty Name="planner" Type="graph.plannerUser" ContainsTarget="true"/>
<NavigationProperty Name="insights" Type="graph.itemInsights" ContainsTarget="true"/>
<NavigationProperty Name="settings" Type="graph.userSettings" ContainsTarget="true"/>
<NavigationProperty Name="onenote" Type="graph.onenote" ContainsTarget="true"/>
<NavigationProperty Name="cloudClipboard" Type="graph.cloudClipboardRoot" ContainsTarget="true"/>
<NavigationProperty Name="photo" Type="graph.profilePhoto" ContainsTarget="true"/>
<NavigationProperty Name="photos" Type="Collection(graph.profilePhoto)" ContainsTarget="true"/>
<NavigationProperty Name="activities" Type="Collection(graph.userActivity)" ContainsTarget="true"/>
<NavigationProperty Name="onlineMeetings" Type="Collection(graph.onlineMeeting)" ContainsTarget="true"/>
<NavigationProperty Name="presence" Type="graph.presence" ContainsTarget="true"/>
<NavigationProperty Name="authentication" Type="graph.authentication" ContainsTarget="true"/>
<NavigationProperty Name="chats" Type="Collection(graph.chat)" ContainsTarget="true"/>
<NavigationProperty Name="joinedTeams" Type="Collection(graph.team)" ContainsTarget="true"/>
<NavigationProperty Name="permissionGrants" Type="Collection(graph.resourceSpecificPermissionGrant)" ContainsTarget="true"/>
<NavigationProperty Name="teamwork" Type="graph.userTeamwork" ContainsTarget="true"/>
<NavigationProperty Name="solutions" Type="graph.userSolutionRoot" ContainsTarget="true"/>
<NavigationProperty Name="todo" Type="graph.todo" ContainsTarget="true"/>
<NavigationProperty Name="employeeExperience" Type="graph.employeeExperienceUser" ContainsTarget="true"/>
<Annotation Term="Org.OData.Core.V1.AlternateKeys">
<Collection>
<Record Type="Org.OData.Core.V1.AlternateKey">
<PropertyValue Property="Key">
<Collection>
<Record Type="Org.OData.Core.V1.PropertyRef">
<PropertyValue Property="Alias" String="userPrincipalName"/>
<PropertyValue Property="Name" PropertyPath="userPrincipalName"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Collection>
</Annotation>
</EntityType>
For some reason I am not able to perform the steps you mentioned here. designer is not able to connect to application and fetch the schema.
Do you have connection from your designer to your Identity Vault? (you can "Refresh Connection" to be sure, that command will not be lost)
The designer does several steps:
1. Shutdown current driver session
2. Start a new session
3. Pass to driver SHIM getSchema() command.
4. SHIM requested Schema "refresh" from the app
5. Session restarted again
App schema stored in DirXML-ApplicationSchema attribute.
Trace:
[11/01/24 22:09:31.571]:AAD2 ST:Subscriber thread starting.
[11/01/24 22:09:43.964]:AAD2 ST:Reading XML attribute vnd.nds.stream://IDM-TREE/system/driverset1/AAD2#DirXML-ApplicationSchema.
[11/01/24 22:09:44.373]:AAD2 ST:Loading Java shim com.novell.nds.dirxml.remote.driver.DriverShimImpl.
[11/01/24 22:09:44.373]:AAD2 ST:Driver \IDM-TREE\system\driverset1\AAD2 supports Subscriber Service Channel.
[11/01/24 22:09:44.373]:AAD2 ST:Calling DriverShim.getSchema().
[11/01/24 22:09:44.405]:AAD2 ST:Reading XML attribute vnd.nds.stream://IDM-TREE/system/driverset1/AAD2#DirXML-ShimConfigInfo.
[11/01/24 22:09:44.459]:AAD2 ST:Substituting password value for reference to named password 'user-clientsecret'.
[11/01/24 22:09:44.459]:AAD2 ST:Substituting password value for reference to named password 'user-keystorepwd'.
[11/01/24 22:09:44.470]:AAD2 ST:Substituting password value for reference to named password 'proxyPassword'.
[11/01/24 22:09:44.470]:AAD2 ST:Substituting password value for reference to named password 'subKeystorePassword'.
[11/01/24 22:09:44.502]:AAD2 ST:Substituting password value for reference to named password 'database-password'.
[11/01/24 22:09:44.502]:AAD2 ST:Reading XML attribute vnd.nds.stream://IDM-TREE/system/driverset1/AAD2#DirXML-DriverStorage.
...
[11/01/24 22:09:44.536]:AAD2 ST:Remote Interface Driver: start getSchema()
[11/01/24 22:09:44.536]:AAD2 ST:Remote Interface Driver: Opening connection...
...
[11/01/24 22:09:58.367]:AAD2 ST:Remote Interface Driver: Received
[11/01/24 22:09:58.742]:AAD2 ST:
<nds dtdversion="4.x" ndsversion="8.x">
<source>
<product version="5.2.0.0100">NetIQ Identity Manager Driver for Azure AD and Office365</product>
<contact>NetIQ Corporation</contact>
</source>
<output>
<schema-def application-name="Windows Azure AD" hierarchical="false">
<class-def class-name="managedDeviceMobileAppConfigurationAssignment">
<attr-def attr-name="psexecute" multi-valued="true" type="string"/>
<attr-def attr-name="target" multi-valued="false" type="structured"/>
</class-def>
<class-def class-name="privilegedAccessRoot">
<attr-def attr-name="group" multi-valued="true" type="dn"/>
<attr-def attr-name="psexecute" multi-valued="true" type="string"/>
</class-def>
<...
<class-def class-name="user">
<attr-def attr-name="aboutMe" multi-valued="false" type="string"/>
<attr-def attr-name="accountEnabled" multi-valued="false" type="state"/>
<attr-def attr-name="activities" multi-valued="true" type="dn"/>
<attr-def attr-name="ageGroup" multi-valued="false" type="string"/>
<attr-def attr-name="agreementAcceptances" multi-valued="true" type="dn"/>
<attr-def attr-name="appRoleAssignments" multi-valued="true" type="dn"/>
<attr-def attr-name="assignLicense" multi-valued="true" type="string"/>
<attr-def attr-name="assignedLicenses" multi-valued="true" type="structured"/>
<attr-def attr-name="assignedPlans" multi-valued="true" type="structured"/>
<attr-def attr-name="authentication" multi-valued="true" type="dn"/>
<attr-def attr-name="authorizationInfo" multi-valued="false" type="structured"/>
<attr-def attr-name="birthday" multi-valued="false" type="structured"/>
<attr-def attr-name="businessPhones" multi-valued="true" type="string"/>
<attr-def attr-name="calendar" multi-valued="true" type="dn"/>
<attr-def attr-name="calendarGroups" multi-valued="true" type="dn"/>
<attr-def attr-name="calendarView" multi-valued="true" type="dn"/>
<attr-def attr-name="calendars" multi-valued="true" type="dn"/>
<attr-def attr-name="chats" multi-valued="true" type="dn"/>
<attr-def attr-name="city" multi-valued="false" type="string"/>
<attr-def attr-name="cloudClipboard" multi-valued="true" type="dn"/>
<attr-def attr-name="companyName" multi-valued="false" type="string"/>
<attr-def attr-name="consentProvidedForMinor" multi-valued="false" type="string"/>
<attr-def attr-name="contactFolders" multi-valued="true" type="dn"/>
<attr-def attr-name="contacts" multi-valued="true" type="dn"/>
<attr-def attr-name="country" multi-valued="false" type="string"/>
<attr-def attr-name="createdDateTime" multi-valued="false" type="structured"/>
<attr-def attr-name="createdObjects" multi-valued="true" type="dn"/>
<attr-def attr-name="creationType" multi-valued="false" type="string"/>
<attr-def attr-name="customSecurityAttributes" multi-valued="false" type="structured"/>
<attr-def attr-name="department" multi-valued="false" type="string"/>
<attr-def attr-name="deviceEnrollmentLimit" multi-valued="false" type="int"/>
<attr-def attr-name="deviceManagementTroubleshootingEvents" multi-valued="true" type="dn"/>
<attr-def attr-name="directReports" multi-valued="true" type="dn"/>
<attr-def attr-name="displayName" multi-valued="false" type="string"/>
<attr-def attr-name="drive" multi-valued="true" type="dn"/>
<attr-def attr-name="drives" multi-valued="true" type="dn"/>
<attr-def attr-name="employeeExperience" multi-valued="true" type="dn"/>
<attr-def attr-name="employeeHireDate" multi-valued="false" type="structured"/>
<attr-def attr-name="employeeId" multi-valued="false" type="string"/>
<attr-def attr-name="employeeLeaveDateTime" multi-valued="false" type="structured"/>
<attr-def attr-name="employeeOrgData" multi-valued="false" type="structured"/>
<attr-def attr-name="employeeType" multi-valued="false" type="string"/>
<attr-def attr-name="events" multi-valued="true" type="dn"/>
<attr-def attr-name="extensions" multi-valued="true" type="dn"/>
<attr-def attr-name="externalUserState" multi-valued="false" type="string"/>
<attr-def attr-name="externalUserStateChangeDateTime" multi-valued="false" type="structured"/>
<attr-def attr-name="faxNumber" multi-valued="false" type="string"/>
<attr-def attr-name="followedSites" multi-valued="true" type="dn"/>
<attr-def attr-name="givenName" multi-valued="false" type="string"/>
<attr-def attr-name="hireDate" multi-valued="false" type="structured"/>
<attr-def attr-name="identities" multi-valued="true" type="structured"/>
<attr-def attr-name="imAddresses" multi-valued="true" type="string"/>
<attr-def attr-name="inferenceClassification" multi-valued="true" type="dn"/>
<attr-def attr-name="insights" multi-valued="true" type="dn"/>
<attr-def attr-name="interests" multi-valued="true" type="string"/>
<attr-def attr-name="isManagementRestricted" multi-valued="false" type="state"/>
<attr-def attr-name="isResourceAccount" multi-valued="false" type="state"/>
<attr-def attr-name="jobTitle" multi-valued="false" type="string"/>
<attr-def attr-name="joinedTeams" multi-valued="true" type="dn"/>
<attr-def attr-name="lastPasswordChangeDateTime" multi-valued="false" type="structured"/>
<attr-def attr-name="legalAgeGroupClassification" multi-valued="false" type="string"/>
<attr-def attr-name="licenseAssignmentStates" multi-valued="true" type="structured"/>
<attr-def attr-name="licenseDetails" multi-valued="true" type="dn"/>
<attr-def attr-name="mail" multi-valued="false" type="string"/>
<attr-def attr-name="mailFolders" multi-valued="true" type="dn"/>
<attr-def attr-name="mailNickname" multi-valued="false" type="string"/>
<attr-def attr-name="mailboxSettings" multi-valued="false" type="structured"/>
<attr-def attr-name="managedAppRegistrations" multi-valued="true" type="dn"/>
<attr-def attr-name="managedDevices" multi-valued="true" type="dn"/>
<attr-def attr-name="manager" multi-valued="false" type="dn"/>
<attr-def attr-name="memberOf" multi-valued="true" type="dn"/>
<attr-def attr-name="messages" multi-valued="true" type="dn"/>
<attr-def attr-name="mobilePhone" multi-valued="false" type="string"/>
<attr-def attr-name="mySite" multi-valued="false" type="string"/>
<attr-def attr-name="oauth2PermissionGrants" multi-valued="true" type="dn"/>
<attr-def attr-name="officeLocation" multi-valued="false" type="string"/>
<attr-def attr-name="onPremisesDistinguishedName" multi-valued="false" type="string"/>
<attr-def attr-name="onPremisesDomainName" multi-valued="false" type="string"/>
<attr-def attr-name="onPremisesExtensionAttributes" multi-valued="false" type="structured"/>
<attr-def attr-name="onPremisesImmutableId" multi-valued="false" type="string"/>
<attr-def attr-name="onPremisesLastSyncDateTime" multi-valued="false" type="structured"/>
<attr-def attr-name="onPremisesProvisioningErrors" multi-valued="true" type="structured"/>
<attr-def attr-name="onPremisesSamAccountName" multi-valued="false" type="string"/>
<attr-def attr-name="onPremisesSecurityIdentifier" multi-valued="false" type="string"/>
<attr-def attr-name="onPremisesSyncEnabled" multi-valued="false" type="state"/>
<attr-def attr-name="onPremisesUserPrincipalName" multi-valued="false" type="string"/>
<attr-def attr-name="onenote" multi-valued="true" type="dn"/>
<attr-def attr-name="onlineMeetings" multi-valued="true" type="dn"/>
<attr-def attr-name="otherMails" multi-valued="true" type="string"/>
<attr-def attr-name="outlook" multi-valued="true" type="dn"/>
<attr-def attr-name="ownedDevices" multi-valued="true" type="dn"/>
<attr-def attr-name="ownedObjects" multi-valued="true" type="dn"/>
<attr-def attr-name="passwordPolicies" multi-valued="false" type="string"/>
<attr-def attr-name="passwordProfile" multi-valued="false" type="structured"/>
<attr-def attr-name="pastProjects" multi-valued="true" type="string"/>
<attr-def attr-name="people" multi-valued="true" type="dn"/>
<attr-def attr-name="permissionGrants" multi-valued="true" type="dn"/>
<attr-def attr-name="photo" multi-valued="false" type="octet"/>
<attr-def attr-name="photos" multi-valued="true" type="dn"/>
<attr-def attr-name="planner" multi-valued="true" type="dn"/>
<attr-def attr-name="postalCode" multi-valued="false" type="string"/>
<attr-def attr-name="preferredDataLocation" multi-valued="false" type="string"/>
<attr-def attr-name="preferredLanguage" multi-valued="false" type="string"/>
<attr-def attr-name="preferredName" multi-valued="false" type="string"/>
<attr-def attr-name="presence" multi-valued="true" type="dn"/>
<attr-def attr-name="print" multi-valued="false" type="structured"/>
<attr-def attr-name="provisionedPlans" multi-valued="true" type="structured"/>
<attr-def attr-name="proxyAddresses" multi-valued="true" type="string"/>
<attr-def attr-name="psexecute" multi-valued="true" type="string"/>
<attr-def attr-name="registeredDevices" multi-valued="true" type="dn"/>
<attr-def attr-name="responsibilities" multi-valued="true" type="string"/>
<attr-def attr-name="schools" multi-valued="true" type="string"/>
<attr-def attr-name="scopedRoleMemberOf" multi-valued="true" type="dn"/>
<attr-def attr-name="securityIdentifier" multi-valued="false" type="string"/>
<attr-def attr-name="serviceProvisioningErrors" multi-valued="true" type="structured"/>
<attr-def attr-name="settings" multi-valued="true" type="dn"/>
<attr-def attr-name="showInAddressList" multi-valued="false" type="state"/>
<attr-def attr-name="signInActivity" multi-valued="false" type="structured"/>
<attr-def attr-name="signInSessionsValidFromDateTime" multi-valued="false" type="structured"/>
<attr-def attr-name="skills" multi-valued="true" type="string"/>
<attr-def attr-name="solutions" multi-valued="true" type="dn"/>
<attr-def attr-name="sponsors" multi-valued="true" type="dn"/>
<attr-def attr-name="state" multi-valued="false" type="string"/>
<attr-def attr-name="streetAddress" multi-valued="false" type="string"/>
<attr-def attr-name="surname" multi-valued="false" type="string"/>
<attr-def attr-name="teamwork" multi-valued="true" type="dn"/>
<attr-def attr-name="todo" multi-valued="true" type="dn"/>
<attr-def attr-name="transitiveMemberOf" multi-valued="true" type="dn"/>
<attr-def attr-name="usageLocation" multi-valued="false" type="string"/>
<attr-def attr-name="userPrincipalName" multi-valued="false" type="string"/>
<attr-def attr-name="userType" multi-valued="false" type="string"/>
<attr-def attr-name="deletedDateTime" multi-valued="false" type="structured"/>
<attr-def attr-name="psexecute" multi-valued="true" type="string"/>
</class-def>
Today I have a chance to play with AAD EA manipulations.
Driver SHIM can read EA info but has an issue with writing EA. (It can be a SHIM bug or a non-implemented functionality).
1. I used PowerShell to populate some values to EA attributes.
$params = @{ onPremisesExtensionAttributes = @{ extensionAttribute1 = "Test123" } } Update-MgUser -UserId AlexW@midm.onmicrosoft.com -BodyParameter $params PS C:\ps> Get-MgUser -UserId AlexW@midm.onmicrosoft.com -Property onPremisesExtensionAttributes | select -ExpandProperty onPremisesExtensionAttributes |fl ExtensionAttribute1 : Test123 ExtensionAttribute10 : test ExtensionAttribute11 : ExtensionAttribute12 : ExtensionAttribute13 : ExtensionAttribute14 : ExtensionAttribute15 : ExtensionAttribute2 : ExtensionAttribute3 : ExtensionAttribute4 : ExtensionAttribute5 : ExtensionAttribute6 : ExtensionAttribute7 : ExtensionAttribute8 : ExtensionAttribute9 : AdditionalProperties : {}
2. I am able to see these attributes from my policy:
<rule>
<description>50 onPremisesExtensionAttributes</description>
<conditions>
<and>
<if-local-variable mode="nocase" name="lvContinue" op="equal">true</if-local-variable>
</and>
</conditions>
<actions>
<do-set-local-variable name="lvOnPremisesExtensionAttributesNS" scope="policy">
<arg-node-set>
<token-dest-attr name="onPremisesExtensionAttributes"/>
</arg-node-set>
</do-set-local-variable>
</actions>
</rule>
<rule>
<description>50 get onPremisesExtensionAttributes values</description>
<conditions>
<and>
<if-local-variable mode="nocase" name="lvContinue" op="equal">true</if-local-variable>
<if-local-variable name="lvOnPremisesExtensionAttributesNS" op="available"/>
</and>
</conditions>
<actions>
<do-set-local-variable name="lvEA1" scope="policy">
<arg-string>
<token-xpath expression="$lvOnPremisesExtensionAttributesNS/component[@name='extensionAttribute1']/text()"/>
</arg-string>
</do-set-local-variable>
<do-set-local-variable name="lvEA10" scope="policy">
<arg-string>
<token-xpath expression="$lvOnPremisesExtensionAttributesNS/component[@name='extensionAttribute10']/text()"/>
</arg-string>
</do-set-local-variable>
<do-trace-message>
<arg-string>
<token-text xml:space="preserve">
----------------------------------------------------------------
eA1 : </token-text>
<token-local-variable name="lvEA1"/>
<token-text xml:space="preserve">
eA10: </token-text>
<token-local-variable name="lvEA10"/>
</arg-string>
</do-trace-message>
</actions>
</rule>
3. Attempt to set the value to EA failed.
<rule>
<description>60 set onPremisesExtensionAttributes</description>
<conditions>
<and>
<if-local-variable mode="nocase" name="lvContinue" op="equal">true</if-local-variable>
</and>
</conditions>
<actions>
<do-set-dest-attr-value direct="true" name="onPremisesExtensionAttributes">
<arg-value type="structured">
<arg-component name="extensionAttribute2">
<token-text xml:space="preserve">My extensionAttribute2</token-text>
</arg-component>
</arg-value>
</do-set-dest-attr-value>
</actions>
</rule>
4. A review of the RemoteLoader trace shows, that SHIM uses GET operation (to receive the current value, current value doesn't require for MS GRAPH attribute EA update). I can't see any PATCH operation.
Example of HTTP debug info initiated from PowerShell (sensitive information replaced with XXX):
Performing the operation "Update-MgUser_Update" on target "Call remote 'PATCH /users/{user-id}' operation".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):
DEBUG: ============================ HTTP REQUEST ============================
HTTP Method:
PATCH
Absolute Uri:
graph.microsoft.com/.../AlexW@midm.onmicrosoft.com
Headers:
FeatureFlag : 00000043
Cache-Control : no-store, no-cache
User-Agent : XXX PowerShell/5.1.19041.5007
Accept-Encoding : gzip
SdkVersion : graph-powershell/2.23.0
client-request-id : c275adb5-9947-445f-b792-81018fb69359
Body:
{
"onPremisesExtensionAttributes": {
"extensionAttribute3": "EA3"
}
}
Confirm
Continue with this operation?
[Y] Yes [A] Yes to All [H] Halt Command [S] Suspend [?] Help (default is "Y"): y
DEBUG: ============================ HTTP RESPONSE ============================
Status Code:
NoContent
Headers:
Strict-Transport-Security : max-age=31536000
request-id : a6a0ea4e-d3bc-4cd5-ab2f-94fd472ae25e
client-request-id : c275adb5-9947-445f-b792-81018fb69359
x-ms-ags-diagnostic : {"XXX}}
x-ms-resource-unit : 1
Cache-Control : no-cache
Date : Sat, 02 Nov 2024 18:51:20 GMT
PS C:\ps> Get-MgUser -UserId AlexW@midm.onmicrosoft.com -Property onPremisesExtensionAttributes | select -ExpandProperty onPremisesExtensionAttributes |fl
ExtensionAttribute1 : Test123
ExtensionAttribute10 : test
ExtensionAttribute11 :
ExtensionAttribute12 :
ExtensionAttribute13 :
ExtensionAttribute14 :
ExtensionAttribute15 :
ExtensionAttribute2 :
ExtensionAttribute3 : EA3
ExtensionAttribute4 :
ExtensionAttribute5 :
ExtensionAttribute6 :
ExtensionAttribute7 :
ExtensionAttribute8 :
ExtensionAttribute9 :
AdditionalProperties : {}
RemoteLoader trace:
Found a MS article:
If it is not cloud only user these attributes become read only.
Namespace: microsoft.graph
The return type of the onPremisesExtensionAttributes property of the user object and extensionAttributes property of the device object. Returns 15 custom extension attribute properties. Each attribute can store up to 1024 characters.
On the user entity and for an onPremisesSyncEnabled user, the source of authority for this set of properties is the on-premises Active Directory that is synchronized to Microsoft Entra ID, and is read-only. For a cloud-only user (where onPremisesSyncEnabled is false
or null
), these properties can be set during creation or update. If a cloud-only user was previously synced from on-premises Active Directory, these properties can't be managed via the Microsoft Graph API. Instead, they can be managed through the Exchange Admin Centre or the Exchange Online V2 module in PowerShell.
I also saw this article and this info is not very accurate.
Usually users created in AAD by AADConnect (now EntraID Connect) and AADConnect flowing on-premise extension attributes to the cloud.
I don’t have AADConnect in my test environment and I able to update EA with PowerShell.
Our current issue doesn’t related to MS article. It more related to the driver SHIM.
since you say it is structured attribute, does it then expect you to provide extensionAttribute1 through 15 ? Have seen this type of error when you miss to pass all the components of a structured variable.