Hinter „Machen wir das mit PowerShell oder mit Graph?“ stecken zwei Fragen: Welche Schnittstelle bietet überhaupt, was ich brauche? und Wie läuft das Ding in sechs Monaten, wenn keiner mehr draufschaut? Und wer schon einmal das Graph-PowerShell-SDK mit PnP.PowerShell oder dem Exchange-Online-Modul in einem Skript kombiniert hat, kennt die dritte Frage, die erst gestellt wird, wenn es knallt: Laufen diese Module überhaupt zusammen?
Kurz gesagt
- Exchange Online PowerShell für Exchange-Administration (Postfachberechtigungen, Transportregeln, Organisationseinstellungen). Graph deckt das größtenteils nicht ab.
- Microsoft Graph für Identitäten, dienstübergreifende Daten und alles, was eine Anwendung tut: Integrationen, Mailversand, Reporting. Für den Postfachzugriff durch Anwendungen ist Graph die Schnittstelle, auf die du baust.
- Die klassische Falle beim Kombinieren: Assembly-Konflikte (
Microsoft.Identity.Client,Microsoft.Graph.Core,Azure.Core…) zwischen Microsoft.Graph, PnP.PowerShell, ExchangeOnlineManagement und Az. Die Verbindungsreihenfolge ist Glückssache; Prozess-Isolation und weniger Module sind die eigentliche Lösung.
Wofür du dich tatsächlich entscheidest
Das Microsoft-Graph-PowerShell-SDK ist PowerShell und ruft unter der Haube Graph auf. Gewählt wird also zwischen zwei Arten von Schnittstellen:
- Dienstspezifische Admin-Module: ExchangeOnlineManagement, MicrosoftTeams, PnP.PowerShell für SharePoint. Sie sprechen mit den Admin-Schnittstellen eines Workloads und bieten Dinge, die es in Graph schlicht nicht gibt.
- Microsoft Graph: eine REST-API für Entra ID, Mail, Kalender, Dateien, Teams, Planner und vieles mehr, aufrufbar von überall: Graph-PowerShell-SDK,
Invoke-RestMethod, C#, Python, Logic Apps oder ein Ticketsystem.
Welches von beiden passt, hängt zuerst davon ab, welche Schnittstelle das überhaupt kann.
Wo was gewinnt
| Aufgabe | Meine Wahl | Warum |
|---|---|---|
| Full Access / Send As, Transportregeln, organisationsweite Exchange-Einstellungen | Exchange Online PowerShell | Dafür gibt es keine Graph-Endpunkte. Die neue Exchange Online Admin API (Preview) deckt nur einen kleinen Ausschnitt ab, etwa Ordnerberechtigungen und Send on Behalf. |
| Benutzer, Gruppen, Lizenzen, App-Registrierungen in Entra ID | Graph (SDK oder REST) | Das ist Graphs Heimspiel. |
| Eine Anwendung sendet oder liest Mails (Ticketsystem, Workflows) | Graph | sendMail bzw. Message-Endpunkte mit OAuth, eingegrenzt über RBAC for Applications. |
| Site-Provisioning, Templates, SharePoint-Berechtigungen | PnP.PowerShell | Für Admin-Aufgaben deutlich mächtiger als Graphs SharePoint-Schnittstelle. |
| Teams-Richtlinien (Besprechungen, Telefonie, Messaging) | MicrosoftTeams-Modul | Richtlinien leben in der Teams-Admin-Schnittstelle, nicht in Graph. |
| Dienstübergreifendes Reporting (Benutzer + Lizenzen + Anmeldungen + Gruppen) | Graph | Eine API, ein Auth-Modell, Paging und $select inklusive. |
Meine Faustregel: Führt ein Mensch es aus, nimm das Modul, das am nächsten am Workload ist. Führt eine Anwendung es aus, nimm Graph – und gib dieser Anwendung genau die Rechte, die sie braucht, und keine mehr.
Praxisbeispiel: Mail-Integrationen
Der LinkedIn-Post, der diesen Artikel angestoßen hat, beschreibt die Modernisierung einer ServiceNow-Mailintegration, die Mails jetzt über Microsoft Graph sendet und empfängt. Hier arbeitet eine Anwendung mit Postfächern, kein Mensch, und genau da passt Graph: OAuth, granulare Berechtigungen und ein sauberer Audit-Trail. Eine Mail zu senden ist ein einziger Aufruf:
| |
Was ich aus Sicht regulierter Umgebungen ergänzen würde: Eine App mit der Anwendungsberechtigung Mail.Send darf standardmäßig als jedes Postfach im Tenant senden. Das möchtest du keinem Auditor erklären. Mit RBAC for Applications in Exchange Online grenzt du den Service Principal auf die Postfächer ein, die er wirklich braucht:
| |
Die Ironie dabei: Graph-Berechtigungen konfigurierst du mit Exchange-PowerShell. Genau das meint der Original-Post mit „beides zusammen“.
Modulkonflikte zwischen Graph, PnP und EXO
Angenommen, du schreibst ein völlig vernünftiges Skript: Graph für Benutzerdaten, PnP für SharePoint, EXO für Postfächer. Auf deinem Rechner läuft es. In der nächsten Sitzung scheitert es dann mit etwas wie:
| |
oder
| |
Warum das passiert
Jedes dieser Module ist ein Binärmodul und bringt eigene Kopien gemeinsam genutzter .NET-Bibliotheken mit – allen voran MSAL (Microsoft.Identity.Client.dll), aber auch Azure.Core, Azure.Identity, Microsoft.Graph.Core oder System.Text.Json. PowerShell lädt Assemblies pro Prozess in einen gemeinsamen Kontext, und dort kann von einer Assembly mit gegebenem Namen nur eine Version geladen sein. Wer zuerst lädt, gewinnt; das andere Modul läuft dann gegen eine Version, für die es nie gebaut wurde.
Microsofts eigene PowerShell-Dokumentation beschreibt genau das: Das Ergebnis hängt davon ab, welches Modul zuerst geladen wurde, welcher Codepfad lief und ob die Abhängigkeit beim Import oder erst bei der ersten Nutzung geladen wird. Deshalb funktioniert dasselbe Skript mal, mal nicht, je nachdem, was du vorher in der Sitzung gemacht hast.
Bekannte Kombinationen, die Ärger gemacht haben:
- PnP.PowerShell + Microsoft.Graph: Ältere PnP-Versionen brachten ein veraltetes
Microsoft.Graph.Core(1.25.x) und ältere MSAL-Versionen mit, wodurchConnect-MgGraphundInvoke-MgGraphRequestnach dem Laden von PnP scheiterten. - ExchangeOnlineManagement + Microsoft.Graph: eine lange Geschichte. EXO 3.5.1 brachte einen Konflikt mit
System.Text.Json; im Juni 2026 berichtete Tony Redmond, dass mit Graph SDK 2.38 und EXO 3.10 in PowerShell 7 keine Verbindungsreihenfolge zuverlässig funktionierte: Die eine scheitert mit demMethod not foundoben, die andere mit einerNullReferenceExceptionbeim Token-Abruf. - Az.Accounts + Microsoft.Graph: dasselbe Muster mit
Azure.Core/Azure.Identity.
So erkennst du es
Erste Prüfung: Was ist in der aktuellen Sitzung tatsächlich geladen?
| |
Zeigt Location in den PnP-Ordner, während du Connect-MgGraph aufrufst, hast du den Übeltäter.
Zweite Prüfung: Welche Versionen bringen deine installierten Module mit? Diese Inventur macht Konflikte sichtbar, bevor sie in Produktion zuschlagen:
| |
Manche Module enthalten getrennte Builds für .NET Framework und .NET, du siehst also eventuell zwei Einträge pro Modul. Spannend wird es, wenn dieselbe Assembly modulübergreifend in unterschiedlichen Versionen auftaucht.
So löst du es
In dieser Reihenfolge:
1. Lade nicht, was du nicht brauchst. Ist PnP.PowerShell ohnehin verbunden, brauchst du das Graph-SDK oft gar nicht. Invoke-PnPGraphMethod ruft Graph über die PnP-Verbindung auf (sofern deine PnP-App-Registrierung die nötigen Graph-Berechtigungen hat). Und wenn du das SDK brauchst: Microsoft.Graph.Authentication plus die ein, zwei Submodule installieren, die du wirklich nutzt (oder nur Invoke-MgGraphRequest), statt des kompletten Metamoduls Microsoft.Graph.
2. Pro Prozess isolieren. Assemblies gelten pro Prozess, getrennte Prozesse können also nicht kollidieren. In PowerShell 7 läuft Start-Job in einem Kindprozess:
| |
Der Job liefert deserialisierte Objekte zurück (Eigenschaften ja, Methoden nein), genau richtig, um Daten zwischen Schritten weiterzureichen. Wichtig: Start-ThreadJob und ForEach-Object -Parallel isolieren nicht. Sie laufen im selben Prozess und teilen sich die geladenen Assemblies.
3. Runbooks und Pipelines aufteilen. In Azure Automation getrennte Runtime Environments (PowerShell 7.4) pro Modulkombination statt einer „Alles drin“-Umgebung. In CI/CD ein Job pro Workload. Das bringt nebenbei sauberere Berechtigungen pro Identität, also Least Privilege gleich mit.
4. Versionen bewusst pinnen. #Requires -Modules @{ ModuleName = 'ExchangeOnlineManagement'; RequiredVersion = '<tested-version>' } am Skriptanfang macht aus „nach dem Update kaputt“ ein „startet gar nicht erst“ – der deutlich bessere Fehlerfall. Module gezielt aktualisieren, nach einem Smoke-Test, der jede genutzte Modulkombination importiert und verbindet.
5. Notlösung: Verbindungsreihenfolge und DLL-Tausch. Eine bestimmte Reihenfolge beim Verbinden oder eine neuere Microsoft.Identity.Client.dll in den Ordner eines anderen Moduls kopieren: Beides kursiert in der Community. Das hilft vielleicht über einen Tag, ist aber nicht supportet, wird beim nächsten Update-Module überschrieben und hört, wie der EXO/Graph-Fall von 2026 zeigt, ohne Vorwarnung auf zu funktionieren. Produktive Automatisierung würde ich darauf nicht bauen.
Weitere Fallstricke
- Windows PowerShell 5.1 und PowerShell 7 verhalten sich unterschiedlich. .NET Framework und .NET laden Assemblies anders; eine Kombination, die in 7 scheitert, kann in 5.1 funktionieren und umgekehrt. PnP.PowerShell 3.x setzt ohnehin PowerShell 7.4 oder neuer voraus – „dann eben 5.1“ ist für SharePoint keine Option.
-DisableWAMist keine Lösung. Seit EXO 3.7.0 ist der Web Account Manager der Standard-Broker für interaktive Anmeldungen.Connect-ExchangeOnline -DisableWAMumgeht Konflikte manchmal, Microsoft beschreibt es aber ausdrücklich als vorübergehende Maßnahme.- App-only schlägt interaktiv. Zertifikatsbasierte Anmeldung umgeht die meisten WAM-/Broker-Probleme und ist für geplante Automatisierung ohnehin das Mittel der Wahl.
- Berechtigungen addieren sich. Bei RBAC for Applications wirken Entra-Consent und Exchange-Rollenzuweisung additiv. Ein tenantweiter
Mail.Send-Consent in Entra ID hebelt deine eingegrenzte Zuweisung stillschweigend aus. - Die Exchange Online Admin API ist Preview und bewusst schmal. Sie ergänzt EXO PowerShell, ersetzt es aber nicht.
Fazit
Die Admin-Module liefern die Tiefe des jeweiligen Workloads, Graph gibt Anwendungen einen einheitlichen, nachvollziehbaren Zugang und ist damit der natürliche Ort für jede Postfach-Integration. Wenn du Module kombinierst, behandle sie wie Komponenten mit Abhängigkeiten: weniger laden, pro Prozess isolieren, Versionen pinnen und Updates testen, bevor deine Runbooks es für dich tun.
Quellen
- Resolving PowerShell module assembly dependency conflicts – Microsoft Learn
- Troubleshooting common errors in Microsoft Graph PowerShell – Microsoft Learn
- user: sendMail – Microsoft Graph v1.0
- Role Based Access Control for Applications in Exchange Online – Microsoft Learn
- Overview of the Exchange Online Admin API (preview) – Microsoft Learn
- Resolve issues in the Exchange Online PowerShell module after WAM integration – Microsoft Learn
- PnP PowerShell installation – PnP
- The Grief and Joys of New PowerShell Releases – Office 365 for IT Pros (Tony Redmond)
- Fix conflicts between PnP.PowerShell and Microsoft.Graph.Authentication – topedia
Inspiriert von einem LinkedIn-Post von Anthony Samuel.
