Introduction à Microsoft Graph PowerShell — Gérer Microsoft 365 après la mise hors service d'AzureAD et de MSOnline
· Go Komura · PowerShell, Microsoft 365, Microsoft Entra ID, Microsoft Graph, Systèmes d'information, Automatisation, Amélioration opérationnelle, Sécurité
Pour les équipes qui automatisent l’exploitation de Microsoft 365, le plus grand changement entre 2024 et 2025 a été la mise hors service des modules AzureAD et MSOnline. Un nombre non négligeable de services informatiques avaient écrit des traitements standards — « traitement des départs », « inventaire des licences », « création de comptes pour les nouveaux arrivants » — avec Get-MsolUser ou Get-AzureADUser, et ces scripts cessent de fonctionner les uns après les autres.
La cible de migration est le SDK Microsoft Graph PowerShell. Mais il ne s’agit pas d’un simple remplacement de noms de commandes. La logique d’authentification (scopes et consentement), la manière de récupérer les données (filtrage et pagination OData), la construction de l’exécution sans surveillance (enregistrement d’application et certificat) — les prémisses de conception changent en profondeur. Remplacer mécaniquement sans comprendre cela fait apparaître d’autres problèmes : « ça fonctionne, mais avec des droits excessifs », « sur un tenant volumineux, on n’obtient qu’une partie des résultats ».
Cet article s’adresse aux équipes informatiques qui automatisent avec PowerShell l’exploitation de Microsoft 365 en interne, et rassemble sous une forme directement exploitable : la chronologie de la mise hors service, la connexion et la conception des scopes, la configuration de l’exécution sans surveillance, ainsi que les trois recettes classiques — inventaire, agrégation des licences et traitement des départs.
1. Pour commencer, la conclusion
- MSOnline et AzureAD sont devenus obsolètes le 30 mars 2024 ; MSOnline a cessé d’être fourni le 30 mai 2025, et AzureAD a été mis hors service après la fin de son support le 30 mars 2025.1
- La cible de migration est le SDK Microsoft Graph PowerShell, ou Microsoft Entra PowerShell construit par-dessus (disponibilité générale en mars 2025). Ce dernier est orienté scénarios et propose aussi une option de compatibilité qui facilite la migration depuis AzureAD.2
Microsoft.Graphest un métamodule. L’installer en entier est lourd, donc en pratique on installe seulementMicrosoft.Graph.Authenticationplus les sous-modules des charges de travail utilisées.3- La connexion commence par
Connect-MgGraph -Scopes. Les scopes doivent suivre le moindre privilège. Les droits nécessaires se vérifient avecFind-MgGraphPermission, et le module d’appartenance d’une commande avecFind-MgGraphCommand.45 - L’exécution sans surveillance repose sur l’authentification propre à l’application, via un enregistrement d’application et un certificat. La connexion se fait sans interaction avec
-ClientId,-TenantIdet-CertificateThumbprint. Le certificat est recommandé plutôt qu’un secret client.46 - Les droits nécessaires diffèrent entre le délégué (Delegated) et l’application propre (Application). Le scope demandé change même pour la même opération, donc il faut réattribuer les droits lors du passage à l’exécution sans surveillance.6
- Pour la liste complète,
-All; pour le filtrage,-Filter; pour les propriétés,-Property. Filtrer avec unWhere-Objectcôté client entraîne des récupérations inutiles et un risque de régulation.7 - Un volume d’accès élevé est soumis à une régulation (throttling). Pour une réponse 429, la recommandation officielle est d’attendre selon la valeur de
Retry-After.8 - Séparez les enregistrements d’applications par usage. « Une seule application qui fait tout » voit ses droits enfler, et l’étendue de l’impact en cas d’incident s’élargit d’autant.
2. Chronologie de la mise hors service, et que choisir aujourd’hui
Récapitulons d’abord les faits. Le contenu de cet article correspond à la situation de juillet 2026. Les dates ci-dessous sont toutes des échéances de mise hors service publiées par Microsoft, et sont déjà passées.1
| Module | État |
|---|---|
MSOnline (Get-MsolUser, entre autres) |
Devenu obsolète le 30 mars 2024. Mis hors service le 30 mai 2025 |
AzureAD (Get-AzureADUser, entre autres) |
Devenu obsolète le 30 mars 2024. Support terminé le 30 mars 2025, puis mis hors service |
| Microsoft Graph PowerShell SDK | Actuel. Transposition directe de l’API Graph en applets de commande |
| Microsoft Entra PowerShell | Disponibilité générale en mars 2025. Module orienté scénarios, construit par-dessus le SDK Graph2 |
Voici un repère pour choisir. Si vous voulez manipuler directement la structure de l’API Graph, ou toucher un large éventail de charges de travail (Exchange, Teams, Intune, etc.), optez pour le SDK Graph PowerShell. Si votre priorité est la gestion des identités dans Entra ID (ex-Azure AD) et que vous voulez faciliter au maximum la migration depuis le module AzureAD, choisissez Microsoft Entra PowerShell. Ce dernier interopère avec le SDK Graph PowerShell et propose aussi une option de compatibilité ascendante qui facilite la migration depuis le module AzureAD.2
Cet article se concentre sur le SDK Graph PowerShell, plus polyvalent et mieux documenté.
3. Installation — ne pas installer le métamodule en entier
Microsoft.Graph est un métamodule qui regroupe un grand nombre de sous-modules. L’installer en entier est lourd, aussi bien à l’installation qu’au chargement, et selon l’environnement d’exécution, le seul chargement peut prendre plusieurs dizaines de secondes.3
# [Lourd] Installer toutes les charges de travail
Install-Module Microsoft.Graph -Scope CurrentUser
# [En pratique] Installer uniquement l'authentification + les charges de travail utilisées
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser # Indispensable
Install-Module Microsoft.Graph.Users -Scope CurrentUser # Utilisateurs
Install-Module Microsoft.Graph.Groups -Scope CurrentUser # Groupes
Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser # Licences, etc.
Install-Module Microsoft.Graph.Users.Actions -Scope CurrentUser # Opérations sur les utilisateurs
# (Revoke-MgUserSignInSession, etc.)
# Trouver à quel module appartient une commande et quels droits sont nécessaires
Find-MgGraphCommand -Command Get-MgUser | Select-Object Module, Permissions -First 1
Find-MgGraphPermission user.read -PermissionType Delegated
L’utilisation avec PowerShell 7 est recommandée. Cela fonctionne aussi avec Windows PowerShell 5.1, mais c’est un domaine où les raisons de choisir 7 sont importantes, tant du côté des performances que de la pérennité (« Différences entre Windows PowerShell 5.1 et PowerShell 7 »).3
4. Connexion et scopes — abandonner le réflexe « ReadWrite.All par défaut »
4.1 D’abord, aligner le vocabulaire
Définissons brièvement au préalable les termes qui reviendront souvent par la suite.
| Terme | Signification |
|---|---|
| Délégué (Delegated) | Mode d’exécution en tant que l’utilisateur connecté lui-même. Ce qu’il est réellement possible de faire est déterminé par la combinaison du « scope consenti à l’application » et du « rôle détenu par cet utilisateur »6 |
| Propre à l’application (Application) | Mode d’exécution en tant que l’application elle-même, sans passer par un utilisateur. C’est ce mode qui convient aux traitements par lots sans surveillance. Authentification par certificat ou secret client6 |
| Enregistrement d’application / principal de service | L’enregistrement d’application est la définition de l’application. Le principal de service en est la matérialisation dans le tenant, et c’est à lui que sont rattachés les droits et les rôles |
| Scope (autorisation d’accès) | Nom de droit comme User.Read.All. Le mode délégué et le mode application sont des catégories distinctes : même avec le même nom, il faut réattribuer le droit6 |
| OData | Open Data Protocol. C’est le socle de la syntaxe de requête de Graph : -Filter, -Property, etc. sont convertis en options de requête OData correspondantes et traités côté serveur7 |
| Régulation (throttling) | Mécanisme par lequel le service refuse délibérément les requêtes lorsqu’elles sont trop nombreuses. Renvoyé sous forme de HTTP 429 avec l’en-tête Retry-After8 |
| Évaluation d’accès continue (CAE) | Mécanisme par lequel la ressource reçoit un événement (comme une révocation) et coupe l’accès sans attendre l’expiration du jeton. Ne fonctionne qu’avec les applications et ressources qui le prennent en charge9 |
La différence entre ces deux modes est, dans cet article, le point qui mène le plus souvent à des incidents. Sous forme de schéma, cela donne ceci.
flowchart TD
subgraph D["Délégué (Delegated) — connexion interactive"]
U["L'administrateur se connecte"] --> DS["Scope délégué consenti"]
U --> DR["Rôle d'administrateur détenu par la personne"]
DS --> DX["Ce qui est possible =<br/>intersection du scope et du rôle"]
DR --> DX
end
subgraph A["Propre à l'application (Application) — exécution sans surveillance"]
C["Authentification par certificat"] --> AS["Autorisation d'application<br/>consentement de l'administrateur requis"]
AS --> AX["Ce qui est possible =<br/>exactement l'autorisation accordée"]
end
DX -.->|"Lors du passage au mode sans surveillance,<br/>réattribuer les droits"| AS
Il n’y a qu’une chose à retenir. Délégué = exécution en tant que l’utilisateur lui-même ; propre à l’application = traitement par lots sans surveillance. En mode délégué, « ce que la personne ne peut pas faire, l’application ne peut pas le faire non plus » ; en mode propre à l’application, « l’application peut toujours faire exactement ce qui lui a été accordé ».
4.2 Connexion interactive
La connexion interactive se fait avec Connect-MgGraph -Scopes. Un écran de consentement apparaît pour les scopes indiqués, et le résultat du consentement est enregistré dans le tenant.4
# Usage d'inventaire en lecture seule. Ne pas demander de droits d'écriture
Connect-MgGraph -Scopes 'User.Read.All', 'Organization.Read.All' -NoWelcome
Get-MgContext | Format-List Account, TenantId, Scopes, AuthType # Vérifier la connexion actuelle
Disconnect-MgGraph
C’est le point où la différence est la plus marquée lors de la migration. À l’époque de Get-MsolUser, « se connecter avec un compte administrateur permettait tout de faire » ; avec Graph, en revanche, chaque opération a un scope défini, et le script ne fonctionne que dans les limites du consentement donné. Ce n’est pas une contrainte, c’est un dispositif de sécurité. Un incident où un script d’inventaire effectuerait par erreur une écriture ne peut pas se produire si seul .Read.All a été consenti.
Il y a trois principes.
- Ne demandez pas de scope d’écriture pour un usage de lecture (si
User.Read.Allsuffit, ne demandez pasUser.ReadWrite.All) - Séparez les enregistrements d’applications par usage (inventaire, création de comptes, gestion des licences)
- Le consentement doit être donné consciemment par l’administrateur (un consentement une fois accordé reste enregistré dans le tenant)
Lorsque vous ne savez pas quel scope est nécessaire, recherchez des candidats avec Find-MgGraphPermission, et vérifiez les droits requis par une commande avec Find-MgGraphCommand.5
5. Exécution sans surveillance — enregistrement d’application et certificat
Pour une exécution nocturne depuis le planificateur de tâches, la connexion interactive n’est pas utilisable. Il faut basculer vers l’authentification propre à l’application, via un enregistrement d’application (principal de service) et un certificat.6
La procédure comporte quatre étapes. C’est le point où l’on bute le plus souvent en pratique, donc les emplacements dans l’interface et les commandes sont détaillés de façon concrète.6
5.1 Enregistrer l’application
Dans le centre d’administration Microsoft Entra (https://entra.microsoft.com), suivez le chemin suivant.
Identité > Applications > Inscriptions d’applications > Nouvelle inscription
Saisissez un nom (par exemple M365-Inventory-Batch), choisissez « Comptes dans cet annuaire d’organisation uniquement (locataire unique) » comme type de compte pris en charge, puis cliquez sur « S’inscrire ». L’URI de redirection n’a pas besoin d’être configurée si l’usage se limite à une exécution sans surveillance depuis un script.
Notez les deux valeurs suivantes affichées sur la page « Vue d’ensemble » après l’inscription. Ce sont celles qui serviront à la connexion.
| Nom affiché sur la page Vue d’ensemble | Paramètre de Connect-MgGraph |
|---|---|
| ID d’application (client) | -ClientId |
| ID de l’annuaire (locataire) | -TenantId |
Notez que les noms de navigation du centre d’administration Entra peuvent être renommés. Même si le libellé du menu de gauche change, le nom de la page à laquelle vous devez aboutir est « Inscriptions d’applications ». Utilisez ce repère.
5.2 Ajouter des autorisations d’application et donner le consentement de l’administrateur
Dans l’écran de la même application, suivez le chemin suivant.
Gérer > Autorisations API > Ajouter une autorisation > Microsoft Graph > Autorisations d’application
Le point essentiel ici est de choisir « Autorisations d’application ». Si vous choisissez à côté « Autorisations déléguées », cela ne fonctionnera pas en exécution sans surveillance. Cochez les droits nécessaires (par exemple User.Read.All pour un inventaire), puis validez avec « Ajouter des autorisations ».
À ce stade, ce n’est pas encore utilisable. En haut du même écran,
« Accorder un consentement admin pour (nom du tenant) »
cliquez sur ce bouton pour valider. Une fois cliqué, c’est terminé lorsque la colonne « État » de la liste affiche « Accordé pour (nom du tenant) ». Oublier cette étape et se retrouver avec « les droits sont ajoutés mais l’exécution échoue avec une erreur 403 » est un blocage classique.
5.3 Créer, téléverser et placer le certificat
Un certificat auto-signé suffit. La création se fait avec New-SelfSignedCertificate en PowerShell.
# [1] Créer le certificat (validité de 2 ans. À ajuster selon l'exploitation)
$cert = New-SelfSignedCertificate `
-Subject 'CN=M365-Inventory-Batch' `
-CertStoreLocation 'Cert:\CurrentUser\My' `
-KeySpec Signature `
-KeyExportPolicy Exportable `
-KeyAlgorithm RSA `
-KeyLength 2048 `
-HashAlgorithm SHA256 `
-NotAfter (Get-Date).AddYears(2)
# [2] Noter l'empreinte (thumbprint) utilisée pour la connexion
$cert.Thumbprint
# [3] Exporter uniquement la clé publique en .cer pour le téléversement (la clé privée n'est pas incluse)
Export-Certificate -Cert $cert -FilePath 'C:\temp\M365-Inventory-Batch.cer'
Téléversez le .cer exporté depuis l’écran de l’application dans le centre d’administration Entra,
Gérer > Certificats et secrets > Onglet Certificats > Charger un certificat
Seul le .cer (clé publique) doit être téléversé. Ne téléversez jamais un .pfx contenant la clé privée.
L’emplacement de la clé privée doit correspondre au compte d’exécution du planificateur de tâches.
| Compte d’exécution | Magasin de certificats | Façon de le passer à la connexion | Remarque |
|---|---|---|---|
| Utilisateur spécifique / compte de service | Cert:\CurrentUser\My (créé/importé avec ce compte) |
-CertificateThumbprint |
Invisible depuis un compte autre que celui qui l’a créé |
| SYSTEM, ou utilisé depuis plusieurs comptes | Cert:\LocalMachine\My |
-Certificate (à charger et passer soi-même) |
La création nécessite des droits d’administrateur. Accorder au compte d’exécution l’autorisation de lecture de la clé privée |
Ce qui échappe facilement ici, c’est que -CertificateThumbprint et -CertificateSubjectName ne cherchent que dans le magasin de certificats de l’utilisateur courant.4 Si le certificat est placé dans Cert:\LocalMachine\My, il ne sera pas trouvé même en passant l’empreinte. Si vous utilisez le magasin de l’ordinateur local, chargez-le vous-même et passez-le via -Certificate.
# Si placé dans LocalMachine, procéder ainsi
$cert = Get-ChildItem -Path 'Cert:\LocalMachine\My\A1B2C3D4E5F6...'
Connect-MgGraph -ClientId $clientId -TenantId $tenantId -Certificate $cert -NoWelcome
Le problème du type « ça fonctionnait en local mais le certificat n’est introuvable uniquement depuis le planificateur de tâches » vient presque toujours de cette incohérence entre l’emplacement et la façon de le passer.
5.4 Se connecter depuis un script
# Connexion pour exécution sans surveillance (sans interaction)
$connect = @{
ClientId = '11111111-2222-3333-4444-555555555555'
TenantId = '66666666-7777-8888-9999-000000000000'
CertificateThumbprint = 'A1B2C3D4E5F6...' # Certificat présent dans le magasin CurrentUser du compte d'exécution
NoWelcome = $true
}
Connect-MgGraph @connect
# Vérifier le mode de connexion : si AuthType vaut AppOnly, la connexion propre à l'application a réussi
Get-MgContext | Format-List AppName, ClientId, TenantId, AuthType, Scopes
try {
# Traitement métier
}
finally {
Disconnect-MgGraph
}
La première fois, avant d’enregistrer réellement la tâche, exécutez le script ci-dessus avec le compte d’exécution du planificateur de tâches pour vérifier. Les pièges côté planificateur de tâches (compte d’exécution, paramètre « Exécuter que l’utilisateur soit connecté ou non », répertoire de travail, etc.) sont détaillés dans « Les tâches du planificateur de tâches ne s’exécutent pas et se terminent avec le code 0x1 ».
5.5 Points de vigilance
Il y a deux points de vigilance.
(1) Les droits nécessaires diffèrent entre le mode délégué et le mode propre à l’application. Lorsque vous rendez sans surveillance un script qui fonctionnait de manière interactive avec -Scopes 'User.Read.All', il faut réattribuer côté enregistrement d’application le même type de droit sous forme d’autorisation d’application, avec consentement de l’administrateur.6
(2) Le certificat a une date d’expiration. Voir tous les traitements nocturnes s’effondrer le jour même de l’expiration est un incident réellement fréquent. Inscrivez la date d’expiration dans un calendrier et documentez la procédure de renouvellement. Pour le stockage des identifiants, consultez aussi « Gérer les identifiants en toute sécurité avec PowerShell ». Un secret client permet aussi de se connecter, mais en raison du risque de le faire circuler en clair et de la complexité de la gestion de son expiration, le certificat est recommandé.
6. Récupérer les données — -All / -Filter / -Property
Graph est une API conçue autour de la pagination. Par défaut, seule une page est renvoyée ; si vous avez besoin de tous les résultats, ajoutez -All.7
# [À éviter] Filtrer côté client sans tenir compte de la pagination (lent, récupération excessive, cause de régulation)
Get-MgUser | Where-Object { $_.Department -eq 'Ventes' }
# [Correct] Filtrer côté serveur, ne récupérer que les propriétés nécessaires, obtenir toutes les pages
Get-MgUser -All -Filter "department eq 'Ventes'" `
-Property Id, DisplayName, UserPrincipalName, AccountEnabled, Department |
Select-Object DisplayName, UserPrincipalName, AccountEnabled
Il y a trois points clés.
-Filterest une expression OData qui filtre côté serveur.Where-Objectfiltre en local, ce qui revient à tout récupérer avant d’écarter- Restreindre les colonnes avec
-Propertyallège la réponse. Certaines propriétés non renvoyées par défaut le sont si vous les indiquez explicitement - Les propriétés récupérées avec
-Propertydoivent aussi figurer dansSelect-Object. La récupération et l’affichage sont distincts ; n’en préciser qu’un seul laisse des colonnes vides
Pour des requêtes avancées comme startsWith ou endsWith, ou lorsque vous voulez uniquement un décompte, combinez -ConsistencyLevel eventual avec -CountVariable.7
# Ne compter que le nombre d'utilisateurs actifs (obtenir le décompte sans tout récupérer)
Get-MgUser -Filter 'accountEnabled eq true' -ConsistencyLevel eventual -CountVariable total -Top 1 | Out-Null
"Utilisateurs actifs : $total"
-Top 1 | Out-Null est un idiome peu familier, précisons-le. Ce qui est placé dans -CountVariable est le nombre total d’éléments correspondant à la condition, indépendamment de la valeur de -Top. Ce que détermine -Top, c’est uniquement le nombre d’objets renvoyés dans une réponse. Autrement dit, -Top 1 n’est pas une instruction « ne compter qu’un seul élément » : c’est une instruction visant à minimiser les données réelles renvoyées, pour une requête qu’il faut de toute façon envoyer une fois pour obtenir le décompte. En l’omettant, une page entière d’objets utilisateur (selon la valeur par défaut) serait renvoyée, pour être ensuite jetée avec Out-Null. Notez que -ConsistencyLevel eventual est une indication obligatoire pour utiliser -CountVariable ou des requêtes avancées comme startsWith.7
Un accès massif entraîne une régulation (throttling), et une réponse HTTP 429 est renvoyée. La recommandation officielle est d’« attendre selon le nombre de secondes indiqué dans l’en-tête Retry-After, puis de réessayer ».8 Les applets de commande du SDK effectuent une certaine quantité de nouvelles tentatives en interne, mais lorsque vous exécutez une boucle sur plusieurs milliers d’éléments, il est plus sûr de réduire d’abord le nombre de récupérations elles-mêmes (filtrer avec -Filter et -Property, obtenir les informations nécessaires en une seule fois). Pour la conception des tentatives elle-même, voir « Gestion des erreurs et conception des tentatives dans PowerShell ».
7. Trois recettes classiques
(1) Exporter l’inventaire des utilisateurs en CSV
# SignInActivity (date de dernière connexion) ne peut pas être récupéré avec User.Read.All seul ;
# AuditLog.Read.All est nécessaire en plus. Si vous ne l'utilisez pas, retirez-le de -Property
Connect-MgGraph -Scopes 'User.Read.All', 'AuditLog.Read.All' -NoWelcome
$users = Get-MgUser -All -Property Id, DisplayName, UserPrincipalName, AccountEnabled,
Department, JobTitle, CreatedDateTime, SignInActivity |
Select-Object DisplayName, UserPrincipalName, Department, JobTitle, AccountEnabled,
@{ n = 'DateCreation'; e = { $_.CreatedDateTime } },
# Pour détecter les comptes dormants, utiliser la "connexion réussie". LastSignInDateTime
# correspond à une "tentative" de connexion interactive, incluant les échecs, hors non interactif
@{ n = 'DerniereConnexionReussie'; e = { $_.SignInActivity.LastSuccessfulSignInDateTime } },
@{ n = 'DerniereTentativeConnexionInteractive'; e = { $_.SignInActivity.LastSignInDateTime } },
@{ n = 'DerniereConnexionNonInteractive'; e = { $_.SignInActivity.LastNonInteractiveSignInDateTime } }
# Un CSV contenant des caractères accentués s'ouvre correctement dans Excel s'il est encodé en UTF-8 (avec BOM).
# Le nom de l'encodage diffère selon la version : utf8BOM à partir de 7, UTF8 (avec BOM) en 5.1
$enc = if ($PSVersionTable.PSVersion.Major -ge 6) { 'utf8BOM' } else { 'UTF8' }
$users | Export-Csv -Path "D:\Inventaire\users_$(Get-Date -f yyyyMMdd).csv" -Encoding $enc -NoTypeInformation
La structure des colonnes du CSV produit reprend directement, comme ligne d’en-tête, l’ordre et les noms indiqués dans Select-Object. Comme Export-Csv entoure par défaut tous les champs de guillemets, la première ligne se présente ainsi.
"DisplayName","UserPrincipalName","Department","JobTitle","AccountEnabled","DateCreation","DerniereConnexionReussie","DerniereTentativeConnexionInteractive","DerniereConnexionNonInteractive"
À partir de la deuxième ligne, les valeurs de chaque utilisateur s’alignent dans le même ordre. Si les colonnes attendues restent vides, c’est que vous avez oublié de l’écrire dans -Property ou dans Select-Object, ou que les droits sont insuffisants. En particulier, si les trois colonnes à partir de DerniereConnexionReussie sont toutes vides, suspectez le manque de AuditLog.Read.All évoqué ci-dessous.
SignInActivity est efficace pour repérer les comptes dormants. Mais le champ que vous regardez est déterminant. LastSignInDateTime enregistre une tentative de connexion interactive (y compris les échecs), sans inclure les connexions non interactives des applications ou services. Se fier uniquement à ce champ peut faire apparaître comme « utilisé » un compte qui n’a subi que des tentatives de connexion échouées par un attaquant, ou faire paraître « dormant » un compte de service réellement actif. Pour détecter les comptes dormants, utilisez LastSuccessfulSignInDateTime, qui reflète les connexions interactives et non interactives réussies.10 Cependant, cette seule propriété n’est pas accessible avec User.Read.All : AuditLog.Read.All est nécessaire en plus (avec, en outre, des exigences de licence côté tenant). Si les droits sont insuffisants, vous obtenez soit une erreur, soit une valeur vide ; retirez-la de -Property si vous ne l’utilisez pas. Les droits nécessaires peuvent être vérifiés avec Find-MgGraphPermission.10 La gestion de l’encodage des CSV est détaillée dans « Automatiser Excel et le traitement des CSV avec PowerShell ».
(2) Agréger la consommation des licences
Connect-MgGraph -Scopes 'Organization.Read.All' -NoWelcome
Get-MgSubscribedSku | Select-Object `
SkuPartNumber,
@{ n = 'Achete'; e = { $_.PrepaidUnits.Enabled } },
@{ n = 'Attribue'; e = { $_.ConsumedUnits } },
@{ n = 'Disponible'; e = { $_.PrepaidUnits.Enabled - $_.ConsumedUnits } } |
Sort-Object Disponible
« Racheter des licences alors qu’il en restait de disponibles » ou « des licences de personnes parties non libérées » sont des situations qu’une exécution mensuelle suffit à éviter.
(3) Traitement des départs (blocage de la connexion et révocation de session)
Le code suivant est un exemple d’exécution en mode « délégué (connexion interactive) ». Le fait d’indiquer -Scopes en est le signe, et l’exécution demandera une connexion dans un navigateur. Pour en faire une exécution sans surveillance, il faut la reconstruire en mode propre à l’application, comme indiqué au chapitre 5. Récapitulons d’abord la différence entre les deux.
| Délégué (exemple de code ci-dessous) | Propre à l’application (exécution sans surveillance) | |
|---|---|---|
| En tant que qui | La personne elle-même, connectée | L’application (principal de service) elle-même |
Paramètres de Connect-MgGraph |
-ClientId + -Scopes |
-ClientId + -TenantId + -CertificateThumbprint |
| Certificat | Non requis | Requis (ou un secret client) |
| Type de droit | Autorisation déléguée | Autorisation d’application (consentement de l’administrateur requis)6 |
| Ce qui est nécessaire en plus | Le rôle d’administrateur Entra de la personne connectée elle-même (détaillé plus loin)11 | Si la cible est un administrateur, attribution d’un rôle supérieur à l’application elle-même11 |
| Cas d’usage adapté | Traitement ponctuel au cas par cas, vérification préalable avec -WhatIf |
Traitement nocturne, intégration avec le système RH |
# ── Exemple d'exécution en mode délégué (connexion interactive) ──
# L'exécution affiche un écran de connexion. Aucun certificat requis
#
# Comme cela implique de l'écriture, exécuter avec un enregistrement d'application
# et un scope dédiés. Omettre -ClientId connecte via l'application partagée par défaut
# du SDK, et les droits consentis s'accumulent alors sur cette application (partagée
# au niveau de l'organisation). Ce sont précisément les opérations destructrices,
# comme le traitement des départs, qu'il faut isoler dans un enregistrement d'application dédié
#
# Les droits nécessaires diffèrent par opération, donc demander les deux d'un coup
# Modifier accountEnabled → User.EnableDisableAccount.All (droit minimal)
# Révoquer la session de connexion → User.RevokeSessions.All (droit minimal)
# Les deux sont aussi possibles avec User.ReadWrite.All, mais le droit devient plus large
$connect = @{
ClientId = '99999999-aaaa-bbbb-cccc-dddddddddddd' # Enregistrement d'application dédié au traitement des départs
TenantId = '66666666-7777-8888-9999-000000000000'
Scopes = 'User.Read.All', 'User.EnableDisableAccount.All', 'User.RevokeSessions.All'
NoWelcome = $true
}
Connect-MgGraph @connect
# Revoke-MgUserSignInSession nécessite Microsoft.Graph.Users.Actions
Import-Module Microsoft.Graph.Users.Actions
$upn = 'taro.yamada@example.co.jp'
$user = Get-MgUser -UserId $upn -Property Id, DisplayName, AccountEnabled
# 1. Bloquer la connexion (la suppression n'intervient qu'après un délai de grâce)
Update-MgUser -UserId $user.Id -AccountEnabled:$false
# 2. Révoquer le jeton d'actualisation et le cookie de session du navigateur
# Attention : un jeton d'accès déjà émis peut rester utilisable jusqu'à son expiration (voir plus loin)
Revoke-MgUserSignInSession -UserId $user.Id
Write-Host "Connexion suspendue pour $($user.DisplayName)"
Il y a trois points à retenir ici. Le premier concerne l’enregistrement d’application ciblé par la connexion. Omettre -ClientId connecte via l’application par défaut du SDK Microsoft Graph PowerShell, et les droits consentis sont enregistrés sur cette application partagée au niveau de l’organisation. Pour appliquer le principe du chapitre 4 « séparer les enregistrements d’applications par usage », il faut expliciter votre propre enregistrement d’application via -ClientId.4
Le deuxième concerne les scopes. Dans Graph, chaque opération a un droit défini, et « pouvoir écrire sur les utilisateurs » ne signifie pas « pouvoir tout faire ». Comme la modification d’accountEnabled et la révocation de la session de connexion ont chacune leur propre droit minimal, ne consentir qu’à l’un des deux permet la connexion mais échoue avec une erreur de droit insuffisant sur l’une des commandes intermédiaires. Vérifiez les droits nécessaires à chaque opération avec Find-MgGraphPermission et le tableau des droits de la référence Graph API correspondante.119
Et troisièmement, en exécution déléguée, le scope seul ne suffit pas. Lorsque l’exécution se fait sous la connexion de l’utilisateur lui-même, comme ci-dessus, la modification d’accountEnabled nécessite aussi un rôle d’administrateur Microsoft Entra. En effet, le droit délégué détermine uniquement « ce que l’application peut faire à la place de cet utilisateur », et n’élève jamais les droits propres de l’utilisateur connecté. Même avec le droit consenti, l’exécution échoue avec une erreur 403 si le rôle est absent. La documentation définit Privileged Authentication Administrator comme le rôle minimal permettant de mettre à jour cette propriété pour tous les administrateurs du tenant, et pose en général la règle « un rôle d’administrateur supérieur à la cible est nécessaire ».11 Comme le rôle nécessaire varie selon que la cible du traitement des départs est un utilisateur ordinaire ou un administrateur, décidez à l’avance ce qui sera attribué au compte de la personne en charge de l’exploitation. Même en exécution propre à l’application par certificat, si la cible est un administrateur, l’attribution d’un rôle supérieur à l’application elle-même est nécessaire.11
Si vous voulez faire de ce traitement un lot nocturne, seule la partie connexion change.
# ── Pour la reconstruire en mode propre à l'application (exécution sans surveillance) ──
# Préparation préalable : dans l'enregistrement d'application, sous « Autorisations API > Autorisations d'application »,
# ajouter User.Read.All / User.EnableDisableAccount.All / User.RevokeSessions.All
# et donner le consentement de l'administrateur (5.2). Utiliser le certificat créé en 5.3
$connect = @{
ClientId = '99999999-aaaa-bbbb-cccc-dddddddddddd'
TenantId = '66666666-7777-8888-9999-000000000000'
CertificateThumbprint = 'A1B2C3D4E5F6...' # Ne pas indiquer -Scopes
NoWelcome = $true
}
Connect-MgGraph @connect
# La suite (Import-Module à Revoke-MgUserSignInSession) est strictement identique à la version déléguée
Il faut aussi comprendre précisément l’étendue de l’effet de Revoke-MgUserSignInSession. Ce que cette commande invalide, ce sont le jeton d’actualisation et le cookie de session du navigateur ; un jeton d’accès déjà émis peut rester utilisable jusqu’à l’expiration de sa propre durée de validité.9 Si vous voulez un blocage immédiat, cela suppose une application et une ressource prenant en charge l’évaluation d’accès continue (CAE). Ne partez pas du principe que « la révocation arrête immédiatement tout accès » ; dans les cas critiques, combinez-la avec la désactivation du compte, et prenez en compte le délai avant que l’effet ne se propage.
La pratique établie consiste à éviter la suppression immédiate du compte et à d’abord bloquer la connexion. Supprimer avant que le transfert de la boîte aux lettres ou de OneDrive ne soit terminé complique la récupération. Ce type d’« opération irréversible » est plus sûr lorsqu’il est enveloppé dans une fonction personnalisée prenant en charge -WhatIf, affichant d’abord la liste des cibles pour vérification avant l’exécution (« Conception des paramètres et modularisation en PowerShell »).
8. Bonnes pratiques du terrain (tableau de décision)
| Point | Options | Repère de décision |
|---|---|---|
| Module | Graph SDK / Entra PowerShell | Entra si la priorité est la gestion des identités et la migration depuis AzureAD ; Graph SDK pour un large éventail de charges de travail2 |
| Installation | Tout inclus / Sous-modules uniquement | Par sous-module, pour limiter le temps de démarrage et les dépendances3 |
| Authentification (interactive) | S’en remettre au compte administrateur / Scope minimal | Pour un inventaire, uniquement la famille .Read.. Le consentement reste enregistré dans le tenant4 |
| Authentification (sans surveillance) | Secret client / Certificat | Le certificat est recommandé. Définir d’abord la gestion de l’expiration et la procédure de renouvellement6 |
| Granularité des droits | Une seule application tout-en-un / Un enregistrement d’application par usage | Permet de limiter l’étendue de l’impact en cas d’incident |
| Récupération de listes | Where-Object / -Filter + -All + -Property |
Filtrer côté serveur. Le volume récupéré détermine directement la vitesse et la stabilité7 |
| Contre-mesure au 429 | Nouvelle tentative immédiate / Suivre Retry-After |
Recommandation officielle. Réduire d’abord le nombre de récupérations lui-même8 |
| Opérations à risque | Exécution directe / -WhatIf + vérification préalable de la liste des cibles |
Pour le traitement des départs ou les suppressions en masse, toujours rendre les cibles vérifiables visuellement |
9. Conclusion
- MSOnline et AzureAD sont mis hors service. La cible de migration est le SDK Microsoft Graph PowerShell, ou Microsoft Entra PowerShell construit par-dessus.
Microsoft.Graphétant un métamodule, en pratique on installe seulement l’authentification plus les sous-modules des charges de travail utilisées.- Le principe de la connexion est la minimisation des scopes. Ne demandez pas de droits d’écriture pour un usage d’inventaire, et séparez les enregistrements d’applications par usage.
- L’exécution sans surveillance repose sur un enregistrement d’application et un certificat. Le fait que les droits nécessaires diffèrent entre délégué et propre à l’application, et le fait que le certificat a une date d’expiration, sont les sources d’incidents.
- La récupération des données repose sur le trio
-All(pagination),-Filter(filtrage côté serveur) et-Property(restriction des propriétés). Pour un 429, suivezRetry-After. - Le simple fait d’exécuter mensuellement ces trois traitements — inventaire, agrégation des licences, traitement des départs — réduit considérablement les deux grands risques fréquents que sont le gaspillage de licences et les comptes laissés à l’abandon.
Téléchargement du code d’exemple
Le code présenté dans cet article est distribué sous une forme directement exécutable. Il comprend la connexion, l’extraction des comptes dormants, l’agrégation des licences et le traitement des départs.
Télécharger le code d’exemple (zip)
Les exemples de cet article dépendant de Windows et du tenant, ils n’ont pas été vérifiés par exécution. Une analyse syntaxique et une analyse statique avec PSScriptAnalyzer ont été effectuées sur tous les fichiers, mais vérifiez impérativement le fonctionnement sur votre propre machine de test.
# Analyse syntaxique + analyse statique (exécutable même hors Windows)
./Invoke-SampleTests.ps1
Les valeurs de configuration (chemins, noms de serveurs, ID de tenant, etc.) sont des exemples. Ne les exécutez pas telles quelles en production : adaptez-les à votre propre environnement.
Articles connexes
- Gérer les identifiants en toute sécurité avec PowerShell — Bannir les mots de passe en clair des scripts
- Différences entre Windows PowerShell 5.1 et PowerShell 7 — Guide pratique de migration des scripts internes
- Automatiser Excel et le traitement des CSV avec PowerShell — Recettes pratiques d’agrégation, de rapprochement et d’édition de rapports
- Gestion des erreurs et conception des tentatives dans PowerShell — Des pièges où try/catch échoue jusqu’aux bonnes pratiques de code de sortie et de nouvelle tentative
- Intégrer la connexion Microsoft Entra ID dans une application WinForms / WPF
- Les tâches du planificateur de tâches ne s’exécutent pas et se terminent avec le code 0x1 — Diagnostic des causes et conception d’une exploitation sûre
Domaines de conseil associés
合同会社小村ソフト (Komura Software LLC) prend en charge la migration vers Graph des scripts d’exploitation Microsoft 365, la revue des enregistrements d’applications et de la conception des droits, ainsi que l’automatisation de traitements standards comme l’inventaire ou le traitement des départs.
- Conseil technique et revue de conception
- Migration et valorisation des actifs existants
- Développement de systèmes métier
- Contact
Références
-
Microsoft Community Hub (Microsoft Entra Blog), Action required: MSOnline and AzureAD PowerShell retirement - 2025 info and resources. Sur le fait que les modules PowerShell MSOnline et AzureAD sont tous deux devenus obsolètes le 30 mars 2024, que la mise hors service de MSOnline a eu lieu au printemps 2025 avec un arrêt de la fourniture au 30 mai 2025, qu’AzureAD a vu son support se terminer le 30 mars 2025 avant sa mise hors service, et que la cible de migration est le SDK Microsoft Graph PowerShell ainsi que Microsoft Entra PowerShell. ↩ ↩2
-
Microsoft Learn, What is Microsoft Entra PowerShell?. Sur le fait que Microsoft Entra PowerShell est un module orienté scénarios construit par-dessus le SDK Microsoft Graph PowerShell, qu’il interopère avec les applets de commande du SDK Graph PowerShell, et qu’il propose une option de compatibilité ascendante facilitant la migration depuis le module AzureAD. L’annonce de la disponibilité générale (GA) se trouve dans Microsoft Entra PowerShell module now generally available (mars 2025). ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Install the Microsoft Graph PowerShell SDK. Sur le fait que Microsoft.Graph est un métamodule regroupant un ensemble de sous-modules, qu’il est possible d’installer individuellement uniquement les sous-modules nécessaires, que Microsoft.Graph.Authentication est indispensable à l’authentification, et sur les versions de PowerShell prises en charge. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Connect-MgGraph. Sur la demande d’autorisation déléguée via -Scopes, l’authentification propre à l’application via -ClientId / -TenantId / -CertificateThumbprint, le fait que -CertificateThumbprint et -CertificateSubjectName récupèrent le certificat depuis le magasin de certificats de l’utilisateur courant (charger soi-même le certificat et le passer via -Certificate lorsqu’on utilise le magasin de l’ordinateur local), la vérification des informations de connexion actuelles avec Get-MgContext, et la déconnexion avec Disconnect-MgGraph. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Find Microsoft Graph PowerShell commands and permissions. Sur la recherche, via Find-MgGraphCommand, du module d’appartenance d’une commande, des droits nécessaires et de l’API correspondante, et sur la recherche de noms de droits via Find-MgGraphPermission. ↩ ↩2
-
Microsoft Learn, Use app-only authentication with the Microsoft Graph PowerShell SDK. Sur la procédure en trois étapes — enregistrement d’application, autorisation d’application, consentement de l’administrateur —, la configuration de l’authentification sans surveillance à l’aide d’un certificat, et la différence entre autorisation déléguée et autorisation d’application. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Microsoft Learn, Paging Microsoft Graph data in your app. Sur le fait que les réponses de Microsoft Graph sont paginées, qu’indiquer -All dans le SDK PowerShell permet de récupérer toutes les pages, sur le filtrage côté serveur via -Filter / -Property correspondant à $filter et $select, et sur -ConsistencyLevel eventual et l’obtention d’un décompte pour les requêtes avancées. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Microsoft Graph throttling guidance. Sur le fait qu’une régulation par ressource renvoie un HTTP 429, qu’il convient d’attendre le nombre de secondes indiqué dans l’en-tête Retry-After de la réponse avant de réessayer, et sur le fait qu’une conception réduisant le nombre de requêtes lui-même est recommandée. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, user: revokeSignInSessions (Microsoft Graph API). Sur le fait que User.RevokeSessions.All est cité comme droit minimal pour révoquer une session de connexion, que le consentement de l’administrateur est nécessaire, que l’invalidation porte sur le jeton d’actualisation et le cookie de session du navigateur, qu’un jeton d’accès déjà émis peut rester utilisable jusqu’à son expiration (l’évaluation d’accès continue intervenant pour une prise d’effet immédiate). ↩ ↩2 ↩3
-
Microsoft Learn, signInActivity resource type. Sur le fait que la récupération de la propriété signInActivity d’un utilisateur nécessite à la fois AuditLog.Read.All et User.Read.All, sur les exigences de licence du tenant, et sur le fait que lastSignInDateTime représente une tentative de connexion interactive (succès et échecs inclus), tandis que lastSuccessfulSignInDateTime représente les connexions interactives et non interactives réussies, et lastNonInteractiveSignInDateTime les connexions non interactives. ↩ ↩2
-
Microsoft Learn, Update user (Microsoft Graph API). Sur le fait que les droits nécessaires à la mise à jour d’un utilisateur sont définis propriété par propriété, que User.EnableDisableAccount.All est cité comme droit minimal pour modifier accountEnabled, et qu’il est aussi possible d’utiliser le plus large User.ReadWrite.All. Sur le fait qu’en scénario délégué, un rôle d’administrateur Microsoft Entra est nécessaire en plus du scope adapté, que Privileged Authentication Administrator est le rôle minimal permettant de mettre à jour accountEnabled pour tous les administrateurs du tenant, qu’en général un rôle d’administrateur supérieur à la cible est requis, et qu’en scénario propre à l’application, si la cible est un administrateur, l’attribution d’un rôle d’administrateur supérieur à l’application est aussi nécessaire. ↩ ↩2 ↩3 ↩4 ↩5
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Automatiser le déploiement de postes avec winget et PowerShell — Rendre le manuel de procédure exécutable
Ce guide explique comment rendre reproductible la configuration des PC des nouveaux employés : installation d'applications et export/impo...
Renforcer la sécurité de PowerShell — journalisation, AMSI, mode de langage, JEA
Ce guide rassemble la pratique pour utiliser PowerShell en toute sécurité sans l'interdire : activation de la journalisation des blocs de...
Distribuer et mettre à jour des modules PowerShell en interne — PSResourceGet et le dépôt interne
Ce guide explique comment sortir de la pratique consistant à copier et réutiliser des fichiers .ps1 depuis un dossier partagé. Il couvre ...
Gérer les informations d'identification en toute sécurité sous PowerShell — bannir les mots de passe en clair de vos scripts
Un guide pratique pour faire migrer les mots de passe en clair d'un script PowerShell vers un stockage sécurisé : la réalité et les limit...
Introduction à PowerShell Remoting (WinRM) ── gérer plusieurs machines Windows en une seule fois
Une introduction à la gestion en masse de plusieurs machines Windows avec PowerShell Remoting (WinRM). Le fonctionnement, les ports 5985/...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Les modules AzureAD et MSOnline sont-ils encore utilisables ?
- Non, les deux sont mis hors service. Les modules MSOnline et AzureAD sont tous deux devenus obsolètes (deprecated) le 30 mars 2024 ; MSOnline a cessé d'être fourni le 30 mai 2025, et le support d'AzureAD s'est terminé le 30 mars 2025 avant sa mise hors service. Même si un script semble encore fonctionner, il peut s'arrêter à tout moment. La cible de migration est le SDK Microsoft Graph PowerShell, ou le module Microsoft Entra PowerShell construit par-dessus (disponibilité générale en mars 2025).
- L'installation du module Microsoft.Graph est lourde. Peut-on l'alléger ?
- Oui. Microsoft.Graph est un métamodule qui regroupe un grand nombre de sous-modules, donc l'installer en entier prend du temps, aussi bien à l'installation qu'au chargement. En pratique, il est plus réaliste de n'installer que les sous-modules correspondant aux charges de travail utilisées : Microsoft.Graph.Users pour la gestion des utilisateurs, Microsoft.Graph.Groups pour les groupes, et Microsoft.Graph.Authentication, indispensable, pour l'authentification. Vous pouvez déterminer à quel module appartient une commande avec Find-MgGraphCommand.
- Je veux exécuter le script sans surveillance depuis le planificateur de tâches, mais une connexion interactive apparaît.
- Basculez vers une authentification propre à l'application, via un enregistrement d'application (principal de service) et un certificat. Enregistrez une application dans Microsoft Entra ID, accordez le consentement de l'administrateur aux autorisations d'application (Application permissions) nécessaires, puis passez -ClientId, -TenantId et -CertificateThumbprint à Connect-MgGraph pour vous connecter sans interaction. Un certificat est plus sûr qu'un secret client, et la gestion de l'expiration est aussi plus claire. Placez le certificat dans le magasin de certificats du compte d'exécution, et définissez impérativement une procédure de renouvellement avant son expiration.
- Que faut-il indiquer dans -Scopes de Connect-MgGraph ?
- N'indiquez que le minimum de droits requis par les commandes que vous voulez exécuter. Ce qui est nécessaire peut se vérifier avec Find-MgGraphPermission ou la documentation de la commande ; pour de la simple lecture, un droit de la famille .Read., comme User.Read.All, suffit. Si l'objectif est un inventaire ou un audit, ne demandez pas de droits d'écriture. Comme un droit une fois consenti reste enregistré dans le tenant, « consentir provisoirement à Directory.ReadWrite.All » constitue un risque futur. Il est plus sûr de séparer les enregistrements d'applications par usage, et de séparer les droits en conséquence.
- Avec Get-MgUser, sur un tenant comportant beaucoup d'objets, je n'obtiens qu'une partie des résultats.
- C'est parce que les réponses de Microsoft Graph sont paginées. Ajouter -All parcourt automatiquement toutes les pages. De plus, une récupération massive peut déclencher une régulation (throttling) et renvoyer une réponse 429 ; la base consiste donc à restreindre les propriétés avec -Property, et à confier le filtrage au serveur via OData (-Filter) plutôt qu'à un Where-Object côté client. Si vous voulez uniquement un décompte, la combinaison -ConsistencyLevel eventual et -CountVariable permet d'obtenir uniquement le nombre.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.