The Invoke-Command cmdlet is one way to leverage PowerShell Remoting. In today’s post, I will give you an overview of Invoke-Command.

Michael Pietroforte

Michael Pietroforte is the founder and editor of 4sysops. He is a Microsoft Most Valuable Professional (MVP) with more than 30 years of experience in IT management and system administration.

Introducing PowerShell Remoting ^

When it comes to managing remote computers with PowerShell, you have essentially three options. You can open an interactive session with the Enter-PSSession cmdlet (One-to-One Remoting). An alternative is the Invoke-Command cmdlet, which allows you to run remote commands on multiple computers (which is why it is called One-to-Many Remoting). The third option is to use one of those cmdlets that offer a ComputerName parameter. In most cases, PowerShell Remoting isn’t involved then. To list those cmdlets, you can use this command:

Cmdlets with the ComputerName parameter

Cmdlets with the ComputerName parameter

In addition to the built-in cmdlets, quite a few PowerShell modules exist that support remote management. For instance, you can manage Active Directory from your workstation after installing the Active Directory module. Whenever one of these options exists, I would avoid enabling PowerShell Remoting on the remote machine.

Because PowerShell Remoting presents a security risk, it has to be enabled even within an Active Directory domain. You can also use Remoting on workgroup computers, but you will have to take care of a few extra configurations. If you don’t configure Remoting properly, you will run into errors such as WinRM cannot process the request or Access denied. In the latter case, you probably failed to provide admin credentials.

PowerShell Remoting depends on Windows Remote Management (WinRM), which is Microsoft’s implementation of the WS-Management (WS-Man) protocol. The protocol relies on HTTP or HTTPS and uses the TCP ports 5985 and 5986, respectively.

WS-Management encrypts all PowerShell communication even if you only work with HTTP. However, this kind of encryption is vulnerable to man-in-the middle attacks. I therefore recommended to use HTTPS instead of HTTP in insecure networks. I will say more about this topic in a follow-up post.

Executing cmdlets with Invoke-Command ^

Perhaps the most interesting form of PowerShell remote management is One-to-Many Remoting with the Invoke-Command cmdlet. This cmdlet allows you to execute PowerShell commands on multiple remote computers that contain cmdlets that don’t feature the -ComputerName parameter.

Two ways exist to connect to remote computers with Invoke-Command. You usually use the ‑ComputerName parameter to manage Windows machines. Alternatively, you can pass the ‑ConnectionUri parameter to manage backend applications, such as Exchange, or cloud services such as Azure.

In the remainder of this article, I will focus on managing remote Windows machines that are Active Directory domain members. The command below executes the Get-ChildItem cmdlet on the machine whose computer name is stored in the $RemoteComputer variable:

PowerShell automatically creates a so-called PSSession on the remote computer, which essentially is a user-created PowerShell session as opposed to a session that a PowerShell host such as the PowerShell console or PowerShell ISE creates. The output of the executed commands in the script block is automatically relayed to your local session. As usual, you can interrupt the displaying of the output with CTRL+C.

As with the Enter-PSSession cmdlet, you have to be an administrator on the remote machine. Otherwise, you will receive an Access denied error message. If you are not logged on as domain administrator on your local machine, you have to provide sufficient credentials with the -Credential parameter.

You can pass multiple computers to the -ComputerName parameter by separating their names with commas. If you also want to include the local computer, you add “localhost” or simply “.” to the list.

To run multiple commands, you can separate them with semicolons in the script block. However, a better option is to use the -FilePath parameter instead of the -ScriptBlock parameter, which allows you to execute an entire PowerShell script:

By default, PowerShell will send the script immediately to all computers if 32 or fewer computer names are passed. If more than 32 computer names are passed, PowerShell will queue the surplus computers until the script completes in one of the first 32 PSSessions. You can change this default behavior with the -ThrottleLimit parameter. However, you should be aware of the implications on the load of your computer and the network when the results come pouring in.

Testing if Remoting is enabled ^

In particular, if you plan to execute commands on many machines, you might want to test their availability with the Test-Connection cmdlet first. This cmdlet sends ICMP echo request packets (pings, that is) to the remote computers:

The -Quiet parameter suppresses the output of the cmdlet and returns $True if the remote machine answers the pings.

This method is fine if you can be sure that Remoting is enabled on the remote machines. If you want a more bullet-proof test, you could use the Test-WSMan cmdlet to check whether the WinRM service can be accessed on the remote machine. However, a downside of this method is that the cmdlet’s timeout can be several seconds and can’t be configured. If you have many computers in your list, this can slow down your control script on the local machine significantly.

An alternative is to work with a try-catch block:

If Invoke-Command fails to execute the command on the computer, the corresponding computer name is added to a text file (line 10). This enables you to repeat the command later.

Passing local variables to the remote session ^

If you want to pass variables from your control script on the local computer to the commands that run on the remote computers, you can work with the -ArgumentList parameter:

In the example, the values of the $Name and $CPU variables are passed to the $RName and $RCPU variables, which the Get-Process cmdlet will use on the remote computer to determine all processes with the name “svchost” where the CPU usage (CPU property of Get-Process) is greater than 100.

By the way, this example also demonstrates that PowerShell Remoting is sometimes your only option even if the cmdlet you want to use offers a -ComputerName parameter. The Get-Process cmdlet cannot retrieve the CPU usage from a remote computer if you run it in a local session. However, if you use the Invoke-Command cmdlet, Get-Process runs in a remote session and you can then read the CPU usage.

Accessing methods in PowerShell Remoting ^

Using Invoke-Command also has a downside. The objects that PowerShell returns as output differ depending on whether you run the command in a PSSession or in a session that the PowerShell host creates. The objects even differ if you run Invoke-Command on the local computer.

The first thing you notice is that the returned objects have an additional property: the PSComputerName property. You guessed it—it contains the name of the computer where the corresponding PSSession ran.

The PSComputerName property

The PSComputerName property

You need this additional property to ascribe the results to computers if you executed commands on multiple machines. Okay, an additional property is not really a downside, but the result objects also lack a crucial feature. Try this:

The methods of the Process object

The methods of the Process object

And then this:

The methods of a deserialized object

The methods of a deserialized object

Obviously, PowerShell stripped off all the interesting methods. In this case, this means that you can’t use the object that Invoke-Command returns to kill a remote process because the Kill() method is unavailable.

Another thing you notice is that the TypeNames are different. In the first case, it is System.Diagnostics.Process, and the second command returns Deserialized.System.Diagnostics.Process. If you execute a command with Invoke-Command, it packs the result object in XML, transmits it to the calling session, and creates a so-called static object that lacks all methods except GetType() and ToString().

The conversion to XML is called serialization; the opposite process is deserialization. This is why TypeName is “Deserialized.” The lack of methods makes sense because the objects to which you could apply the methods (in our example, the processes) are only available in the remote session, and you therefore can’t manipulate them in the local session.

The one remaining question is how you can use methods of objects on remote machines. Let’s stay with our example from above. Because the Kill() method is unavailable in the deserialized static object, you have to kill the process in the remote session before the result object is transmitted to your local session. The following command kills Notepad on the remote computer:

Thus, you can access methods of PSSessions objects. All you have to do is build the logic that needs the methods, not in your control script on the local computer but in the script that runs on the remote machine.

In one of my next posts, I will talk about running PowerShell commands remotely in disconnected sessions.

Win the monthly 4sysops member prize for IT pros

Share
6+

Users who have LIKED this post:

  • avatar

18 Comments
  1. Marc 2 years ago

    There is missing a } at the end of the last example-script.

    1+

  2. Michael Pietroforte 2 years ago

    Marc, thanks a lot for the hint. Corrected it now.

    0

  3. Mark B 2 years ago

    Marc, I hope you can help--I'm running a script basically using the process you note above,
    "Invoke-Command -ComputerName PC1,PC2,PC3 -FilePath C:\myFolder\myScript.ps1". It works great, with the huge exception that I get an "access denied" error when I try to output the results back to the server that I'm running the script from ( I'll call it the source server). All servers are in a domain, and I'm an admin on all of them. My solution so far has been to place the output into a file on each target server, then in another script I copy all of my little output files from all of the target servers back to the source server. Dumb, and a waste of time. How can I avoid getting this access denied error?

    0

  4. Ram 2 years ago

    Mark I have one question for you. How can I call a PS script file or a batch file from my local system. Scenario is I have to run Windows patching on all Windows servers so I have created one PS batch file and placed on every server. Now I have to login on the server and manually run the batch file. So this is completely free manual his time. I want to automated this. For this I have to run batch file from my local system and pass the target server name to run the script. Is this possible

    0

  5. Erik 2 years ago

    I have three local powershell scripts that I want to pipeline on 500 computers. something like .\1.ps1 | \2.ps1 | .\3.ps1

    can I use Invoke-Command -ComputerName $RemoteComputer -ScriptBlock {.\1.ps1 | \2.ps1 | .\3.ps1}

    1+

  6. Heron 2 years ago

    Hello,

    I need some help.

    I need to write an script on windows powershell to connect ssh admin@10.10.10.10 -p 1234

    than add the password, send text "W2KTT", send another text "change my stuff", than save screen (this is the command to save echo -e '\033[?47h') in a especific location and than logoff.

    Any idea?

    0

  7. Amal 1 year ago

    The Below Job is not running as expected any suggestions?

     

    {

    $script = "C:\Test\enable_device_mon_xml.ps1"

    $ps_session = New-PSSession -ComputerName $server -Credential $credential

    Enter-PSSession $ps_session

    Invoke-Command -Session $ps_session -FilePath $script -AsJob

    Exit-PSSession

    }

    0

    • Author

      Amal, "not running as expected" can mean a lot. What is the error message? What I see is that you don't need Enter-PSSession because this cmdlet is only for interactive remote sessions and I guess you just want to execute a script remotely. You also don't need Exit-PSSession which ends an interactive session.

      0

  8. Azad 1 year ago

    3+

    Users who have LIKED this comment:

    • avatar
  9. shailesh 12 months ago

    PS C:\WINDOWS\system32> Invoke-Command  -ComputerName WSCTXMER003V -FilePAth D:\scripts\FileServerStorageNetAppMigration

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

    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 use

    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,

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

    + FullyQualifiedErrorId : PSSessionStateBroken

     

    Just to update you that omputers are members of an Active Directory domain

    0

    • Author
      Michael Pietroforte 12 months ago

      First thing I do in case of a PowerShell remoting connection error is to try to connect to a file share on that computer because in most most cases it is not a WinRM or PowerShell remoting problem and just a general connection or authentication issue.

      If connecting to the file share works, I connect with Computer Management and try some typical admin tasks like adding a user account to ensure that my user account has sufficient privileges.

      Sometimes you  get new error messages that help you solve the problem. These error message are more common and it is easier to find solutions on the web.

      If you are sure that the computer is properly integrated and authenticated in your domain, you can start searching for a  specific PowerShell remoting problem.

      0

  10. Prabhakar. G 9 months ago

    Hi Michael,

    I want to know how to execute the Powershell scripts from Ubuntu to Windows machine.

    I have installed Powershell in Ubuntu and when I'm trying to execute the command :

    getting error

    Could you please help me out how to execute the Powershell scripts from Ubuntu to Windows.

    0

  11. yaro 9 months ago

    Is -FilePath pointing to a local o remote location specified in -ComputerName as I'm not clear whether this will run a local script on the remote machine or trigger a script located on the remote machine?

    0

    • Prabhakar G 9 months ago

      Hi Yaro

      Below is the command I'm trying to execute from Ubuntu to Windows machine.

      -FilePath is pointing to a local machine

      -ComputerName "192.168.x.x" is Windows machine IP Address.

      I'm getting error message as

      Could not find the error message. I have gone through the troubleshooting link https://msdn.microsoft.com/powershell/reference/5.1/Microsoft.PowerShell.Core/about/about_remote_troubleshooting

      but did not get the solution.. Please help me out.

      0

      • Author
        Michael Pietroforte 9 months ago

        It seems you have an authentication problem. We will soon have an article about Windows-Linux remoting from Tim Warner. This is the part from Tim's post that might help you:

        Enable basic authentication and allow unencrypted connections by running the following commands:

        And then in Linux we can do this to make an interactive session with the remote Windows server:

        0

  12. Mitra 2 months ago

    How to Stop Particular windows process from powerchell/powercli (for virtual machine) , please suggest !!

    0

  13. Devops 2 months ago

    I am using invoke-command in a cron job and executing it using a service account/non-person id. I have added service account in admin group of remote server. Execution of same is giving below error:

    "This user isn't allowed to sign in to this computer.:"

    Providing Service account a login rights can be a problem to security. Is there a way to achieve this without providing login rights ?

    0

Leave a reply

Your email address will not be published. Required fields are marked *

*

CONTACT US

Please ask IT administration questions in the forum. Any other messages are welcome.

Sending
© 4sysops 2006 - 2017

Log in with your credentials

or    

Forgot your details?

Create Account