5977

Powershell Script Running Slowly

Question:

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 = @("compName1", "compName2", "compName3", ... "compName15") "" | Out-File C:\temp\javaVersion.txt "" | Out-File C:\temp\javaVersionLog.txt $cred = Get-Credential ForEach ($server in $listServers) { Measure-Command {$javaVersion = Invoke-Command -ComputerName $server -Credential $cred -Authentication Kerberos -ScriptBlock {Get-WmiObject -Class Win32_Product -Filter "Name like 'Java [0-9]%'" | Select -ExcludeProperty Version}} -ErrorAction SilentlyContinue -ErrorVariable errorOutput $errorOutput | Out-File C:\temp\javaVersionLog.txt -Append $server + $javaVersion | Out-File C:\temp\javaVersion.txt -Append }

This takes about 21 seconds to complete according to the Measure-Command output. Is there a reason I'm missing that the script is taking so long to complete?

Edit:

After being distracted by other issues, I finally finished the script.

Start-Transcript C:\temp\javaVersion.txt $listServers = @("compName1", "compName2", "compName3", ... "compName15") $javaVersVerbose = "" Invoke-Command -ComputerName $listServers -ScriptBlock { $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $_); $javaKey = $registry.OpenSubKey('SOFTWARE\JavaSoft\Java Runtime Environment'); $javaVers = $javaKey.GetValue('CurrentVersion'); $javaVersVerbose = $javaKey.GetValue('Java' + $javaVers.Substring(2, 1) + 'FamilyVersion'); $nameKey = $registry.OpenSubKey('SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName'); $name = $nameKey.GetValue('ComputerName'); $name + " " + $javaVersVerbose | echo } -ErrorAction SilentlyContinue -ErrorVariable errorOutput $errorOutput | echo Write-Host -NoNewLine 'Press any key to continue...' $null = $Host.UI.RawUI.ReadKey('NoEcho, IncludeKeyDown')

Answer1:

You don't need to do this in a loop, nor serially. invoke-command takes a collection of ComputerNames, 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.

Answer2:

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.

Answer3:

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.

Answer4:

Queries against Win32_Product trigger a <a href="http://support.microsoft.com/kb/974524" rel="nofollow">reconfiguration of installed packages</a>, which is what makes the process so time-consuming. It also makes such queries <a href="http://sdmsoftware.com/group-policy-blog/wmi/why-win32_product-is-bad-news/" rel="nofollow">potentially harmful</a>, 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

Recommend

  • AS3 - Get JAVA Home path
  • error in installing RWeka package in R
  • How do I use the Dom File API from privileged code?
  • Tkinter Python Continuously update Label from serial data?
  • ArrayIndexOutOfBoundsException: 0 error
  • How to insert html into database using PHP
  • Cuda program does not give the correct output when using a CUDA compatible GPU
  • Cuda char* variable assignment
  • CUDA constant memory value not correct [duplicate]
  • nvcc is picking wrong libcudart library
  • Why does CMake force the use of libcublas with separable compilation?
  • Using CImg: LNK1181: cannot open file “m.lib” on windows 7 x64
  • Creating an object in Swift using the Objective-C factory method gives a compiler error [duplicate]
  • trouble compiling with custom tensorflow gpu op
  • How to save Windows theme part using GetThemeStream? (other than DWMWINDOW)
  • How to focus on a particular portion of an another html page when click on a button or link
  • Cuda Mutex, why deadlock?
  • How to toggle “Use automatic configuration script”
  • Internet explorer and google chrome frame can support webRTC?
  • How to add definitions for cuda source code in cmake
  • FSC: Error FS2024: Static linking may not use assembly that targets different profile
  • JavaFX TableView click sorters not working?
  • Get current day's steps during datapointListener google Fit
  • How to prevent TreeItem selection?
  • disablinging autorecover option for powepoint
  • What is the use of a session store?
  • How to skip require in ruby?
  • Synchronize windows folders
  • Ruby on Rails App deployed to heroku showing “We're sorry, but something went wrong”
  • Detecting null parameter in preprocessor macro
  • configure: error: no acceptable C compiler found in $PATH
  • Saving image to sd with current date and time in name doesn't work
  • C# program and C++ DLL compiled for 32-bit system crash on 64-bit system
  • Eclipse CDT error: Unable to compile
  • Yii2: Config params vs. const/define
  • 0x202A in filename: Why?
  • Windows forms listbox.selecteditem displaying “System.Data.DataRowView” instead of actual value
  • Proper folder structure for lots of source files
  • How do I configure my settings file to work with unit tests?
  • How does Linux kernel interrupt the application?