Remplacer AzureAD et MSOnline par Microsoft Graph PowerShell sans casser les scripts

Inventoriez les commandes, mappez permissions et objets, sécurisez l’authentification et migrez par étapes avec des tests de parité et un retour arrière.

01

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.

02

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.

Inventaire local en lecture seule
$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 UTF8
03

2. 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.

Installation ciblée et découverte des commandes
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'
04

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énarioMéthodeChoix de sécurité
Administration interactivePermissions déléguéesScopes minimaux, connexion humaine et ContextScope Process
Azure Automation ou ressource AzureIdentité managéeAucun secret à distribuer; permissions d’application minimales
Tâche planifiée hors AzureApplication et certificatClé privée protégée, rotation documentée et consentement administrateur
Secret client en clairÀ éviterSecret facilement copié, expiré ou exposé dans les journaux
Connexion interactive, certificat ou identité managée
# 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,ContextScope
05

4. 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 commandeCommande GraphPoint à valider
Connect-AzureAD / Connect-MsolServiceConnect-MgGraphScopes, tenant, type d’authentification et contexte
Get-AzureADUser / Get-MsolUserGet-MgUserPropriétés explicites, filtres et pagination
Get-AzureADGroupGet-MgGroupType de groupe et propriétés retournées
Get-AzureADGroupMemberGet-MgGroupMemberObjets directoryObject; typer ou relire au besoin
Add-AzureADGroupMemberNew-MgGroupMemberByRefURI @odata.id et Object ID
Get-AzureADSubscribedSkuGet-MgSubscribedSkuSkuId, SkuPartNumber et unités disponibles
Set-AzureADUserLicenseSet-MgUserLicenseTableaux addLicenses/removeLicenses et plan de retour
Get-AzureADMSConditionalAccessPolicyGet-MgIdentityConditionalAccessPolicyPermissions Policy.Read.All ou supérieures
Revoke-AzureADUserAllRefreshTokenRevoke-MgUserSignInSessionEffet opérationnel et journalisation
06

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.

Lecture complète avec propriétés explicites
$users = Get-MgUser -All `
  -Property Id,DisplayName,UserPrincipalName,AccountEnabled,Department `
  -Filter "accountEnabled eq true"

$users | Select-Object Id,DisplayName,UserPrincipalName,AccountEnabled,Department
Requête avancée avec comptage
$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"
07

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é.

Ajout de licence avec garde-fou explicite
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
}
08

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.

Comparer deux exports normalisés
$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 -NoTypeInformation
09

8. 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.
VaguePortéeCritère de sortie
0 — InventaireScripts, propriétaires, modules et identitésAucune dépendance oubliée
1 — RapportsLectures simples et non critiquesParité des objets et propriétés
2 — Automatisations pilotesUne application dédiée et quelques ciblesPermissions minimales et reprise testée
3 — ProductionMigration par serviceAlertes, journaux et procédures opérationnels
4 — RetraitAnciennes tâches et consentementsAucun appel AzureAD/MSOnline restant
10

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.