Fellow Windows PowerShell MVP Marco Shaw clued me in on a Microsoft blog post that did a terrific job of comparing, from a performance perspective the different PowerShell 2.0 techniques you can use when managing remote computers. The results are pretty much as I would expect.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Cmdlets with a –computername parameter tend to perform the best. Using Get-WMIObject tend to be the slowest. Certainly your mileage may vary depending on network, servers, and the exact PowerShell expression. In between, you get pretty decent performance using Invoke-Command. Sometimes, using Invoke-Command is the only way to accomplish a remote management task. One major point I want to re-iterate from the original blog posting is that if you need to invoke multiple commands on a remote server, create a saved PSSession and reuse the session. Otherwise you are incurring a lot of overhead in setting up and tearing down WinRM sessions when using –computername.
My performance testing confirms the original findings. When you use –computername, when you use WMI and when you use Invoke-Command depends on what you have to accomplish. To help make it easier to calculate performance, I put together a little script that executes a PowerShell command for a remote computer a certain number of times, and calculates the average time it takes to complete.
1: #requires -version 2.0
2:
3: Function Get-PerfTime {
4: Param ([scriptblock]$expression,
5: [int]$interval=1,
6: [int]$max=10
7: )
8:
9: 1..$max | foreach {
10: $i=($_/$max)*100
11: Write-Progress -Activity "Performance Testing" `
12: -Status "Repeat: $max Interval: $interval MS" `
13: -CurrentOperation $expression `
14: -PercentComplete $i
15: (Measure-Command -Expression $expression).TotalMilliseconds
16: sleep $interval
17: }
18: } #end function
19:
20: $computername="coredc01"
21: #sleep interval in seconds
22: $sleep=1
23: #max number of tries
24: $max=100
25: #create a PSSession for later
26: $session=New-PSSession -ComputerName $computername
27:
28: #define a standard scriptlblock with a cmdlet that supports -computername
29: $Test1={get-process -ComputerName $computername }
30: #define a WMI equivalent
31: $Test2={Get-WmiObject -Class win32_process -ComputerName $computername}
32: #define a test using Invoke-Command
33: $Test3={invoke-command -ComputerName $computername -ScriptBlock {$scriptblock}}
34: #define a test with Invoke-Command and PSSession
35: $Test4={invoke-command -Session $session -ScriptBlock {$wmiscriptblock}}
36: $Test5={invoke-command -Session $session -ScriptBlock {$scriptblock}}
37:
38: $Test1,$Test2,$Test3,$Test4,$Test5 | foreach {
39: $raw=Get-PerfTime -max $max -expression $_ -sleep $sleep
40: $avg=($raw | Measure-Object -average).Average
41:
42: New-Object PSObject -Property @{
43: Computername=$computername
44: Repeat=$max
45: Expression=$_
46: Average=$avg
47: RawData=$raw
48: }
49: }
50: #clean up the PSSession
51: Remove-PSSession $session
52: write-host "Finished!" -ForegroundColor Green
53: #end of script
You know I love objects, so my little script writes a custom object to the pipeline for each tested expression.
Average : 19.406741
RawData : {18.6137, 19.1951, 19.4136, 20.8938...}
Expression : get-process -ComputerName $computername
Computername : coredc01
Repeat : 100
I also added a sleep interval for whatever it might be worth. I know there’s some caching with WMI, but you’d probably likely need a sleep interval of several minutes for it to clear. The object also returns the raw response times if you want to do something else with them. I already calculate the average using Measure-Object. You might use the script like this.
PS S:\> $results=.\PerfCompare.ps1
PS S:\> $results | sort Average | format-table Average,Expression -AutoSize
Average Expression
------- ----------
11.87756 invoke-command -Session $session -ScriptBlock {$Test1}
12.119081 invoke-command -Session $session -ScriptBlock {$Test2}
19.406741 get-process -ComputerName $computername
64.133741 Get-WmiObject -Class win32_process -ComputerName $computername
593.375884 invoke-command -ComputerName $computername -ScriptBlock {$Test1}
One interesting thing to note here is that Invoke command with the original script block ({get-process}) was faster than calling Get-Process –computername. This was over a 100 count loop with a sleep interval of 1 second. I’d be very curious to see how your numbers compare.
Careful with Invoke-Command. if you use a command like
$a=”dir”
Invoke-Command{$a}
It’s not going to actually execute the “dir” commadd and give you a list of files. It will simply output the string “dir”. Obviously, that executes pretty quickly…