skip to content

Cross-Platform and DSC

What changes between Windows PowerShell 5.1 and PowerShell 7 on Linux or macOS, plus declarative machine state through Desired State Configuration. Interviewers ask because 'it works on my Windows box' is exactly the cross-platform gap they want you to have already hit.

part ofCommand-line shellsoverview, primer and where to startread it →
on this pageshow

questions

5

On a Windows machine, `powershell.exe` reports version 5.1 while `pwsh` reports 7.x. Are these the same PowerShell after an upgrade, and what does `$PSVersionTable.PSEdition` tell you about each?

level: middleimportance: must knowfreq 70%

answer

  1. two installs, not one upgrade
  2. different executable at the prompt
  3. one runtime is Windows-only
  4. .NET Framework versus modern .NET
  5. Desktop edition versus Core edition

basics

~20 s

They are two separate products installed side by side. Windows PowerShell 5.1 (powershell.exe) runs on .NET Framework and reports PSEdition Desktop; PowerShell 7 (pwsh) is a separate cross-platform install on modern .NET and reports Core.

solid answer

~40 s

Installing PowerShell 7 does not upgrade Windows PowerShell — it puts a second product on the box. Windows PowerShell 5.1 is a Windows component, lives in `System32\WindowsPowerShell\v1.0`, is launched as `powershell.exe`, runs on .NET Framework 4.x, and `$PSVersionTable.PSEdition` returns `Desktop`. PowerShell 7 installs under `C:\Program Files\PowerShell\7`, is launched as `pwsh`, runs on modern cross-platform .NET, and reports `Core`. They keep separate profiles (`Documents\PowerShell` versus `Documents\WindowsPowerShell`), separate user module directories, and separate PSReadLine versions, so a module or profile tweak made in one is not automatically visible in the other. The practical consequence in an interview: never say "PowerShell 7" when a scheduled task, a build agent or a legacy tool is actually invoking `powershell.exe`, because the edition decides which cmdlets and which .NET surface you get.

code

powershell · 9 lines
powershell
# Runs in both engines: which product am I actually in?
$PSVersionTable.PSVersion    # 5.1.x under powershell.exe, 7.x under pwsh
$PSVersionTable.PSEdition    # 'Desktop' under Windows PowerShell, 'Core' under PowerShell 7

if ($PSVersionTable.PSEdition -eq 'Desktop') {
    Write-Host 'Windows PowerShell 5.1 on .NET Framework'
} else {
    Write-Host "PowerShell $($PSVersionTable.PSVersion) on modern .NET, platform $($PSVersionTable.Platform)"
}

go deeper

for a junior

Know that powershell.exe and pwsh start different programs, and that $PSVersionTable tells you which one you are in. Say plainly that installing PowerShell 7 leaves Windows PowerShell 5.1 in place.

for a middle

Explain the runtime split: 5.1 on .NET Framework reporting edition Desktop, 7 on modern cross-platform .NET reporting Core, with separate install paths, profiles and module directories. Name a feature that exists only in 7.

for a senior

Show you design for it: pin the engine in scheduled tasks, CI steps and installers, use #requires -Version, and treat a script that works in pwsh but is launched by powershell.exe as a deployment defect rather than a coding one.

for a principal

Own the migration position for a fleet — which workloads move to pwsh, which stay on 5.1 because of Windows-only modules, and how you stop the estate drifting into an untracked mix of two engines with two module inventories.

## Two products, not one upgrade Microsoft ships two things whose names both contain "PowerShell". **Windows PowerShell** is a component *of Windows*. Its last version is 5.1, it is installed with the OS at `%SystemRoot%\System32\WindowsPowerShell\v1.0`, and its executable is `powershell.exe`. It is built on .NET Framework 4.x — the Windows-only, in-box runtime. It is serviced for security and reliability, but it does not receive new features. **PowerShell** (6.0, then 7.x — the "Core" edition) is a separate, downloadable product built on modern cross-platform .NET. Its executable is `pwsh` (`pwsh.exe` on Windows), and on Windows it installs under `C:\Program Files\PowerShell\7`. It also runs on Linux and macOS, which is the whole reason the split exists: .NET Framework never left Windows, so a portable PowerShell required a different runtime and therefore a different product. Installing PowerShell 7 does **not** replace or remove Windows PowerShell 5.1. Both remain on the machine, both stay on PATH, and each has its own Start-menu entry. That is deliberate: countless scheduled tasks, installers, CI steps and third-party tools hardcode `powershell.exe`, and silently changing what that name runs would break them. ## What `$PSVersionTable` actually reports `$PSVersionTable` is an automatic variable — a read-only hashtable describing the running engine. Two keys matter here: ```powershell $PSVersionTable.PSVersion # 5.1.x under powershell.exe; 7.x under pwsh $PSVersionTable.PSEdition # 'Desktop' under Windows PowerShell; 'Core' under PowerShell 6/7 ``` `PSEdition` names the runtime family, not the version number. `Desktop` means the engine is on .NET Framework; `Core` means it is on the modern .NET. PowerShell 6 and PowerShell 7 both report `Core`, so `PSEdition` alone does not tell you 6 from 7 — read `PSVersion.Major` for that. The table's contents differ by edition too. Windows PowerShell 5.1 carries `CLRVersion` and `BuildVersion`; PowerShell 6+ drops those and adds `Platform` (`Win32NT` or `Unix`), `OS` (a descriptive OS string) and `GitCommitId`. Testing for a key that only one edition has is itself a workable, if crude, probe. ## The version test that people get wrong The safe branch uses something both editions define: ```powershell if ($PSVersionTable.PSEdition -eq 'Desktop') { ... } # or: $PSVersionTable.PSVersion.Major -lt 6 ``` The unsafe branch uses `$IsWindows`. That automatic variable was introduced in PowerShell 6, so under 5.1 it is simply undefined — it evaluates to `$null`, which is falsy, and your "Windows" branch silently never runs on the most Windows machine you own. Any compatibility check has to be written in a dialect *both* engines speak. ## What else is separate * **Profiles.** PowerShell 7 on Windows uses `$HOME\Documents\PowerShell\` (on Linux/macOS, `~/.config/powershell/`); Windows PowerShell uses `$HOME\Documents\WindowsPowerShell\`. `$PROFILE` resolves per engine, so a prompt customisation added in one shell does not appear in the other. * **Modules.** Each edition has its own user module directory. On Windows, PowerShell 7 does append the Windows PowerShell system module path to `PSModulePath` so many in-box modules can still be imported, but a module you installed for 5.1 into the user scope is not automatically on 7's path. * **Removed features.** PowerShell 6 dropped workflows, PowerShell snap-ins, the WMI cmdlets (`Get-WmiObject` and friends) and the `-ComputerName` parameter on many cmdlets. `Get-Service`, `Get-WinEvent` and the registry provider still exist on Windows but have no meaning on Linux or macOS. * **Remoting endpoints.** Enabling remoting for PowerShell 7 registers its own WinRM session configuration; connecting to the default endpoint still lands you in 5.1. ## Why this shows up in interviews Because the failure mode is invisible. A script tested interactively in `pwsh` is later launched by a scheduled task, a Jenkins step or an MSI custom action that says `powershell.exe -File script.ps1`, and it fails on syntax or cmdlets that exist only in 7 (ternary `?:`, pipeline chain operators `&&`/`||`, `ForEach-Object -Parallel`). The correct instinct is to state the target engine explicitly — invoke `pwsh` by name, put `#requires -Version 7.0` at the top of the script, and treat "which edition runs this" as part of the deployment contract rather than an accident of PATH.

  • If one script has to run under both engines, how do you branch on the edition safely?
    Test something both editions define: `$PSVersionTable.PSEdition -eq 'Desktop'` or `$PSVersionTable.PSVersion.Major -lt 6`. Do not branch on `$IsWindows` — it was added in PowerShell 6, so under 5.1 it is undefined, evaluates to `$null`, and the Windows branch never executes. Better still, declare the requirement with `#requires -Version 7.0` and fail loudly instead of degrading.
  • Do the two engines share profiles and modules on the same Windows machine?
    No. PowerShell 7 uses `Documents\PowerShell` for profiles and its own user module directory; Windows PowerShell uses `Documents\WindowsPowerShell`. `$PROFILE` resolves per engine. On Windows, PowerShell 7 does append the Windows PowerShell system module path to `PSModulePath`, so many in-box modules remain importable, but anything you installed per-user for one edition is not automatically visible to the other.
  • Is Windows PowerShell 5.1 being removed from Windows?
    No. It ships as a Windows component and is serviced for security and reliability, but it receives no new features — 5.1 is the final version. All development happens in PowerShell 7. Plan migrations toward `pwsh`, but expect `powershell.exe` to keep existing on Windows machines, which is exactly why scripts must state which engine they need.

saying these in an interview costs you the question

  • Says PowerShell 7 is just an update to 5.1
  • Claims installing pwsh removes or replaces powershell.exe
  • Thinks PSEdition reports the version number
  • Uses $IsWindows as an edition check
  • Assumes any 5.1 module loads unchanged in 7

context

open as a page

You need one PowerShell 7 script to run on Windows, Linux and macOS. What does the engine give you for detecting the current operating system, and how do you build file paths and read environment variables so they work on all three?

level: juniorimportance: should knowfreq 52%

basics

~20 s

PowerShell 7 defines the automatic variables $IsWindows, $IsLinux and $IsMacOS for OS detection. Build paths with Join-Path rather than hardcoded backslashes, use $HOME instead of $env:USERPROFILE, and spell environment variable names exactly, since Unix names are case-sensitive.

open as a page

In PowerShell Desired State Configuration, what does a configuration compile into, and how does the Local Configuration Manager apply it in push mode versus pull mode?

level: middleimportance: should knowfreq 35%

basics

~20 s

A DSC configuration compiles into one MOF document per target node describing desired state. The Local Configuration Manager agent on the node applies it: in push mode you send the MOF with Start-DscConfiguration; in pull mode the agent fetches it on a schedule and can re-correct drift.

open as a page

A PowerShell script written for Windows PowerShell 5.1 fails under PowerShell 7 with "Get-WmiObject is not recognized", and one of its modules refuses to import at all. What changed between the engines, and what does PowerShell 7 on Windows offer to keep such a module usable?

level: seniorimportance: should knowfreq 42%

basics

~20 s

PowerShell 6 removed the WMI cmdlets in favour of the CIM cmdlets, and modules whose manifest omits Core from CompatiblePSEditions refuse to load. On Windows, Import-Module -UseWindowsPowerShell proxies such a module through a background 5.1 process.

open as a page

You are choosing how to manage machine state for a fleet that is mostly Windows with a growing Linux share. What is the honest case for PowerShell DSC today against Ansible or Terraform, and how would you decide?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

They solve different problems: Terraform provisions infrastructure, Ansible pushes configuration on demand, and DSC runs an on-node agent that can continuously correct drift on Windows. Choose by whether you need continuous convergence, how strong your Linux story must be, and whether rebuilding beats converging.

open as a page