I currently have a powershell (v2) script that takes a list of host PCs, checks remotely that some files/software are where they should be, and emails a string of results when it is complete. However, when trying to do this sequentially on over 100 rooms one at a time the script takes quite a while to finish.
To speed it up I've been trying to make the script multi threaded by using jobs to report on each host name instead. I have placed the main parts of my code below with some of the larger unrelated sections removed for clarity.
$pcHostNames = Get-Content "$scriptPath\HostNames.txt"
$scriptBlock = {
Param(pcHostName)
$softwareInstalled = $false
# Test to see if host can be reached
If (Test-Connection -ComputerName $pcHostName -Quiet)
{
# Multiple lines of code to check if different files/software are installed on each remote PC
# If they are installed, set $softwareInstalled to true
if (-not $softwareInstalled)
{
$reportList += "$pcHostName`: Not installed`n"
}
else
{
$reportList += "$pcHostName`: Fully installed`n"
}
}
else
{
$reportList += "$pcHostName`: Error - Connection failed`n"
}
} # End - $scriptBlock
$pcHostNames | % { Start-Job -Scriptblock $scriptBlock -ArgumentList $_ } | Get-Job | Wait-Job | Receive-Job
Currently the script pings the PCs and then checks that various files/software are installed, and adds a string to $reportList which is then used in an email to send the report in another part of my code.
Since $reportList is out of scope in each job all I get is a blank email at the end of my script. Since there isn't a way to add a string value to a variable from within a job, is there a way to edit my code for the job pipeline to return a string value which I can then add to a variable?
I believe the issue may be related to the last line of my code so I've tried a few different variations on executing jobs but have not had much luck with what I have been able to find online.
Alternatively, is there a better way to multithread this script via another method?
Variables exist within the scope they are created.
When you declare variables within a job, they exist only within the scope of that job.
If you want to pass details back to the caller from a child job, you need to use Write-Output. Then use Get-Job to monitor the status of the job until it is complete, then use Receive-Job to get the output.
start-job {write-output "hello world"}
get-job | receive job
Edit:
Taking your example code block, the following uses write-output to get return the results from the job, filters the jobs that completed successfully, receives the output from the jobs and stores them in the $reportList variable
$pcHostNames = Get-Content "$scriptPath\HostNames.txt"
$scriptBlock = {
Param(pcHostName)
$softwareInstalled = $false
# Test to see if host can be reached
If (Test-Connection -ComputerName $pcHostName -Quiet)
{
# Multiple lines of code to check if different files/software are installed on each remote PC
# If they are installed, set $softwareInstalled to true
if (-not $softwareInstalled)
{
Write-Output "$pcHostName`: Not installed`n"
}
else
{
Write-Output "$pcHostName`: Fully installed`n"
}
}
else
{
Write-Output "$pcHostName`: Error - Connection failed`n"
}
} # End - $scriptBlock
$pcHostNames | % {
Start-Job -Scriptblock $scriptBlock -ArgumentList $_
}
$reportList = Get-Job | Wait-Job | ?{$_.state -eq "Completed"} | Receive-Job