M365 Tenant härten per PowerShell & Graph

Jeder kennt die Situation bei einem frischen M365 Tenant: Du klickst dich durch ein Admin Center nach dem anderen und stellst alles so ein, wie es passend erscheint. Dabei verlierst du Zeit, denn jedes Portal lädt erst, baut seine Kacheln auf und springt bei jedem Wechsel neu an. Schneller geht es über PowerShell.
Manuelle Klicks im Microsoft 365 Admin Center kosten aber nicht nur Zeit. Sie erzeugen Konfigurationsfehler und lassen sich bei einem Audit nicht reproduzieren. Wer Tenants professionell verwaltet, definiert den Ist-Zustand deshalb über PowerShell.
Die Verwaltung der Organisationseinstellungen läuft über das Microsoft Graph PowerShell SDK, das die Graph API direkt anspricht. Für reine Identitätsaufgaben steht zusätzlich das szenariofokussierte Modul Microsoft Entra PowerShell bereit. Es baut auf dem Graph SDK auf und bringt mit Enable-EntraAzureADAlias einen Kompatibilitätsmodus mit, falls noch betagte AzureAD-Skripte im Bestand liegen.
Voraussetzungen & Berechtigungen
Das Microsoft Graph PowerShell SDK arbeitet nach dem Least-Privilege-Prinzip mit granularen Scopes. Du deklarierst die benötigten Scopes exakt, sonst bricht der Befehl mit einem Autorisierungsfehler ab. Microsoft empfiehlt für Graph und Entra PowerShell den Einsatz von PowerShell 7. Bleibst du bei Windows PowerShell 5.1, brauchst du .NET Framework 4.7.2 und ein aktuelles PowerShellGet.
Ein vorhandener Scope allein reicht in Microsoft Graph nicht immer aus. Für delegierte Zugriffe auf das organization-Objekt dokumentiert Microsoft zusätzlich unterstützte Entra-Rollen. Ohne passende Rolle liefert auch eine formell korrekte Verbindung keine Schreibberechtigung. Zum Lesen des Organisations-Objekts genügen Rollen wie Global Reader, Directory Readers oder Security Reader. Für Änderungen an der authorizationPolicy verlangt das System den Privileged Role Administrator als kleinste unterstützte Rolle, kombiniert mit dem Scope Policy.ReadWrite.Authorization.
Eine zweite Einschränkung betrifft Get-MgOrganization. Anwendungen mit dem Scope User.Read lesen aus dem Organisations-Objekt nur id, displayName und verifiedDomains. Alle übrigen Eigenschaften kommen als null zurück. Für eine belastbare Dokumentation des Organisationsprofils deklarierst du deshalb mindestens Organization.Read.All.
# Nur lesen
Connect-MgGraph -Scopes "Organization.Read.All", "Policy.Read.All"
# Für Änderungen an den Tenant-Kontakten
Connect-MgGraph -Scopes "Organization.ReadWrite.All"
Grundlegende Tenant-Informationen und Kontaktadressen
Ein fehlerfreies Organisationsprofil sorgt dafür, dass kritische Systemwarnungen von Microsoft im Sicherheitsvorfall sofort beim zuständigen Team ankommen. Stehen dort veraltete Adressen, landen diese Benachrichtigungen oft in verwaisten Postfächern ehemaliger Mitarbeiter, und die IT verliert wertvolle Reaktionszeit.
Die hinterlegten Adressen prüfst du über das Organisations-Cmdlet:
# Kontaktadressen des Tenants prüfen
Get-MgOrganization | Select-Object DisplayName, TechnicalNotificationMails,
SecurityComplianceNotificationMails, SecurityComplianceNotificationPhones,
MarketingNotificationEmails

Liefert die Abfrage veraltete Adressen, korrigierst du den Zustand über die Tenant-ID und ein anschließendes Update. Die Graph API erlaubt Schreibzugriffe auf dem Organisations-Objekt nur für klar definierte Felder: marketingNotificationEmails, privacyProfile, securityComplianceNotificationMails, securityComplianceNotificationPhones und technicalNotificationMails.
# Technische Kontakte setzen (Tenant-ID fürs Update ermitteln)
$TenantId = (Get-MgOrganization).Id
Update-MgOrganization -OrganizationId $TenantId `
-TechnicalNotificationMails @("security-team@deine-domain.de")
Organisationsprofil: Standort und Basiswerte prüfen
Die geografischen Basiswerte legen den Standard-Rechenzentrumstandort und die Systemsprache deines Tenants fest. Diese Werte wirken auf DSGVO-relevante Datenresidenz und auf die Abrechnung.
Nicht jeder sichtbare Basiswert lässt sich nachträglich ändern. Für Country/Region dokumentiert Microsoft ausdrücklich, dass der Wert nach der Tenant-Erstellung fixiert bleibt. Er bestimmt verfügbare Dienste, Steuern und Währung. Passt die Region nicht mehr, bleibt nur ein neuer Account mit neuer Subscription und eine Datenmigration. Lege diese Werte deshalb bei der Ersteinrichtung als feste Architekturentscheidung fest.
# Standort, Sprache und Basis-IDs des Tenants
Get-MgOrganization | Select-Object DisplayName, Country, CountryLetterCode,
PreferredLanguage, CreatedDateTime, DefaultUsageLocation
# Verifizierte und Standard-Domänen auflisten
(Get-MgOrganization).VerifiedDomains |
Select-Object Name, IsDefault, IsInitial, Type
Security Defaults: Die Basis-Verteidigungslinie prüfen
Für kleinere Tenants ohne Premium-Lizenzen sind die Security Defaults die einzige integrierte Möglichkeit, eine Multifaktor-Authentifizierung für alle Konten zu erzwingen. Sind sie deaktiviert und existiert keine eigene Conditional-Access-Richtlinie, bleibt der Tenant offen für Passwort-Spraying.
# Prüfen ob Security Defaults aktiv sind
Get-MgPolicyIdentitySecurityDefaultEnforcementPolicy | Select-Object IsEnabled, Description
Ab Entra ID P1 ist Conditional Access der präzisere Weg, weil sich MFA dann pro Benutzergruppe, Standort, Risiko und Client steuern lässt. Beachte den Übergang bei der MFA-Verwaltung: Microsoft hat die alte Per-User-MFA-Verwaltung über die Service Settings ausgemustert, die Steuerung der Methoden gehört in die Authentication Methods Policy. Security Defaults und eine Conditional-Access-Richtlinie schließen sich gegenseitig aus, du aktivierst entweder das eine oder das andere.
Dienste: Globale Kalenderfreigabe prüfen
Die globale Freigaberichtlinie in Exchange Online legt fest, wie viele Termindetails Anwender maximal mit externen Partnern teilen dürfen. Zu lockere Standardwerte geben komplette Termininhalte nach außen frei. Angreifer nutzen das gezielt, um interne Projektabläufe auszukundschaften.
Die Prüfung ist nur belastbar, wenn du die Reichweite der Freigaberichtlinien berücksichtigst. Eine Sharing Policy wirkt erst, wenn sie einem Postfach zugewiesen ist. Hat ein Postfach keine eigene Zuweisung, greift automatisch die Default Sharing Policy.
# Globale Kalenderfreigabe-Richtlinien prüfen
Connect-ExchangeOnline
Get-SharingPolicy | Select-Object Name, Enabled, Domains
Bei der ersten Änderung bestimmter Exchange-Organisationsobjekte verlangt Exchange Online eine Vorbedingung. Microsoft nennt dafür das Cmdlet Enable-OrganizationCustomization. Solange diese Aktivierung fehlt, scheitern Änderungsversuche an den unvorbereiteten Standardobjekten.
# Exchange Online einmalig für Organisationsanpassungen vorbereiten
Enable-OrganizationCustomization
Sicherheit und Datenschutz: App-Berechtigungen der Benutzer
Ein primärer Angriffsweg in der Cloud ist OAuth-Consent-Phishing. Angreifer bringen Benutzer über gefälschte Mails dazu, einer App weitreichende Leserechte zu erteilen. Ein Passwortdiebstahl entfällt, weil die App über ein vom Nutzer bewilligtes Token arbeitet. Diese Angriffsfläche steuerst du über die Autorisierungsrichtlinie.
Microsoft führt dort zentrale Standards zusammen, darunter allowUserConsentForRiskyApps, allowInvitesFrom, allowedToSignUpEmailBasedSubscriptions und allowedToUseSSPR. Für die Härtung prüfst du allowUserConsentForRiskyApps. Der Standardwert ist false, und Microsoft rät ausdrücklich, ihn auf false zu belassen. Andernfalls kompromittieren riskante Anwendungen die Umgebung direkt über OAuth.
Parallel prüfst du permissionGrantPoliciesAssigned. Ein leeres Array schaltet die Benutzereinwilligung für Apps komplett ab. Ein zugewiesener Built-in-Wert wie ManagePermissionGrantsForSelf.microsoft-user-default-low erlaubt Einwilligungen unter einer eng begrenzten Richtlinie. In vielen Tenants steht hier noch der Legacy-Wert ManagePermissionGrantsForSelf.microsoft-user-default-legacy, den du gezielt auf die Low-Variante anhebst.
# Self-Service-Rechte der Standardbenutzer prüfen
Get-MgPolicyAuthorizationPolicy |
Select-Object -ExpandProperty DefaultUserRolePermissions |
Select-Object AllowedToCreateApps, PermissionGrantPoliciesAssigned
Zusammenarbeit: M365-Gruppen-Erstellung einschränken
Darf jeder Anwender beliebig Teams und SharePoint-Websites erstellen, wächst der Tenant in unkontrolliertes Datenchaos, und du verlierst den Überblick über Datenablagen und Berechtigungen. Die Steuerung der M365-Gruppen-Erstellung liegt in den Directory Settings der Vorlage Group.Unified.
Diese tenantweiten Gruppen-Settings liegen weiterhin im Beta-Endpunkt der Graph API. Du sprichst sie über die Beta-Cmdlets aus dem Submodul Microsoft.Graph.Beta.Identity.DirectoryManagement an, sonst findest du die Vorlage Group.Unified nicht zuverlässig:
# Submodul laden und Gruppen-Settings abfragen
Import-Module Microsoft.Graph.Beta.Identity.DirectoryManagement
Connect-MgGraph -Scopes "Directory.Read.All"
Get-MgBetaDirectorySetting |
Where-Object DisplayName -eq "Group.Unified" |
Select-Object -ExpandProperty ValuesAchte beim Beta-Submodul auf den Versionsstand. Lädt deine Session schon eine andere Version von Microsoft.Graph.Authentication, scheitert der Import mit einem Assembly-Konflikt. Bring in dem Fall alle Microsoft.Graph-Module auf denselben Stand und starte eine frische PowerShell-Session.
Willst du ohne Beta-Modul auskommen, liest du dieselbe Einstellung über die REST-Schiene, die nur das Authentication-Modul braucht:
# Alternative ohne Beta-Submodul
(Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/groupSettings").value |
Where-Object displayName -eq "Group.Unified" |
ForEach-Object { $_.values }
Eine leere Ausgabe bedeutet, dass der Tenant die Vorlage noch nie angepasst hat. Dann greift der Default EnableGroupCreation = true, jeder Standardnutzer darf also Gruppen anlegen.
Steht EnableGroupCreation auf True, blockierst du die Funktion per Update und delegierst das Erstellungsrecht über GroupCreationAllowedGroupId an eine definierte IT-Sicherheitsgruppe. Das zwingt Anwender, neue Arbeitsräume über einen geregelten IT-Prozess anzufordern. Für den Schreibzugriff brauchst du den Scope Policy.ReadWrite.DirectorySettings oder Directory.ReadWrite.All.
Identitäten: Gastnutzer-Einladungen (B2B) regulieren
Kann jeder Mitarbeiter externe Gast-Accounts anlegen, füllt sich das Verzeichnis mit unüberwachten Konten. Angreifer kapern verwaiste Gastkonten für laterale Bewegungen im Netzwerk.
# Prüfen, ob Standardnutzer Gäste einladen dürfen
Get-MgPolicyAuthorizationPolicy |
Select-Object -ExpandProperty DefaultUserRolePermissions |
Select-Object AllowedToInviteGuestDer Wert allowInvitesFrom steht in den meisten Cloud-Umgebungen per Default auf everyone. Ziehst du das auf adminsAndGuestInviters zurück, vergibst du das Einladungsrecht nur noch an dedizierte Manager oder an die Rolle Guest Inviter. Den Berechtigungsumfang der Gäste selbst begrenzt du zusätzlich über guestUserRoleId, etwa mit der Rolle Restricted Guest User.

Netzwerke: Tenant-übergreifender Zugriff (Cross-Tenant Access)
Arbeiten Anwender in fremden Tenants, steuern die Cross-Tenant-Access-Richtlinien das Vertrauen in externe MFA-Nachweise. Falsch konfiguriert öffnest du Einfallstore aus schwach geschützten Partner-Umgebungen. Ein sicherer Ansatz fordert MFA zwingend im eigenen Tenant an.
Den Default-Block liest du am saubersten über das dedizierte Cmdlet statt über die Expand-Variante:
# Standardrichtlinien für B2B-Zugriff abrufen
Get-MgPolicyCrossTenantAccessPolicy | Select-Object -ExpandProperty Default
Für gezielte Partner ergänzt du organisationsspezifische Richtlinien über Get-MgPolicyCrossTenantAccessPolicyPartner und konfigurierst dort, ob du dem MFA-Claim des Partners vertraust.
Sicherheit & Datenschutz: Legacy-Zugriffe per MSOL PowerShell blockieren
Die MSOnline-Cmdlets griffen über einen alten Dienstendpunkt auf den Tenant zu, der modernes Conditional Access umgeht. Mit blockMsolPowerShell sperrst du diesen benutzerbasierten Zugriff auf den Legacy-Endpunkt. Microsoft Entra Connect und Microsoft Graph bleiben davon unberührt.
Der Schreibzugriff auf die Autorisierungsrichtlinie ist abgesichert. Du brauchst dafür den Scope Policy.ReadWrite.Authorization, ein reiner Lese-Scope wie Directory.Read.All quittiert den Versuch mit einem 403. Zusätzlich verlangt das Objekt delegiert mindestens die Rolle Privileged Role Administrator. Fehlt sie, lehnt Graph den Schreibzugriff auch bei korrektem Scope ab.
# Mit Schreib-Scope verbinden und Scope prüfen
Connect-MgGraph -Scopes "Policy.ReadWrite.Authorization"
(Get-MgContext).Scopes
# Benutzerzugriff über den MSOnline-Endpunkt sperren
$params = @{
blockMsolPowerShell = $true
}
Update-MgPolicyAuthorizationPolicy -BodyParameter $paramsDas MSOnline-Modul ist seit geraumer Zeit abgeschaltet, der Schalter wirkt deshalb als zusätzliche Absicherung gegen Restzugriffe auf den alten Endpunkt, nicht mehr als alleinige Maßnahme. Setze ihn als Teil deiner Defense-in-Depth. Prüfe parallel über die Sign-in-Logs, ob noch Skripte oder Service-Accounts den Legacy-Pfad ansteuern, denn nur so erkennst du Automatisierungen, die nach dem Setzen des Schalters ins Leere laufen.

Sicherheit & Datenschutz: Self-Service-Käufe
Standardbenutzer erwerben über Self-Service-Käufe eigenständig Lizenzen für Produkte wie Power BI Pro, Power Automate oder Project. Das untergräbt die zentrale Budgetkontrolle, verletzt unter Umständen Compliance-Vorgaben und begünstigt unregulierte Schatten-IT. Steuern lässt sich diese Funktion tenantweit ausschließlich über das Modul MSCommerce.
Das Modul gehört nicht zum Graph SDK und wird separat aus der PowerShell Gallery installiert. Für die Anmeldung brauchst du einen Billing- oder Global-Administrator.
# Modul einmalig installieren
Install-Module -Name MSCommerce -Scope CurrentUser -Force
# Danach verbinden und abfragen
Connect-MSCommerce
Get-MSCommerceProductPolicies -PolicyId AllowSelfServicePurchase |
Sort-Object ProductName | Format-Table ProductName, PolicyValueDie Richtlinie gilt strikt pro Produkt, einen globalen Schalter gibt es nicht. Du bewertest jedes freigegebene Produkt einzeln und deaktivierst es gezielt über Update-MSCommerceProductPolicy. Neue Microsoft-Produkte sind nach Veröffentlichung oft direkt wieder freigegeben. Plane bis zu 72 Stunden ein, bis Änderungen über die Synchronisationsprozesse in allen Diensten greifen. Eine feinere Steuerung pro Benutzer oder pro Sicherheitsgruppe bietet Microsoft an dieser Stelle nicht. Bestehende Abos kannst du übernehmen, Benutzer verschieben oder die Abonnements kündigen.

Weitere Härtungsmaßnahmen per PowerShell
Die folgenden Einstellungen gehören in dieselbe Bestandsaufnahme, weil sie denselben Tenant betreffen und sich ebenfalls skriptgesteuert verwalten lassen. Der Vorteil gegenüber der GUI bleibt derselbe: Du liest den Ist-Zustand reproduzierbar aus und schreibst den Soll-Zustand im selben Skript zurück, statt dich durch verstreute Portale zu klicken. Die Tabelle ordnet jede Maßnahme ihrem Bereich zu und nennt das Cmdlet zum Lesen sowie das passende Gegenstück zum Setzen.
| Bereich | Maßnahme | Lesen / Schreiben |
| Identität | Authentifizierungsmethoden, Nachfolger der Per-User-MFA-Verwaltung | Get-MgPolicyAuthenticationMethodPolicy Update-MgPolicyAuthenticationMethodPolicy |
| Identität | Conditional Access, etwa Block Legacy Auth und MFA für Admins | Get-MgIdentityConditionalAccessPolicy New-MgIdentityConditionalAccessPolicy, Update-MgIdentityConditionalAccessPolicy |
| Identität | Named Locations für standortbasierte Regeln | Get-MgIdentityConditionalAccessNamedLocation New-MgIdentityConditionalAccessNamedLocation |
| Identität | App-Registrierung durch Standardnutzer abschalten (AllowedToCreateApps) | Get-MgPolicyAuthorizationPolicy Update-MgPolicyAuthorizationPolicy |
| Identität | Tenant-Erstellung durch Standardnutzer unterbinden (AllowedToCreateTenants) | Get-MgPolicyAuthorizationPolicy Update-MgPolicyAuthorizationPolicy |
| Identität | Sichtbarkeit anderer Benutzer einschränken (AllowedToReadOtherUsers) | Get-MgPolicyAuthorizationPolicy Update-MgPolicyAuthorizationPolicy |
| Apps und Consent | Riskante Enterprise Apps und veraltete Berechtigungen prüfen | Get-MgServicePrincipal, Get-MgOauth2PermissionGrant Remove-MgOauth2PermissionGrant |
| Apps und Consent | Benutzerzuweisung pro Enterprise App erzwingen (AppRoleAssignmentRequired) | Get-MgServicePrincipal Update-MgServicePrincipal |
| B2B und Geräte | Cross-Tenant-Zugriff und Tenant Restrictions v2 | Get-MgPolicyCrossTenantAccessPolicyDefault Update-MgPolicyCrossTenantAccessPolicyDefault |
| B2B und Geräte | Geräteregistrierung und Entra-Join-Berechtigungen | Get-MgPolicyDeviceRegistrationPolicy Update-MgPolicyDeviceRegistrationPolicy |
Die drei Werte aus der Autorisierungsrichtlinie setzt du nicht einzeln, sondern gebündelt über die verschachtelte Eigenschaft defaultUserRolePermissions:
# App-Registrierung und Tenant-Erstellung für Standardnutzer abschalten
Connect-MgGraph -Scopes "Policy.ReadWrite.Authorization"
$params = @{
defaultUserRolePermissions = @{
allowedToCreateApps = $false
allowedToCreateTenants = $false
}
}
Update-MgPolicyAuthorizationPolicy -BodyParameter $paramsWelche Zeilen für deinen Tenant greifen, hängt von Lizenz und Workloads ab. Für die Consent- und Conditional-Access-Themen brauchst du jeweils die passenden Policy-Scopes mit Schreibrecht, etwa Policy.ReadWrite.ConditionalAccess oder Policy.ReadWrite.Authorization. Ein reiner Lese-Scope quittiert jeden Schreibversuch mit einem 403. Exchange- und SharePoint-Werte erfordern zudem eine eigene Verbindung über Exchange Online PowerShell beziehungsweise die SharePoint-Online-Verwaltung.
Den Ist-Zustand dokumentieren
Für ein anstehendes Audit exportierst du die Einstellungen in eine maschinenlesbare Datei. Der JSON-Export liefert eine strukturierte Basis für spätere Systemvergleiche.
# Organisationseinstellungen als JSON sichern
Get-MgOrganization |
Select-Object DisplayName, TechnicalNotificationMails, Country, PreferredLanguage |
ConvertTo-Json |
Out-File ".\Org-Settings_$(Get-Date -Format 'yyyy-MM-dd').json"
Configuration-as-Code: Microsoft365DSC
Der JSON-Export liefert eine Momentaufnahme. Sobald du mehrere Tenants betreust oder einen definierten Soll-Zustand dauerhaft halten willst, stößt das an Grenzen. Hier setzt Microsoft365DSC an, ein Open-Source-Modul auf Basis von PowerShell Desired State Configuration.
Statt einzelne Werte abzufragen, beschreibst du die komplette Tenant-Konfiguration als Code. Microsoft365DSC exportiert den Ist-Zustand über Workloads wie Entra ID, Exchange Online und Intune hinweg in eine zusammenhängende Konfiguration. Diese Datei wird zu deiner Quelle der Wahrheit. Weicht der Tenant später davon ab, erkennt das Modul diese Drift und meldet sie. Auf Wunsch rollt es den definierten Stand erneut aus und gleicht die Abweichung an. Damit baust du einen neuen Tenant nicht mehr von Hand auf, sondern reichst eine geprüfte Konfiguration ein.
Technisch läuft die kompilierte Konfiguration über den Local Configuration Manager eines Agenten, also einer Maschine oder eines Containers. Der kommuniziert über die Microsoft-365-APIs zurück in den Tenant und braucht deshalb Internetzugang.
# Modul installieren und auf den aktuellen Stand bringen
Install-Module -Name Microsoft365DSC -Force
Update-M365DSCModuleFazit
Die Konfiguration von Microsoft 365 über PowerShell schließt die Lücken, die unübersichtliche Webmenüs hinterlassen. Skripte überführen administrative Prozesse in eine reproduzierbare Struktur und dokumentieren den Systemzustand verlässlich. Die alten Administrationsmodule haben ihr End-of-Life erreicht, der Weg führt über das Microsoft Graph PowerShell SDK und Microsoft Entra PowerShell.
Die Härtung eines Tenants verlangt Präzision. Globale Richtlinien wie die Kalenderfreigabe greifen erst nach Zuweisung zu den Postfächern. Basiswerte wie die Tenant-Region lassen sich nachträglich nicht korrigieren. Wer den App-Consent differenziert konfiguriert, Self-Service-Käufe produktgenau überwacht und Legacy-Zugriffe sperrt, schützt die Umgebung vor Schatten-IT. Die API-Endpunkte hinter den Cmdlets bleiben stabil. Ein sauber geschriebenes Skript liefert auf Knopfdruck den Nachweis für die nächste Sicherheitsüberprüfung.
Sei der Erste und starte die Diskussion mit einem hilfreichen Beitrag.
Kommentar hinterlassen
Dein Beitrag wird vor der Veröffentlichung kurz geprüft — fachlich, respektvoll und auf den Punkt ist hier genau richtig.