Directory Extensions to Customize Data Stored for Entra ID Objects

If you’ve used PowerShell to work with Exchange mailboxes, you probably know about the fifteen single-value and five multi-value custom attributes. Organizations can use custom attributes as they choose to store information about mailboxes, distribution lists, and other mail-enabled objects. Entra ID synchronizes the custom attributes for mailboxes to the user accounts that own the mailboxes and stores the values in the onPremisesExtensionAttributes property. The multi-valued custom attributes aren’t synchronized from Exchange to Entra ID.

The use of custom attributes in Exchange varies from storing details about some processing performed against a mailbox to static details about the mailbox owner (like a badge number). The point is that Microsoft doesn’t dictate how organizations use custom attributes, which exist to make Exchange more useful to customers.

Directory extensions are the Entra ID equivalent. Unlike Exchange custom attributes, directory extensions are specific to individual tenants and are associated with Entra ID-registered apps.

Sounds complex? Well, it’s simpler in practice, so let’s define and use some directory extensions using the Microsoft Graph PowerShell SDK. Unfortunately, Microsoft’s current documentation uses the Azure AD module, but it’s easy to switch out the now-depreciated cmdlets after consulting the Graph-based description of how directory extensions work.

Creating Directory Extensions

The first step is to create a registered Entra ID app or choose an existing registered app to hold extension attributes. We can create a new app using PowerShell or via the Entra ID admin center. The important information to note is the identifier for the app (ID property) because it’s needed to create directory extensions. This code creates a new app and its service principal.

$ExtensionApp = New-MgApplication -DisplayName "Extension App to Store Properties for My Tenant"
New-MgServicePrincipal -AppId $ExtensionApp.AppId

With an app in place, we can add some directory extensions. This example shows how to add two directory extensions to the app. The first directory extension (“CostCenter”) can only be used with user objects, while the other (“Subsidiary”) is available for both users and groups. Other supported types include devices, applications, and administrative units. You can’t include spaces in the names given to directory extensions:

$ExtensionProperty1 = New-MgApplicationExtensionProperty -Name "CostCenter" -DataType "String" -TargetObjects "User" -ApplicationId $ExtensionApp.Id
$ExtensionProperty2 = New-MgApplicationExtensionProperty -Name "Subsidiary" -DataType "String" -TargetObjects "User", "Group" -ApplicationId $ExtensionApp.Id

Unlike PowerShell variables, directory extensions are strongly typed. In this instance, both are defined as strings and can store up to 256 characters. The other supported data types are binary, Boolean, DateTime, Integer, and LargeInteger.

To check the set of directory extensions defined for the registered app and to see what objects the extensions are available for, run the Get-MgApplicationExtensionProperty cmdlet:

Get-MgApplicationExtensionProperty -ApplicationId $ExtensionApp.Id | Format-Table Name, DataType, TargetObjects

Name                                                  DataType TargetObjects
----                                                  -------- -------------
extension_450c03488bae4eb98cc22699c0b46690_Subsidiary String   {User, Group}
extension_450c03488bae4eb98cc22699c0b46690_CostCenter String   {User}

The documentation says that an “owner” app can hold up to five definitions (as Figure 1 shows, this aspect of the documentation might be incorrect or outdated) and an organization can support up to 100 directory extensions for each resource type (like a user).

Ten Entra ID directory extensions assigned to an app
Figure 1: Ten Entra ID directory extensions assigned to an app

Using Directory Extensions

To write values into directory extensions, we create a hash table to hold the names of the extensions to update and the values to assign. In this example, we populate the hash table with the names of the two directory extensions we just defined and the values we want to assign. You can combine extensions owned by different apps.

$Properties = @{}
$Properties.Add('extension_450c03488bae4eb98cc22699c0b46690_CostCenter', "8ZW")
$Properties.Add('extension_450c03488bae4eb98cc22699c0b46690_Subsidiary', "Contoso U.S.A")

$UserId = (Get-MgUser -UserId Tricia.Griffin@office365itpros.com).Id
Update-MgUser -UserId $UserId -AdditionalProperties $Properties

To retrieve the values stored in directory extensions, specify the extension names when running Get-MgUser:

$User = Get-MgUser -UserId $UserId -Property ` extension_450c03488bae4eb98cc22699c0b46690_CostCenter,
extension_450c03488bae4eb98cc22699c0b46690_Subsidiary

$User.AdditionalProperties | Format-List

Key   : @odata.context
Value : https://graph.microsoft.com/v1.0/$metadata#users(extension_450c03488bae4eb98cc22699c0b46690_Subsidiary,extension_450c03488bae4eb98cc22699c0b46690_CostCenter)/$entity

Key   : extension_450c03488bae4eb98cc22699c0b46690_CostCenter
Value : 8ZW

Key   : extension_450c03488bae4eb98cc22699c0b46690_Subsidiary
Value : Contoso U.S.A

When writing scripts, it’s easier to retrieve values for directory extensions with a lookup against the data held in the additionalProperties property. For example:

$User.AdditionalProperties['extension_450c03488bae4eb98cc22699c0b46690_Subsidiary']
Contoso U.S.A

Directory extensions are filterable. These examples show how to use filters to find all user accounts that have a specific value in a directory extension:

[array]$Users = Get-MgUser -Filter "extension_450c03488bae4eb98cc22699c0b46690_CostCenter eq '8ZW'"
[array]$Users = Get-MgUser -Filter "extension_450c03488bae4eb98cc22699c0b46690_SeniorityDate ge '1983-10-02T23:59:00'"
[array]$Users = Get-MgUser -Filter "extension_450c03488bae4eb98cc22699c0b46690_WorkRemote eq 'true'"

As described above, the Update-MgUser cmdlet updates values stored in directory extensions. The exception to the rule is when you want to write a null value into a directory extension. The Update-MgUser cmdlet can’t do this for now (being unable to handle the $Null special variable is one of the foibles of the Graph SDK). The workaround is to pass a hash table containing the directory extension name and the $Null value and use the table as input to a Patch request run by the Invoke-MgGraphRequest cmdlet:

$PropertyNull = @{}
$PropertyNull.add("extension_450c03488bae4eb98cc22699c0b46690_Subsidiary", $Null)

Invoke-MgGraphRequest -Method PATCH -Uri "https://graph.microsoft.com/v1.0/users/Tricia.Griffin@office365itpros.com" -Body $PropertyNull

Removing Directory Extensions

Mistakes are made and sometimes it’s necessary to remove a directory extension. To do this, first find the identifier for the extension:

Get-MgApplicationExtensionProperty -ApplicationId $ExtensionApp.Id | Format-Table Id, Name

Id                                   Name
--                                   ----
cad8d83d-eada-47c7-9bf4-5c9ef87beed9 extension_450c03488bae4eb98cc22699c0b46690_HiringManager
b4948d37-ce31-4214-b885-8b44f5fbefe3 extension_450c03488bae4eb98cc22699c0b46690_RetirementDate
6af6c7fe-a516-4b3b-ba28-b80d0b91bd9a extension_450c03488bae4eb98cc22699c0b46690_BadgeNumber
de02f69a-d599-4f68-9cb1-9a2b07e8c395 extension_450c03488bae4eb98cc22699c0b46690_LeaveAllowance
7ba15153-d8b6-4ffc-8265-edd8ede66cbf extension_450c03488bae4eb98cc22699c0b46690_Subsidiary
5ce0873c-dc0f-4df0-bdeb-a0db895d5c2b extension_450c03488bae4eb98cc22699c0b46690_CostCenter

Then run the Remove-MgApplicationExtensionProperty cmdlet to remove the directory extension. In this example, I’m removing the directory extension that stores Subsidiary information. The cmdlet does not prompt for confirmation before removing the directory extension. Its removal eliminates any data held in the extension for other Entra ID objects, so be very sure that you want to proceed before running the cmdlet.

Remove-MgApplicationExtensionProperty -ApplicationId $ExtensionApp.Id -ExtensionPropertyId 7ba15153-d8b6-4ffc-8265-edd8ede66cbf

It’s possible to recreate a directory extension with the same name. However, the new directory extension has a different identifier and won’t recover the data removed when you delete the other directory extension.

Worth Considering Directory Extensions

Microsoft tenants are spoilt for choice when it comes to storing data in custom attributes. The Exchange custom attributes are available and accessible for every tenant where Exchange Online is used, but you’re restricted to fifteen attributes. Entra ID directory extensions have no dependency on any other application, you can define more extensions (up to the limits mentioned earlier), and they support more object types (see this comparison for some useful background information). You might not have a need to use directory extensions now, but you never know what might happen in the future.

Cybersecurity Risk Management for Active Directory

Discover how to prevent and recover from AD attacks through these Cybersecurity Risk Management Solutions.

About the Author

Tony Redmond

Tony Redmond has written thousands of articles about Microsoft technology since 1996. He is the lead author for the Office 365 for IT Pros eBook, the only book covering Office 365 that is updated monthly to keep pace with change in the cloud. Apart from contributing to Practical365.com, Tony also writes at Office365itpros.com to support the development of the eBook. He has been a Microsoft MVP since 2004.

Leave a Reply