Skip to content

Instantly share code, notes, and snippets.

@Norlunn
Last active September 28, 2019 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Norlunn/641b94523fe6f623bba250558d3c52b9 to your computer and use it in GitHub Desktop.
Save Norlunn/641b94523fe6f623bba250558d3c52b9 to your computer and use it in GitHub Desktop.
Start-RemoteJob.ps1
#Region CanModify
# Manually specify the remote systems on which you'll run code
$Computers = 'Server1', 'Server2', 'Server3'
## Uncomment below to get all computers in an OU
#$Computers = Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=contoso,DC=local' | Select-Object -ExpandProperty Name
## Uncomment below to get list of computers from a CSV file
#$Computers = Import-Csv -Delimiter ';' -Path data.csv | Select-Object -ExpandProperty ComputerName
## Collect data from local system, which can be used when running code on the remote system
$Parameters = @{
SourceComputer = $env:COMPUTERNAME
}
## Code to run on the remote system
$CodeBlock = {
$Parameters = $Using:parameters
## YOUR CODE BELOW
$OS = (Get-CimInstance -ClassName Win32_Operatingsystem | Select-Object -ExpandProperty Caption) -replace 'Microsoft '
Write-Output "Hello, I am $($env:COMPUTERNAME). I run $OS. Nice to meet you $($Parameters.SourceComputer)."
}
# Number of concurrent jobs that can run
$JobLimit = 30
#EndRegion CanModify
#Region DoNotModify
Function Start-RemoteJob
{
<#
.SYNOPSIS
Run code against multiple remote systems in parallel.
.DESCRIPTION
Uses PowerShell runspace to perform fast remote code execution in parallel against multiple systems.
Shows a progressbar to display progress.
.OUTPUTS
Outputs whatever objects your codeblock returns
.EXAMPLE
$Computers = 'Server1', 'Server2', 'Server3'
$Parameters = @{
SourceComputer = $env:COMPUTERNAME
}
## Code to run on the remote system
$CodeBlock = {
$Parameters = $Using:parameters
## YOUR CODE BELOW
$OS = (Get-CimInstance -ClassName Win32_Operatingsystem | Select-Object -ExpandProperty Caption) -replace 'Microsoft '
Write-Output "Hello, I am $($env:COMPUTERNAME). I run $OS. Nice to meet you $($Parameters.SourceComputer)."
}
# Number of concurrent jobs that can run
$JobLimit = 20
Start-RemoteJob -ComputerName $Computers -CodeBlock $CodeBlock -Parameters $Parameters -JobLimit $JobLimit
.NOTES
Author: Martin Norlunn
Updated: 21.07.2019
#>
[CmdletBinding(SupportsShouldProcess)]
Param
(
[Parameter(Mandatory)]
[System.Management.Automation.ScriptBlock]$CodeBlock,
[Parameter(Mandatory)]
[String[]]$ComputerName,
[System.Collections.Hashtable]$Parameters,
[ValidateRange(1, 60)]
[Int]$JobLimit = 10
)
Begin {
Function Get-JobState
{
Param
(
[PSCustomObject]$Job
)
$Flag = 'static','nonpublic','instance'
$_Worker = $Job.PowerShell.GetType().GetField('worker', $Flag)
$Worker = $_Worker.GetValue($Job.PowerShell)
$_CRP = $worker.GetType().GetProperty('CurrentlyRunningPipeline',$Flag)
$CRP = $_CRP.GetValue($Worker)
If ($Job.handle.IsCompleted -AND -NOT [bool]$CRP) {
'Completed'
}
ElseIf (-NOT $Job.handle.IsCompleted -AND [bool]$CRP) {
'Running'
}
ElseIf (-NOT $Job.handle.IsCompleted -AND -NOT [bool]$CRP) {
'NotStarted'
}
}
# Create a pool to hold the runspaces of size equals to $JobLimit
$RunSpacePool = [RunSpaceFactory]::CreateRunspacePool(1, $JobLimit)
$Posh = [PowerShell]::Create()
$Posh.RunSpacePool = $RunSpacePool
$RunSpacePool.Open()
# Array to hold the jobs, so we can refer to them later on
$Jobs = [System.Collections.ArrayList]::new()
# Code block to pass into the job
$RemoteCode = {
Param ($ComputerName, $CodeBlock, $Parameters)
Invoke-Command -ComputerName $ComputerName -ScriptBlock $CodeBlock -ArgumentList $Parameters
}
}
Process {
## Kick off jobs async
foreach ($Computer in $ComputerName)
{
$poshInstance = [powershell]::Create()
$poshInstance.RunSpacePool = $RunSpacePool
[Void]$poshInstance.AddScript($RemoteCode)
[Void]$poshInstance.AddParameter('ComputerName', $Computer)
[Void]$poshInstance.AddParameter('CodeBlock', $CodeBlock)
if ($PSBoundParameters.ContainsKey('Parameters'))
{
[Void]$poshInstance.AddParameter('Parameters', $Parameters)
}
$Handle = $PoshInstance.BeginInvoke()
$Temp = '' | Select-Object -Property PowerShell, Handle
$Temp.PowerShell = $poshInstance
$Temp.Handle = $Handle
[Void]$Jobs.Add($Temp)
}
## Monitor jobs
$TotalJobCount = ($Jobs | Measure-Object).Count
$ProgressId = Get-Random
Write-Verbose -Message "Starting monitoring of $TotalJobCount remote jobs.."
Do
{
$JobStates = Foreach ($Job in $Jobs) { Get-JobState -Job $Job }
$NotStarted = ($JobStates | Where-Object { $_ -eq 'NotStarted' } | Measure-Object).Count
$Running = ($JobStates | Where-Object { $_ -eq 'Running' } | Measure-Object).Count
$Completed = ($JobStates | Where-Object { $_ -eq 'Completed' } | Measure-Object).Count
Write-Progress -Activity "Running remote jobs.." -Status "Total $TotalJobCount | Not Started $($NotStarted) | Running $($Running) | Completed $($Completed)" -Id $ProgressId -PercentComplete ([Math]::Floor(($Completed / $TotalJobCount) * 100))
Start-Sleep -Seconds 1
} Until ($Completed -eq $TotalJobCount)
## Return data
Write-Verbose -Message "Fetching data from jobs.."
$Jobs | ForEach-Object { $_.PowerShell.EndInvoke($_.Handle) }
}
End
{
## Cleanup
Write-Verbose -Message "Cleaning up jobs.."
$Jobs | ForEach-Object { $_.PowerShell.Dispose() }
$RunSpacePool.Close()
$RunSpacePool.Dispose()
}
}
Start-RemoteJob -ComputerName $Computers -CodeBlock $CodeBlock -Parameters $Parameters -JobLimit $JobLimit
#EndRegion DoNotModify
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment