Learn PowerShell - PowerShell 7 Modern Features
Episode 29 of 31

Learn PowerShell - PowerShell 7 Modern Features

Modern PowerShell 7 features: pipeline parallelization with ForEach-Object -Parallel, ternary and null-coalescing operators, chaining operators, a concise error view, and cross-platform support on Linux and macOS.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

After episode 28 automated the AWS and GCP clouds, it's time to return to the core machine. Everything you've learned so far runs on PowerShell — but since version 7, PowerShell has become a more modern, cross-platform language. In this episode 29 we unpack the features that arrived or were refined in PowerShell 7: pipeline parallelization, ternary operators, null coalescing, chaining operators, a friendlier error view, and full support on Linux and macOS. These features change the way you write — shorter, faster, and more portable.

Pipeline Parallelization: ForEach-Object -Parallel

ForEach-Object traditionally processes items one by one sequentially — a single queue at a service counter. Since PowerShell 7, you can open several counters at once: ForEach-Object -Parallel followed by a script block run for each item. A real example, hashing many files:

Hashing files in parallel
$files = Get-ChildItem -Path /mnt/iso -Recurse -File
$files | ForEach-Object -Parallel {
    $hash = Get-FileHash -Path $_.FullName -Algorithm SHA256
    [PSCustomObject]@{ File = $_.FullName; Hash = $hash.Hash }
} -ThrottleLimit 4

-ThrottleLimit limits how many counters open simultaneously. Restaurant kitchen analogy: one chef can cook for several tables at once, but opening too many burners just leaves everything half-cooked. Set -ThrottleLimit based on machine capability — memory and CPU — not enthusiasm.

Thread Safety: Each Parallel Has Its Own World

Parallelism's power comes with responsibility: every parallel iteration runs in a separate runspace, with its own variables. Variables from the outer scope aren't automatically visible; to send their values, you must use the $using: prefix:

Sending parent variables with $using
$baseDir = '/mnt/iso'
$files | ForEach-Object -Parallel {
    $relative = $_.FullName.Substring($using:baseDir.Length + 1)
    Write-Output $relative
} -ThrottleLimit 8

If you write $baseDir without $using:, its value is empty inside the parallel block — a subtle bug that only appears at runtime. Rule of thumb: whatever from the outer scope is used inside the block, prefix it with $using:.

Warning

Because each runspace is isolated, writing to files or shared resources from many threads can collide with each other. If all iterations must record results into one log file, collect the results via the pipeline first, then write once after the parallel block finishes — let the pipeline standardize, not the threads.

Ternary Operator

If you've ever written if-else just to pick one of two values, PowerShell 7 gives you a shortcut: the ternary operator in the form condition ? value-if-true : value-if-false. This operator selects a value — it doesn't run blocks:

Ternary replacing if-else
$umur = 21
$kategori = $umur -ge 17 ? 'dewasa' : 'anak'
Write-Output $kategori

It's like a sign at a road fork: one question, two directions, no detours. Ternary is only for selecting a value; if you need to run several statements as a response, keep using regular if so the logic stays readable.

Null Coalescing

PowerShell often deals with values that can be empty: unset variables, missing properties, or failed results. The ?? operator provides a fallback value when the left side is null:

Fallback value with ??
$port = $env:PORT ?? 8080
$userName = $null
$nama = $userName ?? 'guest'
Write-Output "Port $port, nama $nama"

And there's ??= — assigning a fallback value only if the variable is still null:

Conditional assignment ??=
$config = Get-Content ./config.json -Raw | ConvertFrom-Json
$config.timeout ??= 30
Write-Output $config.timeout

Before ??, the old pattern forced three tedious lines of if. With ?? and ??=, code is shorter and its intent reads directly — you no longer need to guess whether a variable is set.

Chaining Operators

Some commands must run in sequence, and only continue if the previous command succeeded. Unix shells have had && and || for a long time; PowerShell 7 finally has them too:

Execution chaining
dotnet build ./app && dotnet test ./app
./deploy.ps1 -Environment staging || ./rollback.ps1

&& runs the right command only if the left succeeds; || runs the right command only if the left fails. This is concise for build-then-test and try-or-fail flows. For more complex decisions, keep using explicit if — a good shortcut helps, it doesn't hide logic.

Error View: ConciseView and NormalView

Since PowerShell 7, error messages are rendered in a new format called ConciseView — shorter, cause and location clearly separated, and only relevant parts highlighted. Those used to the old format know it as NormalView. Both are selected via the $ErrorView preference variable:

Choosing the error view
$ErrorView = 'ConciseView'
$ErrorView = 'NormalView'

Use ConciseView for fast-to-read terminals, and NormalView when you need full details for debugging. This setting can be written to your profile to apply in every session.

PowerShell 7 Cross-Platform

PowerShell 7 no longer belongs to Windows alone. Modern versions are built on .NET and run fully on Linux and macOS. On Linux, you can install it via your distro's package manager — apt for Debian/Ubuntu and dnf or yum for RHEL/Fedora:

Installing pwsh via apt
sudo apt update && sudo apt install -y powershell
pwsh

Once installed, the pwsh command opens the same REPL as powershell.exe on Windows. The only differences are the executable and some path-related behavior.

SSH Remoting

With PowerShell in two worlds, remoting becomes cross-platform too. PowerShell 7 supports SSH remoting — using SSH as the transport, just like ordinary ssh, without WSMan and without extra ports:

SSH remoting
Enter-PSSession -HostName server.linux.example -UserName admin
Invoke-Command -HostName server.linux.example -UserName admin -ScriptBlock { uname -a }

Its advantage: connections travel through existing SSH infrastructure — key management, firewalls, and jump hosts — so PowerShell remoting feels like coming home in environments already built on SSH.

Different Behavior on Linux

Because Linux file systems are case-sensitive, cmdlets like Get-ChildItem behave more strictly than on Windows. Get in the habit of using -LiteralPath for literal paths, and avoid separator assumptions — use Join-Path instead of writing backslashes or slashes manually. Portable scripts are written once, tested on multiple platforms, and carry no single-OS assumptions.

Conclusion

Episode 29 solidifies PowerShell 7 as a modern language: pipeline parallelization with ForEach-Object -Parallel and -ThrottleLimit, thread-safety understanding via $using:, the ternary ? and : operators, null coalescing ?? along with ??=, the && and || command chaining, the $ErrorView choice between ConciseView and NormalView, and PowerShell comfortable on Linux with SSH remoting. Each feature answers one real complaint: writing fewer lines, working faster, and running on more machines.

Just one episode left. In episode 30 — the series finale — you pack all these capabilities into production scripts: modules and manifests, deployment strategies, production checklists, maintenance, and a summary of best practices. See you there!

Learn PowerShell - PowerShell 7 Modern Features | Learn PowerShell