Extend Azure Active Directory Schema using Graph API (preview)

Edit - Please refer to http://msdn.microsoft.com/en-us/library/azure/dn720459.aspx for official documentation about directory extensions.

Directory schema extensions enable application developers to extend the directory and develop richer applications without worrying about the limitations imposed by an external store. This preview provides REST interfaces for an application to register, unregister, enumerate, read, write, and filter by extension values. Applications that register extensions in the directory are referenced from all the tenants consenting to that Application. Once a customer tenant has consented to an Application (even for read) the extensions registered on that Application are available in the consenting tenant for reading/writing by any Application that has the appropriate access. If the app developer wants to add more extension attributes, she can update her Application (in her developer tenant) and any tenants that are currently consented to this Application will be enabled for the new attributes. If consent is removed, if the extension is deleted, or if the Application is deleted, the extension values will no longer be accessible on the corresponding directory objects.

Types and Limitations
Currently “User”, “Group”, “TenantDetail”, “Device”, “Application” and “ServicePrincipal” entities can be extended with the following single-valued attributes types:

  • Integer
  • LargeInteger
  • DateTime (must be sent in ISO 8601 - DateTime will be stored in UTC)
  • Binary
  • Boolean
  • String

String type extensions can have maximum of 256 characters and binary extensions are limited to 256 bytes. 100 extension values (across ALL types and ALL applications) can be written to any single object. Directory extensions are available only in Graph api-version 1.21-preview. The application must be consented for write access to register an extension.

Registering an Extension
Let’s walk through an example. Contoso has built an OrgChart application and wants to allow users to make Skype calls from it. AAD does not expose a SkypeID user property. The OrgChart developer could use a separate store such as SQL Azure to store a record for each user’s SkypeID. Instead, the developer registers an extension that can expand User type with a String property. He does this by creating an “extensionProperty” on the Application using Graph API.
POST https://graph.windows.net/contoso.com/applications/<applicationObjectID>/extensionProperties?api-version=1.21-preview
“name”: “skypeId”,
“dataType”: “String”,
“targetObjects”: [“User”]
If the operation is successful, it will return 201 along with the fully qualified extension property name to be used for updating the intended types.
201 Created
“objectId”: “5ea3a29b-8efd-46bf-9dc7-f226e839d146”,
“objectType”: “ExtensionProperty”,
“name”: “extension_d8dde29f1095422e91537a6cb22a2f74_skypeId”,
“dataType”: “String”,        
“targetObjects”: [“User”]
Viewing Extensions Registered by your Application
You can view extensions registered by your application by issuing a GET of the extension properties of the application. This will provide object ID, data type, and target objects for each extension registered by the application.
GET https://graph.windows.net/contoso.com/applications/<applicationObjectID>/extensionProperties?api-version=1.21-preview

Unregistering an Extension
You can unregister an extension registered by your application by issuing a DELETE of the extension object ID as follows:
DELETE https://graph.windows.net/contoso.com/applications/<applicationObjectID>/extensionProperties/<extensionObjectID>?api-version=1.21-preview

Writing Extension Values
Once this application is consented by the admin, any user in the tenant can be updated to include this new property. For example,
PATCH https://graph.windows.net/contoso.com/users/joe@contoso.com?api-version=1.21-preview
“extension_d8dde29f1095422e91537a6cb22a2f74_skypeId”: “joe.smith”
The server will return a 204 if user was successfully updated. The extension value can be removed by sending the same PATCH request with “null” value.
PATCH https://graph.windows.net/contoso.com/users/joe@contoso.com?api-version=1.21-preview
“extension_d8dde29f1095422e91537a6cb22a2f74_skypeId”: null

Reading Extension Values
When directory objects are retrieved, they automatically include the extension values. For example:
GET https://graph.windows.net/contoso.com/users/joe@contoso.com?api-version=1.21-preview
200 OK
“objectId”: “ff7cd54a-84e8-4b48-ac5a-21abdbaef321”,
“displayName”: “Joe Smith”,
“userPrincipalName”: “joe@contoso.com“,
“objectType”: “User”,
“mail”: “null”,
“accountEnabled”: “True” ,
“extension_d8dde29f1095422e91537a6cb22a2f74_skypeId”: “joe.smith”
Filtering by Extension Values
The extension values can also be used as a part of $filter to search directory similar to any existing property. For example:
GET https://graph.windows.net/contoso.com/users?api-version=1.21-preview&$filter=extension_d8dde29f1095422e91537a6cb22a2f74_skypeId eq 'joe.smith'
Prefix searches on extensions are limited to 71 characters for string searches and 207 bytes for searches on binary extensions.

Sample Code
We have published a couple of samples to GitHub to showcase and illustrate the use of directory extensions. We plan to enhance them based on your feedback and as the feature evolves.

PHP Sample

This sample shows how to create and use extensions using PHP. The wiki of this GitHub project helps in understanding the sample and steps to modify/use it.

C# Sample

This sample displays an AAD tenant org chart and allows reading extension values right out of the box. The wiki of this GitHub project helps in understanding the sample and steps to modify/use it.

We would love to hear about the scenarios that you can enable using extensions or any feedback about the usage in our forum. This is just a first step in integrating on-premises AD with Azure AD.


Pavan Kompelli
Rakesh Varna
Arvind Suthar

Azure Active Directory Team

Comments (33)

  1. Adam Bradley says:

    Wow! Thanks.


  2. Sam Dzirasa says:

    Can these Extension values be included automatically in the claims, once a user authenticates using Azure AD?

  3. Pavan Kompelli - MSFT says:

    Right now extension values cannot be included in the claims. Can you please let us know the scenario where you need this?

  4. Sam Dzirasa says:

    It saves us from having to create a custom ClaimsAuthorizationManager that reads these Extended Properties and adds them to the "default" claims

  5. Sam Dzirasa says:

    Adding to my comment above.  It will be great if the extensions are automatically included in the claims obtained form using the Active Directory Authentication Library (ADAL).

  6. Shas says:

    I am trying to incorporate the pattern in the C# sample code into my MVC Application but when I run it I get an error when I call the createExtension logic. The error reads Resource <ObjectId> does not exist or one of its queried reference-property objects are not present. I have used the ObjectId after noting it down from the powershell command: get-msolserviceprincipal for my application. This is an MVC application that I have registered on my trial Azure subscription. What could I be doing wrong? Could you please advice?

  7. Rick says:

    This has to do with the availability of extensions to code.  Do all applications that have approriate access then get the extension property or just the one that registered it.  And, if so, is it possible to use the Powershell Cmdlets to get/update the extended attributes.

  8. Pavan Kompelli - MSFT says:

    Thanks Sam for your feedback. We will look into hat in our future releases.

    Shas, you need to use the objectId of the application object to add the extension. You can get the objectId of your application using graphexplorer.cloudapp.net (signin and traverse to graph.windows.net/<your tenant>/applications.

    Rick, All applications have access similar to the access they have for other properties. If an app is consented only for read, they can only read extension property values. If they are consented for write, they will be able to write to the extension property values. We are looking into providing powershell support for future releases.

  9. Mayuri M says:

    Hi Guys, I found this article very useful! Thanks a ton!

    However after downloading c# application, I am having two problems

    1. Unable to locate 'F:TestExtensionAttributeWindowsAzureAD-GraphAPI-Sample-OrgChart-masterWindowsAzureAD-GraphAPI-Sample-OrgChart-master.nugetNuGet.exe' GraphHelper
    2. Also I am not able to resolve reference to Microsoft.WindowsAzure.ActiveDirectory.GraphHelper (tried searching nuget packages but could not find one which will resolve the conflict)

    Can you please help me for this?


  10. Mark Chen says:

    The application is working fine on my local server, and I have a question. there is a GraphHelper project, which is different from the GraphHelper in use in other Azure official tutorial. Wonder if it would be better merging these two GraphHelper packages so we have a single code base for that?

  11. Mayuri M,

    1. Ed has just fixed this problem in GitHub – github.com/…/6c80718f69f5f23eab5142b6156ba43e7fc1b89e
    2. GraphHelper is built using the sources here – github.com/…/GraphHelper. Once the official GraphClient library supports extension attributes, we will port this demo to that library.

    Let us know if you see other problems.


  12. Jameel Syed says:

    Are multi-valued string attributes supported currently? If so, what does it take to create one such attribute?



  13. Pavan Kompelli - MSFT says:


    We are working on releasing an official graph client library and will update the blog once it is available.


    Multi-valued are not currently supported but it will be part of future releases (currently no ETA yet).



  14. Vibhuti says:

    Hi, I am trying to create an extension as mentioned in the example above using the Advanced REST client extension of Chrome.  When I POST the request, I get an error stated below.

    My Application's object ID and all other values are correct. And the JSON format is the same as stated in the blog above. What could be missing here? Could you please help?


    odata.error: {

    code: "Request_BadRequest"

    message: {

    lang: "en"

    value: "Invalid JSON. The property name '' is not valid. The name of a property cannot be empty."






  15. Pavan Kompelli - MSFT says:

    Hi Vibhuti, can you please post the entire request/response (removing any PII information if necessary)?

  16. Phi Huynh says:

    Hi Vibhuti, i created successfully with Postman extension on Chrome.

    Here is my raw request:

    POST graph.windows.net/…/extensionProperties HTTP/1.1

    Host: graph.windows.net

    Connection: keep-alive

    Content-Length: 70

    Cache-Control: no-cache

    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36

    Origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm

    Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhb[***]I6z3fKeI8e-kyq3Td8Dg

    Content-Type: application/json

    Accept: /

    Accept-Encoding: gzip,deflate,sdch

    Accept-Language: en-US,en;q=0.8,vi;q=0.6


    "name": "skypeId",

    "dataType": "String",

    "targetObjects": ["User"]


  17. Abhay (Bushel Tech) says:


    Does the Graph API not support content encoding to compress the response in gzip or some other format?

    1. All the responses we are seeing against operations described by Graph REST API reference (msdn.microsoft.com/…/hh974478.aspx) seem to ignore the "Accept-Encoding: gzip" request header and response is no encoded at all.

    Given the size of some of the directories our clients are using, the response becomes very big and given the textual nature of it, compression would help a lot.

    1. Could you please update the MSDN documentation above to detail the operations on Applications and Devices as well?



  18. Robert Ward - MSFT says:

    Hi Abhay,

    Thanks for the feedback!  Regarding 1), as you've found, the service does not currently support compression.  I will add a work item on our side to get this completed.  In the meantime, if you are not using JSON (the default that is returned with Accept: application/json), then please do so.  ATOM/XML formats are far more verbose, as are other forms of JSON; the default JSON response relatively lightweight.

    Regarding 2), I'll make sure the documentation gets updated appropriately.



  19. Abhay (Bushel Tech) says:

    Hi Robert,

    Thanks for the reply. We are already using JSON but the compression will really help.  I hope to see an update on that in a few months.

    Especially because our application is used on mobile devices, where network and battery life matter a lot, every byte reduced helps.



  20. Abhay (Bushel Tech) says:


    Is there a better forum than this blog comments where we can have a discussion with the Graph API team with requests for better documentation on a particular feature or ask for new features?



  21. Ilya Lipkind says:

    Thank you for info. This is great. I wonder if there is any thoughts to keep extension visible to requested application only or somehow define visibility In our case User object shared between many applications and we would like to keep extensions application specific instead of sharing all user attributes across all applications.

  22. Abhishek Sinha says:

    Great Article!!

    Do we have this functionality available in the GraphClient library?

  23. Goku says:

    Thanks a lot!!! for the article.

    We are getting exception while adding user to an existing group from MVC app to WAAD and also while deleting a user from an exiting group. It was working fine till yesterday, and suddenly now we are getting exception saying

    "Resource '<group_id / objectId>' does not exist or one of its queried reference-property objects are not present"

    following is the piece of code:

    Group refreshedGroup = DirectoryDataServiceAttributes.DataService.groups.Where(it => (it.objectId == PermissionId.Trim())).SingleOrDefault();

                       if (refreshedGroup.dirSyncEnabled == null)


                           DirectoryDataServiceAttributes.DataService.DeleteLink(refreshedGroup, "members", users);



    we are getting error while executing SaveChanges() method. we are passing correct group id /object Id.

    "refreshgroup" variable is having data of the groups which we expect and "users" variable is having the user details what we expect.

    Any help will be appreciated.


  24. Alessandro Alfano says:

    Hi, how can we use extensions with v.2.0.0.?



  25. Alessandro Alfano says:

    Any help for v2.0.2?



  26. Twang says:

    Will there are be support for extensions that can store more than 256 bytes?

  27. Linda says:

    Once you have used the Graph API to extend the Azure AD schema with custom attributes, is there a way to delete those or change their value type if you make a mistake on some?  I have someone that created over 50 using the wrong attribute type.

  28. Rakesh Varna says:


      You should be able to delete the custom attributes and recreate them with the correct type. You can find more information in the MSDN link available at the beginning of the article. I am adding it here for easy reference – msdn.microsoft.com/…/dn720459.aspx

  29. Vikram Gore says:


    I want to get all users and their groups in one call. How the URL will be?? I tried a lott on google . Its urgent requirement in our project..

  30. Vikram says:


    I want to get all users and their groups in one URL call .

    Right now i am using url- "graph.windows.net/…/users$expand=memberOf&api-version=1.0"

    It shows all users and their groups. but groups displayed are<20. How can I display all groups of each user with minimum time?

  31. TSCH says:

    I have an application in a B2C AAD with all read and write directory data permission to the AAD. I can use it to add all or a single Extended Property. When I try and use it to delete one:

    DELETE /[Same as with a GET]?api-version=1.5


    Host: graph.windows.net

    Authorization: Bearer eyJ0eXA…g

    Cache-Control: no-cache

    The response is


     "odata.error": {

       "code": "Authorization_RequestDenied",

       "message": {

         "lang": "en",

         "value": "Insufficient privileges to complete the operation."




    Also, I see in my B2C Blade extended properties I have added using ADAL, under User Attributes with ATTRIBUTE TYPE of "Custom". However, if I read a user from the same B2C AAD, I do not see the extended properties listed:

    Get [MICROSOFT AZURE AD GRAPH API ENDPOINT from View Endpoints in Classic Azure Portal]/users/[user object id]?api-version=1.5


    Host: graph.windows.net

    Authorization: Bearer eyJ..Wg

    Cache-Control: no-cache



       "odata.metadata": "graph.windows.net/279925ff-681f-45c8-a464-f1bd61fd9d9b$metadata#directoryObjects/Microsoft.DirectoryServices.User/@Element",

       "odata.type": "Microsoft.DirectoryServices.User",

       "objectType": "User",

       "objectId": "85d6bb2a-cb4e-4df5-96a7-0e270b06cbfb",

       "deletionTimestamp": null,

       "accountEnabled": true,

       "userType": "Member"


    without any extended properties listed.

  32. Eric Vogelpohl says:

    I'm not a programmer, nor do I have rights to extend schemas that I know of..

    However, I'm using Excel PowerQuery and the Azure AD REST graph API to retrieve user data from our AD.  

    I need to both retrieve the value of ExtensionAttribute5 as well as FILTER it.  

    I've tried this REST query and get an error:  graph.windows.net/…/users$filter=accountEnabled+eq+true+and+ExtensionAttribute5+eq+'Vendor'

    (You can see, I need to get all users who are vendors, from regular employees – we store that here)

    Is there now, or will there be a version of the Azure Graph API that simply reveals all the attributes for a user object so one doesn't have to write code to make new apps?


    Thanks – Eric.

  33. Phil0002 says:

    Really interested to know if we use directory schema extensions how do we get the new extension property of a user returned as a claim?

    I see from prior commentresponse that it is not possible?

    Seems like a massive oversight. Why would we add a property to a user if we didn't want it? Why would we want to use graph API to manually retrieve the value of said property rather than just have it properly encased within the token issued by the identity provider – containing what the identity provider knows about the individual.

Skip to main content