How To

PowerShell 7 vs Windows PowerShell 5.1 on Windows Server 2025

Most admins install PowerShell 7 on a new server expecting it to take over from Windows PowerShell 5.1, and it doesn’t. The two shells sit side by side on Windows Server 2025. Every role module Microsoft ships still lives in the 5.1 module folder. The PowerShell 7 vs Windows PowerShell 5.1 question on a server is really a question about those modules: which ones PowerShell 7 can load into its own process, which ones it quietly hands to a hidden 5.1 process, and which ones it refuses outright.

Original content from computingforgeeks.com - post 180729

We answered that on a lab VM with the DHCP Server and Hyper-V roles, the RSAT tools for Active Directory, Group Policy and WSUS, and Windows Server Backup installed, then imported each module in PowerShell 7 and timed it. The article explains how the compatibility layer decides, what the -UseWindowsPowerShell session costs you, and how Microsoft Update keeps PowerShell 7 patched once you opt in. The behavior described here was checked against PowerShell 7.6 LTS on Windows Server 2025 Datacenter (build 26100) in October 2026, starting from 7.6.6 and letting Microsoft Update move it to 7.6.9.

What differs between PowerShell 7 and Windows PowerShell 5.1 on a server

The difference that matters is the runtime. Windows PowerShell 5.1 runs on .NET Framework 4.8.1, ships inside Windows and is patched by the monthly cumulative update. PowerShell 7 runs on modern .NET (the 7.6 LTS line is built on .NET 10), installs separately and has its own support clock. A module that was compiled against .NET Framework types can only run where those types exist, so the runtime split is what drives every compatibility result later in this article.

The two compare like this on the same Server 2025 box, with the startup row measured on our lab VM:

ItemWindows PowerShell 5.1PowerShell 7 (7.6 LTS)
Executablepowershell.exepwsh.exe
Runtime.NET Framework 4.8.1 (CLR 4.0.30319).NET 10
PSEditionDesktopCore
Installed on Server 2025Yes, part of the OSNo, MSI or MSIX install
Patched byWindows cumulative updateMicrosoft Update or WSUS (MSI installs only)
Support endsWith the Windows release14 November 2028 for 7.6 LTS
AllUsers module folderC:\Program Files\WindowsPowerShell\ModulesC:\Program Files\PowerShell\Modules
Startup on our 2 vCPU VM (median of 5)2,054 ms798 ms

The startup row surprised us, because the usual complaint is that pwsh is the slow one. On this box powershell.exe -NoProfile -Command exit took 1,947 to 2,437 ms across five runs while pwsh.exe took 570 to 865 ms, with one 1,765 ms outlier. That advantage disappears the moment a script touches a module that has to go through the compatibility layer, which we measured further down. If you’re comparing the two for performance, measure your own module mix rather than an empty shell.

You can check what a server already has before installing anything. A fresh Server 2025 install reports only 5.1, and Get-Command pwsh returns nothing:

$PSVersionTable
Get-Command pwsh -ErrorAction SilentlyContinue

Our VM, a clone of our Windows Server template on Proxmox, printed the 5.1 table, trimmed here to the rows that matter, and nothing for pwsh:

PSVersion                 5.1.26100.7462
PSEdition                 Desktop
CLRVersion                4.0.30319.42000

Windows PowerShell 2.0 is no longer part of Server 2025 either. Microsoft removed it with the September 2025 update, and on our box Get-WindowsFeature PowerShell-V2 returned nothing at all. Microsoft says a script that still calls powershell.exe -Version 2 now gets 5.1 instead, so only scripts that depend on 2.0 behavior break, but the flag is worth removing whichever shell you settle on.

How to install PowerShell 7 so Microsoft Update services it

Use the MSI package on a server. Microsoft’s own install page calls it the best choice for Windows Server, and it’s the only package that registers with Microsoft Update. The winget route installs the MSIX package by default, which Microsoft documents as per-user and unable to act as a remoting endpoint, and the Microsoft Update servicing described below is documented for the MSI only. If you do want winget, our winget guide covers the client, and --installer-type wix forces the MSI. Starting with 7.7 there is no MSI at all, so the 7.6 LTS line is the one to standardise servers on for now.

The PowerShell repo publishes its current release tags in tools/metadata.json, and LTSReleaseTag lists both supported LTS lines. Sort them and take the newest so the command keeps working after the next patch release. Run this in an elevated Windows PowerShell 5.1 session:

$tag = (Invoke-RestMethod https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/metadata.json).LTSReleaseTag | Sort-Object { [version]$_.TrimStart('v') } | Select-Object -Last 1
$ver = $tag.TrimStart('v')
$tag
$ver

On the day we tested, the file held {v7.4.22, v7.6.9} and the sort picked the 7.6 line:

v7.6.9
7.6.9

Download the x64 MSI for that tag. On our lab link the 112 MB file took 9 seconds:

$ProgressPreference = 'SilentlyContinue'
$msi = "C:\lab\PowerShell-$ver-win-x64.msi"
New-Item -ItemType Directory C:\lab -Force | Out-Null
Invoke-WebRequest "https://github.com/PowerShell/PowerShell/releases/download/$tag/PowerShell-$ver-win-x64.msi" -OutFile $msi -UseBasicParsing

Two MSI properties control servicing. USE_MU=1 lets Microsoft Update, WSUS or Configuration Manager deliver PowerShell updates, and ENABLE_MU=1 turns on the Microsoft Update service itself so Automatic Updates checks it. Microsoft documents both as defaulting to 1, but we pass them explicitly in scripts so the intent is recorded in the install command and the MSI log:

Start-Process msiexec.exe -ArgumentList "/i $msi /qn ADD_PATH=1 USE_MU=1 ENABLE_MU=1 /l*v C:\lab\pwsh-msi.log" -Wait

We deliberately installed 7.6.6, three patch releases behind, so we could watch Microsoft Update offer the newer build later; to do the same, set $tag and $ver to an older 7.6 release by hand before the download. The install took 85 seconds. The verbose log shows what ENABLE_MU really does: it schedules a custom action that runs a script shipped inside the package.

Select-String -Path C:\lab\pwsh-msi.log -Pattern 'Adding EnableMU property|Installation success or error status' | ForEach-Object { $_.Line }

The EnableMU action calls RegisterMicrosoftUpdate.ps1 with the freshly installed pwsh.exe, and status 0 means the product installed cleanly. The two matching lines, with the MSI timestamp prefixes trimmed:

PROPERTY CHANGE: Adding EnableMU property. Its value is '"C:\Program Files\PowerShell\7\pwsh.exe" -NoProfile -ExecutionPolicy Bypass -File "C:\Program Files\PowerShell\7\RegisterMicrosoftUpdate.ps1"'
Product: PowerShell 7-x64 ... Product Version: 7.6.6.0 ... Installation success or error status: 0

The MSI also records your choices in the registry, which is the quickest way to audit a fleet later. Every property we passed shows up under InstallerProperties:

Get-ItemProperty HKLM:\SOFTWARE\Microsoft\PowerShellCore\InstallerProperties | Select-Object AddToPath,EnablePSRemoting,UseMU,EnableMU

Both servicing flags read 1. EnablePSRemoting is 0 because we did not ask the installer to register a PowerShell 7 remoting endpoint:

AddToPath        : 1
EnablePSRemoting : 0
UseMU            : 1
EnableMU         : 1

If either servicing flag reads 0 on one of your servers, that install was done without the opt-in, and it’s the first thing to check when one box sits on an older 7.6 build than the rest of the fleet.

Which Server 2025 modules load natively in PowerShell 7

Ten of the sixteen modules we tried loaded straight into the PowerShell 7 process, three went through the compatibility session, and three were refused. The deciding factor is a single manifest key, CompatiblePSEditions. When a module lives in the Windows system module folder and its manifest doesn’t list Core, PowerShell 7 will not load it in-process. It imports the module inside a background 5.1 process instead, unless the module is on a deny list.

To repeat the test, save the script below. It imports each module, records whether PowerShell printed a compatibility warning, and times the import. Open the file in Notepad first:

notepad C:\lab\04-modules.ps1

Paste the script and save it. This is the exact file we ran:

$ProgressPreference='SilentlyContinue'
"PS " + $PSVersionTable.PSVersion.ToString()
$mods = 'ServerManager','DhcpServer','Hyper-V','ActiveDirectory','GroupPolicy','ScheduledTasks','NetAdapter','NetSecurity','Storage','SmbShare','WindowsServerBackup','PKI','Dism','UpdateServices','BestPractices','PSScheduledJob'
foreach ($m in $mods) {
  $w = $null
  $t = Measure-Command {
    try { Import-Module $m -ErrorAction Stop -WarningVariable w -WarningAction SilentlyContinue; $script:err = $null }
    catch { $script:err = $_.Exception.Message }
  }
  if ($script:err) { "{0,-20} FAILED  {1,6} ms  {2}" -f $m, [int]$t.TotalMilliseconds, $script:err; continue }
  $g = Get-Module $m | Select-Object -First 1
  $how = if ($w) { 'compat' } else { 'native' }
  "{0,-20} {1,-7} {2,6} ms  {3}  {4}" -f $m, $how, [int]$t.TotalMilliseconds, $g.ModuleType, $g.Path
  if ($w) { "    WARN: $w" }
}
"=== sessions"
Get-PSSession | Format-Table Id,Name,ComputerName,ConfigurationName,State -AutoSize | Out-String -Width 200

Run it with PowerShell 7 in a fresh process, so no module is already loaded from an earlier session:

pwsh -NoProfile -File C:\lab\04-modules.ps1

Our run, with the repeated warning lines kept and the proxy-module GUIDs shortened:

PS 7.6.6
ServerManager        compat    7693 ms  Script  C:\WINDOWS\SystemTemp\remoteIpMoProxy_ServerManager_2.0.0.0_localhost_53049719-...\remoteIpMoProxy_ServerManager_2.0.0.0_localhost_53049719-....psm1
    WARN: Module ServerManager is loaded in Windows PowerShell using WinPSCompatSession remoting session; please note that all input and output of commands from this module will be deserialized objects. If you want to load this module into PowerShell please use 'Import-Module -SkipEditionCheck' syntax.
DhcpServer           compat    4314 ms  Script  C:\WINDOWS\SystemTemp\remoteIpMoProxy_DhcpServer_2.0.0.0_localhost_53049719-...\remoteIpMoProxy_DhcpServer_2.0.0.0_localhost_53049719-....psm1
    WARN: Module DhcpServer is loaded in Windows PowerShell using WinPSCompatSession remoting session; please note that all input and output of commands from this module will be deserialized objects. If you want to load this module into PowerShell please use 'Import-Module -SkipEditionCheck' syntax.
Hyper-V              native     977 ms  Manifest  C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Hyper-V\2.0.0.0\Hyper-V.psd1
ActiveDirectory      compat    2031 ms  Manifest  C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\ActiveDirectory\ActiveDirectory.psd1
    WARN: Error initializing default drive: 'Unable to find a default server with Active Directory Web Services running.'.
GroupPolicy          compat    2096 ms  Script  C:\WINDOWS\SystemTemp\remoteIpMoProxy_GroupPolicy_1.0.0.0_localhost_53049719-...\remoteIpMoProxy_GroupPolicy_1.0.0.0_localhost_53049719-....psm1
    WARN: Module GroupPolicy is loaded in Windows PowerShell using WinPSCompatSession remoting session; please note that all input and output of commands from this module will be deserialized objects. If you want to load this module into PowerShell please use 'Import-Module -SkipEditionCheck' syntax.
ScheduledTasks       native    1473 ms  Manifest  C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\ScheduledTasks\ScheduledTasks.psd1
NetAdapter           native    4898 ms  Manifest  C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\NetAdapter\NetAdapter.psd1
NetSecurity          native    4335 ms  Manifest  C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\NetSecurity\NetSecurity.psd1
Storage              native    6272 ms  Manifest  C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Storage\Storage.psd1
SmbShare             native    2961 ms  Manifest  C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\SmbShare\SmbShare.psd1
WindowsServerBackup  native     702 ms  Binary  C:\WINDOWS\Microsoft.NET\assembly\GAC_MSIL\wsbcmdlet\v4.0_10.0.0.0__31bf3856ad364e35\wsbcmdlet.dll
PKI                  native    1071 ms  Manifest  C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PKI\PKI.psd1
Dism                 native    1481 ms  Script  C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Dism\Dism.psm1
UpdateServices       FAILED      48 ms  Module 'UpdateServices' is blocked from loading using Windows PowerShell compatibility feature by a 'WindowsPowerShellCompatibilityModuleDenyList' setting in PowerShell configuration file.
BestPractices        FAILED      56 ms  Module 'BestPractices' is blocked from loading using Windows PowerShell compatibility feature by a 'WindowsPowerShellCompatibilityModuleDenyList' setting in PowerShell configuration file.
PSScheduledJob       FAILED     121 ms  Module 'PSScheduledJob' is blocked from loading using Windows PowerShell compatibility feature by a 'WindowsPowerShellCompatibilityModuleDenyList' setting in PowerShell configuration file.
=== sessions

Id Name               ComputerName ConfigurationName  State
-- ----               ------------ -----------------  -----
 2 WinPSCompatSession localhost                      Opened

Read the path column, not our compat label. A compatibility module’s path points at a generated remoteIpMoProxy_*.psm1 file in the temp folder (C:\WINDOWS\SystemTemp here, because the test ran as SYSTEM; an interactive admin sees $Env:TEMP), and its type changes to Script, because what PowerShell 7 loaded is a proxy, not the module. ActiveDirectory is a false positive in our script: it printed a warning because the lab VM is not a domain member and found no Active Directory Web Services, but its path is the real ActiveDirectory.psd1, so it loaded natively. The import times come from a 2 vCPU guest on a busy host, so treat them as relative rather than absolute.

The manifests explain the split. We asked PowerShell 7 for each module’s CompatiblePSEditions and path with Get-Module -ListAvailable, which is also a quick way to predict how a module will load before you import it:

foreach ($m in 'ServerManager','DhcpServer','GroupPolicy','Hyper-V','ActiveDirectory','ScheduledTasks','NetAdapter','Storage','UpdateServices','PKI') {
  $mod = Get-Module -ListAvailable $m | Select-Object -First 1
  "{0,-16} {1,-12} {2}" -f $m, (($mod.CompatiblePSEditions) -join ','), $mod.Path
}

Modules that declare Desktop,Core come back with their real path and load natively. The other four come back with neither an edition nor a path, because PowerShell 7 leaves a module in the Windows system folder out of -ListAvailable when its manifest doesn’t list Core. Those four are exactly the ones it proxied or blocked:

ServerManager
DhcpServer
GroupPolicy
Hyper-V          Desktop,Core C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Hyper-V\2.0.0.0\Hyper-V.psd1
ActiveDirectory  Desktop,Core C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\ActiveDirectory\ActiveDirectory.psd1
ScheduledTasks   Desktop,Core C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\ScheduledTasks\ScheduledTasks.psd1
NetAdapter       Desktop,Core C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\NetAdapter\NetAdapter.psd1
Storage          Desktop,Core C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Storage\Storage.psd1
UpdateServices
PKI              Desktop,Core C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PKI\PKI.psd1

The screenshot shows the same check without the path column, run after Microsoft Update moved the box to 7.6.9, with the error an explicit -UseWindowsPowerShell import of UpdateServices returned in our tests. The deny list behind that error is covered in the next section:

PowerShell 7.6.9 listing CompatiblePSEditions for Server 2025 modules and the UpdateServices deny-list error

Our results line up with Microsoft’s PowerShell 7 module compatibility list on most rows and disagree on one. That page lists ServerManager as natively compatible, yet on build 26100 the ServerManager manifest declares no edition and PowerShell 7 loaded it through the proxy every time. DhcpServer and GroupPolicy are marked as untested with the compatibility layer. Both imported through it here; we exercised DhcpServer with real scope reads, while GroupPolicy only got as far as the import because the VM wasn’t in a domain. The summary below is what we observed:

ModuleHow PowerShell 7 loaded itWith -SkipEditionCheck
Hyper-V, ActiveDirectory, ScheduledTasks, NetAdapter, NetSecurity, Storage, SmbShare, PKI, Dism, WindowsServerBackupNative, in-processNot needed
DhcpServerWinPSCompatSession proxyLoaded natively, Get-DhcpServerv4Scope worked
GroupPolicyWinPSCompatSession proxyLoaded natively, 28 commands (not exercised without a domain)
ServerManagerWinPSCompatSession proxyFailed: type load error
UpdateServices, BestPractices, PSScheduledJobBlocked by the deny listUpdateServices loaded; others not tried

How Import-Module -UseWindowsPowerShell works behind the warning

The compatibility feature is implicit remoting to your own machine. When PowerShell 7 meets a module it cannot load, it does not fail; it starts a Windows PowerShell 5.1 process, imports the module there and generates a local proxy whose functions forward every call. Import-Module -UseWindowsPowerShell forces the same path for a module that would otherwise load natively, and an ordinary Import-Module or command autoload triggers it automatically. In order, this is what happened on our server:

  1. Edition check: PowerShell 7 finds DhcpServer under C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules, which is on its PSModulePath, and sees no Core in the manifest.
  2. Child process: it launches powershell.exe -Version 5.1 -s -NoLogo -NoProfile and opens a remoting session to it called WinPSCompatSession.
  3. Proxy module: it writes remoteIpMoProxy_DhcpServer_*.psm1 to $Env:TEMP (C:\WINDOWS\SystemTemp here, because our script ran as SYSTEM) and imports that file into your session.
  4. Serialized calls: each command sends its parameters to the child and gets back serialized results, which is what the warning means by deserialized objects.
  5. Teardown: removing the last proxied module closes the session; exiting pwsh does the same.

You can watch steps 1 to 3 from a PowerShell 7 prompt. Import the module, then look for the session and the child process:

Import-Module DhcpServer
Get-PSSession | Format-Table Id,Name,ComputerName,ConfigurationName,State -AutoSize
Get-CimInstance Win32_Process -Filter "Name='powershell.exe'" | Select-Object ProcessId,ParentProcessId,CommandLine | Format-List

The warning, the session and the 5.1 child all appeared within a few seconds:

WARNING: Module DhcpServer is loaded in Windows PowerShell using WinPSCompatSession remoting session; please note that all input and output of commands from this module will be deserialized objects. If you want to load this module into PowerShell please use 'Import-Module -SkipEditionCheck' syntax.

Id Name               ComputerName ConfigurationName  State
-- ----               ------------ -----------------  -----
 2 WinPSCompatSession localhost                      Opened

ProcessId       : 3204
ParentProcessId : 4776
CommandLine     : "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -Version 5.1 -s -NoLogo -NoProfile

After Remove-Module DhcpServer, Get-PSSession came back empty, which confirms step 5. That matters on a long-running automation host, because each proxied module keeps a whole 5.1 process alive until the last one is removed.

What deserialized objects cost you

Objects that cross the session lose their methods and some formatting, and how much depends on the type. ServerManager shows it plainly. In PowerShell 7, Get-WindowsFeature DHCP,Hyper-V printed an empty Display Name column, and the object type tells you why:

$f = Get-WindowsFeature DHCP
$f.pstypenames
"DisplayName=[" + $f.DisplayName + "]"
$f | Get-Member -MemberType Method | Measure-Object | ForEach-Object { "methods=" + $_.Count }

The object is a Deserialized.* copy with two methods left, and the property is still there even though the table view didn’t render it:

Deserialized.Microsoft.Windows.ServerManager.Commands.Feature
Deserialized.System.Object
DisplayName=[DHCP Server]
methods=2

CIM-based modules fare better. A DHCP scope fetched through the proxy kept its CimInstance#root/Microsoft/Windows/DHCP/DhcpServerv4Scope type name and seven of its nine methods (only Clone and GetObjectData were missing), so pipelines that read properties work unchanged. Scripts that call methods on ServerManager, Group Policy or other .NET-typed results are the ones that break, and they break at run time, not at import.

The proxy is also slower per process. We timed a first Get-DhcpServerv4Scope call in a new process, three times each:

RunWindows PowerShell 5.1 (native)PowerShell 7 via WinPSCompatSession
15,813 ms13,536 ms
24,569 ms11,916 ms
35,021 ms9,655 ms

PowerShell 7 started faster but finished the DHCP job roughly two to two and a half times slower, because it paid for its own startup, a second 5.1 startup and the proxy generation. A scheduled task that runs a short DHCP or ServerManager script every few minutes is better off in powershell.exe.

When -SkipEditionCheck is the better fix

-SkipEditionCheck tells PowerShell 7 to ignore the empty manifest key and load the module in-process. It’s a bet that the module’s code doesn’t use .NET Framework types missing from modern .NET, and we got both outcomes. DhcpServer loaded from its real DhcpServer.psd1, returned the scope and opened no compatibility session. ServerManager failed immediately:

Import-Module ServerManager -SkipEditionCheck

The missing type lives in .NET Framework’s System.Core, which modern .NET does not carry:

Could not load type 'System.Diagnostics.Eventing.EventDescriptor' from assembly 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.

We’d use -SkipEditionCheck only for a module we’ve exercised end to end in PowerShell 7, DhcpServer being the clear candidate from this test. A clean import proves nothing, because the type load can happen on the first call to a particular cmdlet rather than at import time.

Why UpdateServices, BestPractices and PSScheduledJob refuse to load

Those three are on a deny list that ships in $PSHOME\powershell.config.json, and Microsoft’s compatibility list marks all three as not supported by the compatibility layer. The block applies to the compatibility path only, so both a plain import and an explicit Import-Module UpdateServices -UseWindowsPowerShell returned the same error on our server. The config file is short:

Get-Content "$PSHOME\powershell.config.json"

It sets the execution policy and names the three modules:

{
  "Microsoft.PowerShell:ExecutionPolicy": "RemoteSigned",
  "WindowsPowerShellCompatibilityModuleDenyList": [
    "PSScheduledJob",
    "BestPractices",
    "UpdateServices"
  ]
}

Editing that list is not the fix, since Microsoft marks all three as not supported by the compatibility layer. For WSUS administration, the module that hurts most on a WSUS server, the dependable route is to run the WSUS part of a PowerShell 7 script in Windows PowerShell directly. Passing a script block to powershell.exe runs it in a real 5.1 process and returns serialized results:

powershell.exe -NoProfile -Command { Import-Module UpdateServices; (Get-Command -Module UpdateServices).Count; $PSVersionTable.PSVersion.ToString() }

All 16 WSUS cmdlets loaded inside 5.1:

16
5.1.26100.7462

Import-Module UpdateServices -SkipEditionCheck did load in PowerShell 7, but our box had only the WSUS tools and no WSUS server role, so Get-WsusServer threw WsusInvalidServerException in both shells and we couldn’t prove the cmdlets work natively. Treat that path as untested. PSScheduledJob has a better replacement anyway: the native ScheduledTasks module covers the same ground and loaded in-process.

How Microsoft Update services PowerShell 7

Once the MSI has registered it, PowerShell 7 arrives like any other Microsoft product: through Windows Update against Microsoft Update, through WSUS, or through Configuration Manager. Microsoft’s Microsoft Update FAQ for PowerShell says updates stay inside the installed release line, so 7.6 moves to newer 7.6 builds and never jumps to 7.7. The installer’s registration step shows up as Microsoft Update being the default Automatic Updates service:

$sm = New-Object -ComObject Microsoft.Update.ServiceManager
$sm.Services | Select-Object Name,ServiceID,IsDefaultAUService,IsRegisteredWithAU | Format-Table -AutoSize

Microsoft Update, service ID 7971f918-a847-4430-9279-4a52d1efe18d, is registered and default:

Name                 ServiceID                            IsDefaultAUService IsRegisteredWithAU
----                 ---------                            ------------------ ------------------
Microsoft Update     7971f918-a847-4430-9279-4a52d1efe18d               True               True
DCat Flighting Prod  8b24b027-1dee-babb-9a95-3517dfb9c552              False              False
Windows Store (Prod) 855e8a7c-ecb4-4ca3-b045-1dfa50104289              False              False
Windows Update       9482f4b4-e343-43b6-b170-9a65bc822c77              False              False

To see what Microsoft Update would offer without waiting for the next scheduled scan, query it through the same COM API that Windows Update uses. This search took 12 seconds on our VM:

$s = New-Object -ComObject Microsoft.Update.Session
$q = $s.CreateUpdateSearcher()
$q.ServerSelection = 3
$q.ServiceID = '7971f918-a847-4430-9279-4a52d1efe18d'
$r = $q.Search("IsInstalled=0 and Type='Software'")
foreach ($u in $r.Updates) {
  "TITLE: " + $u.Title
  "  BrowseOnly=" + $u.BrowseOnly + "  MaxDownloadMB=" + [int]($u.MaxDownloadSize/1MB)
  "  Categories: " + (($u.Categories | ForEach-Object { $_.Name + ' [' + $_.Type + ']' }) -join '; ')
  "  KB: " + ($u.KBArticleIDs -join ',') + "  Released: " + $u.LastDeploymentChangeTime
}

Among the October cumulative update and Defender items, trimmed here, was the PowerShell entry:

TITLE: PowerShell v7.6.9 (x64)
  BrowseOnly=False  MaxDownloadMB=111
  Categories: PowerShell - x64 [Product]; Security Updates [UpdateClassification]
  KB: 5021051  Released: 10/25/2026 00:00:00

Three details in that entry are worth acting on. The product category is PowerShell - x64, which is the product you tick in WSUS to approve these updates. The classification is Security Updates and BrowseOnly is False. Microsoft’s FAQ says PowerShell updates arrive as optional updates and can take up to two weeks to appear, but this one was offered as a normal update and carried the same 25 October 2026 date as the GitHub release.

We installed only that update with the same API, which is how you’d patch PowerShell 7 alone without pulling the cumulative update. Run it in the same elevated powershell.exe window as the search, since it reuses $s and $r and the MSI replaces pwsh.exe:

$t0 = Get-Date
$c = New-Object -ComObject Microsoft.Update.UpdateColl
foreach ($u in $r.Updates) { if ($u.Title -like 'PowerShell v7*') { $u.AcceptEula(); [void]$c.Add($u); "selected: " + $u.Title } }
$d = $s.CreateUpdateDownloader(); $d.Updates = $c
$dr = $d.Download()
"download result=" + $dr.ResultCode + " secs=" + [int]((Get-Date)-$t0).TotalSeconds
$i = $s.CreateUpdateInstaller(); $i.Updates = $c
$ir = $i.Install()
"install result=" + $ir.ResultCode + " reboot=" + $ir.RebootRequired + " secs=" + [int]((Get-Date)-$t0).TotalSeconds
& "$env:ProgramFiles\PowerShell\7\pwsh.exe" -NoProfile -Command '$PSVersionTable | Format-Table -AutoSize | Out-String -Width 200'

Result code 2 means succeeded. The download took 22 seconds, the whole job 142 seconds, no reboot was needed, and the new pwsh reports the build Microsoft Update delivered:

selected: PowerShell v7.6.9 (x64)
download result=2 secs=22
install result=2 reboot=False secs=142

Name                      Value
----                      -----
PSVersion                 7.6.9
PSEdition                 Core
GitCommitId               7.6.9
OS                        Microsoft Windows 10.0.26100
Platform                  Win32NT
PSCompatibleVersions      {1.0, 2.0, 3.0, 4.0.}
PSRemotingProtocolVersion 2.4
SerializationVersion      1.1.0.1
WSManStackVersion         3.0

One support date is worth putting in your calendar. PowerShell 7.4 LTS and 7.5 both reach end of support on 10 November 2026, according to Microsoft’s lifecycle page. Microsoft Update never moves an LTS release to a newer line, so a server on 7.4 needs a fresh 7.6 MSI install. The Microsoft Update FAQ says a 7.5 install is upgraded to the next LTS once 7.5 support ends, but we did not test that and would not wait on its timing: install the 7.6 MSI before 10 November, after which the servicing described here takes over.

Where PowerShell 7 still hands the job back to 5.1

The common belief is that PowerShell 7 replaces Windows PowerShell on a server. It doesn’t, and on Server 2025 it can’t, because several role modules still declare no Core edition and Windows itself keeps shipping 5.1. The practical model is two shells with a clear split, and these are the cases where we’d keep a script on powershell.exe:

  • WSUS automation: UpdateServices is denied in the compatibility layer, so cleanup and approval scripts belong in 5.1 or in a powershell.exe script block.
  • ServerManager-heavy scripts: the module only runs through the proxy, returns deserialized feature objects and fails outright with -SkipEditionCheck.
  • Short, frequent scheduled tasks: anything that loads a proxied module pays for two process starts every run, which was about twice as slow in our DHCP timing.
  • Best Practices Analyzer and PSScheduledJob: both are denied, and only 5.1 runs them.

Everything else on the list moved to PowerShell 7 cleanly. Hyper-V returned live Microsoft.HyperV.PowerShell.VirtualMachine objects in-process, and ActiveDirectory, Storage, networking, scheduled tasks, PKI and Windows Server Backup all loaded natively. That covers most day-to-day server automation, including the Active Directory build-out with PowerShell we’ve written about before. If you also manage Linux boxes, PowerShell 7 is the version you can run there too, on Ubuntu and Debian or RHEL-family systems, and that is the strongest reason to write new scripts for it. Keep the 5.1 exceptions above where they are.

Keep reading

Create a Bootable Windows 11 USB and Install Windows 11 Windows Create a Bootable Windows 11 USB and Install Windows 11 Set Up a Samba Share on Windows 11 and Access It from Linux Networking Set Up a Samba Share on Windows 11 and Access It from Linux Windows 11 Installation on KVM with Virt-Manager KVM Windows 11 Installation on KVM with Virt-Manager Configure NTP Server on Windows Server 2025 / 2022 (w32tm Guide) Windows Configure NTP Server on Windows Server 2025 / 2022 (w32tm Guide) Windows 11 Keyboard Shortcuts Cheat Sheet (Printable PDF) Cheat Sheets Windows 11 Keyboard Shortcuts Cheat Sheet (Printable PDF) Spacedrive – Best file manager for Linux, Windows, macOS macos Spacedrive – Best file manager for Linux, Windows, macOS

Leave a Comment

Press ESC to close