Last active
August 29, 2015 14:23
-
-
Save bryanvine/bddcc9ba7f6697a3a113 to your computer and use it in GitHub Desktop.
Make any powershell script multithreaded with this powershell function wrapper (Invoke-Multithread)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Requires -Version 3.0 | |
Function Invoke-Multithread{ | |
<# | |
.SYNOPSIS | |
Wrapper function that lets you divide and conquer a server list to multithread it's execution. | |
.DESCRIPTION | |
Similar behavior to Invoke-Command -Asjob which lets you remotely start scriptblocks on target servers, | |
this function starts local jobs that are to be targeted at remote servers. | |
Great for multithreaded push deployments or report generation | |
.PARAMETER ComputerName | |
Specifies one or more server names. | |
.PARAMETER jobheader | |
Optional prefix name for jobs, so you can use more than one Invoke-Multithread on the same machine | |
.PARAMETER ThrottleLimit | |
Limits the number of jobs started. If you have 1000 servers, the default limit of 25 will mean you'll get 25 jobs, each with 40 servers per job. | |
Don't see the limit too large as it will eat up more CPU and RAM which actually might cause things to run slower than executing sequentially. | |
.EXAMPLE | |
Invoke-Multithread -ComputerName (Get-Content .\myserverlist.txt) -ScriptBlock{<your script here, with -computername as a parameter>} | |
Starts up to 25 threads of your code for local execution and returns results back in an object array with computer names | |
.LINK | |
http://www.bryanvine.com/2015/06/powershell-script-invoke-multithread.html | |
.LINK | |
Start-Job | |
.LINK | |
Get-Job | |
.LINK | |
Receive-Job | |
.LINK | |
Invoke-Command | |
.NOTES | |
Author: Bryan Vine | |
Last updated: 6/23/2015 | |
#> | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory=$true,valuefrompipeline=$false,Position=0)] | |
[alias("CN","MachineName","ServerName","Server")][ValidateNotNullOrEmpty()][string[]] | |
$ComputerName, | |
[Parameter(Mandatory=$true)][scriptblock]$ScriptBlock, | |
[string]$jobheader='Multi', | |
[int]$ThrottleLimit = 25 | |
) | |
BEGIN{ | |
$jobs = 1 | |
$i = 0 | |
#Clear out jobs from previous runs | |
Get-Job "$jobheader*"| Remove-Job -Force | |
} | |
PROCESS{ | |
#Calculates number of computers per thread | |
$BatchSize = [math]::Round($ComputerName.count / $ThrottleLimit,0) + 1 | |
#loop while there's still work to be done | |
while($jobs -and $i -lt $ComputerName.count){ | |
#return jobs that are finished and parses output per computer name as nice objects | |
Get-Job "$jobheader*" | ?{$_.State -like "completed"}|%{ | |
$servers = ($_ | select -ExpandProperty Command).replace($ScriptBlock.ToString(),"").replace("-ComputerName ('","").replace("').split(',')","").split(",") | |
$jobreturn = $_ | Receive-Job | |
0..($servers.count -1) | %{ | |
$obj = New-Object PSobject | |
$obj | Add-Member NoteProperty ComputerName $servers[$_] | |
$obj | Add-Member NoteProperty Output $jobreturn[$_] | |
Write-Output $obj | |
} | |
} | |
if($jobs -lt $ThrottleLimit){ | |
#spawn more jobs | |
#break up server list into batches to kick off multiple jobs | |
$batch = @() | |
1..$BatchSize | %{ | |
$batch += $ComputerName[$i++] |?{$_ -notlike ''} | |
} | |
if($batch){ | |
$batchstring = $batch -join "," | |
#start the job by appending the -computername property with the servernames to the scriptblock | |
Start-Job -Name "$jobheader-$i" -ScriptBlock ([scriptblock]::Create("$($ScriptBlock.ToString()) -ComputerName ('$batchstring').split(',')")) | Out-Null | |
} | |
} | |
else{ | |
Start-Sleep -Seconds 1 | |
} | |
#calculate the number of jobs currently running | |
$jobs = (Get-Job "$jobheader*" |?{$_.State -like "Running"})| Measure | select -ExpandProperty count | |
} | |
} | |
END{ | |
#return all jobs after completed | |
Get-Job "$jobheader*" | Wait-Job | %{ | |
$servers = ($_ | select -ExpandProperty Command).replace($ScriptBlock.ToString(),"").replace(" -ComputerName ('","").replace("').split(',')","").split(",") | |
$jobreturn = $_ | Receive-Job | |
0..($servers.count -1) | %{ | |
$obj = New-Object PSobject | |
$obj | Add-Member NoteProperty ComputerName $servers[$_] | |
$obj | Add-Member NoteProperty Output $jobreturn[$_] | |
Write-Output $obj | |
} | |
} | |
#cleanup | |
Get-Job "$jobheader*"| Remove-Job | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment