Administrators Guide To Windows PowerShell Remoting
Administrators Guide To Windows PowerShell Remoting
com
Contents
Windows PowerShell Remoting Technologies..... 5 Understanding Windows PowerShell Sessions....... 13 Understanding Session Configurations ................. 19 Understanding WSMan Settings ........................... 25 Windows PowerShell Remoting Requirements and Gotchas ........................... 27 Background Jobs ............ 31 Troubleshooting .............. 32 Summary ......................... 35
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, its available as a feature.) For down-level versions of Windows, you can download Windows PowerShell 2.0 RTM here: http://support.microsoft.com/kb/968929. 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: http://powershell.com/cs/blogs/tobias/archive/2010/01/21/are-youusing-the-correct-powershell-version.aspx.
Copyright
2010 PowerShell.com ALL RIGHTS RESERVED. No part of this work covered by the copyright herein may be reproduced, transmitted, stored, or used in any form or by any means graphic, electronic, or mechanical, including but not limited to photocopying, recording, scanning, digitizing, taping, Web distribution, information networks, or information storage and retrieval systems except as permitted under Section 107 or 108 of the 1976 United States Copyright Act without the prior written permission of the publisher, PowerShell.com. For permission to use material from the text please contact PowerShell.com at [email protected]. Microsoft Windows PowerShell and Microsoft SQL Server are registered trademarks of Microsoft Corporation in the United Stated and other countries. All other trademarks are the property of their respective owners.
Acknowledgements
The authors of this book would like to extend their sincere gratitude to the following special people for making this Guide possible: Jeffrey Snover for his assistance and incredible feedback. As always, Jeffreys insight proved invaluable as we labored to make this Guide both comprehensive and easy to use. June Blender Rogers for her brilliant technical editing skills. Shay Levy for his technical review and continued support. The authors would also like to thank Rick Pleczko and Rod Endo at Idera for their continued support of the Windows PowerShell community, David Fargo for his part in building PowerShell.com into a fantastic resource for the Windows PowerShell community, David Twamley for doing the difficult work behind the scenes and Conley Smith for her technical writing skills.
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:
PS> Get-Command | Where-Object { $_.Parameters.Keys -contains 'ComputerName' -and $_.Parameters.Keys -notcontains 'Session'}
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:
PS> 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:
PS> Get-Command -CommandType cmdlet | ? { @($_.ParameterSets | ? { $p = $_.Parameters | SelectObject -Expand Name; (($p -contains 'computername') -and ($p -contains 'credential'))}).Count -gt 0} Cmdlet Description clears all event log entries gets performance counter data gets event logs and event log entries gets hotfixes and updates gets running processes gets services x Supports Credential
Cmdlet
Description gets all event log entries including the new XML-based event logs introduced in Windows Vista gets instances of WMI classes calls WMI methods limits the size of event logs creates a new event log and/or an event source subscribes to WMI events deletes an event log and/or event sources deletes an instance of a WMI class restarts the operating system changes the settings of a service creates or updates an instance of a WMI class opens the event log in the Event Viewer window shuts down a computer
Supports Credential x
Get-WinEvent Get-WmiObject Invoke-WmiMethod Limit-EventLog New-EventLog Register-WmiEvent Remove-EventLog Remove-WmiObject Restart-Computer Set-Service Set-WmiInstance Show-EventLog Stop-Computer
x x
x x
Table 1: Cmdlets supporting remote access technologies on their own (ComputerName parameter)
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:
PS> Get-EventLog Application -EntryType Error -ComputerName storage1
Index Time ----- ---51054 Jan 07 20:15 51052 Jan 07 20:15 (...) EntryType --------Error Error Source -----Application Error Application Error InstanceID Message ---------- ------1001 ... 1001 ...
All seems to work just fine. But look what happens when you disable the RemoteRegistry service on the target machine:
PS> (Get-WmiObject Win32_Service -Filter 'name="RemoteRegistry"' -Computer storage1).StopService().ReturnValue
0
When you now repeat the Get-EventLog command, you get a very confusing error message:
PS> 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.
PS> (Get-WmiObject Win32_Service -Filter 'name="RemoteRegistry"' -Computer storage1).StartService().ReturnValue
0
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.
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. TIP: 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:
PS> Set-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System LocalAccountTokenFilterPolicy 1 -Type DWord PS> Start-Service WinRM
If your client is running Windows XP, you also need to set another registry entry:
PS> Set-ItemProperty HKLM:\System\CurrentControlSet\Control\Lsa ForceGuest 0
Finally, you can turn off the WinRM service again and revert the value of the LocalAccountTokenFilterPolicy registry entry:
PS> Stop-Service WinRM PS> Set-ItemProperty 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:
PS> Enable-PSRemoting
TIP: 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:
PS> 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). TIP: You can further harden this setting by using explicit computer names, IP addresses or prefixes instead of *:
PS> PS> PS> PS> 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 192.168.2.110 -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 192.168.2.110. 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:
PS> Enable-PSRemoting -Force PS> 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.
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:
PS> 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:
PS> 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:
PS> Invoke-Command { netstat } -ComputerName PC01
NOTE: 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:
PS> 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.
PS> 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:
PS> Disable-PSRemoting
NOTE: 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. IMPORTANT: 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:
PS> Dir WSMan:\localhost\Listener Recurse | Foreach-Object { $_.PSPath } | Where-Object { (GetItem "$_\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:
PS> Set-Service winrm -StartupType Manual PS> Stop-Service winrm
IMPORTANT: 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 } }
IMPORTANT: 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:
PS> 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:
PS> Get-FirewallRule *Windows?Remote*
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:
PS> Invoke-Computer { $env:computername; $test=1 } -ComputerName PC01 PS> 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:
PS> $session = New-PSSession -ComputerName PC01
To use this session for a particular remoting command, use the Session parameter instead of ComputerName:
PS> Invoke-Computer { $env:computername; $test=1 } -Session $session PS> 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:
PS> $session
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:
PS> Remove-PSSession $session
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.
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.
PS> $session = New-PSSession storage1, localhost, demo5
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:
PS> 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.
To close and discard sessions you no longer need, use Remove-PSSession. You can select sessions to remove either by name or by ID:
PS> Remove-PSSession -Name Session4 PS> Remove-PSSession -Id 1
To close and remove all sessions in one call, use this command:
PS> 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:
PS> $s = New-PSSession -ComputerName $env:computername PS> Get-Process wsmprovhost
Handles ------206 NPM(K) -----34 PM(K) ----45308 WS(K) VM(M) ----- ----49864 562 CPU(s) -----0,47 Id ProcessName -- ----------2712 wsmprovhost
Each session that you create is represented by a process named wsmprovhost. Removing this process also removes the session:
PS> $s
Id Name -- ---2 Session2 ComputerName -----------demo5 State ----Opened ConfigurationName ----------------Microsoft.PowerShell Availability -----------Available
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:
PS> Dir WSMan:\localhost\shell\idletimeout
180000
It returns the number of milliseconds a connection can be disconnected before the server discards the session.
PS> "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.
PS> $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.
NOTE: 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.
# Create a session PS> Enter-PSSession $session [win-khhrllotms3]: PS C:\Users\Tobias\Documents> Get-Module -ListAvailable
ModuleType Name ---------- ---Manifest Manifest Manifest Manifest Manifest Manifest Manifest Manifest ActiveDirectory ADRMS AppLocker BestPractices BitsTransfer PSDiagnostics ServerManager TroubleshootingPack ExportedCommands ---------------{} {} {} {} {} {} {} {}
# 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 ----------Cmdlet Cmdlet Cmdlet Name ---Add-WindowsFeature Get-WindowsFeature Remove-WindowsFeature Definition ---------Add-WindowsFeature [-Name] <Feature[]> [-Include... Get-WindowsFeature [[-Name] <String[]>] [-LogPat... Remove-WindowsFeature [-Name] <Feature[]> [-LogP...
# 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:
PS> Import-PSSession $session -CommandName test
ModuleType Name ---------- ---Script ExportedCommands ----------------
tmp_b4976f56-a449-4470... test
IMPORTANT: 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.
PS> test
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 InvokeCommand:
PS> 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:
PS> $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:
PS> $result.ExportedCommands
Name ---Remove-WindowsFeature Get-WindowsFeature Add-WindowsFeature Value ----Remove-WindowsFeature Get-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:
PS> Get-WindowsFeature xps*
RunspaceId DisplayName Name Installed FeatureType Path Depth DependsOn Parent SubFeatures SystemService Notification AdditionalInfo : bc04f492-3d30-4dee-b8b6-7d91da4eb72b : XPS-Viewer : XPS-Viewer : False : Feature : XPS-Viewer : 1 : {} : : {} : {} : {} : {NumericId}
BestPracticesModelId :
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:
PS> Add-WindowsFeature xps-viewer -WhatIf
(...) RunspaceId Success RestartNeeded FeatureResult ExitCode : : : : : 3e019a31-a1f8-40d1-9562-38a97a5ac622 True Maybe {} 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.
PS> Add-WindowsFeature xps-viewer
RunspaceId Success : 3e019a31-a1f8-40d1-9562-38a97a5ac622 : True
PS> Get-PSSessionConfiguration
Name ---microsoft.powershell microsoft.powershell32 PSVersion --------2.0 2.0 StartupScript ------------Permission ---------BUILTIN\Administrators Access Allowed 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. NOTE: 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.
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. TIP: 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:
PS> 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:
PS> Register-PSSessionConfiguration -Name WithProfile -StartupScript $PsHome\Profile.ps1
Confirm 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 ---WithProfile Confirm 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 Type ---Container Keys ---{Name=WithProfile}
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. NOTE: 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.
# 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:
PS> & $PSHome\restricted.ps1 PS> Get-Command
CommandType ----------Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Name ---Exit-PSSession ForEach-Object Get-Command Get-FormatData Get-Help Get-Process Get-Service Measure-Object Out-Default Select-Object Where-Object Definition ---------Exit-PSSession [-Verbose] [-Debug... ForEach-Object [-Process] <Script... Get-Command [[-ArgumentList] <Obj... Get-FormatData [[-TypeName] <Stri... Get-Help [[-Name] <String>] [-Pat... Get-Process [[-Name] <String[]>] ... Get-Service [[-Name] <String[]>] ... Measure-Object [[-Property] <Stri... Out-Default [-InputObject <PSObje... Select-Object [[-Property] <Objec... Where-Object [-FilterScript] <Scr...
Next, create a new session configuration that uses your script to restrict the session:
PS> 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:
PS> Enter-PSSession -ComputerName $env:COMPUTERNAME -ConfigurationName Restricted [demo5]: PS> Get-Command
CommandType ----------Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Cmdlet Name ---Exit-PSSession ForEach-Object Get-Command Get-FormatData Get-Help Get-Process Get-Service Measure-Object Out-Default Select-Object Where-Object Definition ---------Exit-PSSession [-Verbose] [-Debug] [-ErrorAction... ForEach-Object [-Process] <ScriptBlock[]> [-Inpu... Get-Command [[-ArgumentList] <Object[]>] [-Verb ... Get-FormatData [[-TypeName] <String[]>] [-Verbos... Get-Help [[-Name] <String>] [-Path <String>] [-C... Get-Process [[-Name] <String[]>] [-ComputerName ... Get-Service [[-Name] <String[]>] [-ComputerName ... Measure-Object [[-Property] <String[]>] [-InputO... Out-Default [-InputObject <PSObject>] [-Verbose]... Select-Object [[-Property] <Object[]>] [-InputOb... Where-Object [-FilterScript] <ScriptBlock> [-Inp...
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 Administrators 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.
PS> Set-PSSessionConfiguration -Name Restricted ShowSecurityDescriptorUI
IMPORTANT: When you create restricted sessions, be careful when choosing the cmdlets (and functions) you exclude; otherwise, the restricted session might be useless. Some cmdlets have dependencies, so, for example, Import-PSSession requires a number of other cmdlets to successfully import commands from a session. If you restrict the session too much, you will see error messages like this one:
"Import-PSSession : Import-PSSession cmdlet needs the following commands in the remote session: Get-Command, Get-FormatData, Select-Object. The following commands are used, but optional: Get-Help, Measure-Object. Please make sure the remote session includes the required commands and try again."
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 RemovePSSession. 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 ---AllowRemoteShellAccess IdleTimeout MaxConcurrentUsers MaxShellRunTime MaxProcessesPerShell MaxMemoryPerShellMB MaxShellsPerUser Value ----true 180000 5 2147483647 15 150 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
IMPORTANT: 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.
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:
PS> Dir WSMan:\storage1\shell\MaxShellsPerUser
WSManConfig: Microsoft.WSMan.Management\WSMan::storage1\Shell Name ---MaxShellsPerUser Value ----5
To read this setting for all computers you are connected to, use this:
PS> $row = @{Name='Computer';Expression={$_.PSPath.Split("::")[-1].Split('\')[0]}} PS> Dir WSMan:\*\shell\MaxShellsPerUser | Select-Object name, $row, Value
Name ---MaxShellsPerUser MaxShellsPerUser Computer -------localhost storage1 Value ----10 5
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.
PS> $original = Get-Process PS> $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.
PS> $clone = Import-CliXML $home\persisted.xml
When you compare $original and $clone, you notice that the number of objects did not change:
PS> $original.count
59
PS> $clone.count
59
However, the object type changed. Get-Member proves that the objects in $clone no longer have methods:
PS> $original | Get-Member -MemberType Method
TypeName: System.Diagnostics.Process Name ---BeginErrorReadLine BeginOutputReadLine CancelErrorRead CancelOutputRead Close CloseMainWindow MemberType Definition ---------- ---------Method Method Method Method Method Method System.Void BeginErrorReadLine() System.Void BeginOutputReadLine() System.Void CancelErrorRead() System.Void CancelOutputRead() System.Void Close() bool CloseMainWindow()
CreateObjRef Dispose Equals GetHashCode GetLifetimeService GetType Kill Refresh Start ToString WaitForExit WaitForInputIdle
Method Method Method Method Method Method Method Method Method Method Method Method
System.Runtime.Remoting.ObjRef CreateObjRef(type ... System.Void Dispose() bool Equals(System.Object obj) int GetHashCode() System.Object GetLifetimeService() type GetType() System.Object InitializeLifetimeService() System.Void Kill() System.Void Refresh() bool Start() string ToString() bool WaitForExit(int milliseconds), System.Void W... bool WaitForInputIdle(int milliseconds), bool Wai...
InitializeLifetimeService Method
ToString Method
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. Not only did the serialization process remove all specific methods, it also changed some properties:
PS> $original | Get-Member -MemberType *Property* | Measure-Object | Select-Object -Expand Count
67
Whereas the original object contained 67 properties, the rehydrated objects have 18 fewer properties. Compare-Object can show which properties were removed:
PS> $prop_orig = $original | Get-Member -MemberType *Property* PS> $prop_clone = $clone | Get-Member -MemberType *Property* PS> Compare-Object $prop_orig $prop_clone -Property Name
Name ---ExitCode ExitTime Handle HasExited MainModule MaxWorkingSet MinWorkingSet Modules PriorityBoostEnabled PriorityClass PrivilegedProcessorTime ProcessorAffinity StandardError StandardInput StandardOutput SideIndicator ------------<= <= <= <= <= <= <= <= <= <= <= <= <= <= <=
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 GetProcess via Windows PowerShell Remoting, you receive deserialized objects. All you need to do is compare the results.
PS> $original = Get-Process -ComputerName storage1 PS> $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.
PS> Measure-Command { $original = Get-Process -ComputerName storage1 }
Days Hours Minutes Seconds Milliseconds Ticks TotalDays TotalHours TotalMinutes TotalSeconds : 0 : 0 : 0 : 0 : 141 : 1410010 : 1,63195601851852E-06 : 3,91669444444444E-05 : 0,00235001666666667 : 0,141001
TotalMilliseconds : 141,001
TotalMilliseconds : 10812,4609
When you analyze object properties, you will discover that Windows PowerShell Remoting not only removed some properties but also added others:
PS> $prop_orig = $original | Get-Member -MemberType *Property* PS> $prop_clone = $clone | Get-Member -MemberType *Property* PS> Compare-Object $prop_orig $prop_clone -Property Name
Name ---PSComputerName SideIndicator ------------=>
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.
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:
PS> 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:
PS> 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: 31 Administrators Guide to Windows PowerShell Remoting
PS> 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:
PS> 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:
PS> 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.
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:
PS> Get-Help * -Parameter AsJob
Troubleshooting
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.
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 EnablePSRemoting 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:
PS> 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: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Plugin Or start Windows PowerShell with the "Run as Administrator" option and type:
PS> 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.
PS> $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:
PS> $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:
PS> 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:
PS> 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,
PS> Get-WSManInstance ResourceURI winrm/config/listener Enumerate | Select port, transport, listen*
Port ---5985 Transport --------HTTP ListeningOn ----------{10.10.10.2, 127.0.0.1, 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.
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 nonexisting (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 GetEventLog with its ComputerName parameter to access event log entries remotely, the target system must run the RemoteRegistry service; otherwise you receive confusing error messages.
Summary
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.
Scenario PS Remoting Setup TrustedHosts List Full Admin Privileges for PS Console not necessary not necessary not necessary not necessary not necessary necessary Network Type
Domain-Domain with computername Domain-Domain with IP address Domain-Nontrusted Domain Peer-Peer Peer-Domain local
on target side on both sides on both sides on both sides on both sides enabled
non-public on target side non-public on both sides non-public on both sides non-public on both sides non-public on both sides non-public
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.
PowerShell Plus
Architected and developed by Dr. Tobias Weltner, Ideras PowerShell Plus is the most advanced Interactive Development Environment for Windows PowerShell available today. Designed to help administrators and developers quickly learn and master Windows PowerShell, it also dramatically increases the productivity of expert users. Try PowerShell Plus free for 30-days and see how much more productive you can be!
PowerShell Plus Interactive Console Enables you to work interactively with Windows PowerShell from a feature rich Windows UI. This integration makes working with Windows PowerShell faster and easier to use than ever before!
Advanced Script Editor The advanced debugger and script editor lets you build and test complex Windows PowerShell scripts, try one line Windows PowerShell commands from an embedded console window, and sign your script with a security certificate All from a single workspace!
Comprehensive Learning Center The Comprehensive Learning Center helps you experience Windows PowerShell by example. Short tutorials guide you through basic concepts at your own pace. The Comprehensive Learning Center also includes dynamically created help topics from currently installed Windows PowerShell cmdLets, snap-Ins and WMI objects.
PowerShell.com Sponsors
Idera 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 +1713.523.4433 or visit www.idera.com. Compellent 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. www.compellent.com/powershell. /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 www.nsoftware.com or by calling US: (800) 225-4190 or International: (919) 544-7070.