Windows PowerShell: Dive Deep into Remoting

Remoting is one the most powerful aspects of Windows PowerShell, so it’s well worth looking into at a deeper level.

Don Jones

Remoting is one of the most useful and most remarkable features of Windows PowerShell. This month, let’s dive a little deeper into remoting and how it works, and consider some more ways to make it an effective and valuable production tool.

Go Remoting

First, keep in mind that you’ll need to enable remoting on any computer that you want to remote into. To do so, either run Enable-PSRemoting on the target computer, or configure remoting via Group Policy. You can run help about_remote_troubleshooting in any copy of Windows PowerShell version 2 for instructions.

Remoting does require that you install Windows PowerShell version 2. You can download the shell from the Microsoft Web site for any version of Windows back to Windows XP. Using remoting does also open a TCP port on the computers upon which it’s enabled. If you’re using the Windows Firewall, the Enable-PSRemoting cmdlet will automatically create the necessary firewall connection.

One-to-One Remoting

The simplest way to use remoting is as an interactive shell, much like Secure Shell (SSH) on Unix or Linux systems. Run Enter-PSSession –computernameserver, where server is the remote computer’s name.

You do need to use the computer’s real name, not an alias or an IP address. This will “just work” in a domain environment once remoting is enabled, using Kerberos to mutually authenticate both you and the remote machine. In a non-domain environment, the about_remote_troubleshooting shell help topic has instructions for configuring computers.

Note that your credentials are delegated to the remote computer. Any commands you run on it will be run under your credentials, not some all-powerful system account. You can’t delegate your credentials across additional hops, however, so you can’t generally remote to another computer while you have the remote shell prompt open. To close the connection and return to your computer, run Exit-PSSession.

One-to-Many Remoting

The real power of remoting is the ability to send a command, in parallel, to one or more remote computers. Each remote computer executes the command, serializes the resulting objects into XML and transmits the results to your computer over the network.

Your computer receives the XML and deserializes it back into static objects. This lets you work with the command output in pretty much the same way as any other command output.

It all starts with the Invoke-Command cmdlet. In its simplest form, you simply specify a command to run, in the form of a scriptblock, and one or more computers upon which to run that command. For example, to read a list of computer names from a text file (one name per line) and run a command on each of them, use:

Invoke-Command –scriptblock { Get-EventLog Security –newest 200 }

–computername (Get-Content computernames.txt)

When results come in to your computer, they’ll have an additional property attached called PSComputerName. This lets you see which result came from which computer. This is helpful because they might not all be received in the same order.

You can use this property to sort, group, filter or otherwise manipulate the output once it’s received on your computer. Try to avoid sorting when you have a ton of data coming back, because the Sort-Object cmdlet has to cache all the output in memory in order to perform a sort.

Invoke-Command also supports a –FilePath parameter. You can use this parameter instead of –ScriptBlock to specify a script to run, rather than just running a command or two. There are also parameters that help specify different credentials or to override defaults:

  • –Credential lets you specify a user name, and you’ll be prompted for the password. This credential will connect to the remote computers and run the command or script. You can also provide a Credential object created with the Get-Credential command.
  • –Port lets you specify a port other than the default. Use this if you’ve reconfigured the underlying Windows Remote Management (WinRM) service to listen on a different port.
  • –UseSSL forces the connection to be encrypted, and uses a different TCP port (similar to the manner in which HTTPS uses a different port from non-encrypted HTTP).
  • –ThrottleLimit lets you change the number of computers contacted in parallel from the default of 32. This default has been tested successfully in pretty large environments, and raising it will consume more processor and memory on your computer, so be cautious when using this parameter.

Getting Input to Remote Commands

There are two additional parameters of Invoke-Command that are often overlooked. The first is –ArgumentList, which you can only use in conjunction with –ScriptBlock (and not with –FilePath). The –ArgumentList parameter is designed to pass local variables from your computer into the command being sent to the remote computers.

Within the script block, you define a parameter block that declares variables to be used on the remote computers. The argument list is then passed into those variables. Here’s an example:

Invoke-Command –computername SERVER1 –scriptblock { param($one,$two) Get-EventLog –logname $one –newest $two } –ArgumentList $log,$quantity

Here, the script will pass the contents of the local variable $log into the $one variable, and use it to define the log name retrieved. It will then pass the local variable $quantity into the $two variable, and use that to define the number of event log entries retrieved. The passing in of arguments is done purely by order: $log goes into $one because they’re both listed first, and $quantity goes into $two because they’re both listed second.

The –InputObject parameter is another means of providing input to the remote computers, and works with both –ScriptBlock and –FilePath. Whichever objects are specified for the –InputObject parameter are passed to the remote command or script in the special $input variable.

Getting Output from Remote Commands

Assuming you’re running a single, simple command like Get-EventLog, you’ll get that command output back from Invoke-Command. For example, if you want to dump all that content to an XML file for later, use:

Invoke-Command –computername ONE,TWO,THREE –scriptblock { Get-EventLog Security –newest 100 } | Export-CliXML results.xml

If you’re running an entire script on the remote computer, then your script should generate objects—preferably a single kind of object—as a result. For example, the following script is designed to be sent to remote computers via the –FilePath parameter of Invoke-Command. Note how it creates a new object and combines information from two local objects. The combined custom object is output to the pipeline, which is transmitted to the computer that ran Invoke-Command:

$os = Get-WmiObject –class Win32_OperatingSystem

$bios = Get-WmiObject –class Win32_BIOS

$obj = New-Object –TypeNamePSObject

$obj | Add-Member NotePropertyOSVersion $os.caption

$obj | Add-Member NotePropertyBIOSSerial $bios.serialnumber

Write-Output $obj

The PSComputerName property will be automatically added, so you’ll still be able to tell which result came from which computer. Outputting a single kind of object in this fashion provides the best compatibility with the rest of the shell’s capabilities, and is definitely the “best practice” way of sending output from a remotely invoked script.

Joshua Hoffman

Don Jones is a Microsoft MVP Award recipient and author of “Learn Windows PowerShell in a Month of Lunches” (Manning Publications Co., 2010), a book designed to help any administrator become effective with Windows PowerShell. Jones also offers public and on-site Windows PowerShell training. Contact him through his Web site at ConcentratedTech.com.