Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

batch processing: userPrincipalName already exists when trying to add multiple users async in AAD

I am trying to add multiple users async with AAD graph Like this:

IUser user1 = ...;
IUser user2 = ...;
IUser user3 = ...;
List<Task> addTasks = new List<Task>();
addTasks.Add(activeDirectoryClient.Users.AddUserAsync(user1));
addTasks.Add(activeDirectoryClient.Users.AddUserAsync(user2));
addTasks.Add(activeDirectoryClient.Users.AddUserAsync(user3));
await Task.WhenAll(addTasks);

I get this error message :

{"odata.error":{"code":"Request_BadRequest","message":{"lang":"en","value":"Another object with the same value for property userPrincipalName already exists."},"values":[{"item":"PropertyName","value":"userPrincipalName"},{"item":"PropertyErrorCode","value":"ObjectConflict"}]}}

If I use this code :

IUser user1 = ...;
IUser user2 = ...;
IUser user3 = ...;
List<Task> addTasks = new List<Task>();
await activeDirectoryClient.Users.AddUserAsync(user1);
await activeDirectoryClient.Users.AddUserAsync(user2);
await activeDirectoryClient.Users.AddUserAsync(user3);

This is working well.

Maybe we can't add multiple users in AAD at the same time ?

EDIT Here is the users part:

IUser newStudentUser = new User
{
    DisplayName = $"Etudiant de l'école {school}",
    UserPrincipalName = $"etudiant-{school}@........fr",
    AccountEnabled = true,
    MailNickname = $"Etudiant {school}",
    UsageLocation = "US",
    PasswordProfile = new PasswordProfile
    {
        Password = "......."
    }
};
IUser newTeacherUser = new User
{
    DisplayName = $"Professeur de l'école {school}",
    UserPrincipalName = $"professeur-{school}@........fr",
    AccountEnabled = true,
    MailNickname = $"Professeur {school}",
    UsageLocation = "US",
    PasswordProfile = new PasswordProfile
    {
        Password = "......."
    }
};
IUser newDirectorUser = new User
{
    DisplayName = $"Directeur de l'école {school}",
    UserPrincipalName = $"directeur-{school}@........fr",
    AccountEnabled = true,
    MailNickname = $"Directeur {school}",
    UsageLocation = "US",
    PasswordProfile = new PasswordProfile
    {
        Password = "......."
    }
};
like image 418
trenoncourt Avatar asked Sep 01 '25 03:09

trenoncourt


1 Answers

In your case, you want to batch the creation of multiple users.

At present, the GraphClient does support batch processing but there are some limitation (Batch processing | Graph API concepts):

  • A single batch can contain a maximum of five queries and/or change sets combined.
  • A change set can contain a maximum of one source object modification and up to 20 add-link and delete-link operations combined. All operations in the change set must be on a single source entity.

So in your case you can't batch the creation of more than 5 users.

While adding an entity to Graph API client, you can choose to defer the execution of the query using the deferredSave parameter.

await activeDirectoryClient.Users.AddUserAsync(user, deferredSave: true);

The Graph API client has a DataServiceContextWrapper that tracks changes. It provides a SaveChanges(Async) method.

await activeDirectoryClient.Context.SaveChangesAsync();

Calling this method you can specify the SaveChangesOptions:

  • Batch: All pending changes are saved in a single batch request.
  • BatchWithIndependentOperations: the one you want to use because the creation of the user should be in a single query.
  • ContinueOnError: Pending changes are saved by using multiple requests to the server, and the operation continues after an error occurs.
  • None: Pending changes are saved by using multiple requests to the server, but the operation stops on the first failure (default).
  • PatchOnUpdate:
  • ReplaceOnUpdate: Pending updates are made by replacing all values of the entity in the data source with values from the updated entity.

Now you have enough information to write the code to batch the creation of the users:

// Only 5 users per batch !!!!
var user1 = ...;
var user2 = ...;
var user3 = ...;
await activeDirectoryClient.Users.AddUserAsync(newStudentUser, deferredSave: true);
await activeDirectoryClient.Users.AddUserAsync(newTeacherUser, deferredSave: true);
await activeDirectoryClient.Users.AddUserAsync(newDirectorUser, deferredSave: true);

// In debug mode, you should use the SaveChangesAsync method with the default options
// Becasue the BatchWithIndependentOperations will not throw any exception even if there is a problem while creating the user.
//await activeDirectoryClient.Context.SaveChangesAsync();
await activeDirectoryClient.Context
    .SaveChangesAsync(SaveChangesOptions.BatchWithIndependentOperations);
like image 164
Thomas Avatar answered Sep 02 '25 16:09

Thomas