Get Full Control over your Exchange remote PowerShell session

[Another great post from Shay Levy - original available here] 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 = ''
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 LanguageMode. 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:             I changed the value to FullLanguage, saved the file, recycled the MSExchangePowerShellAppPool application pool and re-created a remote session:
PS > $uri = ''
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
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:SOFTWAREMicrosoftExchangeServerv14Setup’
$exbin = Join-Path (Get-ItemProperty $path).MsiInstallPath ClientAccess
$folder = New-Item -Path $exbinexadmin -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 $exbinPowerShellweb.config $folder.FullName -Force
[xml]$wconfig = Get-Content $exbinexadminweb.config
$wconfig.configuration.appSettings.add.value = 'FullLanguage'

# 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:AppPoolsexadmin -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 "$exbinexadmin" -ApplicationPool $

#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 = ‘’
#$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