PowerShell for Admins

Multithreading using jobs

Jonas Sommer Nielsen
5 min read
Share:

Often I have had to check something against all servers or clients. A classic problem and every time I run into the it it’s time consuming and running the job multithreaded would be nice.

A few years back I found a nice little script for multithreading which I have been using quite often. Unfortunately this wasn’t a module. And I can’t remember where it came from. So this week I set my mind on recreating this as a module and to see if I can publish it on PowerShell Gallery.

Version control 101

I recently watched the crash course Warren did on youtube a month back and I started out creating a repository for the project.

github.com/mrhvid/Start-MultiThread

I will let the video explain the concept. But I already feel more productive and safe while coding. Only thing left is to get in a process where I commit often or at least when it makes sense.

Idea

The original version was just a function I found on google somewhere. It worked fine but it wasn’t too handy to load up each time. And the input for the function was for two files, a script and a text file with a list of ComputerNames.

It would be nice if I could just call it with a list of computer names from whereever. e.g. Get-ADComputer, (Computer1, Computer2, localhost) or (Get-content servers.txt).b

And for quick oneliners if I need something simple it would be nice to be able to just write the script and not have to save a .ps1 file with the command.

Pseudo code:

Multi-Thread -Script { Test-Connection } -Computers [list of computers] ## Execution

First off I needed to figure out a good name.

Get-Verb lists 98 verbs on my machine. Sadly “multi” is not one of them. After som consideration I chose “Start” as a good verb, and “multithread” as the noun.

Start-MultiThread Sounds fair so I created a new folder with this name and a Start-MultiThread.psm1 file for the module.

Snip

A snippet for a full advanced function is always a good starting point. I added this to my version control and things are looking good so far.

https://github.com/mrhvid/Start-Multithread/… (first upload)

It already looks way more organized than what I usually come up with.

Coding

Tuesday afternoon I put on my headphones, started banging away on my keyboard and the result was this code

function Start-Multithread { [CmdletBinding(DefaultParameterSetName='Parameter Set 1', SupportsShouldProcess=$true, PositionalBinding=$false, HelpUri = 'https://github.com/mrhvid/Start-MultiThread/', ConfirmImpact='Medium')] [Alias()] [OutputType([String])] Param ( # Command or script to run. Must take ComputerName as argument to make sense. [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)] $Script, # List of computers to run script against [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=1)] [String[]] $Computers, # Maximum concurrent threads to start [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=2)] [int] $MaxThreads = 20 , # Number of sec to wait after last thred is started. [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=3)] [int] $MaxWaitTime = 600, # Number of Milliseconds to wait if MaxThreads is reached [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=4)] $SleepTime = 500 ) Begin { } Process { if ($pscmdlet.ShouldProcess('Target', 'Operation')) { $i = 0 $Jobs = @() Foreach($Computer in $Computers) { # Wait for running jobs to finnish if MaxThreads is reached While((Get-Job -State Running).count -gt $MaxThreads) { Write-Progress -Id 1 -Activity 'Waiting for existing jobs to complete' -Status "$($(Get-job -State Running).count) jobs running" -PercentComplete ($i / $Computers.Count * 100) Start-Sleep -Milliseconds $SleepTime } # Start new jobs $i++ $Jobs += Start-Job -ScriptBlock $Script -ArgumentList $Computer -Name $Computer -OutVariable LastJob Write-Progress -Id 1 -Activity 'Starting jobs' -Status "$($(Get-job -State Running).count) jobs running" -PercentComplete ($i / $Computers.Count * 100) } # All jobs have now been started # Wait for jobs to finish While((Get-Job -State Running).count -gt 0) { $JobsStillRunning = '' foreach($RunningJob in (Get-Job -State Running)) { $JobsStillRunning += $RunningJob.Name } Write-Progress -Id 1 -Activity 'Waiting for jobs to finish' -Status "$JobsStillRunning" -PercentComplete (($Computers.Count - (Get-Job -State Running).Count) / $Computers.Count * 100) Start-Sleep -Milliseconds $SleepTime } # Output Get-job | Receive-Job # Cleanup Get-job | Remove-Job } } End { } } This is by no means final code. (I already made small changes check GitHub for latest code). But the outline started to look good.

The Foreach just runs through the list of computers supplied and for each one starts a new job with the script code and the ComputerName as argument.  

To make sure the throttle limit is kept I have a small While loop that checks the number of running jobs and just sleeps until it falls under $MaxThreads limit.

When all jobs are started it’s just a matter of waiting for all jobs to finish. (It would be wise to add a timer here and kill hanging jobs after some time)

And lastly I just output all the results.

Testing

 2015-08-19 (6)

 This looks great but unfortunately it fails to receive the computername.

2015-08-19 (7)

It does run the code once for each computer but it asks for a computername each time which kind of defeats the point.

Good thing we have google and good ol’ Don.

2015-08-19 (8)

Adding a parameter() block to the script makes it work.

Clearly there’s still a lot to be done here.

Publishing

But creating modules is only really fun if you can share them with others. And this is where I’m beginning to love PowerShell v5. It turns out it’s quite simpel to do this.

PowerShellGallery.com describes this. After signing up it’s a one-liner.

PS> Publish-Module -Name -NuGetApiKey You need to create a manifest for your module first.

Now I have my module published and it has it’s own page on the internet WUUHU

www.powershellgallery.com/packages/Start-Multithread

Cool as that might seem the really cool stuff comes next.

Installing on a new machine

This requires WMF 5 or newer. Aka. Windows 10 works out of the box. Try it out from your elevated powershell promt.

2015-08-19 (9)

The module is available from the standard PSGallery repository. And installing it on your machine is as simpel as piping this to Install-Module

2015-08-19 (10)

Now you can try out the module on your own machine. Promission to be impressed. 

Help make it better

This module is not flawless so if you have any ideas feel free to get on your GitHub and submit changes 🙂

My idea is to keep it simple and try to follow some good practices e.g. as described in Learn PowerShell Toolmaking in a Month of Lunches.

Contact me

Twitter @mrhvid
Web Jonas.SommerNielsen.dk

Related Articles

Sep 15, 2023

PowerShell Escape Room

PowerShell Escape Room by Michiel Hamers by Michiel Hamers https://about.me/michielhamers/ Why on earth you want to create an Escape Room with PowerShell as backend? I’ve always been a fan of escape rooms, so I decided to create my own for my kids. I wanted to make it something that would be challenging and fun for them, but also educational. I decided to use PowerShell as the backend for the escape room, as I’m a PowerShell developer and I thought it would be a great way to learn more about the language.

Sep 15, 2023

Microsoft Graph PowerShell Module: Getting Started Guide

Microsoft Graph PowerShell Module: Getting Started Guide by Jeff Brown Microsoft is retiring the Azure AD Graph API sometime after June 30, 2023 (announcement). This retirement includes the Azure AD PowerShell module. In its place, Microsoft has released the Microsoft Graph PowerShell module. The Microsoft Graph PowerShell module is the next-generation way of managing Microsoft cloud services using PowerShell. If you have used MSOnline or Azure AD PowerShell in the past, you’ll need to read on to learn about this new module.

Oct 8, 2021

ICYMI: PowerShell Week of 08-October-2021

Topics include VMWare, Windows 11, Web Reports and more… Special thanks to Robin Dadswell, Prasoon Karunan V, Kiran Patnayakuni and Kevin Laux How to gather your vCenter inventory data with this VMware PowerShell script by Scott Matteson on 7th October Inventory reports are a common request when administering a VMware vCenter environment. Learn how this VMware PowerShell script can make such requests quick and easy Building a Web Report in PowerShell, use the -Force Luke by Chris Noring on 8th October