Secrets of Powershell Remoting
Secrets of Powershell Remoting
of PowerShell Remoting
Table of Contents
ReadMe
Remoting Basics
Session Management
Introduced in Windows PowerShell 2.0, Remoting is one of PowerShell's most useful, and
most important, core technologies. It enables you to run almost any command that exists on
a remote computer, opening up a universe of possibilities for bulk and remote administration.
Remoting underpins other technologies, including Workflow, Desired State Configuration,
certain types of background jobs, and much more. This guide isn't intended to be a complete
document of what Remoting is and does, although it does provide a good introduction.
Instead, this guide is designed to document all the little configuration details that don't
appear to be documented elsewhere.
ReadMe
Introduced in Windows PowerShell 2.0, Remoting is one of PowerShell's most useful, and
most important, core technologies. It enables you to run almost any command that exists on
a remote computer, opening up a universe of possibilities for bulk and remote administration.
Remoting underpins other technologies, including Workflow, Desired State Configuration,
certain types of background jobs, and much more. This guide isn't intended to be a complete
document of what Remoting is and does, although it does provide a good introduction.
Instead, this guide is designed to document all the little configuration details that don't
appear to be documented elsewhere.
This guide is released under the Creative Commons Attribution-NoDerivs 3.0 Unported
License. The authors encourage you to redistribute this file as widely as possible, but ask
that you do not modify the document.
Was this book helpful? The author(s) kindly ask(s) that you make a tax-deductible (in the
US; check your laws if you live elsewhere) donation of any amount to The DevOps
Collective to support their ongoing work.
Check for Updates! Our ebooks are often updated with new and corrected content. We
make them available in three ways:
Our main, authoritative GitHub organization, with a repo for each book. Visit
https://github.com/devops-collective-inc/
Our GitBook page, where you can browse books online, or download as PDF, EPUB, or
MOBI. Using the online reader, you can link to specific chapters. Visit
https://www.gitbook.com/@devopscollective
On LeanPub, where you can download as PDF, EPUB, or MOBI (login required), and
"purchase" the books to make a donation to DevOps Collective. You can also choose to
be notified of updates. Visit https://leanpub.com/u/devopscollective
GitBook and LeanPub have slightly different PDF formatting output, so you can choose the
one you prefer. LeanPub can also notify you when we push updates. Our main GitHub repo
is authoritative; repositories on other sites are usually just mirrors used for the publishing
process. GitBook will usually contain our latest version, including not-yet-finished bits;
LeanPub always contains the most recent "public release" of any book.
Remoting Basics
Windows PowerShell 2.0 introduced a powerful new technology, Remoting, which was
refined and expanded upon for PowerShell 3.0. Based primarily upon standardized protocols
and techniques, Remoting is possibly one of the most important aspects of PowerShell:
future Microsoft products will rely upon it almost entirely for administrative communications
across a network.
Unfortunately, Remoting is also a complex set of components, and while Microsoft has
attempted to provide solid guidance for using it in a variety of scenarios, many
administrators still struggle with it. This "mini e-book" is designed to help you better
understand what Remoting is, how it works, and-most importantly-how to use it in a variety
of different situations.
Note This guide isn't meant to replace the myriad of existing books that cover Remoting
basics, such as Don's own Learn Windows PowerShell in a Month of Lunches (
http://MoreLunches.com) or PowerShell in Depth. Instead, this guide supplements those by
providing step-by-step instructions for many of the "edge" cases in Remoting, and by
explaining some of the more unusual Remoting behaviors and requirements.
What is Remoting?
In essence, Remoting enables you to access remote machines across a network and
retrieve data from or execute code on one or many remote computers. This is not a new
idea, and in the past a number of different remoting technologies have evolved. Some
cmdlets have traditionally provided their own limited remoting capabilities while the majority
of cmdlets do not support remoting on their own.
With PowerShell remoting there is finally a generic remoting environment that allows remote
execution for literally any command that can run in a local PowerShell. So instead of adding
remoting capabilities to every single cmdlet and application, you simply leave it to
PowerShell to transfer your PowerShell code to the target computer(s), execute it there, and
then marshal back the results to you.
Throughout this eBook, we will focus on PowerShell remoting and not cover non-standard
private remoting capabilities built into selected cmdlets.
Remoting Basics
On the remote computer, in the proper terminology the server (which does not refer to the
operating system), the Windows Remote Management (WinRM) service runs. This service is
configured to have one or more listeners. Each listener waits for incoming WS-MAN traffic
on a specific port, each bound to a specific protocol (HTTP or HTTPS), and on specific IP
addresses (or all local addresses).
When a listener receives traffic, the WinRM service looks to see which endpoint the traffic
is meant for. For our purposes, an endpoint will usually be launching an instance of Windows
PowerShell. In PowerShell terms, an endpoint is also called a session configuration. This is
because, in addition to launching PowerShell, it can auto-load scripts and modules, place
restrictions upon what can be done by the connecting user, and apply additional session
specific settings not mentioned here.
Note Although we show PowerShell.exe in our diagram, that's for illustration purposes.
PowerShell.exe is the PowerShell console application, and it would not make sense to have
this running as a background process on a remote computer. The actual process is called
Wsmprovhost.exe, which hosts PowerShell in the background for Remoting connections.
As you can see, a single remote computer can easily have dozens or even hundreds of
endpoints, each with a different configuration. PowerShell 3.0 sets up three such endpoints
by default: One for 32-bit PowerShell (on 64-bit systems), the default PowerShell endpoint
(which is 64-bit on x64 systems), and one for PowerShell Workflow. Beginning with Windows
Server 2008 R2, there is a fourth default endpoint for Server Manager Workflow tasks.
Enabling Remoting
Most client versions of Windows, beginning with Windows Vista, do not enable incoming
Remoting connections by default. Newer Windows Server versions do, but older versions
may not. So your first step with Remoting will usually be to enable it on those computers
which you want to receive incoming connections. There are three ways to enable Remoting,
and table 1.1 compares what is achievable with each of them.
Table 1.1 Comparing the ways of enabling remoting
Remoting Basics
EnablePSRemoting
Set WinRM to
auto-start and
start the
service
Configure
HTTP listener
Configure
HTTPS listener
Configure
endpoints /
session
configurations
Configure
Windows
Firewall
exception
Group Policy
Manually Step-by-Step
Yes
Yes
Yes
No
No
Yes - use
PSSessionConfiguration
cmdlets
Yes*
No
Yes
Yes*
Note Existing client versions of Windows, such as Windows Vista, do not permit firewall
exceptions on any network identified as "Public". Networks must either be "Home" or
"Work/Domain" in order to permit exceptions. In PowerShell 3.0, you can run EnablePSRemoting with the -SkipNetworkProfileCheck switch to avoid this problem.
We'll be enabling Remoting in our test environment by running Enable-PSRemoting. It's
quick, easy, and comprehensive; you'll also see most of the manual tasks performed in the
upcoming sections.
Test Environment
We'll be using a consistent test environment throughout the following sections; this was
created on six virtual machines at CloudShare.com, and is configured as shown in figure
1.2.
Remoting Basics
Remoting Basics
10
not have administrative privileges. You can check permissions programmatically with this
(whoami /all | select-string S-1-16-12288) -ne $null from the PowerShell console. In an
elevated shell True is returned, otherwise False is.
Enabling Remoting
We began by running Enable-PSRemoting on all six computers. We took care to ensure that
the command ran without error; any errors at this point are a signal that you must stop and
resolve the error before attempting to proceed. Figure 1.3 shows the expected output.
Remoting Basics
11
Remoting Basics
12
Figure 1.5: Checking remoting connectivity from COMPANY.loc's CLIENTA to the DCA
domain controller.
Caution: If you're following along in your own test environment, don't proceed until you've
confirmed Remoting connectivity between two computers in the same domain. No other
scenario needs to work right now; we'll get to them in the upcoming sections.
1-to-1 Remoting
The Enter-PSSession command connects to a remote computer and gives you a commandline prompt on that computer. You can run whatever commands are on that computer,
provided you have permission to perform that task. Note that you are not creating an
interactive logon session; your connection will be audited as a network logon, just as if you
Remoting Basics
13
were connecting to the computer's C$ administrative share. PowerShell will not load or
process profile scripts on the remote computer. Any scripts that you choose to run (and this
includes importing script modules) will only work if the remote machine's Execution Policy
permits it.
Enter-PSSession -computerName DC01
Note: While connected to a remote machine via Enter-PSSession, your prompt changes and
displays the name of the remote system in square brackets. If you have customized your
prompt, all customizations will be lost because the prompt is now created on the remote
system and transferred back to you. All of your interactive keyboard input is sent to the
remote machine, and all results are marshaled back to you. This is important to note
because you cannot use Enter-PSSession in a script. If you did, the script would still run on
your local machine since no code was entered interactively.
1-to-Many Remoting
With this technique, you specify one or more computer names and a command (or a
semicolon-separated list of commands); PowerShell sends the commands, via Remoting, to
the specified computers. Those computers execute the commands, serialize the results into
XML, and transmit the results back to you. Your computer deserializes the XML back into
objects, and places them in the pipeline of your PowerShell session. This is accomplished
via the Invoke-Command cmdlet.
Invoke-Command -computername DC01,CLIENT1 -scriptBlock { Get-Service }
If you have a script of commands to run, you can have Invoke-Command read it, transmit
the contents to the remote computers, and have them execute those commands.
Invoke-Command -computername DC01,CLIENT1 -filePath c:\Scripts\Task.ps1
Note that Invoke-Command will, by default, communicate with only 32 computers at once. If
you specify more, the extras will queue up, and Invoke-Command will begin processing
them as it finishes the first 32. The -ThrottleLimit parameter can raise this limit; the only cost
is to your computer, which must have sufficient resources to maintain a unique PowerShell
session for each computer you're contacting simultaneously. If you expect to receive large
amounts of data from the remote computers, available network bandwidth can be another
limiting factor.
Sessions
Remoting Basics
14
When you use persistent sessions, on the other hand, re-connections are much faster, and
since you are keeping and reusing sessions, they will preserve state. So here, the second
call to Invoke-Command will still be able to access the variable $test that was set up in the
first call
PS> $Session = New-PSSession -ComputerName CLIENT1
PS> Invoke-Command -Session $Session -scriptBlock { $test = 1 }
PS> Invoke-Command -Session $Session -scriptBlock { $test }
1
PS> Remove-PSSession -Session $Session
Various other commands exist to check the session's status and retrieve sessions (GetPSSession), close them (Remove-PSSession), disconnect and reconnect them (DisconnectPSSession and Reconnect-PSSession, which are new in PowerShell v3), and so on. In
PowerShell v3, you can also pass an open session to Get-Module and Import-Module,
enabling you to see the modules listed on a remote computer (via the opened PSSession),
or to import a module from a remote computer into your computer for implicit Remoting.
Review the help on those commands to learn more.
Note: Once you use New-PSSession and create your own persistent sessions, it is your
responsibility to do housekeeping and close and dispose the session when you are done
with them. Until you do that, persistent sessions remain active, consume resources and may
Remoting Basics
15
Once the results travel back to you, you can no longer invoke object methods because now
you work with "rehydrated" objects that are detached from the live objects and do not
contain any methods anymore:
Serializing and deserializing is relatively expensive. You can optimize speed and resources
by making sure that your remote code emits only the data you really need. You could for
example use Select-Object and carefully pick the properties you want back rather than
serializing and deserializing everything.
Remoting Basics
16
A lot of newcomers will get a bit confused about remoting, in part because of how
PowerShell executes scripts. Consider the following, and assume that SERVER2 contains a
script named C:\RemoteTest.ps1:
Enter-PSSession -ComputerName SERVER2
C:\RemoteTest.ps1
If you were to sit and type these commands interactively in the console window on your
client computer, this would work (assuming remoting was set up, you had permissions, and
all that). However, if you pasted these into a script and ran that script, it wouldn't work. The
script would try to run C:\RemoteTest.ps1 on your local computer.
The practical upshot of this is that Enter-PSSession is really meant for interactive use by a
human being, not for batch use by a script. If you wanted to send a command to a remote
computer, from within a script, Invoke-Command is the right way to do it. You can either set
up a session in advance (useful if you plan to send more than one command), or you can
use a computer name if you only want to send a single command. For example:
$session = New-PSSession -ComputerName SERVER2
Invoke-Command -session $session -ScriptBlock { C:\RemoteTest.ps1 }
Obviously, you'll need to use some caution. If those were the only two lines in the script, then
when the script finished running, $session would cease to exist. That might disconnect you
(in a sense) from the session running on SERVER2. What you do, and even whether you
need to worry about it, depends a lot on what you're doing and how you're doing it. In this
example, everything would probably be okay, because Invoke-Command would "keep" the
local script running until the remote script finished and returned its output (if any).
Remoting Basics
17
18
19
"dca" is what goes into the certificate. We're creating a certificate that just says "dca" and
we'll make sure our computers can resolve that to an IP address.
20
21
22
23
CA. In a large environment, this can be done via Group Policy, if desired.
24
25
26
Figure 2.10: Beginning the import process into the Personal store
As shown in figure 2.11, browse to the certificate file that you downloaded from your CA.
Then, click Next.
Caution: If you downloaded multiple certificates - perhaps the CA's root certificates along
with the one issued to you - make sure you're importing the SSL certificate that was issued
to you. If there's any confusion, STOP. Go back to your CA and download just YOUR
certificate, so that you'll know which one to import. Don't experiment, here - you need to get
this right the first time.
27
28
29
30
There are two or three pieces of information you'll need to place into this command:
In place of *, you can put an individual IP address. Using * will have the listener listen to
all local IP addresses.
In place of xxx, put the exact computer name that the certificate was issued to. If that
includes a domain name (such as dc01.ad2008r2.loc), put that. Whatever's in the
certificate must go here, or you'll get a CN mismatch error. Our certificate was issued to
"dca," so I put "dca."
In place of yyy, put the exact certificate thumbprint that you copied earlier. It's okay if
this contains spaces.
That's all you should need to do in order to get the listener working.
Note: We had the Windows Firewall disabled on this server, so we didn't need to create an
exception. The exception isn't created automatically, so if you have any firewall enabled on
your computer, you'll need to manually create the exception for port 5986.
You can also run an equivalent PowerShell command to accomplish this task:
New-WSManInstance winrm/config/Listener -SelectorSet @{Address='\*';
Transport='HTTPS'} -ValueSet @{HostName='xxx';CertificateThumbprint='yyy'}
In that example, "xxx" and "yyy" get replaced just as they did in the previous example.
31
This ensured that the HOSTS file was read into the DNS name cache. The results are in
figure 2.16. Note that I can't access DCA by using its IP address directly, because the SSL
certificate doesn't contain an IP address. The SSL certificate was issued to "dca," so we
need to be able to access the computer by typing "dca" as the computer name. Using the
HOSTS file will let Windows resolve that to an IP address.
Note: Remember, there are two things going on here: Windows needs to be able to resolve
the name to an IP address, which is what the HOSTS file accomplishes, in order to make a
physical connection. But WinRM needs mutual authentication, which means whatever we
typed into the -ComputerName parameter needs to match what's in the SSL certificate.
That's why we couldn't just provide an IP address to the command - it would have worked for
the connection, but not the authentication.
32
We provided a valid password for the Administrator account, but as expected the command
didn't work. Finally:
Enter-PSSession -computerName DCA -credential COMPANY\Administrator -UseSSL
Again providing a valid password, we were rewarded with the remote prompt we expected. It
worked! This fulfills the two conditions we specified earlier: We're using an HTTPS-secured
connection and providing a credential. Both conditions are required because the computer
isn't in my domain (since in this case the source computer isn't even in a domain). As a
refresher, figure 2.17 shows, in green, the connection we created and used.
33
Figure 2.17: The connection used for the HTTPS listener test
Modifications
There are two modifications you can make to a connection, whether using InvokeCommand, Enter-PSSession, or some other Remoting command, which relate to HTTPS
listeners. These are created as part of a session option object.
-SkipCACheck causes WinRM to not worry about whether the SSL certificate was
issued by a trusted CA or not. However, untrusted CAs may in fact be untrustworthy! A
poor CA might issue a certificate to a bogus computer, leading you to believe you're
connecting to the right machine when in fact you're connecting to an imposter. This is
risky, so use it with caution.
-SkipCNCheck causes WinRM to not worry about whether the SSL certificate on the
remote machine was actually issued for that machine or not. Again, this is a great way
34
to find yourself connected to an imposter. Half the point of SSL is mutual authentication,
and this parameter disables that half.
Using either or both of these options will still enable SSL encryption on the connection - but
you'll have defeated the other essential purpose of SSL, which is mutual authentication by
means of a trusted intermediate authority.
To create and use a session object that includes both of these parameters:
$option = New-PSSessionOption -SkipCACheck -SkipCNCheck
Enter-PSSession -computerName DCA -sessionOption $option
-credential COMPANY\Administrator -useSSL
Caution: Yes, this is an easy way to make annoying error messages go away. But those
errors are trying to warn you of a potential problem and protect you from potential security
risks that are very real, and which are very much in use by modern attackers.
Certificate Authentication
Once you have an HTTPS listener set up, you have the option of authenticating with
Certificates. This allows you to connect to remote computers, even those in an untrusted
domain or workgroup, without requiring either user input or a saved password. This may
come in handy when scheduling a task to run a PowerShell script, for example.
In Certificate Authentication, the client holds a certificate with a private key, and the remote
computer maps that certificate's public key to a local Windows account. WinRM requires a
certificate which has "Client Authentication (1.3.6.1.5.5.7.3.2)" listed in the Enhanced Key
Usage attribute, and which has a User Principal Name listed in the Subject Alternative Name
attribute. If you're using a Microsoft Enterprise Certification Authority, the "User" certificate
template meets these requirements.
35
Policy", and click Next again. Select the User template, and click Enroll.
36
37
38
Figure 2.22: Placing the certificate into the Trusted People store.
When prompted for credentials, enter the username and password of a local account with
Administrator rights.
Note: It is not possible to specify the credentials of a domain account for certificate mapping,
even if the remote computer is a member of a domain. You must use a local account, and
the account must be a member of the Administrators group.
39
Once you have this thumbprint, you can authenticate to the remote computer by using either
the Invoke-Command or New-PSSession cmdlets with the -CertificateThumbprint parameter,
as shown in figure 2.25.
Note: The Enter-PSSession cmdlet does not appear to work with the -CertificateThumbprint
parameter. If you want to enter an interactive remoting session with certificate
authentication, use New-PSSession first, and then Enter-PSSession.
Note: The -UseSSL switch is implied when you use -CertificateThumbprint in either of these
commands. Even if you don't type -UseSSL, you're still connecting to the remote computer
over HTTPS (port 5986, by default, on Windows 7 / 2008 R2 or later). Figure 2.26
demonstrates this.
40
Figure 2.26: Demonstrating that the connection is over SSL port 5986, even without the UseSSL switch.
41
42
43
44
45
46
47
That should fix the problem. Note that this does disable User Account Control (UAC) on the
machine where you ran it, so make sure that's okay with you before doing so.
48
On your intermediate server(s), you make a similar change to the above, but in a
different section of the configuration:
Set-Item WSMAN:\localhost\service\auth\credssp -value $true
Your domain policy must permit delegation of fresh credentials. In a Group Policy object
(GPO), this is found in Computer Configuration > Policies > Administrative Templates >
System > Credential Delegation > Allow Delegation of Fresh Credentials. You must
provide the names of the machines to which credentials may be delegated, or specify a
wildcard like "*.ad2008r2.loc" to allow an entire domain. Be sure to allow time for the
updated GPO to apply, or run Gpupdate on the originating computer (or reboot it).
Note: Once again, the name you provide here is important. Whatever you'll actually be
typing for the -computerName parameter is what must appear here. This makes it really
tough to delegate credentials to, say, IP addresses, without just adding "*" as an allowed
delegate. Adding "*," of course, means you can delegate to ANY computer, which is
potentially dangerous, as it makes it easier for an attacker to impersonate a machine and get
hold of your super-privileged Domain Admin account!
When running a Remoting command, you must specify the "-Authentication CredSSP"
parameter. You must also use the -Credential parameter and supply a valid
DOMAIN\Username (you'll be prompted for the password) - even if it's the same
username that you used to open PowerShell in the first place.
After setting the above, we were able to use Enter-PSSession to go from our domain
controller to my member server, and then use Invoke-Command to run a command on a
client computer - the connection illustrated in figure 2.34.
49
Where "name" is the name of the computers that you plan to remote to next. This can be a
wildcard, like *, or a partial wildcard, like *.AD2008R2.loc. Then, on the intermediate
computer (the one to which you will delegate your credentials), run this:
Enable-WSManCredSSP -Role Server
50
Between them, these two commands will accomplish almost all of the configuration points
we listed earlier. The only exception is that they will modify your local policy to permit fresh
credential delegation, rather than modifying domain policy via a GPO. You can choose to
modify the domain policy yourself, using the GPMC, to make that particular setting more
universal.
51
52
53
54
"NoLanguage" are options; the latter permits only functions and cmdlets to run. There's
also "RestrictedLanguage" which allows a very small subset of the scripting language to
work - see the help for details.
-ModulesToImport: A comma-separated list of module names to load into the endpoint.
You can also use hash tables to specify specific module versions; read the command's
full help for details.
-PowerShellVersion: '2.0' or '3.0,' specifying the version of PowerShell you want the
endpoint to use. 2.0 can only be specified if PowerShell v2 is independently installed on
the computer hosting the endpoint (installing v3 "on top of" v2 allows v2 to continue to
exist).
-ScriptsToProcess: A comma-separated list of path and file names of scripts to run when
a user connects to the endpoint. You can use this to customize the endpoint's runspace,
define functions, load modules, or do anything else a script can do. However, in order to
run, the script execution policy must permit the script.
-SessionType: "Empty" loads nothing by default, leaving it up to you to load whatever
you like via script or the parameters of this command. "Default" loads the normal
PowerShell core extensions, plus whatever else you've specified via parameter.
"RestrictedRemoteServer" adds a fixed list of seven commands, plus whatever you've
specified; see the help for details on what's loaded.
Caution: Some commands are important - like Exit-PSSession, which enables someone to
cleanly exit an interactive Remoting session. RestrictedRemoteServer loads these, but
Empty does not.
-VisibleAliases, -VisibleCmdlets, -VisibleFunctions, and -VisibleProviders: These
comma-separated lists define which of the aliases, cmdlets, functions, and PSProviders
you've loaded will actually be visible to the endpoint user. These enable you to load an
entire module, but then only expose one or two commands, if desired.
Note: You can't use a custom endpoint alone to control which parameters a user will have
access to. If you need that level of control, one option is to dive into .NET Framework
programming, which does allow you to create a more fine-grained remote configuration.
That's beyond the scope of this guide. You could also create a custom endpoint that only
included proxy functions, another way of "wrapping" built-in commands and adding or
removing parameters - but that's also beyond the scope of this guide.
Once you've created the configuration file, you're ready to register it. This is done with the
Register-PSSessionConfiguration command, as shown in figure 3.4.
55
56
57
58
59
Figure 3.9: Only eight commands, including the Get-ADUser one we added, are available
within the endpoint.
In reality, it's unlikely that a Sales user like Sally would be running commands in the
PowerShell console. More likely, she'd use some GUI-based application that ran the
commands "behind the scenes." Either way, we've ensured that she has exactly the
functionality she needs to do her job, and nothing more.
60
run:
PS C:\> & { Import-Module ActiveDirectory; Get-ADUser -filter \* | Remove-ADObject }
Eek! This can be especially dangerous if you configured the endpoint to use a RunAs
credential to run commands under elevated privileges. It's also somewhat easy to let this
happen by mistake, because you set the language mode when you create the new session
configuration file (New-PSSessionConfigurationFile), not when you register the session
(Register-PSSessionConfiguration). So if you're using a session configuration file created by
someone else, pop it open and confirm its language mode before you use it!
You can avoid this problem by setting the language mode to NoLanguage, which shuts off
script blocks and the rest of the scripting language. Or, go for RestrictedLanguage, which
blocks script blocks while still allowing some basic operators if you want users of the
endpoint to be able to do basic filtering and comparisons.
Understand that this isn't a bug - the behavior we're describing here is by design. It can just
be a problem if you don't know about it and understand what it's doing.
Note: Much thanks to fellow MVP Aleksandar Nikolic for helping me understand the logic of
this loophole!
61
Diagnostics Examples
For the following scenarios, we started by importing the PSDiagnostics module (note that
this is implemented as a script module, and requires an execution policy that permits it to
run, such as RemoteSigned or Unrestricted). Figure 4.1 also shows that we ran the EnablePSWSManCombinedTrace command, which starts the extended diagnostics logging.
62
63
64
65
66
67
the communications language of Web services). Figure 4.8 shows a couple of these 1500byte chunks. Notice that the actual payload is pretty much gibberish.
68
69
70
destination : Client
messageType : SessionCapability
pipelineId : 00000000-0000-0000-0000-000000000000
runspaceId : 00000000-0000-0000-0000-000000000000
data : <Obj RefId="0"><MS><Version
N="protocolversion">2.2</Version><Version
N="PSVersion">2.0</Version><Version
N="SerializationVersion">1.1.0.1</Version></MS></Obj>
Next is the server's $PSVersionTable object, which lists various versioning information:
destination : Client
messageType : ApplicationPrivateData
pipelineId : 00000000-0000-0000-0000-000000000000
runspaceId : 4358d585-0eab-47ef-a0e6-4b98e71f34ab
data : <Obj RefId="0"><MS><Obj N="ApplicationPrivateData"
RefId="1"><TN RefId="0"><T>System.Management.Automation.
PSPrimitiveDictionary</T><T>System.Collections.Hashtable
</T><T>System.Object</T></TN><DCT><En><S
N="Key">PSVersionTable</S><Obj N="Value"
RefId="2"><TNRef RefId="0" /><DCT><En><S
N="Key">PSVersion</S><Version
N="Value">2.0</Version></En><En><S
N="Key">PSCompatibleVersions</S><Obj N="Value"
RefId="3"><TN RefId="1"><T>System.Version[]</T><T>System
.Array</T><T>System.Object</T></TN><LST><Version>1.0</Ve
rsion><Version>2.0</Version><Version>3.0</Version></LST>
</Obj></En><En><S N="Key">BuildVersion</S><Version
N="Value">6.2.8314.0</Version></En><En><S
N="Key">PSRemotingProtocolVersion</S><Version
N="Value">2.2</Version></En><En><S
N="Key">WSManStackVersion</S><Version
N="Value">3.0</Version></En><En><S
N="Key">CLRVersion</S><Version
N="Value">4.0.30319.261</Version></En><En><S
N="Key">SerializationVersion</S><Version N="Value">1.1.0
.1</Version></En></DCT></Obj></En></DCT></Obj></MS></Obj
>
Next the server sends information about the runspace that will be used:
destination : Client
messageType : RunspacePoolStateInfo
pipelineId : 00000000-0000-0000-0000-000000000000
runspaceId : 4358d585-0eab-47ef-a0e6-4b98e71f34ab
data : <Obj RefId="0"><MS><I32
N="RunspaceState">2</I32></MS></Obj>
71
A bit later we'll see the result of the CD C:\ command, which is the new PowerShell prompt
reflecting the new folder location:
destination : Client
messageType : PowerShellOutput
pipelineId : c913b8ae-2802-4454-9d9b-926ca6032018
runspaceId : 4358d585-0eab-47ef-a0e6-4b98e71f34ab
data : <S>PS C:\> </S>
Next we'll look at the output of the Dir command. This first bit is writing the column headers
for Mode, LastWriteTime, Length, Name, and so forth. This is all being sent to our client we'll just include the first few lines, each of which comes across in its own block:
destination : Client
messageType : RemoteHostCallUsingPowerShellHost
pipelineId : c259c891-516a-46a7-b287-27c96ff86d5b
runspaceId : 4358d585-0eab-47ef-a0e6-4b98e71f34ab
data : <Obj RefId="0"><MS><I64 N="ci">-100</I64><Obj N="mi"
RefId="1"><TN RefId="0"><T>System.Management.Automation.
Remoting.RemoteHostMethodId</T><T>System.Enum</T><T>Syst
em.ValueType</T><T>System.Object</T></TN><ToString>Write
Line2</ToString><I32>16</I32></Obj><Obj N="mp"
RefId="2"><TN RefId="1"><T>System.Collections.ArrayList<
/T><T>System.Object</T></TN><LST><S>Mode
LastWriteTime Length Name
</S></LST></Obj></MS></Obj>
destination : Client
messageType : RemoteHostCallUsingPowerShellHost
pipelineId : c259c891-516a-46a7-b287-27c96ff86d5b
runspaceId : 4358d585-0eab-47ef-a0e6-4b98e71f34ab
data : <Obj RefId="0"><MS><I64 N="ci">-100</I64><Obj N="mi"
RefId="1"><TN RefId="0"><T>System.Management.Automation.
72
Remoting.RemoteHostMethodId</T><T>System.Enum</T><T>Syst
em.ValueType</T><T>System.Object</T></TN><ToString>Write
Line2</ToString><I32>16</I32></Obj><Obj N="mp"
RefId="2"><TN RefId="1"><T>System.Collections.ArrayList<
/T><T>System.Object</T></TN><LST><S>----
------------- ------ ----
</S></LST></Obj></MS></Obj>
destination : Client
messageType : RemoteHostCallUsingPowerShellHost
pipelineId : c259c891-516a-46a7-b287-27c96ff86d5b
runspaceId : 4358d585-0eab-47ef-a0e6-4b98e71f34ab
data : <Obj RefId="0"><MS><I64 N="ci">-100</I64><Obj N="mi"
RefId="1"><TN RefId="0"><T>System.Management.Automation.
Remoting.RemoteHostMethodId</T><T>System.Enum</T><T>Syst
em.ValueType</T><T>System.Object</T></TN><ToString>Write
Line2</ToString><I32>16</I32></Obj><Obj N="mp"
RefId="2"><TN RefId="1"><T>System.Collections.ArrayList<
/T><T>System.Object</T></TN><LST><S>d----
8/25/2010 8:11 AM IT Structures
</S></LST></Obj></MS></Obj>
destination : Client
messageType : RemoteHostCallUsingPowerShellHost
pipelineId : c259c891-516a-46a7-b287-27c96ff86d5b
runspaceId : 4358d585-0eab-47ef-a0e6-4b98e71f34ab
data : <Obj RefId="0"><MS><I64 N="ci">-100</I64><Obj N="mi"
RefId="1"><TN RefId="0"><T>System.Management.Automation.
Remoting.RemoteHostMethodId</T><T>System.Enum</T><T>Syst
em.ValueType</T><T>System.Object</T></TN><ToString>Write
Line2</ToString><I32>16</I32></Obj><Obj N="mp"
RefId="2"><TN RefId="1"><T>System.Collections.ArrayList<
/T><T>System.Object</T></TN><LST><S>d----
7/13/2009 11:20 PM PerfLogs
</S></LST></Obj></MS></Obj>
You'll also see periodic exchanges about the state of the pipeline - this indicates that the
command is done:
73
destination : Client
messageType : PowerShellStateInfo
pipelineId : f5c8bc7a-ec54-4180-b2d4-86479f9ea4b9
runspaceId : 4358d585-0eab-47ef-a0e6-4b98e71f34ab
data : <Obj RefId="0"><MS><I32
N="PipelineState">4</I32></MS></Obj>
There's definitely a lot of data passing back and forth - but it's possible to make sense of it
using these tools. Frankly, most Remoting problems take place during the connection phase,
meaning once that's completed successfully you have no further problems. So in the next
scenarios, we'll focus on specific connection errors.
Note: To clear the log and prepare for a new trace, we usually delete the .ETL files and go
into Event Viewer to clear the Applications and Services Logs > Microsoft > Windows >
Windows Remote Management log. If you're getting errors when running EnablePSWSManCombinedTrace, one of those two tasks probably hasn't been completed.
74
75
76
77
But you can also log on as an Administrator (or open a shell under Administrator
credentials), enable a trace, and then have the other user (or your other user account) try
whatever it is they're trying. Go back in as Administrator and disable the trace, then examine
the log. Figure 4.14 shows what you're looking for.
78
79
80
Figure 4.17: The error message gives good clues as to how to solve this problem
Figure 4.18 shows that WinRM still sends its initial salvo of traffic to the server. It's when the
reply comes back that the client realizes it can't authenticate this server, and the error is
generated. What you see in the log is pretty much what shows up in the shell, verbatim.
81
Figure 4.18: The diagnostic log content when attempting to connect to an untrusted host
Figure 4.19 shows a good second step to take: Run Test-WSMan. Provide the same
computer name or IP address, but leave off the -Credential parameter. The cmdlet can at
least tell you that WS-MAN and WinRM are up and running on the remote computer, and
what version they're running. That at least narrows the problem down to one of
authentication: Either your permissions (which would have resulted in an "Access Denied")
or the mutual authentication component of Remoting.
82
83
running telnet machine_name:5985. You should get a blank screen, and Ctrl+C will end
the session. If this doesn't work, there's a basic connectivity problem (such as a blocked
port) you need to resolve.
4. Use Test-WSMan as described earlier, using an alternate credential if necessary. Make
sure you're either using the machine's real name as it appears in Active Directory, or
that you've taken one of the other approaches (TrustedHosts plus a credential, or SSL
plus a credential) that we outlined in Section 2 of this guide. If that doesn't work, you
have a problem in the WS-MAN configuration.
Simply walking through these four steps, in this order, can help you pinpoint at least the
general cause of most problems.
Summary
So why did we bother going through the logs when, in most of our examples, the logs simply
echoed what was on the screen? Simple: As PowerShell becomes embedded in more and
more GUI applications, you might not always have a console, with its nice error messages,
to rely upon. What you can do, however, is use the console to start a trace, run whatever
GUI app is failing, and then dig into the log to see if you find some of the signs we've shown
you here.
84
Session Management
When you create a Remoting connection between two machines, you're creating - in
PowerShell terminology - a session. There are an incredible number of options that can be
applied to these sessions, and in this portion of the guide we'll walk you through them.
Session Management
85
Session Management
86
Session Options
Whenever you run a Remoting command that creates a session - whether persistent or adhoc - you have the option of specifying a -SessionOption parameter, which accepts a
PSSessionOption object. The default option object is used if you don't specify one, and that
object can be found in the built-in $PSSessionOption variable. It's shown in figure 5.3.
Session Management
87
Session Management
88
Session Management
89
Figure 5.5: Creating a new PSSessionOption object to use with a 1-to-1 connection
By specifying intelligent values for these various options, you can help ensure that
disconnected sessions don't hang around and run forever and ever. A reasonable idle
timeout, for example, ensures that the session will eventually close itself, even if an
administrator disconnects from it and subsequently forgets about it. Note that, when a
session closes itself, any data within that session - including background job results - will be
lost. It's probably a good idea to get in the practice of having data saved into a file (by using
Export-CliXML, for example), so that an idle session doesn't close itself and lose all of your
work.
Session Management
90
91
execute. This provides a much better opportunity for delegated administration. Rather than
having Administrators log onto consoles and do whatever they please, you can have them
connect to restricted, secured endpoints and only complete those specific tasks that the
endpoint permits.
92
Remoting is Security-Transparent
As stated, Remoting neither adds anything to, nor takes anything away from, your existing
security configuration. Remote commands are executed using the delegated credentials of
whatever user invoked the commands, meaning they can only do what they have permission
to do - and what they could presumably do through a half-dozen other tools anyway.
Whatever auditing you have in place in your environment cannot be bypassed by Remoting.
Unlike many past "remote execution" solutions, Remoting does not operate under a single
"super-privileged" account unless you expressly configure it that way (which requires several
steps and cannot possibly by accomplished accidentally, as it requires the creation of
custom endpoints).
Remember: Anything someone can do via Remoting, they can already do in a half-dozen
other ways. Remoting simply provides a more consistent, controllable, and scalable means
of doing so.
93
Summary
At this point, denying PowerShell Remoting is like denying Ethernet: It's ridiculous to think
you'll successfully operate your environment without it. For the first time, Microsoft has
provided a supported, official, baked-in technology for remote server administration that
does not use elevated credentials, does not store credentials in any way, that supports
mutual authentication, and that is complete security-transparent. This is the administration
technology we should have had all along; moving to it will only make your environment more
manageable and more secure, not less.
94
GPO Caveats
One thing to keep in mind is that GPOs can only create configuration changes; they can't
necessarily change the active state of the computer. In other words, while a GPO can
configure a service's start mode to "Automatic," it can't start the service. That'll happen
automatically when the computer is restarted. It isn't so much that a restart is needed, just
that the computer only starts services after booting. So in many cases, the changes you
make with a GPO (with regard to Remoting) won't actually take effect until the next time the
affected computers are restarted, because in most cases the computer only looks at the
configuration at boot time. Just be aware of that.
Also, everything in this section assumes that PowerShell is already installed on the target
computers - something that can also be accomplished with a GPO or other software
deployment mechanism, but not something we're going to cover here. Note that most of this
section should apply to either PowerShell v2 or v3; we're going to run through the examples
using v2 on a Windows 7 client computer belonging to a Windows Server 2008 R2 domain.
Note: Some of the GPO settings we'll be reviewing became available in Windows 2008 and
Windows 2008 R2, but you should be able to install the necessary administrative templates
into any domain controller. The Windows 7 (and later versions) Remote Server
Administration Toolkit (RSAT) contains the necessary templates.
We don't know for sure that the GPO configuration steps need to be accomplished in the
order we present them; in most cases, we expect you'll do them all at once in a single GPO,
so it won't matter. We're taking them step-by-step in this order so that we can check the
individual results along the way.
95
96
This service is set to start automatically on newer server operating systems (Windows
Server 2003 and later), but not on clients. So this step will only be required for client
computers. Again, this won't start the service, but the next time the computer restarts, the
service will start automatically.
Microsoft suggests accomplishing this task by running a PowerShell command - which does
not require that Remoting be enabled in order to work:
Set-Service WinRM -computername $servers -startup Automatic
You can populate $servers any way you like, so long as it contains strings that are computer
names, and so long as you have Administrator credentials on those computers. For
example, to grab every computer in your domain, you'd run the following (this assumes
PowerShell v2 or v3, on a Windows 7 computer with the RSAT installed):
Import-Module ActiveDirectory
$servers = Get-ADComputer -filter \* | Select -expand name
Practically speaking, you'll probably want to limit the number of computers you do at once by
either specifying a -Filter other than "*" or by specifying -SearchBase and limiting the search
to a specific OU. Read the help for Get-ADComputer to learn more about those parameters.
Note that Set-Service will return an error for any computers it couldn't contact, or for which
the change didn't work, and then continue on with the next computer.
Alternately, you could configure this with a GPO. Under Computer Configuration\Windows
Settings\Security Settings\System Services, look for "Windows Remote Management."
Right-click it and set a startup mode of Automatic. That's what we did in figure 7.2.
97
98
99
Give it a Try!
After applying the above GPO changes, we restarted our client computer. When the WinRM
service starts, it checks to see if it has any configured listeners. When it finds that it doesn't,
it should try and automatically configure one - which we've now allowed it to do via GPO.
The Firewall exception should allow the incoming traffic to reach the listener.
As shown in figure 7.5, it seems to work. We've found the newly created listener!
100
101
Figure 7-6: Initiating a 1-to-1 Remoting session with the GPO-configured client computer
102