Get Full Control over your Exchange remote PowerShell session

August 22, 2011

12 comments

PowerShell 2.0 supports two kinds of remote configurations: fan-in and fan-out. When we execute a command against a bunch of servers we use fan-out (one to many). Fan-in is used when multiple users are connecting to a remote server (many to one). Exchange server implements a fan-in configuration via a virtual directory on the Exchange server’s IIS. We can connect to the virtual directory (http connection) and manage our Exchange server remotely, without having to install the Exchange Management Tools locally on our admin station.

We can create a remote session (using the currently logged on credentials) with the New-PSSession cmdlet. If you need to connect with alternate credentials, add the –Credential parameter:

PS > $uri = 'http://dc1.homelab.com/PowerShell' 
PS > $session = New-PSSession -ConfigurationName Microsoft.Exchange `
-ConnectionUri $uri -Authentication Kerberos

The session is created and we can import the commands from the remote session into our local session and use them as if they were installed locally (aka, implicit remoting).

PS> Import-PSSession –Session $session


When the session finished importing, all commands from the remote session are available in our local session. In the background a module is created that contains all remote commands. Let’s try one command:

PS > Get-MailboxDatabase

Name                           Server   Recovery  ReplicationType
----                           ------   --------  ---------------
Mailbox Database 0311695863    DC1      False     None

Now let’s try to get the database size:

PS > $db = Get-MailboxDatabase -Identity 'Mailbox Database 0311695863' -Status
PS > $db.DatabaseSize
152.1 MB (159,449,088 bytes)

 

We get back the size of the database in MB and in bytes. Exchange supports several methods to format the size of an object (mailbox/database) through a series of methods: ToBytes(), ToKB(), ToMB() etc. Let’s try to format the size of the database and get the value in bytes:

PS > $db.DatabaseSize.ToBytes()
Method invocation failed because [System.String] doesn't contain a method named 'ToBytes'.
At line:1 char:25
+ $db.DatabaseSize.ToBytes <<<< ()
    + CategoryInfo          : InvalidOperation: (ToBytes:String) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

 

We get an error that the DatabaseSize property doesn’t contain the ToBytes method. We can also see that DatabaseSize is a String. This is the expected behavior in a remoting session. When a source computer sends a script to a remote computer, the code is serialized first (converted to XML), when it gets to the destination machine it’s converted back (deserialized) and executed. The result is serialized again and sent back to the source computer. It is important to understand that when the source computer gets the final result, it convert it back to objects but the objects are “dehydrated”, and contains a bunch of Note properties and one method – ToString (some “primitive” types, like Int, can be deserialized better than others). As a consequence, if we want to execute methods on the “real” object we need to do that on the remote end. When we want to get hold of the object itself (and it’s members) we invoke the command on the remote end with the Invoke-Command cmdlet:

PS > $identity = 'Mailbox Database 0311695863'
PS > $sb = {(Get-MailboxDatabase -Status –Identity $identity).DatabaseSize.ToBytes()} PS > Invoke-Command -Session $session -ScriptBlock $sb Method calls are not allowed in restricted language mode or a Data section. + CategoryInfo : ParserError: (ToGB:Token) [], ParseException + FullyQualifiedErrorId : MethodCallNotSupportedInDataSection

 

Another error! We cannot run methods in restricted language mode. What does this mean? The Exchange configuration is locked down (restricted session). By default, only administrators can connect to the end point, but they are restricted as well!

A few words on the LanguageMode property. There are three possible values: NoLanguage, RestrictedLanguage, and FullLanguage. In FullLanguage you can do whatever you want. In NoLanguage mode only commands that are using the Runspace APIs are allowed, and in RestrictedLanguage mode commands that contain scripts that need to be evaluated are not allowed.

This was a bit disappointing. If the server admin cannot have full access to the remote session then who can? I’m not sure why the Exchange team decided to lock the environment. The notion of connecting to any remote server and managing it without having to install local tools is not fulfilled here.

I started to look for a way to bypass that limitation and it appears that I was looking in the wrong direction! Hats off to my friend, MVP Aleksandar Nikolic, for a great tip! We can change the language mode by opening the web.config file in the PowerShell virtual directory:

image_thumb image_thumb3

 

I changed the value to FullLanguage, saved the file, recycled the MSExchangePowerShellAppPool application pool and re-created a remote session:

PS > $uri = 'http://dc1.homelab.com/PowerShell' 
PS > $session = New-PSSession -ConfigurationName Microsoft.Exchange `
-ConnectionUri $uri -Authentication Kerberos

PS > $identity = 'Mailbox Database 0311695863'
PS > $sb = {(Get-MailboxDatabase -Status –Identity $identity).DatabaseSize.ToBytes()}
PS > Invoke-Command -Session $session -ScriptBlock $sb
159449088

 

And it worked, we can invoke methods in the remote session without having to parse strings. The final step was to create a separate environment, for admins only, one that doesn’t change the original configuration made by the Exchange team. I reverted back the value in the web.config file and wrote the following script to automate the process. Log on to your Exchange server, open PowerShell (not EMS) and run it (comments inline):

# load the IIS module
Import-Module WebAdministration

# get the path to the exchange server installation directory 
# and create a new folder for the exadmin application
$path = ‘HKLM:\SOFTWARE\Microsoft\ExchangeServer\v14\Setup’ $exbin = Join-Path (Get-ItemProperty $path).MsiInstallPath ClientAccess $folder = New-Item -Path $exbin\exadmin -ItemType Directory -Force # copy the web.config file to the new directory, load it (as xml) and
# change the language mode (from RestrictedLanguage) to FullLanguage Copy-Item $exbin\PowerShell\web.config $folder.FullName -Force $wconfig = Get-Content $exbin\exadmin\web.config $wconfig.configuration.appSettings.add.value = 'FullLanguage' $wconfig.Save("$exbin\exadmin\web.config") # Create a new IIS application pool, and start it $pool = New-WebAppPool -Name exadmin # Configure the exadmin app pool to run under the LocalSystem account (0) Set-ItemProperty IIS:\AppPools\exadmin -Name ProcessModel -Value @{identityType=0} # start app pool Start-WebAppPool -Name exadmin # Create a new IIS Web Application. $application = New-WebApplication -Name exadmin -Site 'Default Web Site' `
-PhysicalPath "$exbin\exadmin" -ApplicationPool $pool.name #Set the application SSL settings to accept client certificates (if they are provided) Set-WebConfigurationProperty -Filter //security/access –Name SslFlags `
-Value SslNegotiateCert -PSPath IIS:\ -Location 'Default Web Site/exadmin' # create new end point configuration and allow administrators to remotely run commands
# a dialog is shown with the local administrators group selected, and we can add
# users/groups we want to have access to the end point #Get-PSSessionConfiguration exadmin | Unregister-PSSessionConfiguration -Force Register-PSSessionConfiguration -Name exadmin -Force Set-PSSessionConfiguration -Name exadmin -ShowSecurityDescriptorUI -Force # testing the new environment, uncomment and change database identity # create a fan-in session (notice we are connecting to exadmin) and try to
# invoke the ToBytes method – it works
#$sb = { (Get-MailboxDatabase -Status -Identity 'Mailbox Database 0311695863').DatabaseSize.ToBytes() }
#$uri = ‘http://dc1.homelab.com/exadmin
#$session = New-PSSession -ConfigurationName Microsoft.Exchange –ConnectionUri $uri
#Invoke-Command $session –ScriptBlock $sb

Now you can connect from any computer that has PowerShell 2.0 installed to your Exchange server and gain full access. I hope this has been helpful, here’s some related resources you may find useful as well:

How objects are sent to and from remote sessions

Configuring PowerShell for Remoting – Part 1

Configuring PowerShell for Remoting – Part 2 (Fan-In)

Administrator’s Guide to Windows PowerShell Remoting

Layman’s Guide to PowerShell 2.0 remoting

Deep Dive video: Constrained PowerShell Endpoints – Aleksandar Nikolic

Book: Microsoft Exchange 2010 PowerShell Cookbook – Mike Pfeiffer















Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

12 comments

  1. DonSeptember 13, 2011 ב 13:49

    Sorry to be a pest.. I followed this exactly and I get ”
    Connecting to remote server failed with the following error message : The WinRM client received an HTTP s
    us code of 403 from the remote WS-Management service. For more information, see the about_Remote_Troubleshooting Hel
    opic.
    + CategoryInfo : OpenError: (System.Manageme….RemoteRunspace:RemoteRunspace) [], PSRemotingTransportE
    eption
    + FullyQualifiedErrorId : PSSessionOpenFailed
    Import-PSSession : Cannot validate argument on parameter ‘Session’. The argument is null. Supply a non-null argument
    d try the command again.
    At C:\psscripts\exchange\openExchange.ps1:2 char:17
    + Import-PSSession <<<< $Session
    + CategoryInfo : InvalidData: (:) [Import-PSSession], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.ImportPSSessionCommand

    I also reset iis in case there was something there but no dice. Any thoughts?

    Reply
  2. DonSeptember 13, 2011 ב 13:49

    Sorry.. just in case.. I am running as a user with relevant permissions.

    Reply
  3. DonSeptember 13, 2011 ב 14:21

    Last (hopefully) update..
    I changed the web.config to allow fulllanguage and even that doesn’t seem to return me the correct details to get .toMB or any similar to work. Back to square one..
    Bloody Microsoft creating remote tools that are not remote!

    Reply
  4. ScriptFanaticSeptember 13, 2011 ב 15:46

    Hey Don

    Remoting can be quite a challenge! Anyway, did you run the script on the server? Can you make sure that the virtual directory is not configured to use SSL?

    Reply
  5. DonSeptember 13, 2011 ב 16:12

    Thanks for the fast reply!

    SSL definitely off, and I ran on the server (as admin). I also tried setting the web.config as Full onto the powershell default but that doesn’t seem to be any different?

    Reply
  6. ScriptFanaticSeptember 13, 2011 ב 16:28

    Try to recycle the associated application pool.

    Reply
  7. DonSeptember 13, 2011 ב 16:40

    No, nothing.. argh!
    I have been testing using
    (get-mailbox ).prohibitSendQuota (works)
    (get-mailbox
    ).prohibitSendQuota.value (returns blank)

    This is Exchange 2010 SP1 and I have rollup 4 installed.

    Reply
  8. ScriptFanaticSeptember 13, 2011 ב 16:50

    Do you get anything with this:

    $host.Runspace.SessionStateProxy.LanguageMode

    Reply
  9. DonSeptember 13, 2011 ב 17:00

    Seems to be empty – should there be a variable assigned to $host?

    Reply
  10. ScriptFanaticSeptember 13, 2011 ב 18:26

    Yes, it’s a built-in variable. I will run some tests and keep you posted.

    Reply
  11. DonSeptember 19, 2011 ב 13:22

    Some more feedback:
    I ended up running variations of the command above; and when I get to $host.Runspace.SessionStateProxy; all values are empty.

    Is it possible that the web.config at the inetpub level is overriding? That has no setting for LanguageMode.

    Reply
  12. RamblingCookieMonsterJanuary 25, 2013 ב 11:42

    Hello Shay,

    I gave this a try on our servers. I can remote in without issue, but I still end up in a RestrictedLanguage languagemode, just like Don.

    A few specifics:
    http://powershell.org/discuss/viewtopic.php?f=29&t=1115&p=4731#p4731

    Any ideas? I’m hoping cycling IIS tonight will help, but it doesn’t sound like you needed to do this?

    Thanks!
    CM

    Reply