<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>PowerShell on DR.365</title><link>https://blog.dr365.io/tags/powershell/</link><description>Recent content in PowerShell on DR.365</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Sun, 11 Oct 2026 07:00:00 +0200</lastBuildDate><atom:link href="https://blog.dr365.io/tags/powershell/index.xml" rel="self" type="application/rss+xml"/><item><title>PowerShell or Microsoft Graph? Choosing the Right Interface and Fixing Module Conflicts</title><link>https://blog.dr365.io/p/powershell-vs-microsoft-graph-module-conflicts/</link><pubDate>Sun, 11 Oct 2026 07:00:00 +0200</pubDate><guid>https://blog.dr365.io/p/powershell-vs-microsoft-graph-module-conflicts/</guid><description>&lt;img src="https://blog.dr365.io/p/powershell-vs-microsoft-graph-module-conflicts/cover.png" alt="Featured image of post PowerShell or Microsoft Graph? Choosing the Right Interface and Fixing Module Conflicts" /&gt;&lt;p&gt;&amp;ldquo;Should I do this with PowerShell or with Graph?&amp;rdquo; breaks down into two questions: &lt;em&gt;which interface actually exposes what I need&lt;/em&gt;, and &lt;em&gt;how will this thing run in six months when nobody is watching it&lt;/em&gt;. If you&amp;rsquo;ve ever mixed the Graph PowerShell SDK with PnP.PowerShell or the Exchange Online module in one script, you know the third question, the one nobody asks until it bites: &lt;em&gt;will these modules even load together?&lt;/em&gt;&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Exchange Online PowerShell&lt;/strong&gt; for Exchange administration (mailbox permissions, transport rules, org config). Graph doesn&amp;rsquo;t cover most of it.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Microsoft Graph&lt;/strong&gt; for identity, cross-service data and anything an &lt;em&gt;application&lt;/em&gt; does: integrations, mail sending, reporting. For application access to mailboxes, Graph is the interface to build on.&lt;/li&gt;
&lt;li&gt;The classic trap when combining them: &lt;strong&gt;assembly conflicts&lt;/strong&gt; (&lt;code&gt;Microsoft.Identity.Client&lt;/code&gt;, &lt;code&gt;Microsoft.Graph.Core&lt;/code&gt;, &lt;code&gt;Azure.Core&lt;/code&gt; …) between Microsoft.Graph, PnP.PowerShell, ExchangeOnlineManagement and Az. Connection order is a lottery; &lt;strong&gt;process isolation and fewer modules&lt;/strong&gt; are the real fix.&lt;/li&gt;
&lt;/ul&gt;

 &lt;/blockquote&gt;
&lt;h2 id="what-youre-actually-choosing-between"&gt;What you&amp;rsquo;re actually choosing between
&lt;/h2&gt;&lt;p&gt;The Microsoft Graph PowerShell SDK &lt;em&gt;is&lt;/em&gt; PowerShell, and it calls Graph under the hood. So the choice is between two kinds of interface:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Service-specific admin modules:&lt;/strong&gt; ExchangeOnlineManagement, MicrosoftTeams, PnP.PowerShell for SharePoint. They talk to the admin surfaces of a workload and expose things that simply don&amp;rsquo;t exist in Graph.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Microsoft Graph:&lt;/strong&gt; one REST API for Entra ID, mail, calendars, files, Teams, Planner and much more, callable from anything: the Graph PowerShell SDK, &lt;code&gt;Invoke-RestMethod&lt;/code&gt;, C#, Python, Logic Apps or a ticketing system.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Which one to use depends first on &lt;em&gt;which surface has the capability at all&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="where-each-one-wins"&gt;Where each one wins
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Task&lt;/th&gt;
					&lt;th&gt;My pick&lt;/th&gt;
					&lt;th&gt;Why&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Full Access / Send As permissions, transport rules, org-wide Exchange settings&lt;/td&gt;
					&lt;td&gt;Exchange Online PowerShell&lt;/td&gt;
					&lt;td&gt;Graph has no endpoints for these. The new Exchange Online Admin API (preview) only covers a small subset like folder permissions and Send on Behalf.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Users, groups, licences, app registrations in Entra ID&lt;/td&gt;
					&lt;td&gt;Graph (SDK or REST)&lt;/td&gt;
					&lt;td&gt;This is Graph&amp;rsquo;s home turf.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;An application sends or reads mail (ticketing, workflows)&lt;/td&gt;
					&lt;td&gt;Graph&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;sendMail&lt;/code&gt; / message endpoints with OAuth, scoped via RBAC for Applications.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Site provisioning, templates, SharePoint permissions&lt;/td&gt;
					&lt;td&gt;PnP.PowerShell&lt;/td&gt;
					&lt;td&gt;Far richer than Graph&amp;rsquo;s SharePoint surface for admin tasks.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Teams policies (meeting, calling, messaging)&lt;/td&gt;
					&lt;td&gt;MicrosoftTeams module&lt;/td&gt;
					&lt;td&gt;Policy management lives in the Teams admin surface, not in Graph.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Cross-service reporting (users + licences + sign-ins + groups)&lt;/td&gt;
					&lt;td&gt;Graph&lt;/td&gt;
					&lt;td&gt;One API, one auth model, paging and &lt;code&gt;$select&lt;/code&gt; built in.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;My rule of thumb: &lt;strong&gt;if a person runs it, use the module that&amp;rsquo;s closest to the workload. If an application runs it, use Graph&lt;/strong&gt; – and give that application exactly the permissions it needs, nothing more.&lt;/p&gt;
&lt;h2 id="a-practical-example-mail-integrations"&gt;A practical example: mail integrations
&lt;/h2&gt;&lt;p&gt;The LinkedIn post that inspired this article describes modernising a ServiceNow mail integration so that it sends and receives mail through Microsoft Graph. Here an application, not a person, works with mailboxes, and that&amp;rsquo;s where Graph fits: OAuth, granular permissions and a clean audit trail. Sending a message is a single call:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Microsoft Graph v1.0 – send mail as the service mailbox (app-only)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Required application permission: Mail.Send (scoped via RBAC for Applications, see below)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Ticket INC0012345 updated&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Text&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Your ticket has been updated.&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;toRecipients&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;emailAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;adele.vance@contoso.com&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;saveToSentItems&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;$true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Invoke-MgGraphRequest&lt;/span&gt; &lt;span class="n"&gt;-Method&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Uri&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://graph.microsoft.com/v1.0/users/servicedesk@contoso.com/sendMail&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Body&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;ConvertTo-Json&lt;/span&gt; &lt;span class="n"&gt;-Depth&lt;/span&gt; &lt;span class="mf"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;-ContentType&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;What I&amp;rsquo;d add from a regulated-environment perspective: an app with the application permission &lt;code&gt;Mail.Send&lt;/code&gt; can, by default, send as &lt;em&gt;any&lt;/em&gt; mailbox in the tenant. That&amp;rsquo;s not something you want to explain to an auditor. Use &lt;strong&gt;RBAC for Applications in Exchange Online&lt;/strong&gt; to scope the service principal to the mailboxes it actually needs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Exchange Online PowerShell – scope a ticketing app to its service mailboxes only&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Prerequisite: the Entra app has NO tenant-wide Mail.Send consent (otherwise permissions add up)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# IDs from &amp;#34;Enterprise applications&amp;#34; in Entra, not from &amp;#34;App registrations&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;New-ServicePrincipal&lt;/span&gt; &lt;span class="n"&gt;-AppId&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;app-id&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;-ObjectId&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;service-principal-object-id&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;-DisplayName&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Ticketing&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;New-ManagementScope&lt;/span&gt; &lt;span class="n"&gt;-Name&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Ticketing-Mailboxes&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-RecipientRestrictionFilter&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;CustomAttribute10 -eq &amp;#39;Ticketing&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;New-ManagementRoleAssignment&lt;/span&gt; &lt;span class="n"&gt;-App&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;service-principal-object-id&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Role&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Application Mail.Send&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-CustomResourceScope&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Ticketing-Mailboxes&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Verify before you go live&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Test-ServicePrincipalAuthorization&lt;/span&gt; &lt;span class="n"&gt;-Identity&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;service-principal-object-id&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;-Resource&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;servicedesk@contoso.com&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Note the irony: you configure Graph permissions with Exchange PowerShell. That&amp;rsquo;s exactly what the original post means by &amp;ldquo;use both&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="module-conflicts-between-graph-pnp-and-exo"&gt;Module conflicts between Graph, PnP and EXO
&lt;/h2&gt;&lt;p&gt;Say you write a perfectly reasonable script: Graph for user data, PnP for SharePoint, EXO for mailboxes. It works on your machine. Then it fails in the next session with something like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Could not load type &amp;#39;Microsoft.Identity.Client.BaseAbstractApplicationBuilder`1&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;from assembly &amp;#39;Microsoft.Identity.Client, Version=4.50.0.0&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;or&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Method not found: &amp;#39;!0 Microsoft.Identity.Client.BaseAbstractApplicationBuilder`1.WithLogging(...)&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="why-it-happens"&gt;Why it happens
&lt;/h3&gt;&lt;p&gt;Every one of these modules is a binary module that ships its own copies of shared .NET libraries – most notably &lt;strong&gt;MSAL&lt;/strong&gt; (&lt;code&gt;Microsoft.Identity.Client.dll&lt;/code&gt;), but also &lt;code&gt;Azure.Core&lt;/code&gt;, &lt;code&gt;Azure.Identity&lt;/code&gt;, &lt;code&gt;Microsoft.Graph.Core&lt;/code&gt; or &lt;code&gt;System.Text.Json&lt;/code&gt;. PowerShell loads assemblies into one shared context per process, and &lt;strong&gt;only one version of an assembly with a given name can be loaded there&lt;/strong&gt;. Whichever module loads it first wins; the other module then runs against a version it was never built for.&lt;/p&gt;
&lt;p&gt;Microsoft&amp;rsquo;s own PowerShell documentation describes exactly this: the outcome depends on which module was loaded first, which code path ran, and whether the dependency is loaded at import or only on first use. That&amp;rsquo;s why the same script works, fails, and works again depending on what you did earlier in the session.&lt;/p&gt;
&lt;p&gt;Known combinations that have caused trouble:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PnP.PowerShell + Microsoft.Graph&lt;/strong&gt;: older PnP builds bundled an outdated &lt;code&gt;Microsoft.Graph.Core&lt;/code&gt; (1.25.x) and older MSAL versions, which broke &lt;code&gt;Connect-MgGraph&lt;/code&gt; and &lt;code&gt;Invoke-MgGraphRequest&lt;/code&gt; once PnP was loaded.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ExchangeOnlineManagement + Microsoft.Graph&lt;/strong&gt;: a long-running story. EXO 3.5.1 introduced a &lt;code&gt;System.Text.Json&lt;/code&gt; clash; in June 2026 Tony Redmond reported that with Graph SDK 2.38 and EXO 3.10, &lt;em&gt;neither&lt;/em&gt; connection order worked reliably in PowerShell 7: one order fails with the &lt;code&gt;Method not found&lt;/code&gt; error above, the other with a &lt;code&gt;NullReferenceException&lt;/code&gt; while acquiring a token.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Az.Accounts + Microsoft.Graph&lt;/strong&gt;: same pattern with &lt;code&gt;Azure.Core&lt;/code&gt; / &lt;code&gt;Azure.Identity&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="how-to-recognise-it"&gt;How to recognise it
&lt;/h3&gt;&lt;p&gt;First check: what is actually loaded in the current session?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Which version of the usual suspects is loaded in this session – and from which module folder?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$suspects&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Microsoft.Identity.Client&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Microsoft.Graph.Core&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Azure.Core&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Azure.Identity&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;System.Text.Json&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;System.AppDomain&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;CurrentDomain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;GetAssemblies&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Where-Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="py"&gt;Name&lt;/span&gt; &lt;span class="n"&gt;-in&lt;/span&gt; &lt;span class="nv"&gt;$suspects&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Select-Object&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Assembly&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="py"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="py"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Location&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;If &lt;code&gt;Location&lt;/code&gt; points into the PnP folder while you&amp;rsquo;re calling &lt;code&gt;Connect-MgGraph&lt;/code&gt;, you&amp;rsquo;ve found your culprit.&lt;/p&gt;
&lt;p&gt;Second check: which versions do your installed modules ship? This inventory makes conflicts visible &lt;em&gt;before&lt;/em&gt; they hit production:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$modules&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Microsoft.Graph.Authentication&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;PnP.PowerShell&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ExchangeOnlineManagement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Az.Accounts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;MicrosoftTeams&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$dlls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Microsoft.Identity.Client.dll&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Microsoft.Graph.Core.dll&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Azure.Core.dll&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Azure.Identity.dll&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Get-Module&lt;/span&gt; &lt;span class="n"&gt;-ListAvailable&lt;/span&gt; &lt;span class="n"&gt;-Name&lt;/span&gt; &lt;span class="nv"&gt;$modules&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;ForEach-Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$module&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Get-ChildItem&lt;/span&gt; &lt;span class="n"&gt;-Path&lt;/span&gt; &lt;span class="nv"&gt;$module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ModuleBase&lt;/span&gt; &lt;span class="n"&gt;-Recurse&lt;/span&gt; &lt;span class="n"&gt;-Include&lt;/span&gt; &lt;span class="nv"&gt;$dlls&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="n"&gt;-ErrorAction&lt;/span&gt; &lt;span class="n"&gt;SilentlyContinue&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;ForEach-Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;pscustomobject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Assembly&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;BaseName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;System.Reflection.AssemblyName&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;GetAssemblyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="py"&gt;Version&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;FullName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Sort-Object&lt;/span&gt; &lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Format-Table&lt;/span&gt; &lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Module&lt;/span&gt; &lt;span class="n"&gt;-AutoSize&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Some modules ship separate builds for .NET Framework and .NET, so you may see two entries per module. What matters is when the &lt;em&gt;same&lt;/em&gt; assembly shows up with different versions across modules.&lt;/p&gt;
&lt;h3 id="how-to-solve-it"&gt;How to solve it
&lt;/h3&gt;&lt;p&gt;In order of preference:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Don&amp;rsquo;t load what you don&amp;rsquo;t need.&lt;/strong&gt; If PnP.PowerShell is already connected, you often don&amp;rsquo;t need the Graph SDK at all. &lt;code&gt;Invoke-PnPGraphMethod&lt;/code&gt; calls Graph with PnP&amp;rsquo;s connection (provided your PnP app registration has the required Graph permissions). And if you do need the SDK, install &lt;code&gt;Microsoft.Graph.Authentication&lt;/code&gt; plus the one or two submodules you use (or just &lt;code&gt;Invoke-MgGraphRequest&lt;/code&gt;) instead of the full &lt;code&gt;Microsoft.Graph&lt;/code&gt; meta-module.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Isolate per process.&lt;/strong&gt; Assemblies are per process, so separate processes can&amp;rsquo;t conflict. In PowerShell 7, &lt;code&gt;Start-Job&lt;/code&gt; runs in a child process:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Graph work in its own process – app-only auth with a certificate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Required application permission: User.Read.All&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$auth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ClientId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;app-id&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;TenantId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;contoso.onmicrosoft.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;Thumbprint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;cert-thumbprint&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Start-Job&lt;/span&gt; &lt;span class="n"&gt;-ArgumentList&lt;/span&gt; &lt;span class="nv"&gt;$auth&lt;/span&gt; &lt;span class="n"&gt;-ScriptBlock&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Connect-MgGraph&lt;/span&gt; &lt;span class="n"&gt;-ClientId&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ClientId&lt;/span&gt; &lt;span class="n"&gt;-TenantId&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;TenantId&lt;/span&gt; &lt;span class="n"&gt;-CertificateThumbprint&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Thumbprint&lt;/span&gt; &lt;span class="n"&gt;-NoWelcome&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Get-MgUser&lt;/span&gt; &lt;span class="n"&gt;-Filter&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;accountEnabled eq true&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;-All&lt;/span&gt; &lt;span class="n"&gt;-Property&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userPrincipalName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Select-Object&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserPrincipalName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Department&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Receive-Job&lt;/span&gt; &lt;span class="n"&gt;-Wait&lt;/span&gt; &lt;span class="n"&gt;-AutoRemoveJob&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Main session: PnP only – Graph&amp;#39;s assemblies were never loaded here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Connect-PnPOnline&lt;/span&gt; &lt;span class="n"&gt;-Url&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://contoso.sharepoint.com/sites/hr&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-ClientId&lt;/span&gt; &lt;span class="nv"&gt;$auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ClientId&lt;/span&gt; &lt;span class="n"&gt;-Tenant&lt;/span&gt; &lt;span class="nv"&gt;$auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;TenantId&lt;/span&gt; &lt;span class="n"&gt;-Thumbprint&lt;/span&gt; &lt;span class="nv"&gt;$auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Thumbprint&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The job returns deserialised objects (properties yes, methods no), which is exactly what you want for passing data between steps. Important: &lt;strong&gt;&lt;code&gt;Start-ThreadJob&lt;/code&gt; and &lt;code&gt;ForEach-Object -Parallel&lt;/code&gt; do &lt;em&gt;not&lt;/em&gt; isolate&lt;/strong&gt;. They run in the same process and share the loaded assemblies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Split runbooks and pipelines.&lt;/strong&gt; In Azure Automation, use separate runtime environments (PowerShell 7.4) per module set rather than one &amp;ldquo;everything&amp;rdquo; environment. In CI/CD, one job per workload. This also gives you cleaner permissions per identity, which helps with least privilege.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Pin versions deliberately.&lt;/strong&gt; &lt;code&gt;#Requires -Modules @{ ModuleName = 'ExchangeOnlineManagement'; RequiredVersion = '&amp;lt;tested-version&amp;gt;' }&lt;/code&gt; at the top of a script turns &amp;ldquo;it broke after an update&amp;rdquo; into &amp;ldquo;it refused to start&amp;rdquo;, which is a much better failure mode. Update modules on purpose, after a smoke test that imports and connects every module combination you use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Last resort: connection order and DLL swapping.&lt;/strong&gt; Connecting in a particular order, or copying a newer &lt;code&gt;Microsoft.Identity.Client.dll&lt;/code&gt; into another module&amp;rsquo;s folder, is circulated widely in the community. It can get you through a day, but it&amp;rsquo;s unsupported, it&amp;rsquo;s overwritten by the next &lt;code&gt;Update-Module&lt;/code&gt;, and, as the 2026 EXO/Graph case shows, it stops working without warning. I wouldn&amp;rsquo;t build production automation on it.&lt;/p&gt;
&lt;h2 id="other-pitfalls"&gt;Other pitfalls
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Windows PowerShell 5.1 and PowerShell 7 behave differently. Assembly loading differs between .NET Framework and .NET, so a combination that fails in 7 can work in 5.1 and vice versa. PnP.PowerShell 3.x requires PowerShell 7.4 or later anyway, so &amp;ldquo;just use 5.1&amp;rdquo; isn&amp;rsquo;t an option for SharePoint.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-DisableWAM&lt;/code&gt; is not a fix. Since EXO 3.7.0, Web Account Manager is the default broker for interactive sign-in. Disabling it with &lt;code&gt;Connect-ExchangeOnline -DisableWAM&lt;/code&gt; sometimes sidesteps conflicts, but Microsoft explicitly describes it as a temporary measure.&lt;/li&gt;
&lt;li&gt;App-only beats interactive for automation. Certificate-based auth avoids most WAM/broker issues and is what scheduled automation should use anyway.&lt;/li&gt;
&lt;li&gt;Permissions add up. With RBAC for Applications, Entra consent and Exchange role assignments are additive. A tenant-wide &lt;code&gt;Mail.Send&lt;/code&gt; consent in Entra ID silently defeats your scoped assignment.&lt;/li&gt;
&lt;li&gt;The Exchange Online Admin API is &lt;strong&gt;preview&lt;/strong&gt; and deliberately narrow. It complements EXO PowerShell; it doesn&amp;rsquo;t replace it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="wrap-up"&gt;Wrap-up
&lt;/h2&gt;&lt;p&gt;Admin modules give you the depth of each workload; Graph gives applications a single, auditable way in, which makes it the natural home for every mailbox integration. When you combine modules, treat them like components with dependencies: load less, isolate per process, pin versions and test updates before your runbooks do it for you.&lt;/p&gt;
&lt;h2 id="references"&gt;References
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/powershell/scripting/dev-cross-plat/resolving-dependency-conflicts" target="_blank" rel="noopener"
 &gt;Resolving PowerShell module assembly dependency conflicts – Microsoft Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/powershell/microsoftgraph/troubleshooting" target="_blank" rel="noopener"
 &gt;Troubleshooting common errors in Microsoft Graph PowerShell – Microsoft Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/graph/api/user-sendmail" target="_blank" rel="noopener"
 &gt;user: sendMail – Microsoft Graph v1.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/exchange/permissions-exo/application-rbac" target="_blank" rel="noopener"
 &gt;Role Based Access Control for Applications in Exchange Online – Microsoft Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/exchange/reference/admin-api-overview" target="_blank" rel="noopener"
 &gt;Overview of the Exchange Online Admin API (preview) – Microsoft Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/troubleshoot/exchange/administration/wam-integration-issues" target="_blank" rel="noopener"
 &gt;Resolve issues in the Exchange Online PowerShell module after WAM integration – Microsoft Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pnp.github.io/powershell/articles/installation.html" target="_blank" rel="noopener"
 &gt;PnP PowerShell installation – PnP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://office365itpros.com/2026/06/22/powershell-woes-and-cmdlets/" target="_blank" rel="noopener"
 &gt;The Grief and Joys of New PowerShell Releases – Office 365 for IT Pros (Tony Redmond)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog-en.topedia.com/2024/04/fix-conflicts-between-pnppowershell-and-microsoftgraphauthentication-module/" target="_blank" rel="noopener"
 &gt;Fix conflicts between PnP.PowerShell and Microsoft.Graph.Authentication – topedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Inspired by a LinkedIn post by &lt;a class="link" href="https://www.linkedin.com/posts/anthony-samuel-m365-iam_powershell-vs-microsoft-graph-which-share-7514768232751243264-hjWG" target="_blank" rel="noopener"
 &gt;Anthony Samuel&lt;/a&gt;.&lt;/p&gt;</description></item></channel></rss>