Solved

How to Modify  this PowerShell Script to Run Concurrently Across Many Hosts

Posted on 2013-01-16
17
307 Views
Last Modified: 2013-01-18
I have written a little powershell script to enumerate an OU and pass a command line argument typed in the console to each machine.  It runs fine but seems to run one at a time.  The OU has hundreds of systems.  How can I restructure this to run concurrently on all systems or at least run in batches of 20 systems at a time?  Are workflows with PS 3 the answer?  I am new to PS and trying to learn it.

#Enumerates OU
$Computers = Get-ADComputer -Filter * -Searchbase 'OU=Servers,OU=PROD,DC=JUGGERNAUT,DC=LOCAL'
#Writes Examples to Console
Write-Host 
Write-Host –foregroundcolor green 'Type in command to run on RDS systems in PSP_RDS OU'
Write-Host
Write-Host –foregroundcolor green 'Reboot Example: SHUTDOWN /r /t:60 /c "System Is Rebooting in 60 Seconds" /d P:4:2'
Write-Host –foregroundcolor green 'Registry Import Example:  reg import \\share\update.reg'
Write-Host
#Asks for command to run on remote systems
$commandtorun = Read-Host 'Enter Client Command to Run on RDS Systems'
Write-Host
#Runs command
foreach ($COMPUTER in $Computers) 
{
write-host –foregroundcolor green "Connecting to $($COMPUTER.Name)and running ($commandtorun)"  
Invoke-Command $COMPUTER.Name {$commandtorun}
}

Open in new window

0
Comment
Question by:Mark Roddy
  • 7
  • 7
  • 3
17 Comments
 
LVL 40

Expert Comment

by:Subsun
ID: 38783846
Ask the questions inside foreach  loop..
#Enumerates OU
$Computers = Get-ADComputer -Filter * -Searchbase 'OU=Servers,OU=PROD,DC=JUGGERNAUT,DC=LOCAL'
#Writes Examples to Console
Write-Host 
Write-Host –foregroundcolor green 'Type in command to run on RDS systems in PSP_RDS OU'
Write-Host
Write-Host –foregroundcolor green 'Reboot Example: SHUTDOWN /r /t:60 /c "System Is Rebooting in 60 Seconds" /d P:4:2'
Write-Host –foregroundcolor green 'Registry Import Example:  reg import \\share\update.reg'
Write-Host
foreach ($COMPUTER in $Computers) 
{
#Asks for command to run on remote systems
$commandtorun = Read-Host 'Enter Client Command to Run on RDS Systems'
Write-Host
#Runs command
write-host –foregroundcolor green "Connecting to $($COMPUTER.Name)and running ($commandtorun)"  
Invoke-Command $COMPUTER.Name {$commandtorun}
}

Open in new window

0
 

Author Comment

by:Mark Roddy
ID: 38783873
Wouldn't that ask each time for each system?  So if I had 100 computers in that OU it would prompt 100 times?
0
 
LVL 40

Expert Comment

by:Subsun
ID: 38783886
Yes it will, you can test it and get back to us.. :-)
0
 

Author Comment

by:Mark Roddy
ID: 38784004
oh..ok..not what I was wanting to accomplish.  Was looking for a way to pass the command entered into the console concurrently\synchronously  to the machines that are pulled from that OU.  Not have to type in the command for every machine in the OU.

So example would be someone updated a sites list in a registry key to define locations for an app.  We need to push that out to all systems immediately.  We have to import that to 500 hosts.  Instead of it happening one at a time I would like it to push the reg import command to all the systems in the OU at the same time.

Something like this example...but I don't know how to restructure my script to do it.

http://newsqlblog.com/2012/04/16/concurrency-in-powershell-background-jobs-2/
0
 
LVL 40

Expert Comment

by:Subsun
ID: 38784055
Okie.. That you cannot do with foreach, because foreach process objects one by one. There is no parallel processing.. You can try Foreach-Parallel function and see if it works for you..

Ref : http://gallery.technet.microsoft.com/scriptcenter/Foreach-Parallel-Parallel-a8f3d22b
0
 
LVL 39

Expert Comment

by:footech
ID: 38784150
I think you would not want to use the foreach loop.  Just run something like:
Invoke-Command $COMPUTERS {$commandtorun}

Open in new window

Though I'm not certain how you would incorporate the Write-Host statement.  To me it doesn't seem like it would apply in a concurrent situation, probably better to incorporate some logging.

For concurrent sessions, if you want, you could use the -ThrottleLimit parameter, default is 32.
0
 

Author Comment

by:Mark Roddy
ID: 38784162
So I would load this function up in my session and then run something like this?

$Computers = Get-ADComputer -Filter * -Searchbase 'OU=Servers,OU=PROD,DC=JUGGERNAUT,DC=LOCAL'

$computers | Foreach-Parallel - Throttle 20 - Timeout 10 - verbose -scriptfile c:\remotecommand.ps1

Open in new window

Which would call remotecommand.ps1 to ask for my screen input...but wouldn't is ask me for input every time this runs in parallel?  How do I pass the command to every parallel session and not have to keep entering it in?

remotecommand.ps1:
Write-Host –foregroundcolor green 'Type in command to run on RDS systems in PSP_RDS OU'
Write-Host
Write-Host –foregroundcolor green 'Reboot Example: SHUTDOWN /r /t:60 /c "System Is Rebooting in 60 Seconds" /d P:4:2'
Write-Host –foregroundcolor green 'Registry Import Example:  reg import \\share\update.reg'
Write-Host
#Asks for command to run on remote systems
$commandtorun = Read-Host 'Enter Client Command to Run on RDS Systems'
Invoke-Command $COMPUTER.Name {$commandtorun}

Open in new window

0
 

Author Comment

by:Mark Roddy
ID: 38784231
Oh..so like this?  After loading up the function Foreach-Parallel

PSRemoteRun.ps1:
$Computers = Get-ADComputer -Filter * -Searchbase 'OU=Servers,OU=PROD,DC=JUGGERNAUT,DC=LOCAL'
Write-Host –foregroundcolor green 'Type in command to run on RDS systems in PSP_RDS OU'
Write-Host
Write-Host –foregroundcolor green 'Reboot Example: SHUTDOWN /r /t:60 /c "System Is Rebooting in 60 Seconds" /d P:4:2'
Write-Host –foregroundcolor green 'Registry Import Example:  reg import \\share\update.reg'
Write-Host
#Asks for command to run on remote systems
$commandtorun = Read-Host 'Enter Client Command to Run on RDS Systems'
$computers | Foreach-Parallel - Throttle 20 - Timeout 10 - verbose -scriptfile c:\remotecommand.ps1

Open in new window



and then remotecommand.ps1:
Invoke-Command {$commandtorun}

Open in new window

0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 40

Expert Comment

by:Subsun
ID: 38784250
You don't have to use both.. Either you can use the ScriptBlock parameter instead of Scriptfile. Or as footech suggested use Invoke-Command against $Computers.
0
 
LVL 39

Assisted Solution

by:footech
footech earned 100 total points
ID: 38784266
You might want to take a look at the Hey! Scripting Guy articles by Jason Hofferle.
http://blogs.technet.com/b/heyscriptingguy/archive/tags/jason+hofferle/
In particular part 1 discusses using ForEach vs. Invoke-Command.  When using Invoke-Command inside a foreach loop, pretty sure what will happen is that the command has to complete and/or the object has to be returned before moving on to the next loop of the foreach, so it really is running one at a time.
0
 
LVL 40

Expert Comment

by:Subsun
ID: 38784536
Try this and see if it works for you..
#Enumerates OU
$Computers = Get-ADComputer -Filter * -Searchbase 'OU=Servers,OU=PROD,DC=JUGGERNAUT,DC=LOCAL'
#Writes Examples to Console
Write-Host 
Write-Host –foregroundcolor green 'Type in command to run on RDS systems in PSP_RDS OU'
Write-Host
Write-Host –foregroundcolor green 'Reboot Example: SHUTDOWN /r /t:60 /c "System Is Rebooting in 60 Seconds" /d P:4:2'
Write-Host –foregroundcolor green 'Registry Import Example:  reg import \\share\update.reg'
Write-Host
#Asks for command to run on remote systems
$commandtorun = Read-Host 'Enter Client Command to Run on RDS Systems'
Write-Host
#Runs command
write-host –foregroundcolor green "Connecting to $($COMPUTER.Name)and running ($commandtorun)"  
Invoke-Command $Computers -ScriptBlock {Invoke-Expression $args[0]} -Args $commandtorun

Open in new window

0
 

Author Comment

by:Mark Roddy
ID: 38784815
thanks...will try in a few when I get home and settled. Hey there again footech!
0
 
LVL 39

Expert Comment

by:footech
ID: 38785078
Hi mroddy77.  :)
0
 

Author Comment

by:Mark Roddy
ID: 38790435
Thanks Subsun

It works after a little tweaking...had to change the $Computers variable to pipe to a Select-Object  - expand Name

So one last nice to have if possible.  Can I have it write to the application log on the machine this script runs from what was ran and on what remote systems?  I am googling but not seeing anything cut and dry when using the invoke command?

This is what I have so far..just creating a event log source.  

#Enumerates OU
$Computers = Get-ADComputer -Filter * -Searchbase 'OU=Servers,OU=PROD,DC=JUGGERNAUT,DC=LOCAL'|Select-Object -expand Name
#create event source if it doesn't exist
if ([System.Diagnostics.EventLog]::SourceExists("RemoteCommandScript") -eq $false) {
    [System.Diagnostics.EventLog]::CreateEventSource("RemoteCommandScript", "Application")
	}
#Writes Examples to Console
Write-Host 
Write-Host –foregroundcolor green 'Type in command to run on RDS systems in PSP_RDS OU'
Write-Host
Write-Host –foregroundcolor green 'Reboot Example: SHUTDOWN /r /t:60 /c "System Is Rebooting in 60 Seconds" /d P:4:2'
Write-Host –foregroundcolor green 'Registry Import Example:  reg import \\share\update.reg'
Write-Host
#Asks for command to run on remote systems
$commandtorun = Read-Host 'Enter Client Command to Run on RDS Systems'
Write-Host
#Runs command
Invoke-Command $Computers -ScriptBlock {Invoke-Expression $args[0]} -Args $commandtorun

Open in new window

0
 
LVL 40

Accepted Solution

by:
Subsun earned 400 total points
ID: 38792269
You can make use of Write-Event function which I have written for another question.. See the example code..


$eventLogID = 20399
$eventLogPrefix = "FileCopyStat"

function Write-Event{
		$msg = $args[0]
		$source = $args[1]
		$type = $args[2]
		$eventid = $args[3]
		if(![System.Diagnostics.EventLog]::SourceExists($source))
			{
				[System.Diagnostics.EventLog]::CreateEventSource($source,'Application')
			}
		$log = New-Object System.Diagnostics.EventLog 
		$log.set_log("Application") 
		$log.set_source($source)
		$log.WriteEntry($msg,$type,$eventid)
}

#Enumerates OU
$Computers = Get-ADComputer -Filter * -Searchbase 'OU=Servers,OU=PROD,DC=JUGGERNAUT,DC=LOCAL'|Select-Object -expand Name

#Writes Examples to Console
Write-Host 
Write-Host –foregroundcolor green 'Type in command to run on RDS systems in PSP_RDS OU'
Write-Host
Write-Host –foregroundcolor green 'Reboot Example: SHUTDOWN /r /t:60 /c "System Is Rebooting in 60 Seconds" /d P:4:2'
Write-Host –foregroundcolor green 'Registry Import Example:  reg import \\share\update.reg'
Write-Host

#Asks for command to run on remote systems
$commandtorun = Read-Host 'Enter Client Command to Run on RDS Systems'

#create event
Write-Event $("Comand "+$commandtorun" run on "+$Computers) $eventLogPrefix "Information" $eventLogID
Write-Host

#Runs command
Invoke-Command $Computers -ScriptBlock {Invoke-Expression $args[0]} -Args $commandtorun

Open in new window

0
 

Author Comment

by:Mark Roddy
ID: 38792988
Thank you Subsun.  Works great.
0
 
LVL 40

Expert Comment

by:Subsun
ID: 38792995
You are welcome!!
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

The recent Microsoft changes on update philosophy for Windows pre-10 and their impact on existing WSUS implementations.
This article explains how to prepare an HTML email signature template file containing dynamic placeholders for users' Azure AD data. Furthermore, it explains how to use this file to remotely set up a department-wide email signature policy in Office …
This tutorial will show how to configure a single USB drive with a separate folder for each day of the week. This will allow each of the backups to be kept separate preventing the previous day’s backup from being overwritten. The USB drive must be s…
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now