Avatar of String :-)
String :-)
Flag for Australia asked on

powershell - How to speed up script on multiple servers

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 :-)
Powershell

Avatar of undefined
Last Comment
footech

8/22/2022 - Mon
ASKER CERTIFIED SOLUTION
footech

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
serialband

You can also try using Start-Process before each Invoke-Command.
footech

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 :-)

ASKER
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




.
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
footech

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 :-)

ASKER
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 :-)

ASKER
Thanks both for contributing.  Have a great week. String
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
SOLUTION
footech

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.