Search code examples
c#azure-active-directorymicrosoft-graph-sdks

MS Graph SDK How to Add/Update a Schema Extension property to a User object


I'm having real trouble finding any code examples in how to add a Schema Extension value against a User in Azure AD.

So far I have successfully created my own schema extension, marked as 'User' TargetType, and updated the status as 'Available'. This was all achieved through the MS Graph SDK

Below is a screenshot showing the extension I created and listed within my app:

enter image description here

This extension is very basic, just a single property exists called 'name' that is set to a type string.

I now want to perform the following functions:

  • Create a new user and assign a value against their profile using my newly created schema extension i.e. this user would have the schema extension property 'name' set to a string value of my choice.
  • Update the value of the schema extension property for an existing user.
  • Run a filtered search for all users in my tenant that have a certain value set within this schema extension.

The only examples given by MS Docs is using the MS Graph API, NOT the SDK which I am using in my app. In addition their only example given is adding a schema extension when creating a new Group.

https://learn.microsoft.com/en-us/graph/extensibility-schema-groups

In their notes, MS say the following: Note: This topic shows you how to create and read schema extension values on a group resource (steps 3-5). The same methods are supported for the administrativeUnit, device, event, message, organization, post, and user resource types as well. You can carry out operations similar to the request examples in this article on any of those resources.

Can someone please advise where I get code example that allows me to create/update users with this extension, as well as using a filtered search for all users in my tenant that have a certain value set in their profile for this particular extension?

UPDATE: In conjunction with the dicussion with Allen Wu in answers below, I have implemented his code example in adding the schema extension as "AdditionalData" when creating a new user. I havent managed to get this working yet as I receive a error saying "Code: BadRequest\r\nMessage: Unsupported Schema Extension Property value of type String\r\nInner error:\r\n\tAdditionalData:\r\n\tdate: 2021-03-31T18:51:32\r\n\trequest-id"

Code example for creating a new user with the schema extension:

var user = new User();
user.UserPrincipalName = TenantUser.UserPrincipalName + "@" + Domain; // * Required
//user.UserPrincipalName = TenantUser.UserPrincipalName; // * Required
user.DisplayName = TenantUser.GivenName + " " + TenantUser.Surname; // * Required
user.GivenName = TenantUser.GivenName; // * Required
user.Surname = TenantUser.Surname; // * Required

if (TenantUser.Password != null)
{
   if (TenantUser.Password != TenantUser.ConfirmPassword)
   {
       ModelState.AddModelError(string.Empty, "\n Passwords do not match!");
       return Page();
   }
   else
   {
       user.PasswordProfile = new PasswordProfile
       {
           Password = TenantUser.ConfirmPassword,
       };
    }
}

user.AccountEnabled = AccountEnabled;
user.JobTitle = TenantUser.JobTitle;
user.Department = TenantUser.Department;
user.EmployeeId = TenantUser.EmployeeId;
user.OfficeLocation = TenantUser.OfficeLocation;
user.CompanyName = TenantUser.CompanyName;
user.StreetAddress = TenantUser.StreetAddress;
user.State = TenantUser.State;
user.City = TenantUser.City;
user.PostalCode = TenantUser.PostalCode;
user.Country = TenantUser.Country;
user.MobilePhone = TenantUser.MobilePhone; // * Required

if (BusinessPhone != null)
{
    user.BusinessPhones = new List<String>()
    {
         BusinessPhone // see public string
    };
}

user.Mail = TenantUser.Mail; // * Required
user.MailNickname = TenantUser.MailNickname;
user.AdditionalData = new Dictionary<string, object>()
{
    {"ext7bxc3e0l_appRoleTemplate", "{\"name\":\"Template1\"}"}             
};

try
{
     await graphClient.Users
     .Request()
     .AddAsync(user);

      LogData = "New User with name [" + UserFullName + "] created by user with forename [" + currentUser.Forename + "] surname [" + currentUser.Surname + "] Succesfully";
      // See helper method at bottom.
      logExtension.WriteLogEvent<EditModel>("Information", "Users", "Created", UserFullName, currentUser.Id, currentUser.Username, currentUser.Forename, currentUser.Surname, LogData, null, _scopeFactory);
}
catch (ServiceException ex)
{
    // Graph service exception friendly message
    var errorMessage = ex.Error.Message;
    LogData = " Error creating the User. Microsoft Graph API encountered a Service Exception with message [" + errorMessage + "]'";
    logExtension.WriteLogEvent<EditModel>("Error", "User", "Created", $"User with Id = {currentUser.Id}", currentUser.Id, currentUser.Username, currentUser.Forename, currentUser.Surname, LogData, null, _scopeFactory);
    ModelState.AddModelError(string.Empty, errorMessage + " Error creating the User.\n Please check the system logs for details");
    return Page();
}

Solution

  • I create a schema extension:

    {
        "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#schemaExtensions/$entity",
        "id": "ext253rtfdu_name",
        "description": "user name extensions",
        "targetTypes": [
            "User"
        ],
        "status": "InDevelopment",
        "properties": [
            {
                "name": "userId",
                "type": "String"
            },
            {
                "name": "userName",
                "type": "String"
            },
            {
                "name": "userType",
                "type": "String"
            }
        ]
    }
    

    Create a user whit a ext253rtfdu_name schema extension:

    GraphServiceClient graphClient = new GraphServiceClient(authProvider);
    
    string extStr = "{\"userId\":\"321\",\"userName\":\"Adele test02\",\"userType\":\"member\"}";
    
    JObject jsonObj = JObject.Parse(extStr);
    
    var user = new User
    {
        AccountEnabled = true,
        DisplayName = "Adele test01",
        MailNickname = "AdeleVtest01",
        UserPrincipalName = "AdeleVtest01@contoso.onmicrosoft.com",
        PasswordProfile = new PasswordProfile
        {
            ForceChangePasswordNextSignIn = true,
            Password = "password-value"
        },
        AdditionalData = new Dictionary<string, object>()
        {
            {"ext253rtfdu_name", extStr}
        }
    };
    
    await graphClient.Users
        .Request()
        .AddAsync(user);
    

    Update the user:

    GraphServiceClient graphClient = new GraphServiceClient(authProvider);
    
    string extStr = "{\"userId\":\"456\",\"userName\":\"Adele test02\",\"userType\":\"member\"}";
    
    JObject jsonObj = JObject.Parse(extStr);
    
    var user = new User
    {
        AdditionalData = new Dictionary<string, object>()
        {
            {"ext253rtfdu_name", jsonObj}
        }
    };
    
    await graphClient.Users["AdeleVtest01@contoso.onmicrosoft.com"]
        .Request()
        .UpdateAsync(user);
    

    I tried to use https://graph.microsoft.com/v1.0/users?$filter=ext253rtfdu_name/userId eq ‘456’&$select=displayName,id,userPrincipalName,ext253rtfdu_name to filter with the extension to get the user but it shows Invalid filter clause. But according to Filtering on schema extension properties known issues, it should be supported.

    I don't understand what happen to this filter but the code sample should be:

    var res = await _graphClient.Users.Request()
        .Filter($"ext253rtfdu_name/userId eq '456'").GetAsync()
    

    A similar post here for your reference.

    You can have a try from your side.