For the time being Windows PowerShell 5.1 is installed with Windows Server. It means that if you want to use or even test PowerShell 7 you need to install it by your own.
To be honest, even if I’m using PowerShell as a DBA more or less every day, I did not take too much care of PowerShell 7 until I used it at a customer place with a new parallelization functionality that we will discuss later on.

Introduction

For reminder, PowerShell 7 is an open-source and cross-platform edition of PowerShell. It means that it can be used on Windows platforms but also on MacOS or Linux.
The good point is that you can install it without removing Windows PowerShell 5.1. Both version can cohabit because of:

  • Separate installation path and executable name
    • path with 5.1 like $env:WINDIR\System32\WindowsPowerShell\v1.0
    • path with 7 like $env:ProgramFiles\PowerShell\7
    • executable with PowerShell 5.1 is powershell.exe and with PowerShell 7 is pwsh.exe
  • Separate PSModulePath
  • Separate profiles for each version
    • path with 5.1 is $HOME\Documents\WindowsPowerShell
    • path with 7 is $HOME\Documents\PowerShell
  • Improved module compatibility
  • New remoting endpoints
  • Group policy support
  • Separate Event logs

Installation

To install PowerShell 7 on Windows Servers the easiest way is to use a MSI package. The last version to download is the 7.5.0. Once downloaded, double click the msi file and follow the installation:

By default and as mentioned previously, PowerShell 7 will be installed on C:\Program Files\PowerShell\ :

Some customization are possible, we will keep the default selections:

Starting with PowerShell 7.2, it is possible to update PowerShell 7 with traditional Microsoft Update:

After some seconds installation is done:

We can start the PowerShell 7 with the cmd pwsh.exe. As we can see below both versions coexist on my Windows Server:

New features

PowerShell 7 introduces some interesting new features compare to PowerShell 5.

  • ForEach-Object with parallel execution
    Execute the script block in parallel for each object. A parameter ThrottleLimit limits the number of script blocks running at the same time, default value is 5.
    Here we search the instance properties by server and limit the parallelization to 2 server at the same time.
    $computers = ‘thor90′,’thor91′,’thor10′,’thor11’
    $InstanceProperties = $computers | ForEach-Object -Parallel {
    $instances = (Find-DbaInstance -ComputerName $_).SqlInstance;
    Get-DbaInstanceProperty -SqlInstance $instances
    } -ThrottleLimit 2
  • Ternary operator
    A simplified if-else statement with <condition> ? <condition true> : <condition false>
  • Pipeline chain operators
    The && operator executes the right-hand pipeline, if the left-hand pipeline succeeded. Reverse, the || operator executes the right-hand pipeline if the left-hand pipeline failed.
  • coalescence, assignment and conditional operators
    PowerShell 7 includes Null coalescing operator ??, Null conditional assignment ??=, and Null conditional member access operators ?. and ?[]
  • New management of error message and new cmdlet Get-Error
    This new cmdlet Get-Error displays the full detailed of the last error with inner exceptions.
    A parameter Newest allows to select the number of error you would like to display

On this blog, post I would to concentrate to the parallelization with the ForEach-Object -Parallel

PowerShell 7 parallelization

This new feature comes with the know ForEach-Object cmdlet which performs an operation on each item in a collection of input objects.
Starting with PowerShell 7.0 a new parameter set, called “Parallel”, gives the possibility to run each script block in parallel instead of sequentially. The “ThrottleLimit” parameter, if used, limits the number of script blocks which will run at the same time, if it is not specified the default value is 5.

ForEach-Object -Parallel <scriptblock> -ThrottleLimit

We can test this new feature with a small example.
If we execute the following script as before, the script block is executed sequentially:

PS C:\Users\administrator.ADSTS> 1..16 | ForEach-Object { Get-Date; sleep 10 }

Friday, February 14, 2025 9:00:02 AM
Friday, February 14, 2025 9:00:12 AM
Friday, February 14, 2025 9:00:22 AM
Friday, February 14, 2025 9:00:32 AM
Friday, February 14, 2025 9:00:42 AM
Friday, February 14, 2025 9:00:52 AM
Friday, February 14, 2025 9:01:02 AM
Friday, February 14, 2025 9:01:12 AM
Friday, February 14, 2025 9:01:22 AM
Friday, February 14, 2025 9:01:32 AM
Friday, February 14, 2025 9:01:42 AM
Friday, February 14, 2025 9:01:52 AM
Friday, February 14, 2025 9:02:02 AM
Friday, February 14, 2025 9:02:12 AM
Friday, February 14, 2025 9:02:22 AM
Friday, February 14, 2025 9:02:32 AM

Each line as 10 seconds more than the previous one.
But, if we execute this script with the new parameter Parallel and use a throttle limit of 4 we have:

PS C:\Users\administrator.ADSTS> 1..16 | ForEach-Object -Parallel { Get-Date; sleep 10 } -ThrottleLimit 4

Friday, February 14, 2025 8:59:01 AM
Friday, February 14, 2025 8:59:01 AM
Friday, February 14, 2025 8:59:01 AM
Friday, February 14, 2025 8:59:01 AM
Friday, February 14, 2025 8:59:11 AM
Friday, February 14, 2025 8:59:11 AM
Friday, February 14, 2025 8:59:11 AM
Friday, February 14, 2025 8:59:11 AM
Friday, February 14, 2025 8:59:21 AM
Friday, February 14, 2025 8:59:21 AM
Friday, February 14, 2025 8:59:21 AM
Friday, February 14, 2025 8:59:21 AM
Friday, February 14, 2025 8:59:31 AM
Friday, February 14, 2025 8:59:31 AM
Friday, February 14, 2025 8:59:31 AM
Friday, February 14, 2025 8:59:31 AM

Here we have 4 groups of 4 lines with the same time as we executed the script block in parallel with limitation of the parallelization to 4.
Of course, the different commands included in the script block are executed sequentially.

This feature uses the PowerShell runspaces to execute script blocks in parallel.
Variables can be passed into the script block with the $using: keyword, the only variable automatically passed is the pipe object.
Each runspace will execute a script block in a thread, so the ThrottleLimit parameter needs to be set according to the number of core of the server where you are running. If you VM has 2 cores, it makes no sense to put the limit to 4…

This new script will execute a maintenance job on different instances of the same server, passing the job name in the block script with the $using: keyword:

PS C:\Users\administrator.ADSTS> $ThrottleLimit = 2
PS C:\Users\administrator.ADSTS> $JobName = 'DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES'
PS C:\Users\administrator.ADSTS> $computers = 'thor90'
PS C:\Users\administrator.ADSTS> $SqlInstances = Find-DbaInstance -ComputerName $computers -EnableException
PS C:\Users\administrator.ADSTS> $SqlInstances

ComputerName InstanceName SqlInstance    Port  Availability Confidence ScanTypes
------------ ------------ -----------    ----  ------------ ---------- ---------
thor90       CMS          thor90\CMS     50074 Available    High       Default
thor90       SQL16_1      thor90\SQL16_1 62919 Available    High       Default
thor90       SQL19_1      thor90\SQL19_1 1433  Available    High       Default
thor90       SQL22_1      thor90\SQL22_1 50210 Available    High       Default
thor90       MSSQLSERVER  thor90         1434  Available    High       Default

PS C:\Users\administrator.ADSTS> $SqlInstances | ForEach-Object -Parallel {
>>     $Out = "Starting Job on $using:JobName [" + $_.SqlInstance + "]"
>>     Write-Host $Out
>>     $res = Start-DbaAgentJob -SqlInstance $_.SqlInstance -Job $using:JobName -Wait
>> } -ThrottleLimit $ThrottleLimit
Starting Job on DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES [thor90\CMS]
Starting Job on DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES [thor90\SQL16_1]
Starting Job on DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES [thor90\SQL19_1]
Starting Job on DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES [thor90\SQL22_1]
Starting Job on DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES [thor90]
PS C:\Users\administrator.ADSTS>

We use this kind of script at a customer place to execute SQL Server Agent Jobs in parallel on instances of a big physical servers with more than 10 instances.

Conclusion

This PowerShell 7 parallelization feature can improve performance in lots of different scenarios. But test it and don’t think that because of parallelization all your scripts will be executed quickly as running a script in parallel adds some overhead which will decrease execution of trivial script.