• John Craddock

Azure AD schema and directory extensions

Updated: Jun 23

In a similar way to on-premises Active Directory (AD), Azure AD has a schema that defines a set of objects that can be created in the directory (tenant). Associated with each object type is a property (attribute) set. Some properties need to be populated to create the object, other property values are set to provide additional information about the subject.


On many occasions, an application can have specific requirements to store additional information about Azure AD object types such as users, group and devices. This additional information could be stored in an external database and the data linked to Azure AD objects using an immutable identifier such as the Azure AD object identifier. Alternatively, the data can be stored in Azure AD.


I was asked by a customer to recommend the best way of storing a custom attribute in Azure AD. Before I could make the recommendation, I had to do some research. In this blog, I will share with you the results of that work, it’s a long read, but hopefully you will find it informative.


There are three ways of adding additional attributes to Azure AD.

  • Azure AD Graph Extensions

  • Microsoft Graph Open Extensions

  • Microsoft Graph Schema Extensions

Stay tuned, and I will delve into this complex and intriguing subject. In addition to extending the directory to store the additional attribute values, you will discover how some extension properties can be synchronized from on-premises AD and can be added to SAML, ID and Access tokens.


Introduction to the Azure AD schema

Figure 1 illustrates the definition of a user object in the Azure AD schema and an instance of the user object in the directory.

The schema defines the type of data for each attribute, for example, Edm.String, Edm.DateTime, Edm.Boolean, etc. Edm refers to the entity data model, which describes structured data. When creating a user object in the directory, values must be defined for specific attributes. The creator of the user object must choose some attribute values, and others are automatically set by Azure AD. For instance, when creating a user object, you have to supply a valid UserPrincipalName and DisplayName. Azure AD fills all the other properties required to instantiate the user object. In most situations, the creator of the object wants to populate more attributes than the minimum necessary for the creation of the object.


If you are familiar with on-premises AD, then you might well have used the Schema Manager (Figure 2) to examine the AD Schema.

The on-premises AD has a schema which you can directly view and understand the relationship between the AD objects and attributes. If you are looking for the same in Azure AD, you will be disappointed; there is no way to view the schema directly. With Azure AD, you will need to rely on documentation for the definition of objects and their associated properties. Programmatic access to the directory is through a Graph API, and there are currently two different APIs. Azure AD Graph and Microsoft Graph you can view the documentation for a user object accessed via the Azure AD Graph API here and via the Microsoft Graph API here.


Microsoft Graph is replacing Azure AD graph and for the Azure AD supports many new datasets and features. Any applications that are currently using the Azure AD Graph API should be updated to use the Microsoft Graph API. In addition to access to Azure AD, Microsoft Graph is the API gateway to Microsoft 365 services. You can find the Microsoft Graph documentation here.


If you use the two different APIs to view the same object, you can get different views of the object. I have tried to illustrate this in Figure 3.


Although you could be accessing the same object, the Microsoft Graph API can give you access to attributes that are not available via the Azure AD API. You may also discover that a property can have a different name depending on which API you use. For instance, the attribute that holds a value linking a synchronized user to their on-premises identity is called immutableId via the Azure AD Graph API and the onPremisesImmutableId via the Microsoft Graph API.


An Azure AD Graph query for

https://graph.windows.net/myorganization/users?$filter=startswith(userPrincipalName,'John@5.ad')&$select=userPrincipalName, immutableId&api-version=1.6

Returns:





To get the same data returned with Microsoft Graph you need to use:

https://graph.microsoft.com/v1.0/users?$filter=startswith(userPrincipalName,'John@5.ad')&$select=userPrincipalName, onPremisesImmutableId

Returns:




To query Microsoft Graph, you can use Graph Explorer; alternatively, you could use Postman (my personal favourite) for both queries to Microsoft Graph and Azure AD Graph. Postman has the advantage of importing and exporting collections. A collection is a collection of API calls, Microsoft has published a Microsoft Graph Postman collection. Read this document to get started.


Azure AD Graph directory schema extensions

Although the Microsoft recommendation is to use Microsoft Graph, you should understand the original Azure AD Graph extensions as these are used when synchronizing custom attributes from on-premises AD.


The property types that can be added are Binary, Boolean, DateTime, Integer, LageInteger and String. Extension attributes can be added to User, Group, TenantDetail, Device, Application and ServicePrincipal objects. For example, you could create a named property of type string that can be used with user, group and device objects.


New extension properties are registered on an application object. Think of an application object as a template definition of the application. Before the application can be used for authentication and authorization, a service principal (SP) is created in the tenant. For a single-tenant app, the application object and SP are in the same tenant. For a multi-tenant app, there is a single application object in the tenant where the app is defined. For each tenant that uses the application, an SP is created in that tenant. The SP is usually created when a user consents to the application. For a single-tenant, the SP is created when the app is registered through the Azure AD Portal. The application template is created in the Portal App registrations blade, and the SP is shown in the Enterprise applications blade.


Although extension properties can be registered on an application object, they cannot be used until the application's SP exists. Once the SP is created in a tenant, the extension properties are usable in that tenant. The extension properties can be read/managed by any application that has the appropriate permissions.


Figure 4 illustrates the relationship between extension properties, application objects, service principals and the objects to which the extension properties apply.

The name of the extension property includes the application id of the application object. For example, if the Application (client) ID is 21695f38-e621-473b-93cf-215065a34b91 the extension property name is extension_21695f38e621473b93cf215065a34b91_CostCentre


If the SP is deleted from the tenant, the extension properties defined on the associated application object become inaccessible. If the SP is recreated, they once again become visible with their previously held values. When a property is set for a particular object, it counts against a quota limit of 100 extension property values for that object. The quota applies regardless of whether the property is visible or not. A properties value must be explicitly set to null to remove it from the quota count.


Although these extension properties are referred to as Azure AD extension properties, they can be managed through the Azure AD Graph API and the Microsoft Graph APIs, but here I will show you how to manage extensions on user objects through PowerShell. For other object types, you will need to resort to Graph.


To create an extension property, you will need an application to add it to. I have an API application called Myapi-19

$appName = 'Myapi-19'

$appObjId = (Get-AzureADApplication -SearchString $appName).Objectid

New-AzureADApplicationExtensionProperty -ObjectId $appObjId -Name "CostCentre" -DataType "String" -TargetObjects 'User' ##Note: The Targetobjects values are casesensitive


After running the above commands, the extension has been added to Myapi-19. You can enumerate all the extensions defined on an application using:

Get-AzureADApplication -ObjectId $appObjId | Get-AzureADApplicationExtensionProperty | fl

The results from running the command are:

Provided an SP for the application has been instantiated you can set and remove extension values on a user object.

$userUpn = 'john@5.ad.xtshub.com'

#Change an extension property value - reload the user to view the change

Set-AzureADUserExtension -ObjectId $userUpn -ExtensionName "extension_21695f38e621473b93cf215065a34b91_CostCentre" -ExtensionValue "Marketing"

#Read all extension attributes values on a user object

Get-AzureADUserExtension -ObjectId $Userupn

#Alternatively

(Get-AzureADUser -ObjectId $userUpn).ToJson()

#Read a single extension property value

(Get-AzureADUserExtension -ObjectId $Userupn).get_item("extension_21695f38e621473b93cf215065a34b91_CostCentre")

#Remove the extension property value and reduce the max resource count (100)

Remove-AzureADUserExtension -ObjectId $userUpn -ExtensionName "extension_21695f38e621473b93cf215065a34b91_CostCentre"

If you delete the service principal, you will lose visibility of all the extension attributes that where defined on the application object. If you try and read the extension attribute value, you will get an error:

Reinstate the SP either by consenting to the application or through the Portal UI (see Figure 5), and you will once again be able to read the attribute values.

After the SP has been recreated:

If I view all the extension attributes on my user object I see:

So what are the extensions with 4709de… in the name? Let's go hunting:

#Searching for an app via AppId GUID

Get-AzureADApplication | Where-Object {$_.AppId -like '4709de*'} |fl -Property AppId,DisplayName

Returns:

The "Tenant Schema Extension App" application is created when you configure the

Directory extension attribute sync feature in Azure AD Connect, see Figure 6. Once you have enabled this feature, you can choose which additional on-premises attributes to sync to the cloud. See Figure 7. Azure AD Connect will create the Tenant Schema Extension App and extension properties in Azure AD. You can see the application in the Portal's Apps registration blade and the SP in the Enterprise apps blade.



$appName = 'Tenant Schema Extension App'

$appObjId = (Get-AzureADApplication -SearchString $appName).Objectid

## Enumerate all extensions on an application

Get-AzureADApplication -ObjectId $appObjId | Get-AzureADApplicationExtensionProperty | fl

Result:

You might be asking yourself why has John added "title" as an extension property. I chose it to show that you can sync any on-premises attribute to the cloud. The on-premises attribute could be inbuilt or a custom on-premises schema extension.


Azure AD Connect creates the appropriate synchronization rules to import the extension attributes from on-premises AD into Meta Verse(MV) and then export the MV attributes to Azure AD you can see these rules in Figure 8.

It should be noted that Azure AD Connect will only allow the configuration of synchronization for extension attributes that it creates on the 'Tenant Schema Extension App'. You cannot add your own and enable synchronization.


Adding extension properties security tokens as claims

You might be wondering if these extension properties can be added to security tokens as claims. The answer to that is yes, no, and it depends. If you go to the App registration blade in the portal and select Token configuration, you can add optional attributes for ID, Access and SAML tokens. However, there is no obvious way to add an extension attribute. Be it one that you created on an application object or the ones created by Azure AD Connect on the Tenant Schema Extension App object. So how do you do it?


Obviously, you click "Learn More". Reading through the documentation, you see that you can add an optional claim through the manifest.

You might assume that you can add any extension property via this method. Here I have added the on-premises synced extension attribute.

The manifest is saved successfully, however, when you look in the Token configuration blade, you see that the claim is not supported. Figure 9.

This caused me no end of head-scratching. Eventually, I solved the puzzle. If you want to add an extension property, and its associated value, to a token, the property must be defined on the application for which the token is intended. You can a really good blog and reference from Sahil Malik here.


Figure 10 shows a frontend website and a backend API. Using OpenID Connect and OAuth 2.0, a user authenticates to the website using an ID token, and the website is authorized to access the API using an Access token. To add an extension attribute to the ID token requires the extension attribute to be created on the website application object. To add an extension attribute to Access token requires the extension attribute to be created on the API application object.

Remember that once a user extension property has been created, it can have values set for each user. When the user signs in these values will appear in the appropriate tokens.


Once you have defined the extension property on an application, it will appear in the list of optional attributes, and you will not need to add it via the manifest. See Figure 11, which shows the extn.Attribute1 property, this is a shortened name.

Once it has been added to a token, the full name can be seen in the manifest:

"name": "extension_3992f756cbab468abdb89a313ff3bb7f_Attribute1"


SAML application claims

Here is a strange anomaly, in addition to augmenting SAML claims in the App registrations blade you can also manage claims via the SAML configuration wizard in the Enterprise apps blade, Figure 12. Using this method of adding claims, you can add Azure AD Connect extended attributes, but not any other property extensions. See Figure 13.


You could be asking the question, why can I add Azure AD Connect extended attributes to SAML tokens, but not ID or Access tokens? I will try and get an answer to that, but for now, we will have to leave it as an open question. Extending properties via Microsoft Graph

Using Microsoft Graph is now the recommended way to add additional properties and values to Azure AD and other Microsoft objects, and there are two methods

  • Open extensions

  • Schema extensions

Azure AD Graph directory extensions are going to be around for a long time; consequently, you can manage these extensions and associated data through Microsoft Graph. Remember that you should migration your applications to just using Microsoft Graph and remove any Azure AD Graph dependencies.


Properties defined using both open and schema extensions can be associated with a broader range of objects including many Microsoft 365 objects.


Open extensions

Open extensions (aka Office 365 extensions) provide methods to add previously undeclare and untyped application properties onto an object. The object must exist before the extension can be added, you cannot create the object and set the extension value(s) during the creation of the object.


When you create an extension for an object, you have to specify an extension name which must unique within the tenant. At the point of creation, you can add untyped data in the form of name:value pairs. A recommended name convention is to name an extension using the reverse DNS format for your verified domain followed by a unique extension identifier. For example, my domain is 5.xtshub.com, a good naming convention for an extension holding food preferences of users would be com.xtshub.5.foodpreferences. Once the extension is created, this name is used as the extension id for further updates and changes.


At any point, you can update the name:value pairs held within a named extension. Figure 14 illustrates the relationship between a user object, the extensions navigation property and individual named extensions.



An update consists of re-writing all the data within a named extension. You can write new values for your name:value pairs or because the property names are not declared you can write new name:value pairs and omit name:value pairs that you no longer require.


Creating an extension

POST: https://graph.microsoft.com/v1.0/users/john@5.ad.xtshub.com/extensions

Body:






Read an extension

GET https://graph.microsoft.com/v1.0/users/john@5.ad.xtshub.com/extensions

Or

GET https://graph.microsoft.com/v1.0/users/john@5.ad.xtshub.com/extensions/{extensionid}

Result:

Update an extension

PATCH: https://graph.microsoft.com/v1.0/users/john@5.ad.xtshub.com/extensions/com.xtshub.5.foodpreferences

Body





Notice in the update I left out the fish name:value pair and added the cake option.

Results:

The open extensions effectively allow us to add a blob of data to a particular object type, and in the examples, I have been using a user object. This blob of data has an extension identify and contains name:value pairs (properties and values). As there is no schema definition defining that a user object will have a particular set of new attributes, you need to add the extension to each user object for which you want to set the data.


An application will create and manage the open extensions either using application or delegated permissions and will require User.ReadWrite.All. You should also be aware that any application with the appropriate permissions can update and delete an open extension regardless of who created it.


The maximum number of named extensions on an object created by an application is two. You can have more than two named extension on object if they have been created by different applications. Each open extension can have a maximum of 2 KB of data, this includes the extension definition.


Open extensions provide you with a quick way of adding undeclared and untyped data to Azure AD directory objects. Azure AD does not support filtering based on the values in the named:value pairs. For example, I cannot filter the results based on all users who have a preference for beef ($Filter=meat eq 'Beef’). If you want declared properties, typed data, and filtering on property values, you need Schema extensions, read on…


Schema extensions

Schema extensions allow you to declare properties with particular data types and the object with which they can be used.

Here is an example of creating a schema extension:


POST: https://graph.microsoft.com/v1.0/schemaExtensions

Body


















Notice that I have declared attribute names, datatypes, and the object (user) to which they apply

Result:


The application requires delegated permissions (Directory.AccessAsUser.All.) to create a schema extension. An application can create a maximum of five schema extensions.


The owner should be set to the application (client) id of the application that will be used to maintain the schema. If a value is not set for the id, it will be automatically populated with the application (client) id of the application creating the schema extension.


The Schema id that you use should be unique. You can use one of your verified domain names provided it is under .com, .net, .gov, .edu or .org top-level domains. If you don’t use a verified domain name, Azure AD prepends eight random alphanumeric characters to the name you specify. In the response above, you can see "id": "ext54rza67m_FoodPreferences.


The status defined the current state of the schema extension. The possible states are:

  • InDevelopment – set when you first create the schema extension

  • Available

  • Depreciated

Only the owner app using the appropriate delegated permissions can transition the schema extension between states. Provided the schema extension is visible, any application with the correct permissions can update the schema extended properties of the object (resource) types, identified in the schema definition.


InDevelopment

  • The owner app can update the Schema extension definition with additive changes or delete the schema definition

  • Any application in the owner app’s tenant can set, read and update schema extension properties for the targeted object(s). The properties can be set on an object or set during the creation of an instance of the object. The application will require the appropriate permissions.

Available

  • The owner app can update the Schema extension definition with additive changes. The schema extension cannot be deleted

  • The schema extensions are available to all Azure AD tenants

  • Any application in any tenant can set, read and update schema extension properties for the targeted object(s). The properties can be set on an object or set during the creation of an instance of the object. The application will require the appropriate permissions.

Depreciated

  • The schema extension can no longer be viewed or modified

  • The schema extension cannot be deleted

  • Any application in any tenant can set, read and update schema extension properties for the targeted object(s).

The developer can work with the schema extension in the InDevelopment state, test it, and additively update it if necessary. If it’s a complete mess, you can delete it and start again.


Only additive updates are supported.

PATCH: https://graph.microsoft.com/v1.0/schemaExtensions/ext54rza67m_FoodPreferences


Request body



















You can view the updated schema with

GET: https://graph.microsoft.com/v1.0/schemaExtensions/ext54rza67m_FoodPreferences

Results:

Notice that the schema extension can now be applied to user and group objects and there is a new property for Drinks.


The property values can be set on a new or existing user object. Here I update an existing user object.

PATCH: https://graph.microsoft.com/v1.0/users/john@5.ad.xtshub.com/

Request body:









You can view the schema extensions on a user object using the $Select option.

GET: https://graph.microsoft.com/v1.0/users/john@5.ad.xtshub.com/?$select=ext54rza67m_FoodPreferences

You can also filter based on extension properties so you could search for all users that like beef

GET: https://graph.microsoft.com/v1.0/users/?$filter=ext54rza67m_FoodPreferences/meat eq 'Beef'&$select=id, DisplayName, ext54rza67m_FoodPreferences


So now we can declare typed-data extension properties that can be added to objects in the Azure AD allowing applications to store any additional data that is required within Azure AD.

In Summary

If you have got this far, you have done well, this is a tough topic. I am so pleased that I have got to this point as I nearly gave up a few time along the way. It has been really tricky to test everything out and to try and clearly explain it. I hope I succeeded. If you like this blog, please follow me on Twitter @john_craddock. I would love to hear from you so please Tweet me with comments and share will friends and colleagues. Until next time, stay safe and well.

1,961 views

Recent Posts

See All

© 2016 XT Seminars Ltd Registered in England No 06412071.
Registered Office: 101 Chandlers Way, Temple Farm Industrial Estate, Southend-on-Sea, Essex, SS2 5SE