SCRIPT LIBRARY · POWERSHELL
Clean Reinstall of the Microsoft Graph PowerShell Modules
When Graph cmdlets start throwing assembly errors, it's almost always mixed module versions. This removes every copy and puts back one matching set.
- What it does
- Finds every Microsoft.Graph module installed through PowerShellGet, uninstalls all versions in a safe order, reinstalls the SDK (and optionally the beta modules), then checks that everything is on the same version.
- Requires
- PowerShell 7.2+ or Windows PowerShell 5.1
- PowerShellGet (built in) and access to the PowerShell Gallery
- Permissions
- None for CurrentUser installs. Run elevated for AllUsers, or to remove copies that were installed for all users.
- Runs on
- Windows, macOS, Linux
- Tested
- Parse-checked and dry-run with mocked PowerShellGet cmdlets in PowerShell 7.4
Part 1 of the thread The Microsoft Graph toolbox
You run Connect-MgGraph and get "Could not load file or assembly Microsoft.Graph.Authentication". Or Get-MgUser suddenly complains about a method that doesn't exist. Nine times out of ten, the cause is the same: you've got more than one version of the Graph modules installed, and PowerShell loaded the authentication module from one and the users module from another.
The obvious fix, Uninstall-Module Microsoft.Graph -AllVersions, doesn't do much. Microsoft.Graph is just a small roll-up module. The thirty-odd Microsoft.Graph.* sub-modules that do the actual work stay right where they were, still mismatched.
This script removes all of them, every version, with Microsoft.Graph.Authentication last since everything else depends on it. Then it reinstalls a clean set and checks the versions line up. It also catches the long-retired Microsoft.Graph.Intune module, if it's still lurking, since it matches the same name pattern. (The original version of this post also closed all your PSSessions first. That doesn't hurt, but it doesn't help either. Remote sessions have nothing to do with which modules are loaded locally.)
<#
.SYNOPSIS
Removes every installed version of the Microsoft Graph PowerShell SDK and installs one
clean, matching set.
.DESCRIPTION
Uninstall-Module Microsoft.Graph only removes the small roll-up module. The 30-plus
Microsoft.Graph.* sub-modules stay behind, and mismatched versions of those are the usual
cause of "Could not load file or assembly" and "method not found" errors. This script
finds every Microsoft.Graph* module installed through PowerShellGet, removes all versions
(Microsoft.Graph.Authentication last, since everything depends on it), reinstalls, and
checks that the versions line up. Supports -WhatIf.
.PARAMETER Scope
Where to reinstall: CurrentUser (no admin needed) or AllUsers (run elevated).
.PARAMETER IncludeBeta
Also reinstall Microsoft.Graph.Beta. Beta modules are always removed if they're found.
.PARAMETER SkipInstall
Only remove. Useful if you want to install a specific version yourself afterwards.
.EXAMPLE
.\Repair-GraphModule.ps1 -WhatIf
.EXAMPLE
.\Repair-GraphModule.ps1 -Scope AllUsers -IncludeBeta
#>
[CmdletBinding(SupportsShouldProcess)]
param(
[ValidateSet('CurrentUser', 'AllUsers')]
[string]$Scope = 'CurrentUser',
[switch]$IncludeBeta,
[switch]$SkipInstall
)
# Loaded assemblies can't be unloaded, so a module that's in use can't be removed cleanly.
$loaded = @(Get-Module -Name 'Microsoft.Graph*')
if ($loaded) {
throw "Graph modules are loaded in this session ($($loaded.Name -join ', ')). Open a fresh window with 'pwsh -NoProfile' (or 'powershell -NoProfile') and run this again."
}
$installed = @(Get-InstalledModule -Name 'Microsoft.Graph*' -ErrorAction SilentlyContinue)
Write-Verbose "Found $($installed.Count) Graph module(s) installed through PowerShellGet."
# Everything else first, the authentication module last.
$ordered = @($installed | Where-Object Name -ne 'Microsoft.Graph.Authentication') + @($installed | Where-Object Name -eq 'Microsoft.Graph.Authentication')
foreach ($module in $ordered) {
$row = [pscustomobject]@{ Module = $module.Name; Action = 'Uninstall (all versions)'; Version = $module.Version; Result = $null }
if ($PSCmdlet.ShouldProcess($module.Name, 'Uninstall all versions')) {
try {
Uninstall-Module -Name $module.Name -AllVersions -Force -ErrorAction Stop
$row.Result = 'Done'
}
catch { $row.Result = "Failed: $($_.Exception.Message)" }
}
else { $row.Result = 'WhatIf' }
$row
}
if ($SkipInstall) { return }
$toInstall = @('Microsoft.Graph')
if ($IncludeBeta) { $toInstall += 'Microsoft.Graph.Beta' }
foreach ($name in $toInstall) {
$row = [pscustomobject]@{ Module = $name; Action = "Install ($Scope)"; Version = $null; Result = $null }
if ($PSCmdlet.ShouldProcess($name, "Install for $Scope")) {
try {
Install-Module -Name $name -Scope $Scope -Repository PSGallery -AllowClobber -Force -ErrorAction Stop
$row.Version = (Get-InstalledModule -Name $name).Version
$row.Result = 'Done'
}
catch { $row.Result = "Failed: $($_.Exception.Message)" }
}
else { $row.Result = 'WhatIf' }
$row
}
# Sanity check: every Graph sub-module should be on the same version.
if (-not $WhatIfPreference) {
$versions = @(Get-InstalledModule -Name 'Microsoft.Graph*' -ErrorAction SilentlyContinue | Where-Object Name -notlike 'Microsoft.Graph.Beta*' | Select-Object -ExpandProperty Version -Unique)
if ($versions.Count -gt 1) {
Write-Warning "Graph modules are still on mixed versions: $($versions -join ', '). Check for copies in other module paths with: Get-Module Microsoft.Graph* -ListAvailable | Select-Object Name, Version, ModuleBase"
}
elseif ($versions.Count -eq 1) {
Write-Host "All Microsoft.Graph modules are on version $($versions[0])." -ForegroundColor Green
}
}
Parameters
| Parameter | Type | Default | What it's for |
|---|---|---|---|
-Scope | string | CurrentUser | Where to reinstall. CurrentUser needs no admin rights. AllUsers does, and makes the modules available to every account on the machine, including scheduled tasks running as SYSTEM. |
-IncludeBeta | switch | — | Also reinstall Microsoft.Graph.Beta. Beta modules are removed either way if they're found. |
-SkipInstall | switch | — | Remove everything and stop, so you can install a specific version yourself. |
-WhatIf | switch | — | List what would be removed and installed without doing any of it. |
Run it
See what's installed and what would happen.
.\Repair-GraphModule.ps1 -WhatIfThe usual fix, from a fresh window with no profile.
pwsh -NoProfile -File .\Repair-GraphModule.ps1A shared admin box or automation server, beta modules included.
.\Repair-GraphModule.ps1 -Scope AllUsers -IncludeBetaPin to a specific release afterwards.
.\Repair-GraphModule.ps1 -SkipInstall; Install-Module Microsoft.Graph -RequiredVersion 2.25.0 -Scope CurrentUserWhat you'll see
Module Action Version Result
------ ------ ------- ------
Microsoft.Graph Uninstall (all versions) 2.19.0 Done
Microsoft.Graph.Groups Uninstall (all versions) 2.19.0 Done
Microsoft.Graph.Intune Uninstall (all versions) 6.1907.1.0 Done
Microsoft.Graph.Users Uninstall (all versions) 2.24.0 Done
Microsoft.Graph.Authentication Uninstall (all versions) 2.24.0 Done
Microsoft.Graph Install (CurrentUser) 2.30.0 Done
All Microsoft.Graph modules are on version 2.30.0.
How it works
- Refuse to fight a loaded module. If any
Microsoft.Graph*module is already in the session, the script stops and tells you to open a fresh window. Trying to uninstall in-use modules is how you end up with half-deleted folders. - Find everything.
Get-InstalledModule -Name 'Microsoft.Graph*'catches the roll-up, every sub-module, the beta modules and the oldMicrosoft.Graph.Intune. - Remove in dependency order. Everything goes first,
Microsoft.Graph.Authenticationgoes last, each with-AllVersions. A module that can't be removed becomes a Failed row instead of stopping the run. - Reinstall one set.
Install-Module Microsoft.Graphpulls every sub-module at the same version, plusMicrosoft.Graph.Betaif you asked for it. - Check the result. It compares the versions of everything that's installed now and warns if they still don't match.
Take it further
- Only install what you use. If a script only needs users and groups, install
Microsoft.Graph.UsersandMicrosoft.Graph.Groups(they pull inMicrosoft.Graph.Authenticationon their own). It's much faster and gives version drift less to work with. - Move to PSResourceGet.
Install-PSResourceandUninstall-PSResourceare the newer replacements for the PowerShellGet cmdlets and noticeably quicker with a module set this size. - Pin versions on automation servers. Scheduled jobs are happiest with a known version. Install it with
-RequiredVersionand update on purpose, not whenever someone runsUpdate-Module.
Things that'll trip you up
- Start from a clean session. Once a Graph module is loaded, its DLLs are locked until the process exits. The script refuses to run if any are loaded. Open a new window with -NoProfile, because a profile that imports Graph will load it before you get a prompt.
- Windows PowerShell and PowerShell 7 keep separate modules. They use different module folders. If you use both, run the script in each one, or you'll fix one and wonder why the other is still broken.
- AllUsers copies need admin to remove. If some of the modules were installed for all users, a non-elevated run shows them as Failed. Run it again as administrator to finish the job.
- It only sees what PowerShellGet installed. Modules copied into a folder by hand, or bundled inside another tool, won't show up in Get-InstalledModule. If the version warning still fires, the command it suggests will show you where those strays live.
- It takes a while. The full SDK is a lot of modules. Removing and reinstalling it can take several minutes, and that's normal. Nothing's hung.