5 PowerShell-Snippets für den Microsoft 365 Admin-Alltag
Das sind die fünf Abfragen, die ich in Microsoft-365-Tenants am häufigsten brauche. Alle lesen nur, keine ändert etwas. Ich habe sie so geschrieben, dass man sie ohne Anpassung kopieren kann.
Vorbereitung
Das komplette Microsoft.Graph-Modul ist riesig und braucht ewig zum Installieren. Für diese Snippets reichen drei Teilmodule:
Install-Module Microsoft.Graph.Users, Microsoft.Graph.Reports, Microsoft.Graph.Identity.SignIns -Scope CurrentUser
Connect-MgGraph -Scopes 'User.Read.All','AuditLog.Read.All','UserAuthenticationMethod.Read.All'
Beim ersten Anmelden fragt Entra nach der Zustimmung für diese Rechte. Je nach Tenant-Einstellung muss das ein Admin bestätigen.
1. Wer hat sich seit 90 Tagen nicht angemeldet?
$cutoff = (Get-Date).AddDays(-90)
Get-MgUser -All -Property DisplayName,UserPrincipalName,AccountEnabled,SignInActivity |
Where-Object {
$_.AccountEnabled -and
$_.SignInActivity.LastSignInDateTime -and
$_.SignInActivity.LastSignInDateTime -lt $cutoff
} |
Select-Object DisplayName, UserPrincipalName,
@{N='LastSignIn';E={$_.SignInActivity.LastSignInDateTime}} |
Sort-Object LastSignIn
Das ist meine Liste für „Brauchen wir die Lizenz noch?“. Zwei Haken: SignInActivity gibt es nur mit Entra ID P1. Und Konten, die sich noch nie angemeldet haben, tauchen hier absichtlich nicht auf, weil sie kein Datum haben. Die prüfe ich separat, oft sind das vergessene Testkonten.
2. Aktive Benutzer ohne Lizenz
Get-MgUser -All -Property DisplayName,UserPrincipalName,AccountEnabled,AssignedLicenses |
Where-Object { $_.AccountEnabled -and $_.AssignedLicenses.Count -eq 0 } |
Select-Object DisplayName, UserPrincipalName
Hier landen Konten, bei denen beim Anlegen die Lizenz vergessen wurde, und Dienstkonten. Geteilte Postfächer erscheinen auch, die brauchen in der Regel keine Lizenz. Deren Anmeldung sollte aber deaktiviert sein. Wenn ein geteiltes Postfach hier als aktiv auftaucht, ist das der eigentliche Fund.
3. Gastkonten, neueste zuerst
Get-MgUser -All -Filter "userType eq 'Guest'" -Property DisplayName,Mail,CreatedDateTime,SignInActivity |
Select-Object DisplayName, Mail, CreatedDateTime,
@{N='LastSignIn';E={$_.SignInActivity.LastSignInDateTime}} |
Sort-Object CreatedDateTime -Descending
Gäste sammeln sich über Teams schnell an. Wer seit einem Jahr nicht da war, fliegt bei mir nach Rücksprache raus.
4. Postfachgrößen als CSV
Install-Module ExchangeOnlineManagement -Scope CurrentUser # einmalig
Connect-ExchangeOnline
Get-EXOMailbox -ResultSize Unlimited |
Get-EXOMailboxStatistics |
Select-Object DisplayName, TotalItemSize, ItemCount |
Export-Csv -Path './mailbox-sizes.csv' -NoTypeInformation -Encoding utf8
Die EXO-Cmdlets sind bei vielen Postfächern deutlich schneller als die alten Get-Mailbox/Get-MailboxStatistics. Ich brauche das vor Migrationen und wenn jemand fragt, warum das Postfach voll ist.
5. Benutzer ohne MFA-Methode
Get-MgReportAuthenticationMethodUserRegistrationDetail -All |
Where-Object { -not $_.IsMfaRegistered } |
Select-Object UserPrincipalName, UserDisplayName, IsAdmin, MethodsRegistered
Das ist derselbe Bericht, den das Entra Admin Center unter Authentifizierungsmethoden → Benutzerregistrierungsdetails zeigt, nur als Liste. Er liefert alle Benutzer in einer Abfrage. Die Spalte IsAdmin zeigt sofort, ob ein Admin dabei ist. Das wäre dann zuerst zu klären.
Auch dieser Bericht braucht Entra ID P1. Ohne P1 geht es nur einzeln pro Benutzer, was bei großen Tenants dauert:
Get-MgUser -All -Filter 'accountEnabled eq true' -Property Id,UserPrincipalName |
ForEach-Object {
$methods = Get-MgUserAuthenticationMethod -UserId $_.Id |
Where-Object { $_.AdditionalProperties['@odata.type'] -notmatch 'passwordAuthenticationMethod$' }
if (-not $methods) { $_.UserPrincipalName }
}
Warum PowerShell statt Portal
Alles davon geht auch im Portal. Mit Skript bekomme ich aber jedes Mal dieselbe Abfrage, kann das Ergebnis als CSV an den Kunden schicken und sehe beim nächsten Mal, was sich geändert hat.