Mit AzureAD und MSOnline verabschieden sich zwei Module, die viele Admins über Jahre begleitet haben. Beide sind jetzt endgültig Geschichte. In vielen Tenants, die ich mir anschaue, laufen trotzdem noch alte Skripte mit diesen Cmdlets, oft tief eingebettet in Automatisierungen oder Compliance-Auswertungen. Seit Microsoft Anfang des Jahres die letzten Schalter auf „retired“ gestellt hat, blieb vielen nur der Sprung ins kalte Wasser. Entweder sie stellen auf das Microsoft Graph PowerShell SDK um, oder sie riskieren, dass Prozesse plötzlich ins Leere laufen.

Bei der Migration bremsen vor allem die Altlasten

An der Technik selbst hakt es bei der Migration selten. Das eigentliche Problem ist die schiere Menge an Altlasten. Mir begegnen ständig Skripte, die noch mit klassischen Befehlen wie Get-AzureADUser oder Set-MsolUser arbeiten. Das Microsoft Graph PowerShell SDK ändert dabei mehr als nur die Cmdlet-Namen, auch der Ansatz ist ein anderer. Vieles ist granularer, die Authentifizierung läuft über Microsoft Graph, und Berechtigungen bekommen deutlich mehr Gewicht. Wer wissen will, wie ein altes Cmdlet im neuen Modul heißt oder welche Berechtigungen es braucht, findet die Antwort schnell mit Find-MgGraphCommand und Find-MgGraphPermission. Genau diese beiden Cmdlets haben mir schon oft geholfen, die passende Migrationsstrategie festzulegen. Außerdem hat Microsoft ausführliche Migrationsschritte veröffentlicht, die ich nur empfehlen kann.

Praxistipp: Oft lohnt es sich, nicht 1:1 zu migrieren. Besser nutzt man die Gelegenheit, Automatisierungen zu modernisieren und Altlasten loszuwerden.

Die Anmeldung läuft jetzt über Scopes, Consent und App-Only-Zugriff

Am stärksten ändert sich die Authentifizierung. Das klassische Connect-AzureAD war simpel. Heute verbindet man sich mit Connect-MgGraph, und auf einmal geht es um Scopes und granularen Consent. Das erhöht die Sicherheit und zwingt einen zugleich, genauer festzulegen, was ein Skript tatsächlich darf. Für interaktive Sitzungen genügt meist eine einfache Anmeldung. Bei unbeaufsichtigter Automatisierung ist dagegen die App-Only-Authentifizierung mit Zertifikat der Standard. Die Einrichtung kostet etwas Zeit, rechnet sich aber auf Dauer, weil man saubere Prozesse aufbaut, statt sich durch Oberflächen zu klicken.

So sieht eine App-Only-Verbindung aus:

Connect-MgGraph -ClientId $appid -TenantId $tenantid -CertificateThumbprint $thumbprint -Scopes "User.Read.All"

Das folgende Diagramm zeigt, wie die Komponenten und Authentifizierungsarten zusammenspielen:

flowchart TD
    User["Admin/Skript"] -->|"Connect-MgGraph (User-Login)"| Graph["Microsoft Graph"]
    App["Automation/App-Only"] -->|"Connect-MgGraph (Zertifikat)"| Graph
    Graph -->|"Scopes/Berechtigungen"| Resource["M365-Ressourcen"]

Abbildung: Verbindungsmöglichkeiten mit dem Microsoft Graph PowerShell SDK

Weitere Details stehen in der offiziellen Dokumentation zu den Authentifizierungsbefehlen.

Der Schalter -All und gute Filter halten große Datenmengen im Griff

Wer mit vielen Objekten arbeitet, stellt schnell fest, dass das Graph SDK standardmäßig nicht alle Ergebnisse auf einmal zurückgibt. Viele Befehle liefern höchstens 100 Objekte, solange der Schalter -All fehlt. Für Berichte ist das besonders wichtig, etwa wenn alle Benutzer, Gruppen oder Geräte abgefragt werden sollen. Man sollte den Schalter trotzdem sparsam einsetzen, denn unnötige Komplettabfragen können sehr langsam werden oder an Limits stoßen. In meinen Skripten arbeite ich meist mit passenden Filtern, damit nur die benötigten Objekte abgerufen werden und nicht gleich das ganze Verzeichnis.

Ein kurzes Skript findet alle Benutzer ohne MFA-Methode

Ein typischer Anwendungsfall ist die Frage, welche Benutzer noch keine MFA-Methode hinterlegt haben. Mit dem Graph PowerShell SDK lässt sich das inzwischen recht elegant lösen. Dabei ist zu beachten, dass Filter in Graph in OData-Syntax geschrieben werden (etwa -Filter "accountEnabled eq true") und nicht in KQL. Das folgende einfache Beispiel nutze ich oft als Grundlage. Es läuft in einer lokalen PowerShell 7 mit installiertem Modul Microsoft.Graph:

Connect-MgGraph -Scopes "User.Read.All","UserAuthenticationMethod.Read.All"
$users = Get-MgUser -All -Filter "accountEnabled eq true" -Property Id,DisplayName,UserPrincipalName
foreach ($user in $users) {
    # Das Passwort zählt ebenfalls als Methode und wird daher ausgefiltert
    $mfa = Get-MgUserAuthenticationMethod -UserId $user.Id |
        Where-Object { $_.AdditionalProperties['@odata.type'] -ne '#microsoft.graph.passwordAuthenticationMethod' }
    if (-not $mfa) {
        Write-Output ("{0} ({1}) hat keine MFA-Methoden" -f $user.DisplayName, $user.UserPrincipalName)
    }
}

In großen Tenants empfiehlt es sich, das Skript weiter zu optimieren, zum Beispiel durch parallele Verarbeitung oder gezielte Filter.

Im Alltag mit dem neuen SDK wechseln sich Frust und Fortschritt ab

Ehrlich gesagt macht so eine Migration selten nur Spaß, vor allem in gewachsenen Umgebungen. Die Einarbeitung kostet spürbar Zeit, und Scopes und granulare Berechtigungen verlangen mehr Aufmerksamkeit. Trotzdem schätze ich die neuen Möglichkeiten. Mit einem einheitlichen Satz moderner Cmdlets lässt sich endlich fast alles im Tenant steuern, von der Benutzerverwaltung über Teams bis zu OneDrive. Außerdem zwingt das SDK dazu, die eigenen Prozesse kritisch zu prüfen, statt alles eins zu eins weiterzuschleppen. Wer sich einmal durchgebissen hat, bekommt einen leistungsfähigen Werkzeugkasten, der den alten Modulen in nichts nachsteht. Manches ist umständlicher geworden, keine Frage. Vieles ist aber durchdachter, und das merkt man spätestens beim dritten überarbeiteten Skript.