I\'m writing a script to check the version on about 15 remote servers and the script is taking much longer to execute than I would expect.
$listServers = @(\"com
Yeah, I think using jobs is the way to go. WMI calls can take a long time, especially if you run into a host or two that aren't responding.
Maybe consider something like this:
$listServers = @((1..15 | % {"compName$_"}))
$jobList = @()
$JavaBlock = {
function checkServer ($serverName) {
$returnValue = Get-WmiObject -computerName $serverName -Class Win32_Product -Credential $cred -Filter "Name like 'Java [0-9]%'" | Select -ExcludeProperty Version
return $returnValue
}
}
foreach ($server in $listServer) {
$job = start-job -InitializationScript $JavaBlock -ScriptBlock { checkServer $args } -argumentList $hostname
$jobList += $job
while (($jobList | where { $_.state -eq "Running" }).count -ge 30) { start-sleep -s 1 }
}
while (($jobList | | where { $_.state -eq "Running" }).count -ge 1) { start-sleep -ms 500 }
The two while statements control the job flow. The one within the foreach statement throttles the jobs so that only 30 are running at once. The last just waits for all jobs to complete before finishing off.
To gather your results, you can use this:
$jobList | % { $jobResults = Receive-Job $_; write-host $jobResults.result }
The Job object has other properties that might be worth exploring as well.
There are a couple ways you could improve performance. PowerShell WorkFlow supports ForEach in parallel, which would hit each computer simultaneously. You could also use Get-WMIObject with -ComputerName to query the list of computers. Get-WMIObject also supports the -AsJob switch, which could also help.
You don't need to do this in a loop, nor serially. invoke-command
takes a collection of ComputerName
s, and can run the requests in parallel.
$listServers = @("compName1", "compName2", "compName3", ... "compName15")
Invoke-Command -throttlelimit 4 -ComputerName $listServers -Credential $cred -Authentication Kerberos -ScriptBlock {Get-WmiObject -Class Win32_Product -Filter "Name like 'Java [0-9]%'" | Select -ExcludeProperty Version}} -ErrorAction SilentlyContinue -ErrorVariable errorOutput
However, as was pointed out by Tim Ferrell, you can use Get-WMIObject to ping the servers remotely, and if you do it as a job, it will run multiple requests in parallel.
Get-WMIObject Win32_Product -Filter "Name like 'Java [0-9]%'" -computername $listServers -throttlelimit 4 -asjob |select -excludeproperty version
Then use the Job cmdlets to receive the results.
Queries against Win32_Product
trigger a reconfiguration of installed packages, which is what makes the process so time-consuming. It also makes such queries potentially harmful, since it may inadvertently reset configuration parameters.
For something like a version check on a particular program you're better off reading the information directly from the (remote) registry:
$listServers | % {
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $_)
$key = $reg.OpenSubKey('SOFTWARE\JavaSoft\Java Runtime Environment')
New-Object -Type PSObject -Property @{
'Server' = $_
'JavaVersion' = $key.GetValue('CurrentVersion')
}
} | Export-Csv 'C:\temp\javaVersion.csv' -NoType