Use PowerShell Invoke-Command for Remoting

Use PowerShell Invoke-Command for Remoting

  • Comments 12
  • Likes

Summary: Microsoft Windows PowerShell MVP, Don Jones, talks about using the Invoke-Command cmdlet for remoting.

Microsoft Scripting Guy, Ed Wilson, here. I am really excited about the idea I had for this week, and I hope you will be too. I asked Candace Gillhoolley at Manning Press about posting some sample works from some of the Manning Press library of books. She responded enthusiastically and shared five samples that we will post this week. Today we present Don Jones’ Learn Windows PowerShell in a Month of Lunches.

Learn Windows PowerShell in a Month of Lunches 

HSG-6-13-11-1

By Don Jones

One of the coolest things in Windows PowerShell is to send a command to multiple remote computers at the same time. In this article, based on chapter 10 of Learn Windows PowerShell in a Month of Lunches, author Don Jones explains how to use the Invoke-Command cmdlet to execute one-to-many, or 1:n, remoting. To save 35% on your next purchase, use Promotional Code jones1035 when you check out at www.manning.com.

manning

Using Invoke-Command for Remoting

The trick I will show you in this article—and one of the coolest things in Windows PowerShell—is to send a command to multiple remote computers at the same time. That’s right, full-scale distributed computing. Each computer will independently execute the command and send the results right back to you. It’s all done with:

Invoke-Command -computername Server-R2,Server-DC4,Server12 `
 -command { Get-EventLog Security -newest 200 |

 Where { $_.EventID -eq 1212 }} 

Try it now

Go ahead and run this command. Substitute the name of your remote computer (or computers) where I’ve put my three computer names.

Everything in those outermost {braces} will get transmitted to the remote computers—all three of them. By default, PowerShell will talk to up to 32 computers at once; if you specified more than that, it will queue them up, so that, as one computer completes, the next one in line will begin. If you have a really awesome network and powerful computers, you could raise that number by specifying the -throttleLimit parameter of Invoke-Command—read the command’s help for more information.

Be careful about the punctuation

We need to pause for a moment and really dig into that example command because this is a case where Windows PowerShell’s punctuation can get confusing, and that confusion can make you do the wrong thing when you start constructing these command lines on your own. There are two commands in that example which use curly braces: Invoke-Command and Where (which is an alias for Where-Object). Where is entirely nested within the outer set of braces. The outermost set of braces enclose everything that is being sent to the remote computers for execution; that includes:

Get-EventLog Security -newest 200 | Where { $_.EventID -eq 1212 }

I should tell you that you won’t see the -command parameter in the Help for Invoke-Command—yet, the command I just showed you will work fine. The -command parameter is actually an alias, or nickname, for the -scriptblock parameter that you will see listed in the Help. I just have an easier time remembering -command, so I tend to use it instead of -scriptblock—but they both work the same way.

If you read the help for Invoke-Command carefully (see how I’m continuing to push those help files?), then you’ll also notice a parameter that lets you specify a script file, rather than a command. That parameter lets you send an entire script to the remote computers—meaning, you can automate some pretty complex tasks and have each computer do its own share of the work.

Try it now

Make sure you can identify the -scriptblock parameter in the help for Invoke-Command and that you can spot the parameter that would enable you to specify the file path and name instead of a scriptblock.

I want to circle back to the -computername parameter for just a bit. When I first used Invoke-Command, I typed a comma-separated list of computer names, just as I did in the example above. But I have a lot of computers I work with, so I didn’t want to have to type them all in every time. I actually keep text files for some of my common computer categories, like Web servers and domain controllers. Each text file contains one computer name per line, and that’s it—no commas, no quotes, no nothing. Windows PowerShell makes it really easy for me to use those files:

Invoke-Command -command { dir } `
 -computerName (Get-Content webservers.txt)

The parentheses there force Windows PowerShell to execute Get-Content first—pretty much the same way parentheses work in math. The results of Get-Content are then stuck into the -computerName parameter, which then works against each of the computers that were listed in the file.

I also sometimes want to query computer names from Active Directory. This is a bit trickier. I can use the Get-ADComputer command (from the ActiveDirectory module in Windows Server 2008 R2) to retrieve computers, but I can’t just stick that command in parentheses like I did with Get-Content. Why not? Because Get-Content is just producing simple strings of text, which -computername is expecting. Get-ADComputer, on the other hand, is producing entire computer objects, and the -computername parameter won’t know what to do with them. So if I want to use Get-ADComputer, I need to find a way to just get the values from those computer objects’ Name properties. Here’s how:

Invoke-Command -command { dir } -computerName (

 Get-ADComputer -filter * -searchBase "ou=Sales,dc=company,dc=pri" |

 Select-Object -expand Name )

Try it now

If you’re running Windows PowerShell on a Windows Server 2008 R2 domain controller or on a Windows 7 computer that has the Remote Server Administration Toolkit installed, then you can run Import-Module ActiveDirectory and then try the above command. If your test domain doesn’t have a Sales OU that contains a computer account, then change ou=Sales to ou=Domain Controllers and be sure to change company and pri to the appropriate values for your domain. (For example, if your domain is mycompany.org, you would substitute mycompany for company and org for pri.)

Within the parentheses, I’ve piped the computer objects to Select-Object, and I’ve used their -expand parameter. I’m telling it to expand the Name property of whatever came in—in this case, those computer objects. So, the result of that entire parenthetical expression will be a bunch of computer names, not computer objects—and computer names are exactly what the -computername parameter wants to see.

By the way, just to be complete, I should mention that the -filter parameter of Get-ADComputer specifies that all computers should be included in the command’s output. The -searchBase parameter tells the command to start looking for computers in the specified location—in this case, the Sales OU of the company.pri domain. The Get-ADComputer command is only available on Windows Server 2008 R2 and on Windows 7 after installing the Remote Administration Server Toolkit (RSAT). On those operating systems, you have to run Import-Module ActiveDirectory to actually load the Active Directory cmdlets into the shell so that they can be used.

Ideas for on your own

One of the Windows PowerShell modules included in Windows 7 is Troubleshooting Pack, which provides command-line access to the new troubleshooting pack functionality in the operating system. I always tell my students and clients to consider enabling Windows PowerShell remoting on all of their client computers, in part because it gives you remote command-line access to those troubleshooting packs. When a user calls for help, rather than walking them through a wizard over the phone, you can just remote in and run the same wizard, in command-line form rather than GUI form, yourself.

Guest Writer’s Week will continue tomorrow when we will have a post from Richard Siddaway.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • As always this is a very nice blog post.  I have been reading the scripting guys blog now for a couple months and it is one of my favorite blogs.  The examples in this post are not highly advanced, but they are a great tutorial for the Invoke-Command cmdlet which in my opinion is one of the most important for using the power of powershell to make large tasks easier.

    I mostly wanted to comment to tell you how much I like your blog and the way that you write your articles.  My only experience with powershell so far is reading the first half of Powershell in Action by Bruce Payette (HIGHLY RECOMMENDED) and reading this blog.  However with this little knowledge I am already finding myself when Ed says,

    "I can use the Get-ADComputer command (from the ActiveDirectory module in Windows Server 2008 R2) to retrieve computers, but I can’t just stick that command in parentheses like I did with Get-Content. Why not?"

    I find myself like a kid in a classroom wanting to throw my hand up going oh oh I know.  Its because we need just the name property of the object not the object itself.  I recommend everyone I talk to about powershell to check out your blog and just wanted to say thanks and keep up the good work.  

  • Invoke-Command -computername (Get-ADComputer| %{write-host $_.name}) -script {dir c:\}

    :|~

  • Sorry wrong line pastred:

    Invoke-Command -computername (Get-ADComputer| %{$_.name}) -script {dir c:\}

    :(

  • Don't forget you can use parameters with your invoke

    $Newest = 200

    $EventID = 1212

    Invoke-Command -Computer Server1, Server2, Server3 -ScriptBlock {param($New, $ID) Get-Event Security -Newest $New | where {$_.EventID -eq $ID}} -ArgumentList $Newest, EventID

    Hope that helps someone.

    Allan

  • I only get a >>

    I'm on v1.0 of PS is that old?

  • yes 1.0 is old :-)

    to use invoke-command you need to be at PowerShell version 2.0

  • Excelent and fantastic site.If you’re running Windows PowerShell on a Windows Server 2008 R2 domain controller or on a Windows 7 computer that has the Remote Server Administration Toolkit installed, then you can run Import-Module ActiveDirectory and then try the above command.So my great thanks for technet.com.

  • I am getting the following error when I try to invoke command to a remote Windows Server 2003 by its cluster name. I have enabled the PS Remoting on both cluster node for this case.

    The invoke command is working fine if I specify the machine name of the active cluster node.

    It seems to be limitation for Windows Server 2003 only as it is working fine with windows Server 2008 R2 clustered server name.

    Any idea on this?

    Connecting to remote server failed with the following error message : WinRM cannot process the request. T

    he following error occured while using Kerberos authentication: The network path was not found.

    Possible causes are:

     -The user name or password specified are invalid.

     -Kerberos is used when no authentication method and no user name are specified.

     -Kerberos accepts domain user names, but not local user names.

     -The Service Principal Name (SPN) for the remote computer name and port does not exist.

     -The client and remote computers are in different domains and there is no trust between the two domains.

    After checking for the above issues, try the following:

     -Check the Event Viewer for events related to authentication.

     -Change the authentication method; add the destination computer to the WinRM TrustedHosts configuration setting or us

    e HTTPS transport.

    Note that computers in the TrustedHosts list might not be authenticated.

      -For more information about WinRM configuration, run the following command: winrm help config. For more information,

    see the about_Remote_Troubleshooting Help topic.

       + CategoryInfo          : OpenError: (:) [], PSRemotingTransportException

       + FullyQualifiedErrorId : PSSessionStateBroken

  • when we run invoke-command using commands that don't return powershell objects then how do we know exactly which computer the result came from?  in those cases the results are in strings so we don't have the PSComputerName property to check.

    example:

    Invoke-Command -Computer Server1, Server2, Server3 -ScriptBlock {$env:windir}

  • how can i define a timeout for jobs running on the remote machine?

  • Hi

    I'm trying to use Invoke-command to stop a remote service. In this instance the name of the remote service is in a $service variable so I doing something like...

    invoke-command -computername myServer -scriptblock { stop-service -name $service }

    The problem that I have is that the value of the $service variable contains a "$". i.e. its typically something like 'AOS60$01'

    If I use the literal value...

    invoke-command -computername myServer -scriptblock { stop-service -name 'AOS60$01' }

    ...then it works fine but when I use a variable containing that value it doesn't find the service which, I guess, is because the remote machine is evaluating $01 as a variable and coming up empty so it actually looks for a service simply called 'AOS60' rather than 'AOS60$01'

    Are there any tricks I can use to pass through 'AOS60$01' without the $ being evaluated on the remote machine or do I have to switch to a different method of controlling the service?

    Malcolm

  • I am attempting to verify the installation of an LDAP Cert and need to run the following command

    keytool -list -alias federatedroot -keystore e:\JDK17\jre\lib\security\cacerts -storepass changeit -noprompt

    But keytool isn't recognized as a valid command