<# .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 } }