Summary: Richard Siddaway explains how to use WindowsPowerShell remoting sessions.
Hey, Scripting Guy! I’ve just starting learning Windows PowerShell, and I understand how to use it as a scripting language and shell on the local machine. But how do I work with remote machines?
—AP
Hello AP,
Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. This is the second part of a series of five posts about remoting. The series includes:
- Remoting Recap
- Remoting Sessions
- Configuring Remoting
- Remoting Security
- Non-Domain Remoting
In my Remoting Recap post, you saw that you can use Invoke-Command to send a command to a remote machine over a WSMAN connection, and then receive the results back on your local machine, for example:
Invoke-Command -ComputerName $computer -ScriptBlock {Get-Service}
This great if you want to send only a single command. But if you have a number of commands, it is more efficient to create a remoting session to the target machine and run the commands over that. You only have one set up and tear down instead of one per command.
You get several cmdlets for working with Windows PowerShell remoting sessions:
- Connect-PSSession
- Disconnect-PSSession
- Enter-PSSession
- Exit-PSSession
- Export-PSSession
- Get-PSSession
- Import-PSSession
- New-PSSession
- Receive-PSSession
- Remove-PSSession
You’ll see a number of them in action in this post. The starting point is to create a session that is connected to a remote machine:
£> $sess = New-PSSession -ComputerName win12r2
£> $sess | fl *
State : Opened
IdleTimeout : 7200000
OutputBufferingMode : Block
DisconnectedOn :
ExpiresOn :
ComputerName : win12r2
ConfigurationName : Microsoft.PowerShell
InstanceId : 818369f6-2458-43f0-b6b2-81d8c8be9d34
Id : 2
Name : Session2
Availability : Available
ApplicationPrivateData : {DebugMode, DebugStop, PSVersionTable, DebugBreakpointCount}
Runspace : System.Management.Automation.RemoteRunspace
In this case, I’m remoting from a computer running Windows 8.1 to a system running Windows Server 2012 R2. That’s Windows PowerShell 4.0 to 4.0. Actually, it’s Window Management Framework 4.0 on both sides. WMF includes Windows PowerShell and the WSMAN stack. You can create remote connections between any of these versions of Windows PowerShell: 4.0, 3.0, or 2.0.
The important point to remember is that the functionality available through your remote session is controlled by the version of Windows PowerShell on the remote machine. You can’t remote to a system running Windows PowerShell 2.0 and expect to use the new cmdlets in Windows PowerShell 4.0 that you have available on your local machine.
After you have created a session, you can work with it interactively, or you can use Invoke-Command against the session. Entering a session enables you to work interactively on the remote session:
£> Enter-PSSession -Session $sess
[win12r2]: PS C:\Users\Richard\Documents> Get-CimInstance -ClassName Win32_OperatingSystem | Format-List
SystemDirectory : C:\Windows\system32
Organization :
BuildNumber : 9600
RegisteredUser : Windows User
SerialNumber : 00252-00105-04511-AA037
Version : 6.3.9600
[win12r2]: PS C:\Users\Richard\Documents> Exit-PSSession
£>
Note I use £> as my prompt.
The session you created earlier is used with Enter-PSsession. It is possible to use the computer name instead of a pre-existing session:
£> Enter-PSSession -ComputerName win12r2
[win12r2]: PS C:\Users\Richard\Documents> Get-CimInstance -ClassName Win32_OperatingSystem | Format-List
SystemDirectory : C:\Windows\system32
Organization :
BuildNumber : 9600
RegisteredUser : Windows User
SerialNumber : 00252-00105-04511-AA037
Version : 6.3.9600
[win12r2]: PS C:\Users\Richard\Documents> Exit-PSSession
£>
I’m using the session because I want to reuse the connection later. In either case, when you are in the session, your prompt changes. In my case the local prompt is £>. The prompt of the session running in the remote session is:
[win12r2]: PS C:\Users\Richard\Documents>
When you open a remote session, no profile scripts are run. What is interesting is the return from Get-PSSnapin. If you open a Windows PowerShell console in Windows 8.1 or Windows 8, and you run Get-PSSnapin (assuming your profile doesn’t load anything), you’ll get one response:
Microsoft.PowerShell.Core
If you enter a session on a remote machine and repeat the call to Get-PSSnapin, you’ll get a bit more back:
Microsoft.PowerShell.Diagnostics
Microsoft.PowerShell.Core
Microsoft.PowerShell.Utility
Microsoft.PowerShell.Host
Microsoft.PowerShell.Management
Microsoft.PowerShell.Security
Microsoft.WSMan.Management
These are reported as snap-ins even though they are modules.
The module auto-load feature is still present, so you don’t explicitly have to import modules. For example, you can use the NetConnection module:
[win12r2]: PS C:\Users\Richard\Documents> Get-NetConnectionProfile
Name : Manticore.org
InterfaceAlias : Virtual LAN
InterfaceIndex : 12
NetworkCategory : DomainAuthenticated
IPv4Connectivity : Internet
IPv6Connectivity : LocalNetwork
Name : Manticore.org
InterfaceAlias : Virtual Wireless
InterfaceIndex : 14
NetworkCategory : DomainAuthenticated
IPv4Connectivity : Internet
IPv6Connectivity : LocalNetwork
After you have finished your interactive work and you can run scripts that exist on the remote machine, you can exit the session by using Exit-PSSession. If you used a session, it will still be available to use.
Yesterday, you saw how to use Invoke-Command to run a command against a remote machine:
Invoke-Command -ComputerName server02 -ScriptBlock {Get-Service}
You can do exactly the same thing when you have a remote session available:
Invoke-Command -Session $sess -ScriptBlock {get-service}
Our session was created against a computer called Win12R2. Let’s create another session against server02:
$sess2 = New-PSSession -ComputerName server02
And then you can do this:
Invoke-Command -Session $sess, $sess2 -ScriptBlock {get-service}
Each object returned has the computer name appended in the PSComputerName as shown in this abbreviated output:
DisplayName : Windows Driver Foundation - User-mode Driver Framework
Status : Stopped
ServiceType : Win32ShareProcess
PSComputerName : win12r2
DisplayName : Active Directory Web Services
Status : Running
ServiceType : Win32OwnProcess
PSComputerName : server02
You can also create a session that spans multiple machines, but first remove the existing sessions:
Get-PSSession | Remove-PSSession
£> $sessM = New-PSSession -ComputerName server02, win12r2
£> $sessM
Id Name ComputerName State ConfigurationName Availability
-- ---- ------------ ----- ----------------- ------------
1 Session1 server02 Opened Microsoft.PowerShell Available
2 Session2 win12r2 Opened Microsoft.PowerShell Available
If you use the session directly, you get results from all machines in the session:
Invoke-Command -Session $sessM -ScriptBlock {get-service}
Alternatively, you can access individual machines within a session:
Invoke-Command -Session (Get-PSSession -Name Session1) -ScriptBlock {get-service}
Invoke-Command -Session (Get-PSSession -Name Session2) -ScriptBlock {get-service}
Creating a remote session to connect to a server running Exchange Server is a little odd compared to what you’ve seen so far. You can create a session exactly as described earlier, and you can run Windows PowerShell commands through it.
What you can’t do is import the Exchange PowerShell snap-in; that is (Microsoft.Exchange.Management.PowerShell.E2010 in Exchange Server 2010 or Microsoft.Exchange.Management.PowerShell.SnapIn in Exchange Server 2013). You will get an error message.
When you install Exchange Server, it creates a specific remoting endpoint for you (endpoints will be covered in more detail later in the series). This is hosted in IIS rather than being a “normal” emoting endpoint. You have to connect like this:
$cred = Get-Credential
$sess = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://MyExchangeServer/PowerShell" -Authentication Kerberos -Credential $cred -ErrorAction Stop
The credentials that you use must be granted the required administrative permissions in Exchange. The connection URI is http://MyExchangeServer/PowerShell, where you substitute your server name for MyExchangeServer.
This tells Windows PowerShell where to find its connection. If your DNS is functioning correctly, you can use only the server name. Otherwise, a FQDN is required.
Out of interest, the Help file for New-PSSession states that the default ConnectionURI is http://localhost:5985/WSMAN, which means for quick tests you can do this:
$sess = New-PSSession
A session is opened on the local machine assuming you have remoting enabled.
The other parameter that you need for a connection to an Exchange server is ConfigurationName. This is the name in use by the session. Compare the session details for an Exchange Server connection with those for a standard Windows PowerShell connection shown earlier:
State : Opened
IdleTimeout : -1
OutputBufferingMode : None
ComputerName : MyExchangeServer
ConfigurationName : Microsoft.Exchange
InstanceId : ce246497-af2b-4087-8a17-6edd1031b579
Id : 1
Name : Session1
Availability : Available
ApplicationPrivateData : {SupportedVersions, ImplicitRemoting, PSVersionTable}
Runspace : System.Management.Automation.RemoteRunspace
Now you’ve got your remote session to Exchange Server. What are you going to do with it?
The Exchange cmdlets are available, and if you try to use them in the same way as the remoting sessions you’ve seen so far, your commands will appear to fail at odd moments. This isn’t a problem. What’s happening is that you have a constrained endpoint. This will be covered in more detail later; but for now, it means that you can’t use variables, and some Windows PowerShell language features aren’t available.
This is where you get to see what is probably one of the coolest remoting features. You can take your remote session and import it into your local machine. This is performed like this:
Import-PSSession $sess
If you don’t want to see all of the messages, you can make the import quiet by using Out-Null:
Import-PSSession $sess | Out-Null
As an aside, this is a good method of suppressing output from any cmdlet that you don’t want to see.
The result of the import is a module with a random name, such as tmp_qyllw5kj.gz1. As long as you have the session open, your module is available, and it is removed when you close the session. If you examine the contents of the module, you’ll find that it contains proxy functions for the cmdlets that are available in the remote session. By using the module on your local machine, it works automatically across the remoting session. You have the full benefit of Windows PowerShell and access to the Exchange cmdlets.
If required, you can restrict the commands that are imported by using the CommandName parameter of Import-PSSSession. Import-PSSession also adds an –AsJob parameter to all of the proxy functions it creates to give you more flexibility for long running commands.
If you want a permanent module that contains the proxy functions for the Exchange cmdlets, use Export-PSSession, which will enable you to set the name of the module and save it to disk on your local machine for future use. Whether you do that or create a new temporary module each time is a preference dictated by your working patterns.
You have seen how to work with remote sessions and how you can import Windows PowerShell functionality from remote machines onto your local machine. Next time, you’ll learn more about configuring remoting endpoints plus one of the issues that many people trip over: running commands on the remote machine against a third machine.
Bye for now.
~Richard
Thanks, Richard. I look forward to reading the rest of this series.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy