Chapter 3: Execute remote commands
Within remoting, there are a couple of ways to run commands or scripts on a remote machine. This includes Invoke-Command cmdlet and interactive remoting sessions. These two methods deserve a detailed discussion for each and hence we will see Invoke-Command method in this chapter and discuss interactive remoting in the next chapter.
Once you have enabled remoting on all your computers, you can use Invoke-Command cmdlet to run commands and scripts on local computer or on remote computer(s). There are many possible variations of this cmdlet.
Run script blocks on local or remote computer
You can invoke a command on local or remote computer(s) using the below method
Invoke-Command -ComputerName SP2010-WFE -ScriptBlock {Get-Process}
The ScriptBlock parameter can be used to specify a list of commands you want to run on the remote computer. ComputerName parameter is not required for running commands on the local machine. If you want to run the same command on multiple remote computers, you can supply the computer names as a comma separated list to ComputerName parameter or use a text file as shown in the example here
Invoke-Command -ComputerName SP2010-WFE, SP2010-DB -ScriptBlock {Get-Process}
OR
Invoke-Command -ComputerName (get-content c:\scripts\servers.txt) -ScriptBlock {Get-Process}
This method is also called fan-out or 1: many remoting. You run the same command on multiple computers in just a single command.
All commands and variables in the ScriptBlock are evaluated on the remote computer. So, if you do something like -ScriptBlock {Get-Process -Name $procName}, PowerShell expects the remote computer session to have $procName defined. You can however pass variables on the local computer to a remote session when using Invoke-Command. This brings us to the next point in our discussion.
Run script files on remote computers
There are a couple of ways of doing this.
First method is to use the –FilePath parameter.
Invoke-Command -ComputerName SP2010-WFE -FilePath C:\scripts\Test.PS1
Note that the script you provide as a value to -FilePath must exist on the local machine or at a place accessible to the local machine. So, what if you want to run a script that exists only on the remote server?
You can use -scriptblock for that.
Invoke-Command -ComputerName SP2010-WFE -scriptBlock { C:\scripts\Test.PS1 }
This way, you can execute the script present on a remote machine but not on the local system.
Passing variables to remote session
Taking the above example, we can pass name of the process you are looking for as a variable to the script block. ArgumentList parameter helps you achieve this. You can do this as shown here.
$procName = "powershell"
Invoke-Command -ComputerName (get-content c:\scripts\servers.txt) `
-ScriptBlock {param ($Name) Get-Process -Name $Name} –ArgumentList $procName
The above example may be a simple one but it shows how to use -ArgumentList parameter to pass local variables to the remote session.
Using persistent sessions with Invoke-Command
Whenever you run Invoke-Command with -ComputerName parameter, a temporary session gets established to execute the remote command.
So, establishing a session every time you use this cmdlet can be time consuming. This may be OK for executing a couple of commands but not when you want to execute more commands or scripts. So, to avoid this we can use a persistent session to the remote computer and that is what -Session uses. You can create a persistent connection to a remote computer by using New-PSSession cmdlet as shown here.
$s = New-PSSession -ComputerName SP2010-WFE
Now, $s contains the session details for the persistent connection. We can use $s to invoke a command on the remote computer and the syntax for that will be
Invoke-Commad -Session $s -ScriptBlock {get-Process}
$s contains all the variables you create / modify when you execute commands on the remote computer. So, subsequent command execution with $s as the session will have access to all of the variables created / updated on the remote computer. For example,
$s = new-pssession -computername SP2010-WFE
Invoke-Command -Session $s -ScriptBlock {$fileCount = (Get-ChildItem D:\ -Recurse).Count}
invoke-command -session $s -scriptblock {$fileCount}
We could access $fileCount variable only because we used a persistent session to run the command. This would not have been possible if used -ComputerName to invoke the remote command.
Running remote command as a background job
The example shown above — which gets the total file count on D:\ of a remote machine — can be quite time consuming based on how big is D:\ on the remote computer. In such case, you will have to wait for the remote command to complete execution. To avoid this, you can use -AsJob parameter to run the command as a background job6 on the remote computer.
Invoke-Command -ComputerName SP2010-WFE -ScriptBlock {(Get-ChildItem D:\ -Recurse).Count} –asJob
Once you run this, you will see the job details listed as shown here
Figure 4 Background Job
When you use –asJob parameter with Invoke-Command cmdlet, the background job gets created locally but runs on the remote computer. Since this job is created locally, we can use *-Job cmdlets to manage the job object. For example, you can use Get-Job to monitor the status of the job and once the job status changes to completed, you can use Receive-Job cmdlet to see the output of the script block specified.
Get-Job -Id 3 | Receive-Job
You can also use Start-Job within the script block to create a background job on the remote computer. However, this way the job output will be available only on the remote computer. So, when you need to get the output from this background job, you need to use Receive-Job within the script block to InvokeCommand.
For a complete discussion on background jobs in remoting, refer to About_Remote_Jobs article on technet.
Specifying credentials required for remoting
As discussed earlier in chapter 2, you can use PowerShell remoting between computers in a workgroup environment. All of the examples I showed above assume that you have access to remote computer as an administrator. This method works quite well in a domain environment where the logged on user has administrator credentials to access any computer in the domain. So, you don’t have to explicitly pass the credentials to Invoke-Command. However, this will not work in a workgroup setup and you need to pass the credentials along with Invoke-Command. To do that,
$cred = Get-Credential
Invoke-Command -ComputerName SP2010-WFE -ScriptBlock { Get-Process} -Credential $cred
In the example above, Get-Credential prompts for the credentials to access remote computer and uses the same while calling Invoke-Command cmdlet.
Note
Summary
In this chapter, we looked at how Invoke-Command cmdlet can be used to execute commands on a remote computer. We looked how to create a persistent session and use that along with InvokeCommand. You can use background jobs in remoting to execute time consuming commands as a job on the remote machine. There are other parameters to Invoke-Command include session options, etc. We will look at this in detail in part 2 of this guide.