Une migration de module n’est pas un rechercher-remplacer
AzureAD, AzureADPreview et MSOnline sont dépréciés. Microsoft Graph PowerShell est leur successeur, mais les anciens scripts ne deviennent pas compatibles en renommant simplement leurs commandes. Les paramètres, les permissions, les filtres, la pagination et la forme des objets retournés peuvent tous changer.
Le risque se trouve surtout dans les scripts silencieux : création de comptes, licences, groupes, révocation de sessions et rapports planifiés. Une migration sûre commence par l’inventaire, reproduit d’abord les lectures, compare les résultats, puis déplace les écritures derrière un mode simulation et une procédure de retour arrière.
1. Inventorier les scripts et leur niveau de risque
Recherchez les imports, les connexions et les commandes AzureAD ou MSOnline dans les dépôts, tâches planifiées, runbooks, serveurs de gestion et outils du centre de services. Pour chaque script, documentez le propriétaire, la fréquence, l’identité d’exécution, les actions d’écriture, les permissions et la conséquence d’un échec.
Priorisez les rapports simples avant les automatisations critiques. Un script de lecture permet d’apprendre les objets Graph sans risquer de retirer une licence ou de modifier un groupe dynamique.
$root = 'C:\Scripts'
$patterns = 'AzureAD','MSOnline','Connect-AzureAD','Connect-MsolService',`
'Get-AzureAD','Set-AzureAD','New-AzureAD','Remove-AzureAD',`
'Get-Msol','Set-Msol','New-Msol','Remove-Msol'
Get-ChildItem $root -Filter *.ps1 -Recurse -File |
Select-String -Pattern $patterns -SimpleMatch |
Select-Object Path,LineNumber,Line |
Export-Csv "$root\migration-graph-inventory.csv" -NoTypeInformation -Encoding UTF82. Installer seulement les modules nécessaires
PowerShell 7 est recommandé, même si le SDK prend aussi en charge Windows PowerShell 5.1. Le module Microsoft.Graph complet contient des dizaines de sous-modules; sur un serveur d’automatisation, installez plutôt ceux réellement utilisés afin de réduire le temps de chargement et de mieux contrôler les mises à jour.
Privilégiez l’API v1.0 en production. Microsoft.Graph.Beta est un module distinct et ses contrats peuvent changer; réservez-le aux fonctions qui n’existent réellement pas encore dans v1.0 et isolez ce choix dans le code.
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser
Install-Module Microsoft.Graph.Users -Scope CurrentUser
Install-Module Microsoft.Graph.Groups -Scope CurrentUser
Install-Module Microsoft.Graph.Identity.SignIns -Scope CurrentUser
Find-MgGraphCommand -Command Get-MgUser
Find-MgGraphCommand -Uri '/users/{id}' -Method GET
Find-MgGraphPermission -SearchString 'Get-MgUser'3. Choisir l’authentification selon le scénario
Connect-MgGraph utilise MSAL et prend en charge les permissions déléguées et d’application. Une tâche non interactive ne doit pas dépendre du jeton personnel d’un administrateur. Utilisez une identité managée lorsque la plateforme le permet, sinon une application dédiée avec certificat. Ne réutilisez pas une seule application sur tous les scripts : une compromission donnerait toutes leurs permissions combinées.
| Scénario | Méthode | Choix de sécurité |
|---|---|---|
| Administration interactive | Permissions déléguées | Scopes minimaux, connexion humaine et ContextScope Process |
| Azure Automation ou ressource Azure | Identité managée | Aucun secret à distribuer; permissions d’application minimales |
| Tâche planifiée hors Azure | Application et certificat | Clé privée protégée, rotation documentée et consentement administrateur |
| Secret client en clair | À éviter | Secret facilement copié, expiré ou exposé dans les journaux |
# Administration interactive
Connect-MgGraph -Scopes 'User.Read.All','Group.Read.All' -ContextScope Process
# Tâche hors Azure avec certificat
Connect-MgGraph -TenantId $TenantId -ClientId $ClientId `
-CertificateThumbprint $Thumbprint -NoWelcome
# Azure Automation, Function ou VM avec identité managée
Connect-MgGraph -Identity -NoWelcome
Get-MgContext | Select-Object TenantId,ClientId,AuthType,Scopes,ContextScope4. Mapper les commandes et les permissions
La table de correspondance Microsoft est un point de départ, pas une preuve d’équivalence. Certaines fonctions n’existent que dans Graph Beta, d’autres n’ont aucun remplacement direct. Vérifiez la documentation de chaque commande et l’API sous-jacente, puis choisissez la permission la moins privilégiée qui permet l’action.
| Ancienne commande | Commande Graph | Point à valider |
|---|---|---|
| Connect-AzureAD / Connect-MsolService | Connect-MgGraph | Scopes, tenant, type d’authentification et contexte |
| Get-AzureADUser / Get-MsolUser | Get-MgUser | Propriétés explicites, filtres et pagination |
| Get-AzureADGroup | Get-MgGroup | Type de groupe et propriétés retournées |
| Get-AzureADGroupMember | Get-MgGroupMember | Objets directoryObject; typer ou relire au besoin |
| Add-AzureADGroupMember | New-MgGroupMemberByRef | URI @odata.id et Object ID |
| Get-AzureADSubscribedSku | Get-MgSubscribedSku | SkuId, SkuPartNumber et unités disponibles |
| Set-AzureADUserLicense | Set-MgUserLicense | Tableaux addLicenses/removeLicenses et plan de retour |
| Get-AzureADMSConditionalAccessPolicy | Get-MgIdentityConditionalAccessPolicy | Permissions Policy.Read.All ou supérieures |
| Revoke-AzureADUserAllRefreshToken | Revoke-MgUserSignInSession | Effet opérationnel et journalisation |
5. Réécrire correctement les lectures
Get-MgUser ne retourne pas toutes les propriétés par défaut. Demandez explicitement celles que le script consomme. Utilisez -All quand le résultat doit couvrir toutes les pages et préférez un filtre côté serveur à un Where-Object appliqué après avoir téléchargé tout le tenant.
Les requêtes avancées avec $count, certains filtres ou certaines recherches exigent ConsistencyLevel eventual. Comparez les résultats par Id ou UserPrincipalName plutôt que par ordre d’affichage.
$users = Get-MgUser -All `
-Property Id,DisplayName,UserPrincipalName,AccountEnabled,Department `
-Filter "accountEnabled eq true"
$users | Select-Object Id,DisplayName,UserPrincipalName,AccountEnabled,Department$count = 0
$guests = Get-MgUser -All -ConsistencyLevel eventual `
-CountVariable count -Filter "userType eq 'Guest'" `
-Property Id,DisplayName,UserPrincipalName,CreatedDateTime
"Invités retournés : $($guests.Count); compteur Graph : $count"6. Encadrer les écritures avec un mode simulation
Ne présumez pas que toutes les commandes Graph prennent en charge -WhatIf. Ajoutez votre propre commutateur DryRun, journalisez la cible et les valeurs prévues, puis exigez une confirmation explicite pour l’exécution. Utilisez les Object ID stables et relisez l’objet après chaque modification critique.
Pour les licences, résolvez d’abord le SkuPartNumber vers son SkuId, conservez l’état précédent et testez sur un compte pilote. Le retrait d’une licence peut avoir des conséquences sur les services et la rétention; le code ci-dessous n’exécute l’ajout que si DryRun est désactivé.
param([string]$UserId, [string]$SkuPartNumber, [switch]$DryRun)
$sku = Get-MgSubscribedSku -All |
Where-Object SkuPartNumber -eq $SkuPartNumber |
Select-Object -First 1
if (-not $sku) { throw "SKU introuvable : $SkuPartNumber" }
$before = Get-MgUserLicenseDetail -UserId $UserId
$plan = [pscustomobject]@{ UserId=$UserId; AddSkuId=$sku.SkuId; Remove=@() }
$plan | ConvertTo-Json -Depth 5
if (-not $DryRun) {
Set-MgUserLicense -UserId $UserId `
-AddLicenses @(@{ SkuId = $sku.SkuId }) -RemoveLicenses @()
Get-MgUserLicenseDetail -UserId $UserId
}7. Comparer ancien et nouveau avant la bascule
Pendant une courte période contrôlée, exécutez les deux versions en lecture seule et normalisez leur sortie vers le même schéma. Comparez le nombre d’objets, les identifiants et les valeurs métier. Les différences peuvent venir d’une propriété absente, d’une page non récupérée, d’un filtre incompatible ou de permissions insuffisantes.
Ne lancez jamais en parallèle deux versions qui écrivent. Pour une automatisation critique, prévoyez un interrupteur de désactivation, un journal de corrélation, une alerte sur échec et la possibilité de réactiver temporairement l’ancienne version sans perdre les entrées en attente.
$old = Import-Csv .\baseline-azuread.csv | Sort-Object Id
$new = Import-Csv .\candidate-graph.csv | Sort-Object Id
Compare-Object $old $new -Property Id,UserPrincipalName,AccountEnabled `
-PassThru | Export-Csv .\graph-differences.csv -NoTypeInformation8. Déployer par vagues et surveiller
- Conserver la version testée des modules dans le dépôt de déploiement.
- Tester les mises à jour du SDK avant de les pousser aux runbooks.
- Journaliser le tenant, l’application, l’opération, la cible et le résultat sans exposer de jeton.
- Traiter les erreurs et la limitation de débit sans boucles infinies.
- Retirer les certificats, secrets et consentements devenus inutiles après la migration.
| Vague | Portée | Critère de sortie |
|---|---|---|
| 0 — Inventaire | Scripts, propriétaires, modules et identités | Aucune dépendance oubliée |
| 1 — Rapports | Lectures simples et non critiques | Parité des objets et propriétés |
| 2 — Automatisations pilotes | Une application dédiée et quelques cibles | Permissions minimales et reprise testée |
| 3 — Production | Migration par service | Alertes, journaux et procédures opérationnels |
| 4 — Retrait | Anciennes tâches et consentements | Aucun appel AzureAD/MSOnline restant |
Checklist avant de retirer AzureAD et MSOnline
La migration la plus fiable est rarement la plus rapide. Elle transforme chaque dépendance implicite en choix explicite : quelles données lire, quelle identité agit, quelles permissions sont nécessaires et comment revenir en arrière. C’est ce travail qui empêche un simple changement de module de devenir une panne de production.
- Tous les scripts, runbooks et tâches planifiées ont un propriétaire.
- Chaque ancienne commande possède une correspondance validée ou une décision documentée.
- Les propriétés, la pagination et les filtres ont été testés sur des volumes réels.
- Les permissions Graph suivent le moindre privilège et le consentement est documenté.
- Les tâches automatisées utilisent une identité managée ou un certificat protégé.
- Les écritures possèdent un mode simulation, des journaux et un retour arrière.
- Les résultats en lecture ont atteint la parité avant la bascule.
- Les équipes de soutien savent reconnaître et traiter un échec Graph.
- Les anciens modules, identités et permissions sont retirés seulement après observation.