Gérer son Azure AD avec l'API graph Microsoft
Depuis le développement d'Azure, Microsoft a mis à disposition différentes API permettant d'accéder et d’interagir avec les services de la plateforme de cloud. Parmi les plus célèbres on trouve l'API Azure AD qui facilite la gestion des utilisateurs de son Azure Active Directory.
Depuis 2015, Microsoft a changé sa politique pour rassembler toutes ses API Graph en une seule : Microsoft Graph. Lors de la build 2017, Microsoft a aussi annoncé que cette API serait utilisée afin d’apporter de nouvelles connectivités avec d’autres plateformes tel que Android et IOS.
Dans cette article nous allons voir plus en détail comment gérer ses utilisateurs Azure Active Directory avec Microsoft Graph.
Le graph Explorer
Pour vous permettre de tester vos requêtes, Microsoft met à disposition un explorateur donnant plusieurs exemples : https://developer.microsoft.com/en-us/graph/graph-explorer.
Comment se connecter ?
Afin de vous connecter avec une application au tenant de votre Azure AD à l'aide d'un OpenId, je vous renvoie vers le GitHub de Microsoft
Une fois les services déclarés, pensez à faire une vérification du délai d’expiration de votre token afin d’éviter les multiples appels à l’API.
private async Task<string> GetAppTokenAsync() { if (!(_authenticationResult != null && _authenticationResult.ExpiresOn.LocalDateTime > DateTime.Now)) { // Instantiate an AuthenticationContext for my directory (see authString above). AuthenticationContext authenticationContext = new AuthenticationContext($"{_azureadConfiguration.Authority}/{_azureadConfiguration.TenantId}", false); // Create a ClientCredential that will be used for authentication. // This is where the Client ID and Key/Secret from the Azure Management Portal is used. ClientCredential clientCred = new ClientCredential(_azureadConfiguration.ClientId, _azureadConfiguration.ClientSecret); // Acquire an access token from Azure AD to access the Azure AD Graph (the resource) // using the Client ID and Key/Secret as credentials. AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(_azureadConfiguration.ResourceUrl, clientCred); _authenticationResult = authenticationResult; } // Return the access token. return _authenticationResult.AccessToken; }
Gérer les time out
Lors de multiples appels dans un délai très court, il est possible d’avoir une erreur renvoyée par l’API qui vous indique que le nombre de requêtes est atteint.
Si cela arrive, vous aurez une exception de type ServiceException, avec un code d’erreur 429 et dans le header de la réponse une variable RetryAfter qui vous donnera le temps à patienter avant de relancer votre demande à l’API.
private static async Task<bool> RetryAction(bool isCallSucceeded, ServiceException serviceException) { if ((uint)serviceException.StatusCode == 429) { if (serviceException.ResponseHeaders != null && serviceException.ResponseHeaders.RetryAfter != null && serviceException.ResponseHeaders.RetryAfter.Delta != null && serviceException.ResponseHeaders.RetryAfter.Delta.HasValue) { await Task.Delay(serviceException.ResponseHeaders.RetryAfter.Delta.Value); } else { await Task.Delay(1000); } } else { isCallSucceeded = true; } return isCallSucceeded; }
Récupérer ses utilisateurs
Une fois votre token récupérer, nous allons voir comment récupérer les utilisateurs de votre annuaire Azure AD.
Par défaut l’API ne vous renvoie que les 100 premiers résultats de votre requête. Pour vous permettre de récupérer les résultats suivants, une variable nommée @odata.nextLink dans le json et NextPageRequest dans l’interface IGraphServiceUsersCollectionPage vous indique l’URL à utiliser afin de récupérer les prochains 100 résultats jusqu’à avoir la valeur nulle.
public async Task<List<User>> GetAllAzureUsers(IStorageService technicalLogger) { try { List<User> result = new List<User>(); var graphServiceClient = await GetGraphServiceClient(); var userRequest = graphServiceClient.Users.Request().Select("id,displayName,givenName,department,jobTitle,surname,userPrincipalName,AccountEnabled,PreferredLanguage"); //Méthode d’extension afin de gérer les time out var users = await userRequest.GetWithRetryAsync(); var request = users.NextPageRequest; while (request != null) { var pageUsers = await request.GetWithRetryAsync(); request = pageUsers.NextPageRequest; } return result; } catch (Exception e) { throw e; } return null; }
Récupérer un utilisateur
La récupération d’un utilisateur peut se faire à l’aide de deux propriétés obligatoires dans l'Azure Active Directory:
- L’identifiant Azure AD
- Le UserPrincipalName (le mail de connexion de l’utilisateur)
public async Task<User> GetUser(UserRH userAD) { User userAzure = null; try { if (!checkOnlyMatricule) { var graphServiceClient = await GetGraphServiceClient(); if (!string.IsNullOrEmpty(userAD.AzureId)) { userAzure = await graphServiceClient.Users[userAD.AzureId].Request().Select(("id,displayName,givenName,department,jobTitle,surname,userPrincipalName,AccountEnabled,PreferredLanguage").GetWithRetryAsync(); } else { userAzure = await graphServiceClient.Users[userAD. UserPrincipalName].Request().Select(("id,displayName,givenName,department,jobTitle,surname,userPrincipalName,AccountEnabled,PreferredLanguage").GetWithRetryAsync(); } } return userAzure; } catch (Exception e) { Throw e } return null; }
Mise à jour d’un utilisateur
Pour mettre à un jour un utilisateur, il vous faut créer un nouvel objet Microsoft.Graph.user et renseigner ou modifier les nouvelles propriétés.
Il vous suffit ensuite de passer cet objet à la méthode UpdateAsync qui ciblera l’utilisateur défini selon son identifiant ou son UserPrincipalName.
public async Task<bool> UpdateUser(User UserAzure) { try { var graphServiceClient = await GetGraphServiceClient(); var modifiedUser = new User(); modifiedUser.PreferredLanguage = "fr-FR"; modifiedUser.JobTitle = "Consultant"; modifiedUser.Department = "Productivité"; modifiedUser.GivenName = "Pierre"; modifiedUser.Surname = "Bouillié"; modifiedUser.DisplayName = "Pierre Bouillié"; modifiedUser.AccountEnabled = true; await graphServiceClient.Users[UserAzure.Id].Request().UpdateWithRetryAsync(modifiedUser); } catch (Exception e) { //Log e return false; } return true; }
Attention, certaines propriétés ne sont pas modifiables, tels que le mail et le champ hireDate. Je vous conseille de tester sur le graph explorer les possibilités car des mises à jour sont faites assez régulièrement.
Suppression d'un utilisateur
La suppression d'un utilisateur se fera à partir de son identifiant unique ou de son userPrincipalName, par défaut l'utilisateur est ajouté dans une liste d'utilisateurs supprimés et conservé pendant 30 jours.
public async Task<bool> DeleteUser(string userId) { try { var graphServiceClient = await GetGraphServiceClient(); await graphServiceClient.Users[userId].Request().DeleteAsync(); return false; } catch (Exception e) { throw e; } }
Si vous souhaitez autoriser une application Azure à supprimer des utilisateurs dans votre AD, il est nécessaire d'exécuter un script powershell afin de lui donner les droits d'administrateur. Pour ce faire il faut récupérer l'objectId de l'application et non celui de la registration.
# Fetch user to assign to role $AppObjectId = "<ObjectId de l'application>" # Fetch User Account Administrator role instance $role = Get-AzureADDirectoryRole | Where-Object {$_.displayName -eq 'User Account Administrator'} # If role instance does not exist, instantiate it based on the role template if ($role -eq $null) { # Instantiate an instance of the role template $roleTemplate = Get-AzureADDirectoryRoleTemplate | Where-Object {$_.displayName -eq 'User Account Administrator'} Enable-AzureADDirectoryRole -RoleTemplateId $roleTemplate.ObjectId # Fetch User Account Administrator role instance again $role = Get-AzureADDirectoryRole | Where-Object {$_.displayName -eq 'User Account Administrator'} } # Add user to role Add-AzureADDirectoryRoleMember -ObjectId $role.ObjectId -RefObjectId $AppObjectId
Vous avez maintenant tout le nécessaire pour la gestion de vos utilisateurs Azure AD avec l'API Graph Microsoft. Dans un prochain article, je vous montrerai comment l'enrichir avec les openExtension.
Commentaires