Executing Remote Powershell Commands
When you begin administering multiple computers, you’ll eventually want to automate the tasks you repeat more than once. Instead of logging into each computer, starting powershell and running the same command, you can use one powershell console locally and send commands to multiple remote computers from that single local console.
In addition to running locally, Powershell can run remotely using WinRM (Windows Remote Management) which uses a management protocol called WS-Man. When your local computer talks to another via Powershell remoting (WinRM over WSMan), a remote session is created, the command is executed, then the remote session is normally terminated. Remote sessions can be opened at any time, and a remote session can be persisted if necessary - more about that later.
If you want to execute a command on another machine, you have a couple of options:
- Start a new Powershell session to the remote machine, execute your code, then close the connection
- Use Invoke-Command, which runs script in a temporary session
- Use one of the few Powershell commands that does not run over WinRM and specify the -ComputerName argument
-
According to “Get-Help about_Remote”, these commands do not need WinRM to execute:
- Get-Counter
- Clear-EventLog
- Limit-EventLog
- New-EventLog
- Get-EventLog
- Remove-EventLog
- Get-HotFix
- Restart-Computer
- Get-Process
- Show-EventLog
- Get-Service
- Stop-Computer
- Get-WinEvent
- Test-Connection
- Get-WmiObject
- Write-EventLog
Powershell Sessions
Powershell uses the concept of sessions within which code is executed and acts as the outer scope for variables.
To execute code on a remote machine, you must first start a remote session then enter that remote session. Sessions can be entered and created using Enter-PSSession, and exited using Exit-PSSession. See New-PSSession, Remote-PSSession and Get-PSSession and Get-Command pssession for further commands.
To enter a new session, run Enter-PSSession and specify the name of the remote machine. This immediately puts the current powershell console into the execution context of the remote machine:
Note how the [computername] shows we are running in the context of the remote machine. To leave a session, use Exit-PSSession - “exit” would also work here.
Note how we are now back to our local session - there is no [computername] at the last prompt.
PS Sessions can be created and entered as necessary, and can also be stored in variables, letting you switch between sessions too.
Powershell Remoting with Invoke-Command
Check out the documentation for Invoke-Command by using the Get-Help command. Use the -Online switch to open the related MSDN web page.
To get a list of logged on users locally, we’d use the following command to call WMI and query the list of logged on users. We then pipe this through a Select-Object filter and fetch the Name.
We can then use the -ScriptBlock argument with Invoke-Command to get a execute the powershell command to get the list of logged on users for that remote machine.
Executing local functions and variables on the remote machine
Functions cannot be passed as references so you must redeclare them in the scope of the command you wish to execute. You can pass variables, and I’ll show you how to do that below.
The following will not work because the -ScriptBlock is executing in a new context on the remote machine, and has no idea what the SayHello function is.
The term ‘sayHello’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
To fix this, we re-declare the function inside the ScriptBlock and then we can call it.
To prove that was working in the context of the remote machine, we’ll add the hostname to the output:
Variables declared locally can be passed through to the ScriptBlock via the $Using: