Is it possible to call a powershell function or script several times in parallel and wait for the data to return before moving on? I have done this in Linux and my pseudo code is a more like how it's done in linux as an example.
Say we have a function called 'Get-OSRebootDates' which returns the recent reboots as a result string. Instead of running it serially, can I run it in parallel?
Normal serial method
$Result = @()
ForEach ($pcname in $pclistarray) {
$Result += Get-OSRebootDates -Target $pcname
# call it one at a time and wait before moving on to the next
}
# then do something with data
Pseudo code of what I would like to do in parallel:
$Result = @()
$($Result += Get-OSRebootDates -Target $pcname1) &
$($Result += Get-OSRebootDates -Target $pcname2) &
$($Result += Get-OSRebootDates -Target $pcname3) &
$($Result += Get-OSRebootDates -Target $pcname4) &
Wait
# then do something with the data
FYI example function
Function Get-OSRebootDates {
# Summary: Search Application log for recent reboots in hours (e.g. $RangeNegHours -> -340) (this search is fast)
# Params: Target is howt, Range is how far to look back
Param(
[Parameter(Mandatory = $true)]$Target,
[Parameter(Mandatory = $false)]$RangeNegHours = -8760 # default 1 year
)
$filters = @{}
$filters.Add("StartTime", ((Get-Date).AddHours([int]$RangeNegHours)))
$filters.Add("EndTime", (Get-Date))
$filters.Add("LogName", "System")
$filters.Add("ID", 6005) #6005 started, #6006 shutdown
$filters.Add("providername", '*EventLog*')
$filters.Add("level", 4)
$RebootDates = [string]::Empty
Try {
$LogRaw = Get-WinEvent -ErrorAction Stop -FilterHashtable $filters -MaxEvents 2500000 -ComputerName $Target | select-object id, machinename, timecreated, message, LevelDisplayName, ProviderName
}
Catch {
Write-Host ("Something went wrong with [" + $MyInvocation.MyCommand + "] for " + $Target + " ..") -ForegroundColor Red
start-sleep -seconds 4
return $RebootDates
}
$Count = 0
ForEach ($Item in $LogRaw) {
If ([string]$Item.TimeCreated) {
$RebootDates = $RebootDates + [string]$Item.TimeCreated + "`n"
$Count += 1
If ($Count -gt 5) {
break;
}
}
}
Return [string]$RebootDates
}
UPDATE: Code Solution
Have a working demo of what I was looking for below. In this example we call a function against a list of servers and it performs the work as jobs. In this example we are calling a function called 'SampleFunction' which does little but return a string.
$servers = @('Server1','Server2','Server3')
$maxthreads = 4 # => Limit num of concurrent jobs
# Sample Function to test
function SampleFunction ([string]$Name) {
Start-Sleep 30
Return "Hello $Name from function .."
}
$funcDef = "function SampleFunction {$function:SampleFunction}"
Get-Job Thread* | Remove-Job | Out-Null
$jobs = foreach ($server in $servers) {
$running = Get-Job -State Running
#write-host("Running:"+$running.Count.ToString()) ;Get-Job Thread*
if ($running.Count -ge $maxthreads) {
$null = $running | Wait-Job
}
#Start-Sleep 1
#Write-Host "Starting job for $server"
$ThreadName = "Thread-$server"
Start-Job -Name $ThreadName {
. ([scriptblock]::Create($using:funcdef)) # => Load the function in this scope
return SampleFunction -Name $using:server
} # => Better to capture the Job instances /// | Out-Null
}
$result = $jobs | Receive-Job -Wait -AutoRemoveJob