Administrator's Guide to Powershell Remoting

This paper explains how to set up and run Windows PowerShell Remoting which is a new feature in Windows PowerShell 2.0 and allows you to run Windows PowerShell commands and scripts remotely. So, before moving on, make sure you have Windows PowerShell 2.0 RTM on your machines.

Windows PowerShell 2.0 is included in Windows 7 and Windows Server 2008 R2 operating systems by default. (For Server Core edition, it’s available as a feature.) For down-level versions of Windows, you can download Windows PowerShell 2.0 RTM here:

If you have installed Windows PowerShell and want to find out whether it is the latest version, or if you would like to examine your enterprise to find out which machines are running a particular version of Windows PowerShell, see:

Topics Covered:

Windows PowerShell Remoting Technologies

Windows PowerShell supports two fundamentally different technologies to access remote systems. You can either use »classic« remoting which is built into individual cmdlets, or you can use Windows PowerShell Remoting to run any command on remote systems.

We will walk you through both remoting technologies to give you an overview of how they work and what they can do. Please do not be frustrated if some of the initial examples cause errors on your system. Accessing remote systems involves many security settings that might not yet be correctly configured on your system. You will learn more about fine-tuning these settings in a later part of this paper. The initial objective is to focus on the basic technology.

Classic Remote Access

With classic remote access, Windows PowerShell is not directly involved in accessing remote systems. Instead, single cmdlets implement the technology required to access remote systems independently. With this type of remoting, it is up to the cmdlet author to choose and implement the appropriate technology. In most cases, this type of remote access uses the same remoting technology present in other remote management products and tools: Remote Procedure Call (RPC) and Distributed COM (DCOM).

You can identify cmdlets with built-in remoting support by searching for cmdlets that support the ComputerName parameter:

Get-Help * -Parameter ComputerName

Some of the cmdlets this command returns are involved with Windows PowerShell Remoting. So, to identify only those that use classic remoting techniques, exclude cmdlets with a Session parameter. Here is an example taken from the Windows PowerShell help files:

Where-Object { $_.Parameters.Keys -contains
$_.Parameters.Keys -notcontains 'Session'}

The next command uses Get-WmiObject to access BIOS information on PC01:

Get-WmiObject Win32_BIOS -ComputerName PC01

By default, you will be authenticated as the current user. One requirement for most remote accesses is that your user account has local administrator privileges on the target system. If you are currently logged on as a domain administrator, by default, you are also a member of the local Administrators group unless your security settings have been changed.

If Windows PowerShell returns an Access Denied error message, you can check whether the cmdlet supports the Credential parameter. If so, use it to log on to the target system as a different user:

Get-WmiObject Win32_BIOS -ComputerName PC01 -Credential (Get-Credential)

To find all cmdlets that support both the ComputerName and the Credential parameters in one parameter set, use this command:

Get-Command -CommandType cmdlet
? { @($_.ParameterSets
? { $p = $_.Parameters
Object -Expand Name; (($p -contains 'computername')
-and ($p -contains 'credential'))}).Count -gt
Table 1

Transparent Authentication

If you do not use the Credential parameter to supply explicit credentials Windows PowerShell tries to authenticate you as the current user. This works well in a domain environment when you are domain administrator. However, in peer-to-peer networks, there is no global domain administrator.

So, if you are not a member of a domain, you can create a "pseudo domain administrator" by adding a local account to your machines which all use the same name and password. Be sure this account is member of the Administrators group on the local computer.

Then, when you log on as this user and access a remote system, you will be authenticated as this user on the remote system. Since there is a matching account on the target system, it will authenticate your local account against the matching local account on the target system, and you do not need to specify the credentials.

If Windows PowerShell still returns an Access Denied error with this technique, the machines involved might not be peer-to-peer machines after all. Machines that are joined to a domain do not support this type of transparent credentials. In this case, you either need to log on with the credentials of a domain administrator or provide the credentials explicitly.

Pitfalls and Gotchas

Classic remote techniques can be awesome when all works right. However, if you do not meet the technical requirements, it is very hard to diagnose the problem. Here is an example. Let's assume you want to get event log entries from a remote system, so you try this:

Get-EventLog Application -EntryType Error -ComputerName storage1

Index Time EntryType Source InstanceID Message
----- ---- --------- ------ ---------- -------
51054 Jan 07 20:15 Error Application Error 1001 ...
51052 Jan 07 20:15 Error Application Error 1001 ...

All seems to work just fine. But look what happens when you disable the RemoteRegistry service on the target machine:

(Get-WmiObject Win32_Service -Filter
'name="RemoteRegistry"' -Computer

When you now repeat the Get-EventLog command, you get a very confusing error message:

Get-EventLog Application -EntryType Error -ComputerName storage1

Get-EventLog : The network path was not found. At line:1 char:13 + get-eventlog <<<< Application -ComputerName Storage1 + CategoryInfo : NotSpecified: (:) [Get-EventLog], IOException + FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.GetEventLogCommand

This error message complains about a missing network path and is a good example of the type of confusing error messages you might receive when important settings or permissions are missing. This error messages won't help much in identifying the real underlying problem.

After you re-enable the RemoteRegisty service on the target machine, Get-EventLog works again.

(Get-WmiObject Win32_Service -Filter
'name="RemoteRegistry"' -Computer

As you can see, error messages with classic remoting can be misleading because the errors are raised internally with the remoting technique the cmdlet chose to use internally.

Windows PowerShell Remoting

Since traditional remote access is implemented by individual cmdlets, it is inconsistent (uses different techniques and demands different requirements) and available only in selected cmdlets. The technology used for remote access can vary from cmdlet to cmdlet and is not readily known to you. Each cmdlet uses whatever remoting technology its author chose. Most cmdlets use Remote Procedure Call (RPC), but might also require additional services and settings on the target system.

Beginning in Windows PowerShell 2.0, there is an alternate and more universal way of accessing remote systems: Windows PowerShell Remoting. With this type of remoting, Windows PowerShell handles remote access for all commands. It transfers your commands to the remote system using the relatively new and highly configurable WinRM service, executes the code in a separate session that runs on the remote system, and returns the results to the calling system.

Table 2

With this architecture, your commands always runs locally, just on different machines, so there is no need for individual cmdlets to support remoting. Any command that can be run locally can also be run remotely. However, to make that happen, a number of prerequisites have to be in place.

Minimal Setup

If your machine is acting as client only (accessing other systems but not being accessible for other computers) in a domain environment, you do not need to configure anything special.

Note that, with this type of minimum setup, your computer cannot provide remoting services. It can only access other systems that have enabled remoting. You will not be able to test remoting on your own computer.

Also, in a peer-to-peer environment (without a domain), minimal setup will not work because you need to change a WinRM setting called TrustedHosts which is only configured when you run the full setup. So if you are running in a peer-to-peer environment or need to access non-trusted domains, run the full setup.

After you run the full setup and configure TrustedHosts, you can then undo some of the changes made by the full setup -- remove the firewall exception and stop the WinRM service -- if you only want to access other systems. This will prevent other systems from connecting to you.

If you want to set up TrustedHosts without calling Enable-PSRemoting, you need to add a registry key and then temporarily run the WinRM service. Run the following lines with full Administrator privileges:


HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\ Policies\System LocalAccountTokenFilterPolicy 1 -Type DWord

Start-Service WinRM

If your client is running Windows XP, you also need to set another registry entry:


HKLM:\System\CurrentControlSet\Control\Lsa ForceGuest 0

Now you can add the TrustedHosts entry:

Set-Item WSMan:\localhost\Client\TrustedHosts –Value * -Force

Finally, you can turn off the WinRM service again and revert the value of the LocalAccountTokenFilterPolicy registry entry:

Stop-Service WinRM


HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\ Policies\System LocalAccountTokenFilterPolicy 0 -Type DWord

Your client is now able to access other systems remotely using Windows PowerShell Remoting and is no longer limited to Kerberos and machines inside a domain.

Full Setup

To turn your computer in a remoting server that is accessible from other machines, the prerequisites for remoting need to be in place:

  • WinRM Service: This service receives requests from other computers and needs to be running.

  • Listener: Inside WinRM, a listener needs to be set up that listens on the network port Windows PowerShell Remoting uses to communicate.
  • Firewall Exception: A firewall exception is needed that allows outside requests to reach the WinRM service.

To set up these prerequisites, launch a Windows PowerShell console with elevated privileges and run this command:


This cmdlet fails when one of your network connections is set to Public. So, if you receive an error that complains about not being able to create the firewall exception, check the troubleshooting section later in this paper to find out how to resolve this issue.

By default, Windows PowerShell Remoting uses Kerberos authentication, which requires a domain controller. If you are not using a domain or need to connect to machines outside your own trusted domain(s), one more step is necessary:

Set-Item wsman:\localhost\client\trustedhosts * -Force

This command configures Windows PowerShell Remoting so that you can connect to any computer, not just those inside your own trusted domain(s).


You can further harden this setting by using explicit computer names, IP addresses or prefixes instead of »*«:

Set-Item wsman:\localhost\client\trustedhosts server_* -Force

Set-Item wsman:\localhost\client\trustedhosts 10.10.10.* -Force -Concatenate

Set-Item wsman:\localhost\client\trustedhosts -Force -Concatenate

Dir wsman:\localhost\client

This example configures TrustedHosts in a way that allows you access to computers with names that start with »server_« or with IP addresses that start with »10.10.10.« or with the particular system »«.

Also, you need to add the Concatenate parameter to add new entries without overwriting existing ones.

These settings do not determine which computers can connect to you. TrustedHosts is just a reminder to you to keep you from connecting to evil machines. If you do not want this type of limitation, set TrustedHosts to »*«.

Why limit yourself to connecting only to machines listed in TrustedHosts? As long as you are using Kerberos authentication within a domain, there is no need for TrustedHosts, because Kerberos provides mutual authentication and assures that your credentials are never passed to a machine outside your trusted domain(s).

However, with other types of authentication, there is no guarantee the computer you connect to is not an evil machine that someone smuggled into your network. If you provide your credentials to such a machine, it could grab them and do bad things with them.

This is why, by default, Windows PowerShell Remoting does not allow connections to these machines and leaves it to you to reduce security by adding exceptions to TrustedHosts.

In reality, though, as a good administrator you know pretty well which machines you connect to and whether they are your own or not. So here, TrustedHosts should be set to »*« so you can decide which computers you want to access.

TrustedHosts essentially works almost like a Force switch. Either the system you want to connect to is already part of TrustedHosts, so you can connect right away, or it is not, in which case any Administrator can simply add it to TrustedHosts.

Setup Recommendations

When you use Windows PowerShell Remoting for the first time, we suggest you set up a test scenario and fully enable Windows PowerShell Remoting on all machines with these two simple commands:

Enable-PSRemoting -Force

Set-Item wsman:\localhost\client\trustedhosts * -Force

This way, you can focus entirely on playing with the remoting cmdlets and not having to worry about remoting setup questions. Once you have played with Windows PowerShell Remoting and feel more confident, before moving to your production systems, evaluate in more detail which settings are really required for particular machines and follow the »least privileged« security rule.

Running Remote Commands

With Invoke-Command, you run commands locally on a remote machine. At a minimum, this cmdlet requires the commands you want to execute. If you do not provide a computer name, it runs your commands locally on your computer, so to work remotely, you also need to specify the name of the computer you want to access.

Next, Invoke-Command sends a request via WinRM on port 5985 to the remote system and instantiates a new Windows PowerShell session on the remote system. After it establishes the session, it transfers the commands you provided to that session and instructs it to run the commands. Finally, the results returned by that command are transferred back to your system.

The next command executes Get-Service on PC01 and uses Windows PowerShell Remoting:

Invoke-Command { Get-Service } -ComputerName PC01

The code in braces runs locally on the target system inside the Windows PowerShell session on that machine. The result is returned to your system.

Get-Service is a cmdlet that has built-in remoting support based on classic remoting techniques, so you could have used either one remote access technologies to receive (almost) the same result

Get-Service -ComputerName PC01

However, the universal Windows PowerShell Remoting approach ensures that any code you want to run can run remotely by using the same procedure. For example, although Get-Service has a ComputerName parameter and would have been able to get information on its own from a remote system, it does not support the Credential parameter. So, to authenticate as a different user, your only choice is Windows PowerShell Remoting:

Invoke-Command { Get-Service } -ComputerName PC01 -Credential (Get-Credential)

In addition, Windows PowerShell Remoting takes care of all remoting requirements and does not require your particular command or cmdlet to support remoting. You can remote any command including native applications such as netstat or ipconfig:

Invoke-Command { netstat } -ComputerName PC01

Windows PowerShell Remoting sends your code to the remote system and executes it inside a separate session on that system. The code runs locally, just on a different machine, so, you can remote any command that can run locally. Just make sure the command you want to run really does exist on the remote system. For example, you might have robocopy.exe on your local machine, but you can run it remotely on another system only if it is present on the remote system, too.

Also, all file paths, variables and locations in your commands are interpreted on the remote system, not your own. So to access folders, they must exist on the remote system.

Finally, beware of second hop issues that occur when you try to access protected resources, such as file shares. Because you have already authenticated on the remote system, your commands cannot pass your credentials to resources outside the target system. This is why your code cannot "hop" to additional machines or access file shares that require authentication. The following example illustrates an illegal second hop call:

Invoke-Command { Get-WmiObject Win32_BIOS -ComputerName PC04 } -ComputerName PC01

In this case, your commands would be transferred to PC01 and executed there. The command would then try to get WMI information from PC04, which requires another authentication and would fail.

To access different systems from inside a remote system, you must explicitly present new credentials or use advanced techniques such as Delegation or CredSSP to allow the target system to pass your credentials forward.

Interactive Remoting

Instead of sending code to a remote session, you can also visit a remote session and work interactively. Use Enter-PSSession to connect to a remote session. Your prompt changes and shows the target name in square brackets. Although you type commands in your local console, anything you enter is executed on the remote system until you leave the remote session by using Exit-PSSession.

Enter-PSSession -ComputerName PC01

When you connect your Windows PowerShell console to a remote session, notice that your prompt might change as well as other settings you may have configured in your local session. This is because the remote session runs on another system and no longer executes any of your local profile scripts. Any adjustments and changes that you made in your local profile scripts will be missing from the remote session. The remote session is a completely separate Windows PowerShell environment.

Disabling Remoting

Enabling Windows PowerShell Remoting is a one-time setting that persists until you disable it. To disable Windows PowerShell Remoting, use this command which requires Administrator privileges:


Disable-PSRemoting does not reverse all changes made by Enable-PSRemoting. For example, Enable-PSRemoting starts the WinRM service if it wasn't running already. Disable-PSRemoting cannot undo this change because it does not know if the WinRM service was enabled by other services in the meantime. This is why the command lists all the changes made by Enable-PSRemoting and asks you to undo part of these changes manually, if necessary.

Removing the Listener

The Disable-PSRemoting function does not remove the WSMan listener. Your computer still listens to requests coming from other machines. To remove all listeners, do this (you need full Administrator privileges to access the "localhost" folder):

Remove-Item WSMan:\localhost\Listener\* -Recurse -WhatIf

In the command, remove the WhatIf parameter to remove all listeners.

By removing the listener, you actually prevent the WinRM service from processing requests sent by other machines. This might be important if you continue to run WinRM for other purposes.

The previous command removes all listeners including listeners that were added by other applications. To remove only listeners that listen on the Windows PowerShell Remoting default port, 5985, use this command:

Dir WSMan:\localhost\Listener –Recurse
Foreach-Object { $_.PSPath }
Where-Object { (Get-
Item "$_\Port").Value -eq 5985 }
Remove-Item -WhatIf

Again, to actually remove listeners, remove the WhatIf parameter from the previous command.

Disabling the WinRM Service

The WinRM service accepts remote requests. When you enable Windows PowerShell Remoting, this service is started and is set to start automatically. Disabling Windows PowerShell Remoting leaves the service running because it cannot determine whether other applications may require this service.

Generally, Windows servers, such as Windows Server 2003 or 2008 run the WinRM service by default, so, on servers, unless there is a specific reason, you should leave the WinRM service running.

By contrast, on clients, the default for the WinRM service is a manual start. So, if you enabled Windows PowerShell Remoting on a client and want to return to the initial defaults, stop the service and set it back to the manual start mode like this:

Set-Service winrm -StartupType Manual

Stop-Service winrm

WinRM might be needed by other applications. Before disabling WinRM, make sure no other application or service depends on it.

Removing Firewall Exception

Enable-PSRemoting enables predefined firewall rules that allow HTTP on port 5985. When you disable Windows PowerShell Remoting, these exceptions remain. To remove them, either manually open the firewall configuration and disable the rules, or use Windows PowerShell.

Here are some functions you can use to manage the Windows firewall. Get-FirewallRule uses a COM object to access the rules defined in your Windows Firewall. The COM object that this function uses has been changed over time, so the function tries to use the modern approach hat was introduced in Windows Vista. If that fails, it falls back to the old way that was used in Windows XP.

function Get-FirewallRule($rule = '*', $port = '*') { try { (New-Object -ComObject HNetCfg.FwPolicy2).Rules | Where-Object { $_.Name -like $rule } | Where-Object { $_.LocalPorts -like $port } } catch [system.Management.Automation.PSArgumentException] { # this handles firewall rules on Windows XP: 0..2 | Foreach-Object { $obj = New-Object -ComObject HnetCfg.FwMgr }{ try { $obj.LocalPolicy.GetProfileByType($_).GloballyOpenPorts } catch {}} | Where-Object { $_.Name -like $rule } | Where-Object { $_.Port.toString() -like $port } } } function Disable-FirewallRule([Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]$rule) { process { $rule.Enabled = $false } } function Enable-FirewallRule([Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]$rule) { process { $rule.Enabled = $true } }

To check for any rule on port 5985, use Get-FirewallRule:

Get-FirewallRule -Port 5985 | Format-Table Name, Enabled, Profiles, LocalPorts, Description -Auto

To disable these rules, pipe the rules to Disable-FirewallRule:

Get-FirewallRule -Port 5985 | Disable-FirewallRule

These functions will disable any rule on port 5985. By default, WinRM is the only service that uses this port. There could be other services using this port if they were added manually. Thus, you may want to filter the results to be even more specific. To select only rules that use port 5985 and the HTTP protocol, try this:

Get-FirewallRule -Port 5985 | Where-Object { $_.Protocol -eq 6 }

There are additional firewall rules that can be enabled manually to extend the reach of Windows PowerShell Remoting. For example, there are compatibility rules that allow Windows PowerShell requests on port 80, and there are additional rules for HTTPS on port 5986. Neither of these rules is enabled by default when you run Enable-PSRemoting.

Play with Get-FirewallRule to get a better picture what might be enabled on your system:

Get-FirewallRule -Port 80

Unfortunately, Firewall rules are localized so their names might differ depending on the locale that your system uses. Use the next command to list all rules associated with Windows PowerShell Remoting:

Get-FirewallRule *Windows?Remote*

Understanding Windows PowerShell Sessions

Windows PowerShell sessions are the core building blocks of Windows PowerShell Remoting so it is a good idea to get a better understanding what they are and what they do.

Whenever you launch a new Windows PowerShell console, you are actually creating a Windows PowerShell session. A Windows PowerShell session essentially is a place where Windows PowerShell code can live. Without remoting, all sessions run locally on your own machine. They are independent environments and cannot interact.

With Windows PowerShell Remoting, you gain a way to create such sessions anywhere, locally as well as on remote systems, and keep a connection between them. So Windows PowerShell Remoting is, to some extent, a marshaling service that enables you to send code to another session and get results.

Temporary Sessions

Whenever you use Windows PowerShell Remoting with the ComputerName parameter, a temporary session is created for you and used only for this particular command. Once the command completes, the session is automatically closed and discarded.

In the following example, each Invoke-Command command creates a separate and independent session, which is why the variable defined in the first command is no longer available to the second command:

Invoke-Computer { $env:computername; $test=1 } -ComputerName PC01

Invoke-Computer { $env:computername; $test } -ComputerName PC01

Temporary sessions are a convenient way for quick remote access, but whenever you plan to access a remote system more than once, they are very inefficient and slow because Windows PowerShell needs to create and discard sessions for every single command.

Reusable Sessions

A more efficient way is to create sessions yourself and keep them around until you no longer need them. Then, whenever you need remote access, you present the existing session and use it, instead of creating a new one. The next command creates a session to PC01 and stores it in a variable called $session:

$session = New-PSSession -ComputerName PC01

To use this session for a particular remoting command, use the Session parameter instead of ComputerName:

Invoke-Computer { $env:computername; $test=1 } -Session $session

Invoke-Computer { $env:computername; $test } -Session $session

Here, both calls share the same session, which is why the variable defined in the first command is still present in the second command. Also, these commands run almost instantaneously because there is less overhead; no session needs to be created or discarded. In essence, reusable sessions work similar to a remote desktop connection that you just disconnect and reconnect without actually logging off or on.

To check the status of a session, dump it. It tells you whether the connection is still alive and available, and which computer the session runs on:


You can also use Get-PSSession to dump all sessions you generated or pick a specific session from the pool of available sessions.

It is your responsibility to close and discard sessions that you no longer need. Use Remove-PSSession:

Remove-PSSession $session

You can also remove all sessions with one command:

Remove-PSSession *

All sessions that you create live in your own local Windows PowerShell session, so when you close Windows PowerShell, all remaining sessions are discarded automatically.

Session Best Practice

Often, you only need to access remote systems once. In this case, it is best to use temporary sessions because these sessions are automatically closed and removed after the call.

If you start to access remote systems more often or would like to keep the session state, use reusable sessions. The caveat is to make sure you clean up behind yourself and close these sessions when they are no longer used. Otherwise, you might quickly run out of sessions, because there is a limit of 5 concurrent sessions that any one user can open to any one remote system.

Accessing Multiple Computers: Fan-Out

While there is a limit of 5 sessions that you can simultaneously maintain on any one remote computer, there is no limit -- other than resources -- to how many sessions you can simultaneously maintain in general.

You can create sessions on many computers at the same time and then use all of these sessions together to execute code simultaneously on many computers. This is what is known as »Fan-Out«. It can help you manage server farms and configure or analyze groups of computers with just one command.

Again, you have the choice of using temporary sessions or use persistent sessions. To execute commands on many machines using temporary sessions, use the ComputerName parameter and supply a comma- separated list of computer names or IP addresses. Windows PowerShell Remoting creates a separate session for each computer, and Invoke-Command runs the code in all sessions at the same time.

Invoke-Command { $env:computername } -ComputerName demo05, localhost, storage1

You will get error messages for any computer Windows PowerShell cannot access, and the results returned by the commands on all other systems.

Alternately, you can create the sessions by using New-PSSession and then by submitting the sessions to Invoke-Command. The next command creates sessions on all computers listed. You will get an error message for any computer that Windows PowerShell cannot access.

$session = New-PSSession storage1, localhost, demo5

All successfully created sessions are saved in $session:


Id Name ComputerName State ConfigurationName Availability
-- ---- ------------ ----- ----------------- ------------
1 Session1 localhost Opened Microsoft.PowerShell Available
2 Session2 demo5 Opened Microsoft.PowerShell Available
3 Session3 storage1 Opened Microsoft.PowerShell Available

As is always the case with Windows PowerShell cmdlets, the result really is an array if the cmdlet returned more than one result. So $session is not a »super session« targeting multiple computers. Instead, it is just a container for many individual sessions. Each session always represents just one Windows PowerShell environment on one particular machine.

Since Invoke-Command accepts both individual sessions and array containing multiple sessions, you can now execute a command on all sessions simultaneously. The next command creates a registry key (HKEY_LOCAL_MACHINE\Software\Test) on all computers on which sessions have been established:

Invoke-Command { Md HKLM:\Software\Test } -Session $session

Throttle Limit

When you access multiple sessions at the same time, a lot of data might be sent from those sessions to your machine. To avoid being overwhelmed and to keep network bandwidth in safe bounds, there is a built-in throttle limit of 32. Only 32 connections are serviced simultaneously.

If you specify more, then Windows PowerShell parks the remaining requests in a queue and waits for the first 32 sessions to complete. After the first session returns their results, the next session in the queue is used. In essence, there are never more than 32 simultaneous commands executing.

Depending on what you want to do and your available network bandwidth, you can adjust the throttle limit with the ThrottleLimit parameter. Lower it on slow networks, or increase it to speed up processes in a fast network.

Managing Sessions and Releasing Resources

Each session that you create with New-PSSession uses a certain amount of resources. If you open too many sessions at the same time, you might run out of resources or allowable connections and at some point will be unable to create any more new sessions.

To check your sessions, use Get-PSSession:


To close and discard sessions you no longer need, use Remove-PSSession. You can select sessions to remove either by name or by ID:

Remove-PSSession -Name Session4

Remove-PSSession -Id 1

To close and remove all sessions in one call, use this command:

Remove-PSSession *

How is session lifetime handled by Windows PowerShell Remoting if you do not clean up? Let's assume you connect to a remote system and start a couple of sessions. Next, you disconnect your machine from the network and walk home. Will your stranded sessions stay alive forever on the server?

Actually, each session is a separate process that you can see when you look in the right place:

$s = New-PSSession -ComputerName $env:computername

Get-Process wsmprovhost

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
206 34 45308 49864 562 0,47 2712 wsmprovhost

Each session that you create is represented by a process named wsmprovhost. Removing this process also removes the session:


Id Name ComputerName State ConfigurationName Availability
-- ---- ------------ ----- ----------------- ------------
2 Session2 demo5 Opened Microsoft.PowerShell Available

Stop-Process -Name wsmprovhost


Id Name ComputerName State ConfigurationName Availability
-- ---- ------------ ----- ----------------- ------------
2 Session2 demo5 Broken Microsoft.PowerShell None

After the process is killed, the session state changes to »broken« and is no longer available.

The same occurs automatically if a remote session loses its connection to the client. Although Windows PowerShell Remoting really acts like a web service and uses stateless connections, the server maintains a heartbeat to the client. If the network connection between client and server is lost for more than approximately three minutes, the server discards the session. This is controlled by a WSMan setting called IdleTimeout:

Dir WSMan:\localhost\shell\idletimeout

It returns the number of milliseconds a connection can be disconnected before the server discards the session.

"Idle Timeout = $((Dir WSMan:\localhost\shell\idletimeout).Value/60000) minutes"
Idle Timeout = 3 minutes

Implicit Remoting

All of the previous examples used explicit remoting, where you explicitly instructed Invoke-Command to run commands in another session. Another way of remoting is implicit remoting in which the command knows that it should run in another session.

Implicit remoting is really just an extra layer of convenience. It uses the same technology and is based on sessions.

The first thing you need for implicit remoting is a session.

$session = New-PSSession -ComputerName WIN-KHHRLLOTMS3 -Credential (Get-Credential)

After you have a session, you can load additional snap-ins or modules or create your own functions in the session.

In the next example, you enter a session that you created. Next, you look at the available modules and import one. In addition, you define a new simple function.

This is a real-world scenario. In this example, I am accessing a Windows Server 2008 R2 which is the first server operating system that ships with Windows PowerShell 2.0 built-in. In addition, this server comes with Windows PowerShell-based management tools allowing you to add or remove server features.

These management commands are available only locally on that server. With Windows PowerShell Remoting and implicit remoting, you can also use these cmdlets on your own computer. This illustrates how flexible server management can be. Instead of having to install management tools on your own computer, Windows PowerShell Remoting brings the server cmdlets to you.

Enter-PSSession $session
[win-khhrllotms3]: PS C:\Users\Tobias\Documents> Get-Module -ListAvailable
CommandType Name
----------- ----
Cmdlet Add-WindowsFeature
Cmdlet Get-WindowsFeature
Cmdlet Remove-WindowsFeature
# Import a module and examine the commands in the module
[win-khhrllotms3]: PS C:\Users\Tobias\Documents> Import-Module ServerManager
[win-khhrllotms3]: PS C:\Users\Tobias\Documents> Get-Command -Module ServerManager
CommandType Name Definition
----------- ---- ----------
Cmdlet Add-WindowsFeature Add-WindowsFeature [-Name] <Feature[]> [-Include...
Cmdlet Get-WindowsFeature Get-WindowsFeature [[-Name] <String[]>] [-LogPat...
Cmdlet Remove-WindowsFeature Remove-WindowsFeature [-Name] <Feature[]> [-LogP...

# Import a module and examine the commands in the module

[win-khhrllotms3]: PS C:\Users\Tobias\Documents> Import-Module ServerManager

[win-khhrllotms3]: PS C:\Users\Tobias\Documents> Get-Command -Module ServerManager

CommandType Name
----------- ----
Cmdlet Add-WindowsFeature
Cmdlet Get-WindowsFeature
Cmdlet Remove-WindowsFeature

# Create a function and exit the session[win-khhrllotms3]: PS C:\Users\Tobias\Documents> Function test { "I am executing on $env:computername." } [win-khhrllotms3]: PS C:\Users\Tobias\Documents> Exit-PSSession PS>

When you leave the remote session and return to your own local session, the remote session is still available in $session. You have imported the ServerManager cmdlets and defined a function called test in the session.

You can import the Test function from the remote session. To do that, use Import-PSSession and specify the command you want to import:

Import-PSSession $session -CommandName test
ModuleType Name ExportedCommands
---------- ---- ----------------
Script tmp_b4976f56-a449-4470... test

The command you selected is imported into your local session as a script. Importing commands from a session only works if you have enabled script execution on your machine. If you get an error message instead, use Get-ExecutionPolicy and make sure the execution policy is set to RemoteSigned.

Now, the Test function is available in your local session. When you call it, you get the result. As you can see from the result, the script block that Test invokes runs on the server, not on your system. This is an example of implicit remoting, because the local command really runs on a different machine without having to explicitly specify this.


I am executing on WIN-KHHRLLOTMS3.

In essence, implicit remoting hasn't enabled you to do something completely new. It is just a convenient way of calling remote commands. Without implicit remoting, you could have achieved the same result with Invoke- Command:

Invoke-Command { test } -Session $session

I am executing on WIN-KHHRLLOTMS3.

Implicit remoting is a great way of making server-side cmdlets available on client computers. For example, to manage the Server 2008 R2 that I used in this example, I can import all cmdlets from the ServerManager module that I imported into my remote session:

$result = Import-PSSession $session -Module ServerManager

I captured the result of Import-PSSession in a variable so I have the list of imported cmdlets when I need it:

Name Value
---- -----
Remove-WindowsFeature Remove-WindowsFeature
Get-WindowsFeature Get-WindowsFeature
Add-WindowsFeature Add-WindowsFeature

Thanks to implicit remoting, I can use the ServerManager cmdlets on my local machine. Behind the scenes (implicitly), they are executed on the server. Whenever I need to add or remove features on that server, I can use Get-WindowsFeature to check certain features, such as the XPS-Viewer:

Get-WindowsFeature xps*
RunspaceId : bc04f492-3d30-4dee-b8b6-7d91da4eb72b
DisplayName : XPS-Viewer
Name : XPS-Viewer
Installed : False
FeatureType : Feature
Path : XPS-Viewer
Depth : 1
DependsOn : {}
Parent :
SubFeatures : {}
SystemService : {}
Notification : {}
BestPracticesModelId :
AdditionalInfo : {NumericId}

The value of False for the Installed property indicates that this particular feature is not yet installed. Note that Windows PowerShell Remoting executed the command on the server and returned the results to my local session. Everything happens behind the scenes.

To add the XPS-Viewer, I can use Add-WindowsFeature. Because I want to determine whether installing this feature has unwanted side effects, I first use the WhatIf parameter to simulate the installation:

Add-WindowsFeature xps-viewer -WhatIf }
RunspaceId : 3e019a31-a1f8-40d1-9562-38a97a5ac622
Success : True
RestartNeeded : Maybe
FeatureResult : {}
ExitCode : Success

In RestartNeeded, I can now discover whether this particular installation requires a server restart. Well, sometimes. If the cmdlet cannot tell for sure, it returns »Maybe« which sounds nicer than »Unknown« but means essentially the same.

To actually install the feature, I run the command again, but remove -WhatIf. This time, you see a progress bar that illustrates the installation progress and, when the command completes, it returns the status. The RestartNeeded property tells you for sure whether a restart is needed and the Success property reports that the installation went smoothly.

Add-WindowsFeature xps-viewer
RunspaceId : 3e019a31-a1f8-40d1-9562-38a97a5ac622
Success : True
RestartNeeded : No
FeatureResult : {XPS-Viewer}
ExitCode : Success

Implicit Remoting Gotchas

Importing commands from a session imposes some gotchas that you should know about. If you try to import a command that also exists in your local session, the import fails unless you add the AllowClobber parameter, in which case the local command is no longer available. Or, you could use the Prefix parameter and specify a prefix. The imported commands are renamed. The prefix that you specify is added to the noun part of the command to differentiate it from local commands.

If you specify a command that cannot be found in the session, you get an error message complaining that Get-Command cannot find the command.

Implicit remoting relies on the session you from which you imported the commands. If the session is not available, because, for example, you removed it or the target machine rebooted, the imported command(s) no longer work.

Understanding Session Configurations

When you create a new session, behind the scenes, a so-called session configuration is used to initialize the session. Windows PowerShell creates at least one default session configuration when you enable Windows PowerShell Remoting. You can use Get-PSSessionConfiguration to view the existing session configurations, but you must start Windows PowerShell with the “Run as Administrator” option.

Name PSVersion StartupScript Permission
---- --------- ------------- ----------
microsoft.powershell 2.0 BUILTIN\Administrators Access Allowed
microsoft.powershell32 2.0 BUILTIN\Administrators Access Allowed

The default session configuration is called microsoft.powershell. On 64-bit machines, there will also be a second session configuration called microsoft.powershell32 that runs on the 32-bit Windows PowerShell host.

Default session configurations are created and secured by Enable-PSRemoting. If you enable Windows PowerShell Remoting manually or by using Set-WSManQuickConfig, there are no default session configurations.

You do not need to fiddle with session configurations if you do not feel like it. In fact, you can ignore them altogether if all you want to do is work remotely. However, taking a closer look at session configurations can be important if you want more control over the sessions that you (and others) create.

For example, you can use session configurations to control how remote commands are executed. Here are some things session configurations can do for you:

  • Pick Host: If you have a script that uses snap-ins or COM objects that run only on 32-bit systems, and you need to run this script on a 64-bit machine, you can force Windows PowerShell to use the 32-bit host by specifying the predefined microsoft.powershell32 session configuration that is created by default on 64-bit machines.
  • Restrict Session: If you want to harden security, you can create a new session configuration that removes unwanted Windows PowerShell commands and, thereby, creates a restricted environment.
  • Custom Environments: Similarly, if you want sessions to be preconfigured with snap-ins, modules or other settings, add a profile script to a new session configuration. Any time you create a session with this session configuration, the profile script runs and initializes the session, pretty similar to how your local profile scripts work.

How Session Configurations are Assigned

When you run a Windows PowerShell Remoting cmdlet that creates a new remote session, such as Invoke- Command, Enter-PSSession or New-PSSession - Windows PowerShell automatically uses the default session configuration microsoft.powershell unless you specify a different session configuration or have secured session configurations.

Whenever you run a remoting cmdlet without specifying a session configuration, Windows PowerShell uses the session configuration defined in the preference variable $PSSessionConfigurationName. The session configuration that is used on the server side is controlled by the client side. This makes sense; you (the caller) choose which session configuration you need.

The server side can control session configurations in a different way: by applying access security to session configurations, the server controls which session configurations are available to clients. Applying access controls on session configurations is discussed below.

If you ignore session configurations, you get a 32-bit session if the target system is a 32-bit system and a 64- bit session if the target system is a 64-bit system - much like launching Windows PowerShell interactively.

You can use the ConfigurationName parameter to pick a session configuration manually. For example, to create a remote session on a 64-bit system running in 32-bit, use this command:

$session32 = New-PSSession -ComputerName Win2k8R2 -ConfigurationName microsoft.powershell32

Session configurations can help initialize sessions with certain defaults. For example, every time you launch Windows PowerShell locally, profile scripts run. These profile scripts can change the prompt, load additional commands and configure your workspace. When you use sessions with the default session configurations, no profile scripts run.

Let's create a new session configuration that loads one of the default profile scripts. Just make sure that this file is accessible to the user who creates the session. In this example, we pick the machine level profile stored in $PsHome\Profile.ps1.

You can run any script in the session configuration. To change the script, simply change the script path in the command. The profile script must be present on the remote machine, so you might want to verify that it exists before using the session configuration. On the machine on which you want to register the new session configuration, launch a Windows PowerShell console with full Administrator privileges, and enter this command:

Notepad $PSHome\Profile.ps1

If the profile script does not yet exist, Notepad asks whether you want to create it. In this example, we use a simple profile script that contains only two commands:

function prompt { "PS> " }

$host.UI.RawUI.CursorSize = 80

It redefines the prompt and sets the cursor size to 80%.

To register a new session configuration, run Register-PSSessionConfiguration. This cmdlet works locally only, that is, it creates the session configuration on the local machine, so you need to run it on the target side with full Administrator privileges:

Register-PSSessionConfiguration -Name WithProfile -StartupScript $PsHome\Profile.ps1
Are you sure you want to perform this action?
Performing operation "Register-PSSessionConfiguration" on Target "Name: WithProfile. This will
allow administrators to remotely run Windows PowerShell commands on this computer".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): y
WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Plugin
Name Type Keys
---- ---- ----
WithProfile Container {Name=WithProfile}
Are you sure you want to perform this action?
Performing operation ""Restart-Service"" on Target "Name: WinRM".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): y

Next, make sure the session configuration was added:

Name PSVersion StartupScript Permission
---- --------- ------------- ----------
microsoft.powershell 2.0 BUILTIN\Administrators Access Allowed
microsoft.powershell32 2.0 BUILTIN\Administrators Access Allowed
WithProfile 2.0 C:\Windows\System...

Using Custom Session Configurations

After you add the new session configuration to the target computer, clients can create sessions on this target computer using the new session configuration. Simply request the session configuration by using the ConfigurationName parameter. If you have no test network, you can test this on your local machine, provided that you have enabled Windows PowerShell Remoting and run Windows PowerShell with full Administrator privileges (for example, right-click PowerShell in your start menu and choose "Run as Administrator").

First, try to connect to an interactive remoting session without the new session configuration:

C:\Windows\system32 PS> enter-pssession -ComputerName $env:computername [demo5]: PS C:\Users\w7-pc9\Documents> Exit-PSSession C:\Windows\system32 PS>

You can connect to the remote session. Nothing special changes because the remote session does not run any profile script. Next, use the new WithProfile session configuration that you created earlier:

C:\Windows\system32 PS> enter-pssession -ComputerName $env:computername [demo5]: PS C:\Users\w7-pc9\Documents> Exit-PSSession C:\Windows\system32 PS>

You can connect to the remote session. Nothing special changes because the remote session does not run any profile script. Next, use the new WithProfile session configuration that you created earlier:

C:\Windows\system32 PS> Enter-PSSession -ComputerName $env:computername -ConfigurationName WithProfile [demo5]: PS> Exit-PSSession C:\Windows\system32 PS>

This time, the prompt changed, because the session was initialized with the profile script that was specified in the session configuration. Also, your Windows PowerShell cursor changes and appears as a block cursor.

After you exit the remote session, the prompt reverts to its original style, but the cursor size remains as an 80% block cursor. Why?

Any changes you make by using $host.UI.RawUI, such as setting the cursor size, buffer size, or console colors, do not change Windows PowerShell settings; they change the current console settings. When the remote session ends, all functions that are defined in the session are reset, this is why you get your old prompt back. However, the console stays the same, so changes to the cursor size are not reversed. This is not related to how sessions or Windows PowerShell Remoting work. It happens because interactive remote sessions share the same console with local Windows PowerShell sessions.

Enumerating Remote Session Configurations

In the previous example, you connected to a remote session by using a custom session configuration registered on the target system. This was easy because you set up the new session configuration yourself, so you knew its name. What if you need to remotely access a system you do not know that well? How do you know about the session configurations that are available on that machine?

To list session configurations on a remote system, use Connect-WSMan to connect to the target system and then use the WSMan: drive to explore the session configurations available on that machine. This example finds the session configurations available on a system called storage1:

Connect-WSMan storage1
cd WSMan:
WSMan:\> dir
ComputerName Type
------------ ----
localhost Container
storage1 Container
WSMan:\> dir .\storage1\plugin
Name Type Keys
---- ---- ----
Event Forwarding Plugin Container {Name=Event Forwarding Plugin}
microsoft.powershell Container {Name=microsoft.powershell}
microsoft.powershell32 Container {Name=microsoft.powershell32}
SEL Plugin Container {Name=SEL Plugin}
WithProfile Container {Name=WithProfile}
WMI Provider Container {Name=WMI Provider}
Registered session configurations show up as plug-ins to WS-Management.
Get-WSManInstance winrm/config/plugin -Enumerate -ComputerName storage1 | Select name
Event Forwarding Plugin
SEL Plugin
WMI Provider

Securing Session Configurations

You can assign session configurations to users automatically. For example, you may want to restrict the commands in a session that are available to some users. This is done in a two-step-process:

1. Create a new session configuration that restricts the session to only a subset of commands.

2. Change security access permissions so that the intended users can only access the new session configuration.

Creating a Restricted Session

First, create a new session configuration that restricts the session to only a safe subset of commands. To do this, run an initialization script in the session configuration that restricts the session.

Open an editor with full Administrator privileges (i.e. run notepad from an elevated Windows PowerShell console) and enter this:

# Disable access to all applications $ExecutionContext.SessionState.Applications.Clear()

# Disable access to scripts $ExecutionContext.SessionState.Scripts.Clear()

# Define a list of allowed commands $RequiredCommands = "Exit-PSSession", "Get-Command", "Get-FormatData", "Get-Help", "Measure- Object", "Out-Default", "Select-Object" $Commands = $RequiredCommands + "Get-Process", "Get-Service", "Where-Object", "ForEach-Object"

# Make everything except the allowed commands private (not visible) Get-Command | Where-Object {$Commands -notcontains $_.Name} | ForEach-Object {$_.Visibility="Private"}

# Restrict the language elements to a very limited set. The possible values are FullLanguage, RestrictedLanguage, and NoLanguage $ExecutionContext.SessionState.LanguageMode="RestrictedLanguage"

Save this as %windir%\system32\WindowsPowerShell\v1.0\restricted.ps1. To make sure the script runscorrectly, run it in a local Windows PowerShell console. If you did everything right, it will restrict this session and allow only the safe subset of commands defined in that script to run in the session:

& $PSHome\restricted.ps1
CommandType Name Definition
----------- ---- ----------
Cmdlet Exit-PSSession Exit-PSSession [-Verbose] [-Debug...
Cmdlet ForEach-Object ForEach-Object [-Process] <Script...
Cmdlet Get-Command Get-Command [[-ArgumentList] <Obj...
Cmdlet Get-FormatData Get-FormatData [[-TypeName] <Stri...
Cmdlet Get-Help Get-Help [[-Name] <String>] [-Pat...
Cmdlet Get-Process Get-Process [[-Name] <String[]>] ...
Cmdlet Get-Service Get-Service [[-Name] <String[]>] ...
Cmdlet Measure-Object Measure-Object [[-Property] <Stri...
Cmdlet Out-Default Out-Default [-InputObject <PSObje...
Cmdlet Select-Object Select-Object [[-Property] <Objec...
Cmdlet Where-Object Where-Object [-FilterScript] <Scr...

Next, create a new session configuration that uses your script to restrict the session:

Register-PSSessionConfiguration -Name Restricted -StartupScript $pshome\restricted.ps1

Launching a Restricted Session Manually

When the new session configuration is in place, remote users can access your computer by using the new session configuration to create a restricted session:

Enter-PSSession -ComputerName $env:COMPUTERNAME -ConfigurationName Restricted
[demo5]: PS> Get-Command
CommandType Name Definition
----------- ---- ----------
Cmdlet Exit-PSSession Exit-PSSession [-Verbose] [-Debug] [-ErrorAction...
Cmdlet ForEach-Object ForEach-Object [-Process] <ScriptBlock[]> [-Inpu...
Cmdlet Get-Command Get-Command [[-ArgumentList] <Object[]>] [-Verb ...
Cmdlet Get-FormatData Get-FormatData [[-TypeName] <String[]>] [-Verbos...
Cmdlet Get-Help Get-Help [[-Name] <String>] [-Path <String>] [-C...
Cmdlet Get-Process Get-Process [[-Name] <String[]>] [-ComputerName ...
Cmdlet Get-Service Get-Service [[-Name] <String[]>] [-ComputerName ...
Cmdlet Measure-Object Measure-Object [[-Property] <String[]>] [-InputO...
Cmdlet Out-Default Out-Default [-InputObject <PSObject>] [-Verbose]...
Cmdlet Select-Object Select-Object [[-Property] <Object[]>] [-InputOb...
Cmdlet Where-Object Where-Object [-FilterScript] <ScriptBlock> [-Inp...
[demo5]: PS>exit-pssession

By default, only members of the Administrators group on the computer have Execute permission to the default session configurations. You can change security descriptors on any session configuration, even on the default one.

To give a group of users permission to connect to the computer remotely only by using a specific session configuration, use Set-PSSessionConfiguration to add Execute permissions for those users to the security descriptors of the session configurations. To set the permissions, use the SecurityDescriptorSDDL or 24 Administrator’s Guide to Windows PowerShell Remoting

ShowSecurityDescriptorUI parameter. The latter opens a familiar GUI where you can reconfigure permissions easily.

The next command opens the property page for the security descriptor of the new Restricted session configuration.

Set-PSSessionConfiguration -Name Restricted –ShowSecurityDescriptorUI

Understanding WSMan Settings

WSMan controls how Windows PowerShell Remoting works. By default, it uses HTTP as transport, uses port 5985, accepts only Kerberos authentication and allows a maximum of 5 remote shells per user. However, WSMan is highly configurable and can be changed to suit your needs.

All WSMan settings are managed through a virtual Windows PowerShell drive called WSMan:. You need Administrator privileges to view and change these settings, and the WinRM service needs to be running.

To illustrate how you read and change WSMan settings, here are some examples of how to change the maximum number of shells and the port used by Windows PowerShell Remoting.

Using More Than 5 Sessions

By default, every user can open a maximum of five sessions to any remote computer. When you try to open more, Windows PowerShell returns an error message:

1..10 | ForEach-Object { New-PSSession -ComputerName storage1 }

This command opens five sessions on the storage1 machine and then returns errors telling you that the maximum number of sessions is reached.

This restriction is a good thing, because it prevents you from carelessly wasting server resources by opening numerous sessions without closing them. When you run into an error like this, you should run Get-PSSession to check the already opened sessions, then remove any sessions you no longer need by running Remove- PSSession.

If you must use more than five simultaneous sessions, you can also change the limit. Here is how to read the currently active limit (again, to access the localhost "folder", you need full Administrator privileges):

Dir WSMan:\localhost\shell

Name Value
---- -----
AllowRemoteShellAccess true
IdleTimeout 180000
MaxConcurrentUsers 5
MaxShellRunTime 2147483647
MaxProcessesPerShell 15
MaxMemoryPerShellMB 150
MaxShellsPerUser 5

MaxConcurrentUsers limits the number of concurrent users that can have a session on this computer. MaxShellsPerUser controls how many sessions each user can open, so, by default, a maximum of 5 users can open a maximum of 5 sessions (25 sessions).

To change a setting, use Set-Item. For example, to change the maximum number of shells per user, use this command:

Set-Item WSMan:\localhost\shell\MaxShellsPerUser 10

Using A Different Port

By default, Windows PowerShell Remoting uses port 5985, but you can change this default to any other port. The currently used port is part of the listener:

dir WSMan:\localhost\listener\*\Port

To change to a different port, use Set-Item again:

Set-Item WSMan:\localhost\listener\*\Port 1000

When you change the port assignment, you also have to add a new Firewall rule allowing other computers to access WinRM via the the port, unless the new port is already listed in other Firewall rules.

Now, all requests to this machine on port 5985 fail because the listener does not listen on port 5985 anymore. Instead, it expects requests to be directed to port 1000.

To access the machine, you would have to add the Port parameter to all of your remoting calls.

$pid Invoke-Command { $pid } -ComputerName $env:computername -Port 1000

Your local Windows PowerShell session returns different values for $pid. The first one is the process ID of your local session. The second one is the process ID of your remote session.

Changing WSMan: Settings Remotely

In the previous examples, you changed the settings on your local machine. However, you can also manage WSMan settings remotely. Simply use Connect-WSMan to connect to the remote machine. After a connection is established, your WSMan: drive shows the remote systems as well:

Connect-WSMan storage1
Dir WSMan:\
ComputerName Type
------------ ----
localhost Container
storage1 Container

To change settings on the remote system, use the same technique as before, but replace paths accordingly. For example, to read the number of maximum shells on the remote system, use this command:

WSManConfig: Microsoft.WSMan.Management\WSMan::storage1\Shell
Name Value
---- -----
MaxShellsPerUser 5

To read this setting for all computers you are connected to, use this:

$row = @{Name='Computer';Expression={$_.PSPath.Split("::")[-1].Split('\')[0]}}
Dir WSMan:\*\shell\MaxShellsPerUser | Select-Object name, $row, Value
Name Computer Value
---- -------- -----
MaxShellsPerUser localhost 10
MaxShellsPerUser storage1 5

Windows PowerShell Remoting Requirements and Gotchas

Windows PowerShell Remoting offers great flexibility yet has limitations as well:

  • Windows PowerShell 2.0 required: To use Windows PowerShell Remoting, Windows PowerShell 2.0 needs to be present on both ends. You cannot access systems that do not have Windows PowerShell 2.0.
  • Resulting Objects are Clones: When Windows PowerShell sends back results from another session to the caller, these objects are temporarily converted to XML. This serialization is necessary to send objects back to the caller. So, the objects you receive from Windows PowerShell Remoting are no longer the live objects. Serialization removes all methods and can change the data type of object properties.
  • Sessions are fragile: Sessions are the heart and soul of Windows PowerShell Remoting and represent a Windows PowerShell instance. When a session is destroyed, you cannot easily recreate it like a broken network connection. For example, when a remote system reboots, all sessions are lost and all remoting calls and imported commands break.

Windows PowerShell 2.0 required on both sides

The most obvious limitation is the way Windows PowerShell Remoting works: it sends commands from your session to another session, so the machine you are remotely accessing must have Windows PowerShell 2.0. This imposes restrictions on the type of systems you can use with Windows PowerShell Remoting. Windows PowerShell 2.0 is not available on Windows 2000, and there may be operational restrictions that prevent you from installing Windows PowerShell 2.0 on some production servers.

This limitation will have less effect over time when Windows PowerShell 2.0 becomes available on all systems in your infrastructure. With Windows 7 on the client side and Windows Server 2008 R2 on the server side, Windows PowerShell 2.0 is an integral part of modern Windows-based infrastructures.

Resulting Objects are Clones

The objects that Windows PowerShell Remoting returns to you are not the real objects, like those that are returned by commands that run in your local session. Instead, Windows PowerShell Remoting needs to squeeze the original objects into XML to transport them back to the caller. This temporary conversion changes the objects significantly. Because Windows PowerShell Remoting occurs in a session that is completely independent of your local session, the objects that are returned lose their connection to the target system. This has three consequences:

  • No Methods: Because the objects that Windows PowerShell Remoting returns have no connection to the system on which they were created, they cannot execute commands to change the target system. This is why the object clones do not have methods.
  • Read-Only Properties: Because the objects that Windows PowerShell Remoting returns have no connection to the system on which they were created, changes to object properties have no effect on the target system. In essence, all object properties are read-only.
  • Changing Property Types: When objects are converted to XML for transport, all object content is serialized in XML-compatible way. Most data types can be preserved, and when the serialized XML is deserialized (turned back into objects), these properties keep their original data type. However, if an object property stores data in special types that Windows PowerShell does not recognize and does not know how to convert back and forth, the property content is converted to plain text. Because of this, some property data types might change. For example, a mailbox size that originally was in some numeric format might now appear as formatted text.

All of these limitations occur only after the objects are returned from the remote session to your local session. As long as the objects stay in the remote session, there is no need for XML transformation and the resulting object changes. So, the best practice is to keep the results in the remote session for as long as possible. The more data that can be processed in the remote session, the better. Having to return data from a remote session to your calling session not only cripples the objects to some extent, it is also »expensive« in time and resources. The less data you transfer, the better.

How Objects Travel Back

In order for Windows PowerShell to transfer results from a remote session back to your own local session, the objects need to be temporarily converted to CliXML (Constrained Language in XML). CliXML is pure text and can easily be transferred across any transport. On the caller side, the received CliXML is then converted back into objects.

This conversion procedure is not specific to remoting. You can use it at any time to serialize objects and store them. At the same time, it illustrates well how Windows PowerShell Remoting changes objects

$original = Get-Process
$original | Export-CliXML $home\persisted.xml

This command serializes the results from Get-Process and save them as XML in a file. This is precisely what Windows PowerShell Remoting does when it sends objects from a remote session back to the caller. Instead of storing the serialized objects in a file, Windows PowerShell Remoting transmits the XML to the caller via WinRM.

The file that Export-CliXML creates can be reimported and turned back into objects. This is often referred to as "rehydrating" the objects.

This is precisely what is happening when your local Windows PowerShell session receives serialized objects.

$clone = Import-CliXML $home\persisted.xml

When you compare $original and $clone, you notice that the number of objects did not change:


However, the object type changed. Get-Member proves that the objects in $clone no longer have methods:

$original | Get-Member -MemberType Method
Name MemberType Definition
---- ---------- ----------
BeginErrorReadLine Method System.Void BeginErrorReadLine()
BeginOutputReadLine Method System.Void BeginOutputReadLine()
CancelErrorRead Method System.Void CancelErrorRead()
CancelOutputRead Method System.Void CancelOutputRead()
Close Method System.Void Close()
CloseMainWindow Method bool CloseMainWindow()
28 Administrator’s Guide to Windows PowerShell Remoting

CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type ...
Dispose Method System.Void Dispose()
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
Kill Method System.Void Kill()
Refresh Method System.Void Refresh()
Start Method bool Start()
ToString Method string ToString()
WaitForExit Method bool WaitForExit(int milliseconds), System.Void W...
WaitForInputIdle Method bool WaitForInputIdle(int milliseconds), bool Wai...
$clone | Get-Member -MemberType Method
TypeName: Deserialized.System.Diagnostics.Process
Name MemberType Definition
---- ---------- ----------
ToString Method string ToString(), string ToString(string format, System.IFormatPr...

Notice the first line that starts with TypeName. It gives you a clue as to just why the objects differ. The original objects returned by Get-Process were of type System.Diagnostics.Process. The »rehydrated« objects that were imported from XML have a type of Deserialized.System.Diagnostics.Process. The object types changed.

ot only did the serialization process remove all specific methods, it also changed some properties:

Get-Member -MemberType *Property*
Select-Object -Expand Count
Get-Member -MemberType *Property*
Select-Object -Expand Count

Whereas the original object contained 67 properties, the rehydrated objects have 18 fewer properties. Compare-Object can show which properties were removed:

$prop_orig = $original | Get-Member -MemberType *Property*
$prop_clone = $clone | Get-Member -MemberType *Property*
Compare-Object $prop_orig $prop_clone -Property Name
Name SideIndicator
---- -------------
ExitCode <=
ExitTime <=
Handle <=
HasExited <=
MainModule <=
MaxWorkingSet <=
MinWorkingSet <=
Modules <=
PriorityBoostEnabled <=
PriorityClass <=
PrivilegedProcessorTime <=
ProcessorAffinity <=
StandardError <=
StandardInput <=
StandardOutput <=
29 Administrator’s Guide to Windows PowerShell Remoting

StartTime <=
TotalProcessorTime <=
UserProcessorTime <=

Windows PowerShell Remoting works very similarly, but not the same way. To compare original objects with deserialized objects received from Windows PowerShell Remoting, you can use a cmdlet that supports remote access internally - like Get-Process. When you run Get-Process with the ComputerName parameter, it uses traditional DCOM technologies to deliver the real objects. When you use Invoke-Command to run Get- Process via Windows PowerShell Remoting, you receive deserialized objects. All you need to do is compare the results.

$original = Get-Process -ComputerName storage1
$clone = Invoke-Command { Get-Process } -ComputerName storage1

The first observation is speed: the second call using Windows PowerShell Remoting is much slower, and you now know why: first Windows PowerShell Remoting needs to instantiate a remote session on the target system. Then the results need to be converted into CliXML format similar to Export-CliXML, which takes considerable time. Then the XMLized objects need to travel back to the caller and be reimported and turned back into objects. This is the price (overhead) you pay for Windows PowerShell Remoting. While the first call took 0,14 seconds, the second call required 10,8 seconds, so it was approximately 80 times slower.

Measure-Command { $original = Get-Process -ComputerName storage1 }
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 141
Ticks : 1410010
TotalDays : 1,63195601851852E-06
TotalHours : 3,91669444444444E-05
TotalMinutes : 0,00235001666666667
TotalSeconds : 0,141001
TotalMilliseconds : 141,001
Measure-Command { $clone = Invoke-Command { Get-Process } -ComputerName storage1 }
Days : 0
Hours : 0
Minutes : 0
Seconds : 10
Milliseconds : 812
Ticks : 108124609
TotalDays : 0,00012514422337963
TotalHours : 0,00300346136111111
TotalMinutes : 0,180207681666667
TotalSeconds : 10,8124609
TotalMilliseconds : 10812,4609

When you analyze object properties, you will discover that Windows PowerShell Remoting not only removed some properties but also added others:

$prop_orig = $original | Get-Member -MemberType *Property*
$prop_clone = $clone | Get-Member -MemberType *Property*
$prop_clone = $clone | Get-Member -MemberType *Property*
Name SideIndicator
---- -------------
PSComputerName =>
30 Administrator’s Guide to Windows PowerShell Remoting

PSShowComputerName =>
RunspaceId =>
ExitCode <=
ExitTime <=
StandardError <=
StandardInput <=
StandardOutput <=

The added properties help you to determine where the objects came from and in which session they were running, which becomes important when you invoke commands simultaneously in multiple sessions.

Windows Powershell Sessions are Fragile

Windows PowerShell Remoting relies on sessions, and a session represents a running Windows PowerShell instance. Therefore, a session isn't just a plain network connection that you can restore easily. Instead, it is a room where Windows PowerShell lives and you can configure a session, load commands, and define functions and variables inside it. That is why Windows PowerShell sessions are unique and cannot automatically be restored easily.

This can be a problem because sometimes sessions end unexpectedly. Because they represent a running Windows PowerShell host, they are closed, like any other program, when a remote system reboots. Rebooting a remote system will, therefore, end all current Windows PowerShell Remoting sessions running on that system. These connections are not restored when the system finishes booting.

The impact of this depends on what you are doing. If all you want to do is fire a remote command to a remote system, then a session lifespan is short and chances are low that this session will break. If, however, you are using implicit remoting to import remote commands that you plan to use in your local session over a long period of time, then a reboot of the target machine may break your environment unexpectedly. Imported commands do remember the session they were created in, so when you use such a command and the original session is lost, there will be an attempt to recreate the session. Since it is no longer the original session, some adjustments you may have made to it will be lost.

Background Jobs

Remoting and background jobs share the same concept and complement each other to some degree which is why background jobs are covered as well in this paper.

A background job is composed of a separate session that runs parallel to your local session to complete tasks asynchronously. Because Windows PowerShell is single-threaded, it can do things only sequentially, and when tasks are complex, you might have to wait until the results come in. With background jobs, you transfer the job to another session and leave it to this session to process and complete it. This way, your own session is not blocked. You can check on jobs and receive the results whenever it suits you.

Running Jobs

To better understand how background jobs work, look at a simple example. The next command finds all log files in your Windows folder and its subfolders, which can take considerable time:

Dir $env:windir *.log -Recurse

While the command runs, your local session is blocked. To execute the same task asynchronously, simply start a new session, and transfer the task to that session. This is done by Start-Job:

Start-Job {Dir $env:windir *.log -Recurse}

The job now runs in the new session that was created transparently by Start-Job. Your own session is neither blocked nor busy working on the task you transferred to the other session. Your own session keeps a connection to the background job session so you can check on the job status and receive its results whenever you feel like it.

To check the status of jobs, use Get-Job:

Get-Job *

This tells you if a particular job is done and has gathered data that is ready to collect. To receive the results a job has gathered, you call Receive-Job:

Receive-Job -Id 3

Once you received data from a job, this data is removed from the job buffer (unless you specify the Keep switch parameter). When a job is complete and you have received all of its results, it is time to clean up and remove the job:

Remove-Job -Id 3

As it turns out, background jobs and remoting code both employ additional Windows PowerShell sessions and provide ways for you to communicate with them. They share the same concept but serve different purposes:

Remoting: Remoting code runs in a separate session to enable you to move the place of execution to other systems. The remote sessions run synchronously, though. Your local session is blocked as long as the remote sessions execute, and when done, the results are transferred back to your local session using WinRM and the XML packaging process you read about.

Background Jobs: Job code runs in a separate session to enable you to execute tasks in parallel and separately from your local session. Your local session is not blocked while jobs execute. The results are buffered by the job sessions and are transferred back to your local session only when you are ready for them and request them through Receive-Job. The background sessions uses the same XML packaging process to transfer the results to your local session that is used by remoting.

Creating Remote Jobs

Invoke-Command is used to create new sessions that can run remotely, but these sessions run synchronously and block your own session. Start-Job is used to create new sessions that can run asynchronously, but you can only create these sessions on your local machine. Is there a way to connect both concepts?

Start-Job supports only local jobs because this cmdlet was designed to run independently from remoting requirements. You can run background jobs without having to enable Windows PowerShell Remoting. However, Invoke-Command has a way of creating remote sessions as jobs. Simply use the AsJob switch parameter to create a remote session that runs asynchronously:

Invoke-Command { $env:windir *.log -Recurse } -ComputerName PC01 -AsJob

This command returns immediately and returns a job object. The remoting task executes asynchronously, and you can use Get-Job and Receive-Job to check its status and collect its results whenever it suits you.

There are many cmdlets that support the AsJob switch parameter and can create asynchronous background jobs on the fly. To find them, look for cmdlets with a AsJob parameter:

Get-Help * -Parameter AsJob


All remoting technologies are governed by a number of network and security settings. It is not always easy to understand what is required and why a remoting call might have failed.

Windows PowerShell Remoting

In a perfect world, setting up and running Windows PowerShell Remoting would be easy and straightforward. Unfortunately, any form of remote access is influenced by a flood of security rules and settings. If your system is not set up with default settings, or if security has been hardened, you may experience difficulties setting up and running Windows PowerShell Remoting.

I cannot set up Windows PowerShell Remoting!

When you try and set up Windows PowerShell Remoting by running Enable-PSRemoting, you might be faced with a number of error messages. Let's see what might cause them and what you can do about it.

  • Access Denied: To set up Windows PowerShell Remoting, local Administrator privileges are required. You need to start Windows PowerShell with elevated privileges. To do so, right-click any Windows PowerShell icon or shortcut, and then click "Run as Administrator".
  • Public Networks Detected: Part of the setup procedure is setting up a local firewall exception. If Enable- PSRemoting detects that your system is connected to a public network, it fails, and all subsequent setup tasks are discarded. Start Network and Sharing Center and make sure that all public connections are either Private or Domain type networks or are disabled.

Enable-PSRemoting also might fail when the predefined firewall rules for WinRM have been deleted. To recreate these rules, run netsh advfirewall reset.

  • Access Denied Despite Admin Privileges: If you are logged on as a user with local Administrator privileges and you start Windows PowerShell with Administrator privileges, and you still get Access Denied errors, log with the built-in Administrator account. Run control userpasswords2, click Advanced and make sure the Administrator account is enabled. Then start Windows PowerShell with this account and try:

Runas /user:Administrator powershell

  • Invalid SSIDs: If you receive an error that complains about invalid SSIDs in XML data, there might be leftover SSIDs in the Windows Registry that refer to accounts that no longer exist. Open the registry editor, and navigate to this location:


Or start Windows PowerShell with the "Run as Administrator" option and type:

Get-Childitem wsman:\localhost\plugin

Using the WSMan: drive is safer than munging with the registry directly, but if permission issues prevent you from accessing WSMan: from inside PowerShell, direct registry cleanup may be required.

Make sure there are no subkeys other than Event Forwarding Plugin, microsoft.powershell, microsoft.powershell32, and WMI Provider. microsoft.powershell and microsoft.powershell32 are predefined session configurations created by Enable-PSRemoting. microsoft.powershell32 is present only on 64-bit systems and represents the 32-bit Windows PowerShell host. As a last resort, you can delete these two and call Enable-PSRemoting to recreate these configurations from scratch.

I cannot create a Session!

To make sure Windows PowerShell Remoting is set up correctly on your own system, first try to create a session to your own computer. To do that, you must be a local Administrator and you must start Windows PowerShell with the "Run as Administrator" option.

$session = New-PSSession -ComputerName localhost

If this fails, Windows PowerShell Remoting may not be set up correctly. Make sure that Enable-PSRemoting ran correctly, and also make sure you adjusted the TrustedHosts list.

Next, try to create a session on a different system:

$session = New-PSSession -ComputerName PC01

If this works, you are fine. If you receive an error message, check the error message:

  • RPC Server unavailable: Your computer was unable to contact the target system. Either, the system is offline, or you may have mistyped its name or IP address. If the computer name or IP address is correct and the system is online, a firewall probably blocks access. Make sure you have set up Windows PowerShell Remoting correctly on the remote system.
  • Access Denied: This is actually a good message because it proves that you successfully connected to the target system. You just did not have access permission. When you do not use the Credential parameter, you are authenticated as the currently logged-on user. Make sure you know the user name and password for an account that has local Administrator privilege, and then use Credential to authenticate as this user. If you provided the credentials of a user that is definitely member of the local Administrators group, and you still get Access Denied, temporarily disable the firewall. If access works now, the firewall blocks important ports. Run Enable-PSRemoting again and make sure the firewall exception is set up correctly.
  • Public Network: Always remember that all firewall exceptions work only with Private and Domain network profiles. If your network is classified as Public network in Network and Sharing Center, other systems cannot access your computer.
  • No Kerberos: When you receive lengthy error messages that complain about WinRM not being able to authenticate you unless you use Kerberos or HTTPS: transports, you know that Kerberos is not available for this particular call. Most likely, you are not a domain member or you are trying to connect to a system outside your own domain. Kerberos is also not available when you specify a remote computer by using its IP address instead of computer name, or when you have no connection to your domain controller. To connect under these conditions, make sure you set the TrustedHosts list to »*« then as described earlier, and try again. You might have to use the Credential parameter to supply appropriate credentials.

I want to check my remoting settings manually:

To make sure all prerequisites for Windows PowerShell Remoting are in place, check them manually:

  • WinRM Service: This service needs to be running. It is handling the network transport. To check the status, use Get-Service WinRM. Next, test your own side:
Test-WSMan -Authentication default

This should return a quick report that includes your operating system and version. If not, WinRM is not set up correctly on your machine.

Next, check the destination computer:

Test-WSMan -ComputerName PC01 -Authentication default

This should return a similar report from the remote system. If not, WinRM is not set up correctly on the remote machine.

  • Listener required: To receive remoting requests, WinRM needs a listener on port 5985. To check your listeners,
Get-WSManInstance –ResourceURI winrm/config/listener –Enumerate | Select port, transport,
Port Transport ListeningOn
---- --------- -----------
5985 HTTP {,, 16...

You should discover at least one listener on port 5985 and transport HTTP. If not, Windows PowerShell Remoting is not set up correctly.

  • Firewall-Exception: When you set up Windows PowerShell Remoting, it adds a firewall exception to your local Windows firewall that allows other computers to send requests to port 5985. If this exception is not present, your computer is unreachable, and you should run Enable-PSRemoting again to (re)create the exception. Note that this firewall exception is not active in your Public network profile, so you may also want to check your current network type in Network and Sharing Center and change it to something other than Public. Use Get-FirewallRule, as described earlier in this paper, to check your firewall rules.

Non-Windows PowerShell Remoting

Problems with cmdlets that have built-in remoting capabilities are harder to diagnose because there is no common transport. Still, they share a number of prerequisites with Windows PowerShell Remoting:

  • RPC Server unavailable: As with Windows PowerShell Remoting, this error indicates a connectivity problem or a missing firewall exception. You might also want to test the physical network connection:
Test-Connection PC01

Try pinging the target system, both with its computer name and its IP address. If the IP address works but the computer name fails, you might have a problem with name resolution in your network infrastructure.

If you do not receive a signal from the target system at all, this does not necessarily indicate a non- existing (offline) system. It also can be caused by inappropriate firewall settings. By default, enabling the Windows PowerShell Remoting firewall exception also enables ICMP, so if Test-Connection fails, this indicates either a non-available system or blocking firewall(s).

Turning off the Windows firewall temporarily on both ends can help diagnose the problem. If this solves the connectivity problem, you know there is a need for some firewall exception. Do not turn off the Windows firewall permanently. It is an important part of your security system. Instead, make sure the firewall is configured correctly.

Always make sure the target system network type is not set to Public because, when it is, all mandatory firewall exceptions are disabled.

  • Access Denied: Your current user account does not have local Administrator privileges on the target machine or you are logged on with a local account but the target machine is domain-joined. If the cmdlet you are using has a Credential parameter, try it with a different user account. If the user account used does have local Administrator privileges on the target system, temporarily disable the Windows firewall. If that solves the problem, you know that firewall exceptions are missing.
  • Other errors: There might be additional prerequisites needed on the target machine, depending on the cmdlet you choose and the technology it uses to access the target system. For example, to use Get- EventLog with its ComputerName parameter to access event log entries remotely, the target system must run the RemoteRegistry service; otherwise you receive confusing error messages.


Windows PowerShell 2.0 essentially adds the ability for sessions to communicate with each other. This enables you to run any Windows PowerShell code either remotely or as a background job - or both.

To create a session on remote systems, use New-PSSession, Invoke-Command or Enter-PSSession with its ComputerName property. To create a session for local background jobs, use Start-Job or use other cmdlets like Get-WmiObject which have the AsJob parameter. For example, to create a remote session as background job, use Invoke-Command with the AsJob parameter.

Windows PowerShell Remoting essentially works like a web service, so it does not need any special setup for the client (caller). Only the server (target) needs to run the WinRM service with a listener and a firewall exception. So, on the server side, make sure you run Enable-PSRemoting first.

On the caller side, you are, by default, limited to Kerberos authentication. If you need to authenticate differently, you need to add the target computers to a TrustedHosts list that is maintained by WinRM. This is why, in a peer-to-peer scenario, you need to enable Windows PowerShell Remoting on the client side as well and then add »*« to the TrustedHosts setting.

Table 3

Receiving results from a session is done by temporarily converting objects to CliXML. Any object you receive from a remote or background job session is really a clone with only a subset of properties and no methods. Sending back results is the most expensive part of remoting, so you should try to limit the information that needs to be returned to you, and try to process objects on the server side for as long as possible.

Classic remoting that is built into individual cmdlets, such as Get-WmiObject, is a completely different ball game and is not related to Windows PowerShell sessions or Windows PowerShell Remoting. This type of remoting returns original objects because it uses DCOM instead of XML serialization. Since it requires less overhead, it can be much faster and does not require Windows PowerShell 2.0 on the target side. However, it is only supported by a subset of cmdlets, uses different kinds of different technologies internally to access systems remotely. Whether classic remoting works for you depends on a number of prerequisites on client and server and is beyond the scope of this paper. Sponsors


Idera delivers a new generation of tools for managing, administering, and securing Microsoft Windows Servers. Idera's products help companies ensure server performance and availability and reduce administrative overhead and expense. Idera provides solutions for SQL Server, SharePoint and Windows PowerShell. Headquartered in Houston, Texas, Idera's products are sold and supported directly and via authorized resellers and distributors around the globe. To learn more, please contact Idera at +1- 713.523.4433 or visit


Compellent is a leading provider of enterprise-class network storage solutions that are highly scalable, feature-rich and designed to be easy to use and cost effective. Compellent Technologies’ principal offices are located in Eden Prairie, Minn.

/n software

/n software is a leading provider of software components for Internet, security, and E-Business development. Founded in 1994, /n software (pronounced 'n software') is based in Research Triangle Park, North Carolina. You can reach the company via email at [email protected], on the World Wide Web at or by calling US: (800) 225-4190 or International: (919) 544-7070.