We help IT Professionals succeed at work.

powershell - How to speed up script on multiple servers

String :-)
String :-) asked
on
Much of my work involves runnning scripts against multiple servers. This morning for example I had to reset IIS on 48 servers:
 
Whilst this script works just fine, it is quite slow because it waits for each server in the list to complete the task before moving on to the next one.

foreach ($server in (Get-Content "D:\Deployment\_ServerLists\AppServers.txt")){

Invoke-Command -cn $Server -ScriptBlock { iisreset.exe -Restart }
}


Is there anyway to tell powershell to invoke-command to each server concurrently but still writing output to transcripts  (I tried running each asjob, but then I lost the output of what it was doing).

cheers for any tips!.
String :-)
Comment
Watch Question

CERTIFIED EXPERT
Top Expert 2014
Commented:
Since you're using PS Remoting, running concurrent jobs is easy.  Just let the Invoke-Command cmdlet do it for you!
$servers = Get-Content "D:\Deployment\_ServerLists\AppServers.txt"
Invoke-Command -cn $Servers -ScriptBlock { iisreset.exe -Restart }

Open in new window

You can add the -ThrottleLimit parameter if you want to limit how many run concurrently to a specific number (default is 32).

This is sometimes known as "fan-out" remoting.
CERTIFIED EXPERT

Commented:
You can also try using Start-Process before each Invoke-Command.
CERTIFIED EXPERT
Top Expert 2014

Commented:
It's been a while since I've run an iisreset command, so I don't remember what kind of output it produces, but it should just be string output (since it's not .Net-based).  Simple objects (like [string], or [int]) that are returned by Invoke-Command are not modified, but when the objects returned are complex objects then the PsComputerName property is added onto the returned object, so it makes it easy to see which remote computer the object came from.

You could do something like include a command to output the computername within the scriptblock to be executed, so you have some idea where the output is coming from.  My preferred method is to output a custom object that includes the string output (that you would normally get/want) as a property, then the PsComputerName property will be added onto that automatically.  Something like this:
$servers = Get-Content "D:\Deployment\_ServerLists\AppServers.txt"
Invoke-Command -ComputerName $servers -ScriptBlock { [pscustomobject]@{ "Output" = & iisreset.exe -Restart} }

Open in new window

Though with that you wouldn't see the full output of the iisreset command on screen - it would be modified by the Format-* commands.  It would be easy to pipe the output to a file, or a variable, or Tee-Object for later review.
String :-)Windows Engineer

Author

Commented:
Hi all,
I tried the above suggestions against our dev servers and timed the duration, but honestly the scripts took approx. same time to run as my original script.

The output was more useful with the pscustomobject.
The speed though wasn't any different.

Unless there are any other responses, I might have to run 2 x script in 2 x ISE tabs concurrently.
Thanks to all who have replied so far.
Cheers
String




.
CERTIFIED EXPERT
Top Expert 2014

Commented:
So what was the duration to run your original script, and the duration to run what I provided?

I'm incredulous that they would take the same time for you.

Even for a simple command, running ipconfig, where the result is returned in approximately 1 second, running it across 15 servers I can get the result from all in 2 seconds using fan-out remoting, while doing them sequentially as in your original takes 17 seconds.  For a script that takes longer to execute, the time difference would be proportionately longer.
String :-)Windows Engineer

Author

Commented:
Hi footech,
Sorry for the delay in replying, I was overseas for a month after xmas.

Thanks a bunch for your help so far.

I tried all the options presented above and there wasnt a dramastic improvement to the speed of doing an iisreset across 48 servers.

At best it was taking approx 20mins ( 48 servers @ 25 seconds each) and our PM was pushing for a marked improvement on this.


First I started using multiple ISE tabs, and selecting groups of servers in my list, but this got really messy, and also ment I couldnt capture every command in one transcript.

For example:

Tab1:
foreach ($server in (Get-Content "D:\Deployment\_ServerLists\servers.txt" | select -skip 0 -first 4 )) {
Invoke-Command -ComputerName $server -ScriptBlock { 
iisreset -restart
}}

Open in new window


Tab2:
foreach ($server in (Get-Content "D:\Deployment\_ServerLists\servers.txt" | select -skip 4 -first 4 )) {
Invoke-Command -ComputerName $server -ScriptBlock { 
iisreset -restart
}}

Open in new window



I then had a play with workflows, a simplified copy of this below:

function IISReset163 {
foreach ($server in (Get-Content "D:\Deployment\_ServerLists\163.txt")) {
Invoke-Command -ComputerName $server -ScriptBlock { 
iisreset -restart
}}
}

function IISReset167 {
foreach ($server in (Get-Content "D:\Deployment\_ServerLists\167.txt")) {
Invoke-Command -ComputerName $server -ScriptBlock { 
iisreset -restart
}}
}


workflow parallelOutput {
    parallel {
        IISReset163
        IISReset167
                
   }
}

Open in new window



I had about 6 functions running at once, and all seemed to run very well.   I am still learning and very open to any comments you have.
Many thanks
String
String :-)Windows Engineer

Author

Commented:
Thanks both for contributing.  Have a great week. String
CERTIFIED EXPERT
Top Expert 2014
Commented:
My recommendation would be to investigate further around fan-out PS Remoting.  It's the standard for performing operations on remote machines in parallel.  Also the syntax is simple (just submitting multiple names to the -computername parameter in a single call).
So rather than trying to come up with workarounds, I would investigate why you're not getting expected results.  About the only scenario I can come up with that doesn't include a syntax error or some odd configuration is if all the remote machines were running as guests (virtual machines) on hosts where they are so resource constrained that running an operation on one pushes resource utilization to the max (or close to it), so trying to perform parallel operations just ends up with each individual operation running slower.

Explore More ContentExplore courses, solutions, and other research materials related to this topic.