Scripting

PowerShell : how to run remote commands and scripts with Invoke-Command?

I. Introduction

Remote administration is a common practice that should be encouraged to improve the efficiency of system and network administrators. PowerShell offers several options to enable the administrator to execute commands or scripts remotely on a set of machines. These may be workstations or servers, as required.

One option is the PowerShell cmdlet Invoke-Command. It lets you execute a command or script on one or more remote computers, via the WinRM or SSH protocols, depending on the context.

The following two methods can also be used:

  • Use a remote PowerShell session, via cmdlets New-PSSession and Enter-PSSession
  • Use the -ComputerName of certain cmdlets to target a remote machine directly

In this tutorial, we will only explore the use of the Invoke-Command cmdlet to execute remote commands on multiple machines. For further information, please refer to this previous article:

II. Requirements for remote execution

Before using Invoke-Command, it is essential to ensure that the environment is correctly configured. Here, we'll use the simplest configuration to avoid weighing down the article with WinRM configuration, namely: executing commands between several machines integrated into an Active Directory domain, which facilitates mutual authentication and approval of hosts.

WinRM is associated with the Windows Remote Management feature. The default state is :

  • On Windows Server, Remote Management is enabled by default, which means that WinRM is listening.
  • On Windows 10 and Windows 11, Remote Management is deactivated by default, so it's not possible to connect to a remote workstation without configuration.

To activate WinRM (Windows Remote Management) on a machine, simply run this command:

Enable-PSRemoting

This command activates WinRM, starting the associate service, listening on port 5985/TCP (HTTP) and configuring the firewall to allow remote connections. To configure WinRM on a set of machines, follow this tutorial :

You can determine whether or not the WinRM service is running on a remote machine, via this command (by adapting the name of the target host):

Test-WSMan -ComputerName W11-01.it-connect.local

III. Using Invoke-Command

Once the environment has been set up, Invoke-Command can be used to execute remote commands. In the remainder of this article, we'll look at a number of examples. Before getting started, please note that this command can :

  • Execute a block of code containing one or more commands via the -ScriptBlock
  • Execute a PowerShell script (.ps1) using the parameter -FilePath

A. Execute a single command

Executing a command on a remote computer named W11-01.it-connect.local is as follows:

Invoke-Command -ComputerName W11-01.it-connect.local -ScriptBlock { Get-Process }

This command retrieves the list of processes running on the remote machine, thanks to the execution of Get-Process.

B. Executing a PowerShell script

It is also possible to execute a complete script by specifying a PowerShell file in PS1 format. If the remote machine doesn't have access to the script via the network, this isn't a problem. What's important is that the script is accessible by the machine from which it was created. Invoke-Command is executed:

Invoke-Command -ComputerName W11-01.it-connect.local -FilePath "C:\Scripts\MonScript.ps1"

This makes it possible to automate tasks on multiple machines, whether workstations or servers.

Note : to run the command or script in the background as a job, add the -AsJob parameter.

If the script to be executed is already located on the remote machine and cannot be accessed from the source computer, use this syntax :

Invoke-Command -ComputerName W11-01.it-connect.local -ScriptBlock { C:\Scripts\MonScript.ps1 }

C. Run a command on several machines

To run a command on several machines, simply specify several host names separated by commas. Here's an example of how to restart the Print Spooler service on 2 different machines:

Invoke-Command -ComputerName W11-01.it-connect.local,W11-02.it-connect.local -ScriptBlock { Restart-Service -Name Spooler }

Personally, I prefer to store the list of machines in an array and call the variable via Invoke-Command. It's also more practical if you have several commands to execute via different calls to Invoke-Command.

$ComputersList = @("W11-01.it-connect.local","W11-02.it-connect.local")
Invoke-Command -ComputerName $ComputersList -ScriptBlock { Restart-Service -Name Spooler }

This simplifies the management of multiple machines. Here's another example of how to reboot a set of machines remotely:

Invoke-Command -ComputerName $ComputersList -ScriptBlock { Restart-Computer -Force }

We could also retrieve a list of computers dynamically from the Active Directory, then execute an action on each machine in this list.

# Get computer list from OU "OU=PC,OU=IT-Connect,DC=it-connect,DC=local"
$ComputersListAD = (Get-ADComputer -Filter * -SearchBase "OU=PC,OU=IT-Connect,DC=it-connect,DC=local").DNSHostName

# Obtain available disk space on each machine in the list
Invoke-Command -ComputerName $ComputersListAD -ScriptBlock { Get-CimInstance -Class Win32_LogicalDisk | Select-Object -Property      DeviceID, Name, @{ 
      label='UsedSpace' 
      expression={(($_.Size - $_.FreeSpace)/1GB).ToString('F2')} 
} }

Here, we use the -ScriptBlock parameter, but it's perfectly possible to run a script on the same principle using the -FilePath.

D. Specify identifiers

If the current user does not have the necessary rights, it is possible to provide credentials by adding the parameter -Credential. It expects as value an object PSCredential with a username and password, which we can generate via Get-Credential :

$Identifiants = Get-Credential
Invoke-Command -ComputerName W11-01.it-connect.local -Credential $Identifiants -ScriptBlock { Get-Service }

This ensures that execution takes place with the permissions of the specified user, otherwise the current user in the PowerShell session is used.

E. Use a persistent session

Another way of doing this, rather than using -Credential and -ComputerName is to use a WinRM session (persistent) or an SSH connection. In the first case, you need to create a new session via New-PSSession and use the -Session while in the second case the parameter -SSHConnection must be specified.

For example:

$MaSession = New-PSSession -ComputerName W11-01.it-connect.local -Credential IT-Connectadmin.pc
Invoke-Command -Session $MaSession -ScriptBlock { Restart-Computer -Force }

When you use -ComputerName PowerShell will create a session to execute the command, then close the session in the process. If you need to share data between several independently executed commands, you'll need to use a persistent session, as in the example above. This method has the advantage of keeping data in the same session until it is closed.

F. Use arguments

You can use arguments to pass values or variable values between the local and remote machines. This is done by using the parameter -ArgumentList and the special variable $Args.

In the example below, the variable $ServiceName is defined on the local machine. Its value is passed to the remote machine using the parameter -ArgumentList and this value is used when executing the command via the $args[0]. Since there may be several arguments (separated by a comma), this generates an array whose first value is stored at index 0.

$ServiceName = "winrm"
Invoke-Command -ComputerName $ComputersList -ScriptBlock { Get-Service $args[0] } -ArgumentList $ServiceName

The picture below illustrates how it works.

IV. Conclusion

Invoke-Command is an indispensable command for remote administration of machines via PowerShell, whether to execute a single command or a complete script. Thanks to this mechanism, you can optimize the management of your infrastructure and gain in efficiency.

To learn more about PowerShell, check out my online course and my book :

author avatar
Florian Burnel Co-founder of IT-Connect
Systems and network engineer, co-founder of IT-Connect and Microsoft MVP "Cloud and Datacenter Management". I'd like to share my experience and discoveries through my articles. I'm a generalist with a particular interest in Microsoft solutions and scripting. Enjoy your reading.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.