PowerShell Remoting in Multi-Platform Environments – Use Cases

Page content

In our previous post we discussed how to implement OpenSSH (the plumbing) as the transport layer for PowerShell remoting. In this post, we’re going to leverage that configuration and look at some common remoting use cases. This is one of the core things I use everyday when I work with PowerShell. Remoting gives me the ability to administer scale up and administer large collections of systems. So like I said in my very first post about my PowerShell journey, it’s become a part of my every day life and techniques like this are at the core of how I use PowerShell. So let’s get started…

We’re going to look at the following

  • Entering a remote command line interface on a server
  • Using sessions
  • Executing commands against one remote system
  • Executing commands on a collection of remote systems

This is going to be awesomeness…Yep, I said awesomeness, when I’m finished you’ll agree that’s about the only way to describe this.

I do want to point out that we’re using Beta software here, things change. The version of PowerShell I’m using here is Version 6 Beta 2.

Remote Command Line Interface

Up first, let’s cover the simplest remoting case, where we use remoting to get a command line interface to a remote system.

Enter-PSSession -HostName server1.demo.local

Nothing special here, simple syntax, but the seasoned PowerShell remoting pro will notice that we’re using a new parameter here -HostName. Normally on Windows PowerShell you have the -ComputerName parameter. Now, I don’t know exactly why this is different, but perhaps the PowerShell team needed a way to differentiate between OpenSSH and WinRM based remoting. Further, Enter-PSSession now has a new parameter -SSHTransport which at the moment doesn’t seem to do much since remoting cmdlets currently use OpenSSH by default. But if you read the code comments here, it looks like WinRM will be the default and we can use this switch parameter to specify SSH as the transport.

Once we execute this command, you’ll have a command prompt to the system that passed as a parameter to -HostName. The prompt below indicates you’re on a remote system by putting the server name you’re connected to in square brackets then your normal PowerShell prompt. That’s it, you now have a remote shell. Time to get some work done on that server, eh? Want to get out of the session, just type exit.

[server1.demo.local]: PS /home/aen>

Using Sessions

So, in PowerShell, when you exit from a remoting session your session goes away. So any work, variables or program state you had will disappear. But what if we wanted to keep our session’s state, log out and log back into that session again? We can leverage sessions to help us persist our remote sessions. We can create a session connect and disconnect from it at will. So let’s check that out.

We create a new session by calling New-PSSession. This will start up the remoting session just like we did with Enter-PSSession, via SSH, but we won’t attach to the remote terminal. The output we do get from New-PSSession shows us the details of the session created. We use Get-PSSession to get a list of all current sessions.

PS /Users/aen> New-PSSession -HostName server1.demo.local -UserName aen

 Id Name            ComputerName    ComputerType    State         ConfigurationName     Availability
 -- ----            ------------    ------------    -----         -----------------     ------------
  2 SSH2            server1.demo... RemoteMachine   Opened        DefaultShell             Available
We can connect to an existing session with Enter-PSSession. If there are multiple entries in your session list…just use the correct Id from the session list to identify the session you want to connect to.
PS /Users/aen> Enter-PSSession -Id 2
Now, when you exit from this session with the exit command, your session will persist. Let’s use Get-PSSession to see.
PS /Users/aen> Get-PSSession

Id Name ComputerName ComputerType State ConfigurationName Availability


2 SSH2 server1.demo… RemoteMachine Opened DefaultShell Available

Cool, our session is still there, let’s reuse it. This time we’re going to assign it to a variable this time. This will keep our command line syntax simple in the upcoming examples.
PS /Users/aen> $s = Get-PSSession -Id 2

Executing Commands Against One Remote System

We can use PowerShell remoting to execute a command against a remote system, so let’s look at the simplest case…just one command. Here you can see, we’re using the Invoke-Command cmdlet to execute the Get-Process command on the remote system and it’s output returns to our console. Here we have the top 5 processes by CPU on the remote system.
PS /Users/aen> Invoke-Command -Session $s { Get-Process | Sort-Object -Descending CPU -Top 5 }

 NPM(K)    PM(M)      WS(M)     CPU(s)     Id  SI ProcessName        PSComputerName
 ------    -----      -----     ------     --  -- -----------        --------------
      0     0.00     761.55   2,805.86  14770 704 sqlservr           server1.demo.local
      0     0.00       8.37     183.29  13480 479 vmtoolsd           server1.demo.local
      0     0.00       7.24      65.31  13553 550 ManagementAgent    server1.demo.local
      0     0.00       0.00      61.74    401   0 xfsaild/dm-0       server1.demo.local

Executing Commands Against a Collection of Remote Systems

Now, let’s add one more session to our list of sessions. With a new session to server2.
PS /Users/aen> New-PSSession -HostName server2.demo.local -UserName aen

Id Name ComputerName ComputerType State ConfigurationName Availability


3 SSH3 server2.demo… RemoteMachine Opened DefaultShell Available

New-PSSession just returns the session created, so let’s get a list of all of the sessions with Get-PSSession. There you can see our two remoting sessions are currently opened the new session has a new Id.
PS /Users/aen> Get-PSSession

Id Name ComputerName ComputerType State ConfigurationName Availability


2 SSH2 server1.demo… RemoteMachine Opened DefaultShell Available 3 SSH3 server2.demo… RemoteMachine Opened DefaultShell Available

This time, let’s take both our sessions and save them to a variable. We’re going to reuse $s.
PS /Users/aen> $s = Get-PSSession
Now to the amazing part…let’s run a command against a collection of remote systems.
PS /Users/aen> Invoke-Command -Session $s { Get-Process | Sort-Object -Descending CPU -Top 5 }

NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName PSComputerName


  0     0.00       0.57       5.09    666 666 rngd                             server2.demo.local
  0     0.00      93.82       2.03  24218 218 powershell                       server2.demo.local
  0     0.00       6.57       1.78      1   1 systemd                          server2.demo.local
  0     0.00       8.37       0.91  13475 474 vmtoolsd                         server2.demo.local
  0     0.00       0.00       0.89     94   0 kworker/0:2                      server2.demo.local
  0     0.00     761.41   2,810.99  14770 704 sqlservr                         server1.demo.local
  0     0.00       8.37     183.69  13480 479 vmtoolsd                         server1.demo.local
  0     0.00       7.24      65.48  13553 550 ManagementAgent                  server1.demo.local
  0     0.00       0.00      61.88    401   0 xfsaild/dm-0                     server1.demo.local
  0     0.00       1.69      58.19  13749 749 crond                            server1.demo.local</pre>
This example brings up a very interesting point in PowerShell remoting, everything inside the curly braces of the Invoke-Command call happens on the remote system. This means, all the sorting and processing is remote, the remote systems deal with all the computational aspects and less data has to traverse the network. There’s a deeper topic of serialized objects we’ll cover in a later post. Now the output from these systems comes back to your console as fast as it comes back, so there’s really no order here. So you might get a list of data that’s all commingled. But we can control that by sorting locally. Let’s look at that.
Now, let’s say you want to get the top 5 processes across all the systems in your sessions list, easy enough.
PS /Users/aen> Invoke-Command -Session $s { Get-Process } | Sort-Object -Descending CPU -Top 5

NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName PSComputerName


  0     0.00     761.41   2,814.59  14770 704 sqlservr                         server1.demo.local
  0     0.00       8.37     183.95  13480 479 vmtoolsd                         server1.demo.local
  0     0.00       7.25      65.59  13553 550 ManagementAgent                  server1.demo.local
  0     0.00       0.00      61.96    401   0 xfsaild/dm-0                     server1.demo.local
  0     0.00      94.83      61.93  24464 393 powershell                       server2.demo.local</pre>

We move the sorting to the pipeline on our local system, notice the Invoke-Command call is going to return the entire process list from all machines, then it will sort the data locally and output to the local console. So you can see in this list here we have the top 5 processes across our two systems. Imagine you had a web farm of servers and you needed to chase down a bad process fast, this would be useful, right?

When we’re all finished, you’ll want to clean up your sessions. We can do that a by passing the $s variable into Remove-PSSession

PS /Users/aen> $s | Remove-PSSession

And that’s it, so like I said in my very first post about my PowerShell journey, it’s become a part of my every day life and techniques like this are at the core of how I use PowerShell.