PowerShell has been the backbone of Windows administration for over a decade. With the release of PowerShell 7 (and its continued evolution through 7.x releases), Microsoft fundamentally changed the direction of this critical tool — making it cross-platform, open-source, and packed with modern language features that dramatically improve scripting productivity.
If you are still running Windows PowerShell 5.1, this guide will walk you through everything that is new, why you should upgrade, and how to migrate your existing scripts with confidence.
PowerShell 5.1 vs PowerShell 7: A Side-by-Side Comparison
Before diving into the details, here is a high-level comparison of the two major PowerShell versions:
| Feature | Windows PowerShell 5.1 | PowerShell 7.x |
|---|---|---|
| Runtime | .NET Framework 4.x | .NET 8+ (modern .NET) |
| Platforms | Windows only | Windows, Linux, macOS |
| Open Source | No | Yes (MIT License) |
| Executable | powershell.exe | pwsh.exe / pwsh |
| Ternary Operator | Not supported | condition ? true : false |
| Null-Coalescing | Not supported | $x ?? 'default' |
| Pipeline Chain | Not supported | cmd1 && cmd2 |
| ForEach -Parallel | Not supported | Built-in parallel processing |
| SSH Remoting | WinRM only | SSH and WinRM |
| Side-by-Side Install | Ships with Windows | Installs alongside 5.1 |
| Long-Term Support | In maintenance mode | Actively developed |
Key Point: Windows PowerShell 5.1 is not going away — it ships with Windows and will continue to receive security fixes. However, all new feature development happens exclusively in PowerShell 7.x.
Why Upgrade to PowerShell 7?
Cross-Platform Administration
PowerShell 7 runs natively on Windows, Linux, and macOS. This means you can use the same scripts, the same modules, and the same automation workflows across your entire infrastructure — whether you are managing Windows Server, Ubuntu VMs, or macOS developer machines.
Modern Language Features
PowerShell 7 introduces syntax improvements that developers have been requesting for years: ternary operators, null-coalescing, pipeline chain operators, and more. These features make scripts shorter, more readable, and less error-prone.
Performance Improvements
Built on modern .NET (8+), PowerShell 7 delivers significantly better performance than 5.1 for most workloads. The ForEach-Object -Parallel feature alone can reduce execution time by orders of magnitude for bulk operations.
Active Development and Community
As an open-source project on GitHub, PowerShell 7 receives frequent updates, community contributions, and transparent roadmap planning. Bugs get fixed faster, and feature requests are taken seriously.
Installing PowerShell 7
Windows
The easiest method on Windows is using winget:
winget install --id Microsoft.PowerShell --source winget
Alternatively, download the MSI installer from the PowerShell GitHub releases page or use the Microsoft Store.
After installation, launch PowerShell 7 with:
pwsh
Tip: Both
powershell.exe(5.1) andpwsh.exe(7.x) can coexist on the same system. They use separate profiles, modules directories, and configurations.
Linux (Ubuntu/Debian)
# Update package index and install prerequisites
sudo apt-get update
sudo apt-get install -y wget apt-transport-https software-properties-common
# Import the Microsoft repository GPG key
wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb"
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
# Install PowerShell
sudo apt-get update
sudo apt-get install -y powershell
# Launch PowerShell
pwsh
macOS
# Using Homebrew
brew install powershell/tap/powershell
# Launch PowerShell
pwsh
New Syntax Features in PowerShell 7
Ternary Operator
No more verbose if/else blocks for simple conditional assignments:
# PowerShell 5.1 way
if ($service.Status -eq 'Running') {
$statusText = 'Healthy'
} else {
$statusText = 'Down'
}
# PowerShell 7 ternary operator
$statusText = ($service.Status -eq 'Running') ? 'Healthy' : 'Down'
Null-Coalescing Operators
The ?? operator provides a default value when the left operand is $null, and ??= assigns only if the variable is $null:
# Provide a default value if $config is null
$serverName = $config.ServerName ?? 'localhost'
# Assign only if $logPath is currently null
$logPath ??= 'C:\Logs\default.log'
Pipeline Chain Operators (&& and ||)
Conditional execution based on the success or failure of the previous command — familiar to any Linux administrator:
# Only restart the service if the stop succeeded
Stop-Service -Name 'MyApp' && Start-Service -Name 'MyApp'
# Run the fallback only if the first command fails
Test-Connection -ComputerName 'server01' -Count 1 -Quiet || Write-Warning 'Server unreachable!'
ForEach-Object -Parallel
This is arguably the most impactful feature for administrators. Process pipeline items in parallel using runspaces:
# Ping 100 servers in parallel (up to 10 at a time)
$servers = Get-Content -Path 'C:\servers.txt'
$servers | ForEach-Object -Parallel {
$result = Test-Connection -ComputerName $_ -Count 1 -Quiet
[PSCustomObject]@{
Server = $_
Online = $result
}
} -ThrottleLimit 10
Compare this to PowerShell 5.1, where you would need to use Start-Job, Invoke-Command, or third-party modules like PoshRSJob to achieve the same result. The -Parallel parameter makes it a one-liner.
Real-World Example: Parallel Service Inventory
# Collect service status from 50 servers simultaneously
$servers = Get-ADComputer -Filter {OperatingSystem -like "*Server*"} |
Select-Object -ExpandProperty Name
$inventory = $servers | ForEach-Object -Parallel {
$computerName = $_
try {
$services = Invoke-Command -ComputerName $computerName -ScriptBlock {
Get-Service | Where-Object { $_.StartType -eq 'Automatic' -and $_.Status -ne 'Running' }
} -ErrorAction Stop
foreach ($svc in $services) {
[PSCustomObject]@{
Computer = $computerName
Service = $svc.Name
Status = $svc.Status
StartType = $svc.StartType
}
}
} catch {
[PSCustomObject]@{
Computer = $computerName
Service = 'CONNECTION_ERROR'
Status = $_.Exception.Message
StartType = 'N/A'
}
}
} -ThrottleLimit 20
$inventory | Export-Csv -Path 'C:\Reports\stalled-services.csv' -NoTypeInformation
Error Handling Improvements
PowerShell 7 introduces cleaner error handling patterns:
Error View Options
# Concise view (default in PS7) -- shows just the essential error info
$ErrorView = 'ConciseView'
# For detailed debugging, switch to the normal view
$ErrorView = 'NormalView'
# Get the full error record with Get-Error
Get-Error
Chained Error Handling with Pipeline Operators
# Try the primary path, fall back on failure
Get-Content 'C:\config\primary.json' 2>$null || Get-Content 'C:\config\fallback.json'
Improved -ErrorAction
The Clean block (introduced in PowerShell 7.3+) runs during cleanup regardless of whether an error occurred:
function Invoke-DatabaseQuery {
[CmdletBinding()]
param([string]$Query)
begin { $connection = Open-DatabaseConnection }
process {
$connection.Execute($Query)
}
clean {
# Always runs -- even after terminating errors
if ($connection) { $connection.Close() }
}
}
SSH Remoting: Beyond WinRM
One of the most significant changes for mixed-OS environments is native SSH remoting. Instead of relying on WinRM (which requires certificates, trusted hosts configuration, and is Windows-only), you can use SSH:
Setting Up SSH Remoting
On the remote machine (Windows or Linux), configure the SSH subsystem in sshd_config:
# /etc/ssh/sshd_config (Linux) or C:\ProgramData\ssh\sshd_config (Windows)
Subsystem powershell /usr/bin/pwsh -sshs -NoLogo
On Windows, you may need to install OpenSSH Server first:
# Install OpenSSH Server on Windows
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Start-Service sshd
Set-Service -Name sshd -StartupType Automatic
Using SSH Remoting
# Interactive session over SSH
Enter-PSSession -HostName server01.knowledgexchange.xyz -UserName admin
# Run a command on a remote Linux server
Invoke-Command -HostName ubuntu-web01 -UserName deploy -ScriptBlock {
systemctl status nginx
}
# Run the same command on mixed Windows and Linux servers
$linuxServers = @('web01', 'web02', 'db01')
Invoke-Command -HostName $linuxServers -UserName admin -ScriptBlock {
Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 5
}
Advantage: SSH remoting works across firewalls, requires no additional Windows configuration (no WinRM listeners, no trusted hosts), and uses standard SSH key authentication.
Compatibility with Existing Scripts
Microsoft built a Windows Compatibility Layer into PowerShell 7 that allows you to use many Windows PowerShell 5.1 modules that have not been ported yet:
# Import a Windows PowerShell module using the compatibility layer
Import-Module ActiveDirectory -UseWindowsPowerShell
# Check module compatibility
Get-Module -ListAvailable | Where-Object { $_.CompatiblePSEditions -contains 'Desktop' }
Modules That Work Natively in PowerShell 7
Most common administration modules already support PowerShell 7:
- ActiveDirectory — works via compatibility layer
- Az (Azure) — native support
- Microsoft.Graph — native support
- Exchange Online (ExchangeOnlineManagement) — native support
- VMware PowerCLI — native support
- Pester (testing framework) — native support
- PSReadLine — ships with PowerShell 7
Running Both Versions Side-by-Side
Because PowerShell 7 installs as pwsh.exe alongside powershell.exe, you can run both versions simultaneously. Each has its own:
- Profile paths (
$PROFILE) - Module paths (
$env:PSModulePath) - Configuration files
# Check which version you are running
$PSVersionTable.PSVersion
# From PowerShell 7, launch a 5.1 session
powershell.exe -Command { Get-Module -ListAvailable | Measure-Object }
# From PowerShell 5.1, launch a 7 session
pwsh -Command { $PSVersionTable.PSVersion }
Practical Examples for Windows Admins
Managing Remote Servers with SSH
# Define your server inventory
$servers = @(
@{ HostName = 'dc01.corp.local'; UserName = 'admin' },
@{ HostName = 'web01.corp.local'; UserName = 'admin' },
@{ HostName = 'linux-db01'; UserName = 'deploy' }
)
# Check disk space across all servers
$servers | ForEach-Object -Parallel {
$server = $_
Invoke-Command -HostName $server.HostName -UserName $server.UserName -ScriptBlock {
if ($IsLinux) {
df -h / | tail -1 | awk '{print $5}'
} else {
Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='C:'" |
Select-Object @{N='FreeGB';E={[math]::Round($_.FreeSpace/1GB,2)}}
}
}
} -ThrottleLimit 5
Automating Active Directory Tasks
# Bulk create users from a CSV file with null-coalescing for defaults
$users = Import-Csv -Path 'C:\HR\new-hires.csv'
foreach ($user in $users) {
$department = $user.Department ?? 'General'
$office = $user.Office ?? 'HQ'
$title = $user.Title ?? 'Employee'
$displayName = "$($user.FirstName) $($user.LastName)"
$samAccount = "$($user.FirstName.Substring(0,1))$($user.LastName)".ToLower()
$params = @{
Name = $displayName
GivenName = $user.FirstName
Surname = $user.LastName
SamAccountName = $samAccount
UserPrincipalName = "$samAccount@corp.local"
Department = $department
Office = $office
Title = $title
Path = "OU=$department,DC=corp,DC=local"
AccountPassword = (ConvertTo-SecureString 'TempP@ss2026!' -AsPlainText -Force)
Enabled = $true
}
New-ADUser @params
Write-Output "Created user: $displayName ($samAccount)"
}
Parsing JSON APIs
# Query a REST API and process results with modern PowerShell 7 syntax
$response = Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases' `
-Headers @{ 'User-Agent' = 'PowerShell-Admin' }
$latestStable = $response |
Where-Object { -not $_.prerelease } |
Select-Object -First 1
$version = $latestStable.tag_name ?? 'Unknown'
$downloadUrl = ($latestStable.assets |
Where-Object { $_.name -like '*win-x64.msi' })?.browser_download_url ?? 'Not found'
Write-Output "Latest stable: $version"
Write-Output "Download: $downloadUrl"
Note: The
?.(null-conditional member access) operator in PowerShell 7 prevents errors when accessing properties on potentially null objects — extremely useful when processing API responses.
Migration Checklist: From PowerShell 5.1 to 7
Use this checklist when migrating your scripts and workflows:
Assessment Phase
- Inventory all scripts, scheduled tasks, and automation workflows using PowerShell 5.1
- Identify which modules each script depends on
- Check module compatibility using
Get-Module -ListAvailablein PowerShell 7 - Test each script in PowerShell 7 in a non-production environment
Compatibility Fixes
- Replace
$PSScriptRootusage if scripts rely on 5.1-specific behavior - Update any
-OutputEncodingsettings (PowerShell 7 defaults to UTF-8) - Review string comparison behavior changes (more consistent in PS7)
- Replace
New-WebServiceProxywithInvoke-RestMethodwhere applicable - Add
-UseWindowsPowerShellflag for modules that need the compatibility layer
Deployment
- Install PowerShell 7 on all target machines (use Group Policy or SCCM for enterprise deployment)
- Update scheduled tasks to use
pwsh.exeinstead ofpowershell.exe - Update CI/CD pipelines to reference
pwsh - Configure SSH remoting on servers that need remote management
- Update PowerShell profiles (
$PROFILE) for the new environment
Validation
- Run Pester tests against migrated scripts
- Verify logging and error handling behavior
- Confirm remote execution works (both SSH and WinRM)
- Monitor for encoding issues in output files
Summary
PowerShell 7 represents a major leap forward for Windows administrators. The combination of cross-platform support, modern syntax features like ternary operators and null-coalescing, parallel pipeline processing, and SSH remoting make it an essential upgrade for anyone serious about infrastructure automation.
The side-by-side installation model means there is no risk in trying it out — your existing PowerShell 5.1 scripts will continue to work exactly as before while you gradually migrate to PowerShell 7. Start by installing pwsh on your workstation, test your most critical scripts, and begin taking advantage of the new features in your daily workflow.
The future of PowerShell is cross-platform, open-source, and built on modern .NET. The sooner you start using PowerShell 7, the better positioned you will be to manage the increasingly hybrid infrastructure that defines modern IT environments.
Additional Resources
- PowerShell 7 Documentation
- PowerShell GitHub Repository
- Migrating from Windows PowerShell 5.1 to PowerShell 7
For related Microsoft and Windows Server administration topics, check out our articles on connecting to remote Office 365 with PowerShell and managing Exchange Server.