PowerShell 2.0 remoting guide: Part 4 – Execute commands or scripts on a remote computer using Invoke-Command

I’ve published a free book on PowerShell 2.0 remoting. You can download it at:

In this part of PowerShell remoting series, I will discuss how to run commands or scripts on remote computer(s). Within remoting, there are 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 separate post for each and hence I will discuss the Invoke-Command method in today’s post.

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. I will cover most of them here.

Invoke-Command to run commands 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}

All command names 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.

Passing local variables as arguments to remote computer

Taking the above example, we can pass the 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.

Invoke-Command to execute scripts on remote computer(s)

Using ScriptBlock parameter can be quite tedious when you have to execute a bunch of PowerShell commands. This can be confusing when you have loops and conditional statements inside the scriptblock. Invoke-Command provides FilePath parameter to address this. You can use this parameter as shown below

Invoke-Command -ComputerName SP2010-WFE -FilePath C:\scripts\Test.PS1

Make a note that the script you provide as FilePath must exist on the local machine or at a place accessible to the local machine.

Using -Session parameter for better performance and sharing data between commands

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. So, to avoid that we can use a persistent connection 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 C:\ -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 C:\ of a remote machine — can be quite time consuming based on how big is C:\ 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 job on the remote computer.

 Invoke-Command -ComputerName SP2010-WFE -ScriptBlock {(Get-ChildItem C:\ -Recurse).Count} -asJob

Once you run this, you will see the job details listed as shown here

Remote Job

Remote Job

Now, you can use Get-Job and receive job cmdlets to see the output from the background job as shown below.

Get-Job -id 1 | Receive-Job

A complete discussion on Background jobs deserves a series of posts. I will plan to do that next. If you don’t want to wait and learn about it right away, you can read about it @ http://technet.microsoft.com/en-us/library/dd315273.aspx

Specifying credentials required for remoting

As we have seen the enable remoting post, you can use PowerShell remoting between computers in a workgroup environment too. 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. However, this will not work in a workgroup setup. Within a workgroup 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.

This is the end of this article on Invoke-Command. Invoke-Command has many other parameters which are not discussed here. They are more advanced and used in specific scenarios. I will discuss those use cases as we proceed further in this series of posts on PowerShell 2.0 remoting.

  • Pingback: PowerShell 2.0 remoting guide: Part 11 – Interpreting, formatting and displaying remote output()

  • Ismail Ahmed Syed

    Hi,
    Can i know , how i can execute the custom powershell functions written by me in the script file using the invoke-command.

  • http://www.ravichaganti.com ravikanthchaganti

    You can use -FilePath parameter. But the script should exist at the same location on the remote system also. The script should exist on the local machine

  • http://muegge.com/blog David Muegge

    To: ravikanthchaganti

    FYI, your comment is incorrect

    From – Get-help Invokecommand -full
    This command allows you to run the script on the remote computers, even if the script file is not accessible to the remote computers.

  • http://www.ravichaganti.com ravikanthchaganti

    @David, I did not understand your comment. In the blog post, I mentioned that
    Invoke-Command -ComputerName SP2010-WFE -FilePath C:\scripts\Test.PS1
    “Make a note that the script you provide as FilePath must exist on the local machine or at a place accessible to the local machine.”

    That file should really exist on the local computer. FilePath parameter takes the local script converts it in to a script block and executes on the remote computer.

  • Pingback: Executing script existing only on a remote system using Invoke-Command()

  • http://muegge.com/blog David Muegge

    Ravikanth

    Thanks for your reply

    Based on your comment below.
    “You can use -FilePath parameter. But the script should exist at the same location on the remote system also”

    It sounded to me like you were saying the script has to exist on the local and remote computers, but it only needs to exist on the local machine the script is being executed on. This just confused me a little so I looked it up and tested it. I thought I would point it out, sorry if I misunderstood.

    I found some great info on your blog, Thanks!

    Dave

  • http://www.ravichaganti.com ravikanthchaganti

    Oh..I get it now. I am so sorry, it was my mistake. I need to edit that comment. I did not even realize that I said something like that. Thanks for pointing it out

  • Karthi Kumarasamy

    Hi Ravi,

    Glad to read your blog. I’m new to powershell. I’m in middle of writing powershell script to execute a batch file in remote system which contains commands to execute a installer. When i trigger this script from my local system it’s throwing below error in middle after creating new session

    Error Message:
    “Processing data for a remote command failed with the following error message: Bad Request (Invalid Hostname) F
    or more information, see the about_Remote_Troubleshooting Help topic.
        + CategoryInfo          : OperationStopped: (System.Manageme…pressionSyncJob:PSInvokeExpressionSyncJob) [], PSRe
       motingTransportException
        + FullyQualifiedErrorId : JobFailure ”

    Script :
    —————————————————-
    $Username = “username”
    $Password = “password”
    $ComputerName = “computername”

    $script= {c:PPCSample.ps1}
     
    $SecurePassWord = ConvertTo-SecureString -AsPlainText $Password -Force
    $Cred = New-Object -TypeName “System.Management.Automation.PSCredential” -ArgumentList $Username, $SecurePassWord

    $Session =New-Pssession -ComputerName $ComputerName -credential $Cred

    Invoke-Command -Session $Session -ScriptBlock $script

    Remove-PSSession -Session $Session

    —————————————————

    Can you please help me to fix the issue ?

    Thanks,

    karthi K

  • kiquenet kiquenet

    My tests:

    $ItemLocation = “C:TempFolderInRemoteMachine” + “$FileName”
    $ItemLocation2 = “.” + “$FileName”

    Invoke-Command  works OK:

    Invoke-Command -credential $testCred -ComputerName $serverName -FilePath “$ItemLocation2″

    Invoke-Command -credential $testCred -computer $serverName -Script{ C:TempFolderInRemoteMachine\Script1.ps1 }

    Invoke-Command  doesn’t works: 

     Invoke-Command -credential $testCred -computer $serverName -Script{ “$ItemLocation” }

  • kiquenet kiquenet

    Passing local variables (more than 1 variable) as arguments to remote computer would be very useful. Thx

  • Ivan

    U can use

    invoke-command -Session $Session -FilePath $script