PowerShell v5.x and v7.x differences

This is a question I often get: Why or when should I use PowerShell v5 or v7? In this blog post, I will highlight key points from the excellent Microsoft Learn article on this topic and share some personal experiences.

Why should I care?

When you install Windows (Client or Server), you get PowerShell v5 out of the box. (Version 2 is going to be removed, too. See https://blogs.windows.com/windows-insider/2025/07/03/announcing-windows-11-insider-preview-build-27891-canary-channel/). And that’s fine, a lot of scripts work just fine in PowerShell v5. But… It depends on whether that’s acceptable to you and your organization. More and more features are only available in v7 and will no longer be developed for v5. Modules may only work in version 7, requiring you to install version 7 on servers and clients to keep your automation scripts running smoothly.

Personally, I develop everything in PowerShell v7. I do that because it’s faster, and all new modules work in that, too 🙂 The .NET versions from 7.4 and 7.5 (Version 8 and 9) are still supported, older v7 builds have older non-supported versions. However, there are some things missing or different in PowerShell v7. I will highlight some of those in the next chapter.

Things missing in v7

Modules

These modules are not shipped with v7:

  • ISE
  • Microsoft.PowerShell.LocalAccounts
  • Microsoft.PowerShell.ODataUtils
  • Microsoft.PowerShell.Operation.Validation
  • PSScheduledJob
  • PSWorkflow
  • PSWorkflowUtility

This doesn’t mean that they are no longer supported in v7; I can still use Get-LocalUser from Microsoft.PowerShell.LocalAccounts module, for example. (The module is still in C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules) It might work, but it is not supported 😉

PowerShell ISE

You can’t develop v7 scripts in the PowerShell ISE editor natively; there are workarounds for that, but just use VSCode 😀

Executable name

PowerShell.exe starts v5, pwsh.exe starts v7. Other changes in running the command-line are: (Important for starting script from Task Scheduler of Intune Win32 commands)

  • Changed the first positional parameter from -Command to -File. This change fixes the usage of #! (aka as a shebang) in PowerShell scripts that are being executed from non-PowerShell shells on non-Windows platforms. It also means that you can run commands like pwsh foo.ps1 or pwsh fooScript without specifying -File. However, this change requires that you explicitly specify -c or -Command when trying to run commands like pwsh.exe -Command Get-Command.
  • pwsh accepts the -i (or -Interactive) switch to indicate an interactive shell. This allows PowerShell to be used as a default shell on Unix platforms.
  • Removed parameters -ImportSystemModules and -PSConsoleFile from pwsh.exe.
  • Changed pwsh -Version and built-in help for pwsh.exe to align with other native tools.
  • Invalid argument error messages for -File and -Command and exit codes consistent with Unix standards
  • Added -WindowStyle parameter on Windows. Similarly, package-based installations updates on non-Windows platforms are in-place updates.

The shortened name is also consistent with naming of shells on non-Windows platforms.

UseWindowsPowerShell Switch

You can import older modules for v5 in v7 by running Import-Module nameofmodule -UseWindowsPowerShell. (This will create a proxy module in v7 in the background.) I used this a lot for SharePoint modules in v7.

WindowsUpdate support

Starting with PowerShell v7.2, you can enable automatic updates for PowerShell through Microsoft Update. Nice! But updating by using WinGet is my personal preference 😉

ErrorActionPreference

Previously, if -Verbose or -Debug were specified, it overrode the behavior of $ErrorActionPreference. With this change, -Verbose and -Debug no longer affect the behavior of $ErrorActionPreference.

Also, the -Debug parameter sets $DebugPreference to Continue instead of Inquire.

Job Control

Putting & at the end of a pipeline causes the pipeline to be run as a PowerShell job. When a pipeline is backgrounded, a job object is returned. Once the pipeline is running as a job, all of the standard *-Job cmdlets can be used to manage the job. Variables (ignoring process-specific variables) used in the pipeline are automatically copied to the job so Copy-Item $foo $bar & just works. The job is also run in the current directory instead of the user’s home directory.

String comparison

PowerShell 7.1 is built on .NET 5.0, which introduced the following breaking change:

As of .NET 5.0, culture invariant string comparisons ignore non-printing control characters.

For example, the following two strings are considered to be identical:

PowerShellCopy

# Escape sequence "`a" is Ctrl-G or [char]7
'Food' -eq "Foo`ad"

OutputCopy

True

New Cmdlets

New Get-Uptime cmdlet

The Get-Uptime cmdlet returns the time elapsed since the last boot of the operating system. The cmdlet was introduced in PowerShell 6.0.

New Remove-Alias cmdlet

The Remove-Alias cmdlet removes an alias from the current PowerShell session. The cmdlet was introduced in PowerShell 6.0.

New cmdlet Remove-Service

The Remove-Service cmdlet removes a Windows service in the registry and in the service database. The Remove-Service cmdlet was introduced in PowerShell 6.0.

New Markdown cmdlets

Markdown is a standard for creating readable plaintext documents with basic formatting that can be rendered into HTML.

The following cmdlets were added in PowerShell 6.1:

  • ConvertFrom-Markdown – Convert the contents of a string or a file to a MarkdownInfo object.
  • Get-MarkdownOption – Returns the current colors and styles used for rendering Markdown content in the console.
  • Set-MarkdownOption – Sets the colors and styles used for rendering Markdown content in the console.
  • Show-Markdown – Displays Markdown content in the console or as HTML

New Test-Json cmdlet

The Test-Json cmdlet tests whether a string is a valid JavaScript Object Notation (JSON) document and can optionally verify that the JSON document against a provided schema.

This cmdlet was introduced in PowerShell 6.1

New cmdlets to support Experimental Features

The following cmdlets were added in PowerShell 6.2 to support Experimental Features.

Get-ExperimentalFeature

Disable-ExperimentalFeature

Enable-ExperimentalFeature

Cmdlet changes

Parallel execution added to ForEach-Object

Beginning in PowerShell 7.0, the ForEach-Object cmdlet, which iterates items in a collection, now has built-in parallelism with the new Parallel parameter.

By default, parallel script blocks use the current working directory of the caller that started the parallel tasks.

This example retrieves 50,000 log entries from 5 system logs on a local Windows machine:

PowerShellCopy

$logNames = 'Security','Application','System','Windows PowerShell','Microsoft-Windows-Store/Operational'
$logEntries = $logNames | ForEach-Object -Parallel {
    Get-WinEvent -LogName $_ -MaxEvents 10000
} -ThrottleLimit 5
$logEntries.Count
50000

The Parallel parameter specifies the script block that is run in parallel for each input log name.

The new ThrottleLimit parameter limits the number of script blocks running in parallel at a given time. The default is 5.

Use the $_ variable to represent the current input object in the script block. Use the Using: scope modifier to pass variable references to the running script block.

For more information, see ForEach-Object.

Remove -ComputerName from *-Service cmdlets

In order to encourage the consistent use of PSRP, the -ComputerName parameter was removed from *-Service cmdlets.

NoTypeInformation Is The default on Export-Csv

Previously, the Export-Csv cmdlet would output a comment as the first line containing the type name of the object. The change excludes the type information by default because it’s not understood by most CSV tools. This change was made to address customer feedback.

Use -IncludeTypeInformation to retain the previous behavior.

Group-Object now sorts the groups

As part of the performance improvement, Group-Object now returns a sorted listing of the groups. Although you should not rely on the order, you could be broken by this change if you wanted the first group. We decided that this performance improvement was worth the change since the impact of being dependent on previous behavior is low.

Get-PfxCertificate -Password

Get-PfxCertificate now has the Password parameter, which takes a SecureString. This allows you to use it non-interactively:

PowerShellCopy

$certFile = '\\server\share\pwd-protected.pfx'
$certPass = Read-Host -AsSecureString -Prompt 'Enter the password for certificate: '
$certThumbPrint = (Get-PfxCertificate -FilePath $certFile -Password $certPass ).ThumbPrint

Where-Object -Not

With the addition of -Not parameter to Where-Object, can filter an object at the pipeline for the non-existence of a property, or a null/empty property value.

For example, this command returns all services that don’t have any dependent services defined:

PowerShellCopy

Get-Service | Where-Object -Not DependentServices

Remoting Support

PowerShell Remoting (PSRP) using WinRM on Unix platforms requires NTLM/Negotiate or Basic Auth over HTTPS. PSRP on macOS only supports Basic Auth over HTTPS. Kerberos-based authentication is not supported for non-Windows platforms.

PowerShell also supports PowerShell Remoting (PSRP) over SSH on all platforms (Windows, macOS, and Linux). For more information, see SSH remoting in PowerShell.

Wrapping up

These were some of the highlights, but there are many more in the detailed post here: https://learn.microsoft.com/en-us/powershell/scripting/whats-new/differences-from-windows-powershell?view=powershell-7.5. Have a lovely weekend!

2 thoughts on “PowerShell v5.x and v7.x differences

  1. Could you comment on how 5x gui scripts using Windows Forms or other graphical functions need to either be abandoned or adjusted for 7.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.