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:

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
[xml]$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
If you're in a mixed-mode environment with both Exchange 2003 and Exchange 2007/2010 you may have noticed this message when using the Get-* cmdlets in the Exchange Management Shell:
WARNING: The object domin.com/Users/UserName has been corrupted, and it's in an inconsistent state. The following validation errors happened:
WARNING: Property expression "xx xxx" isn't valid. Valid values are: Strings formed with characters from A to Z (uppercase or lowercase), digits from 0 to 9, !, #, $, %, &, ', *, +, -, /, =, ?, ^, _, `, {, |, } or ~. One or more periods may be embedded in an alias, but each period should be preceded and followed by at least one of the other characters. Unicode characters from U+00A1 to U+00FF are also valid in an alias, but they will be mapped to a best-fit US-ASCII string in the e-mail address, which is generated from such an alias.
Or one of the following:
WARNING: Object <distinguished name of the recipient> has been corrupted and it is in an inconsistent state. The following validation errors have been encountered:
WARNING: <alias of the recipient> is not valid for Alias.
These messages (there are others as well) appears when you try to manage a recipient with spaces (or any other invalid character) in its alias using the Exchange management tools. For example, in Exchange Server 2003, you could create recipients with spaces in aliases. Exchange Server 2007/2010 does not allow recipients to have spaces in their aliases. The biggest problem with invalid aliases - you will not be able to move a mailbox to an Exchange 2007/2010 server. To mitigate this I've written the following function.
Note: In Exchange 2010, the mailbox's alias is generated based on the Name property. Invalid characters in the name will be replaced with a question mark (?) when the alias is generated.
function Test-ExchangeAlias
{
param(
[Parameter(
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true
)]
[ValidateLength(1,64)]
[string]$Alias,
[switch]$RemoveIllegalCharacters
)
begin
{
$IllegalCharacters = 0..34+40..41+44,47+58..60+62+64+91..93+127..160
}
process
{
if($RemoveIllegalCharacters)
{
foreach($c in $IllegalCharacters)
{
$escaped = [regex]::Escape([char]$c)
if($Alias -match $escaped)
{
Write-Verbose "illegal character code detected: '$c'"
$Alias = $Alias -replace $escaped
}
}
$Alias
}
else
{
for($c=0; $c -lt $Alias.Length; $c++)
{
$code = [int][char]$Alias[$c]
Write-Verbose "Testing current Alias character code: $code"
if($IllegalCharacters -contains $code)
{
Write-Verbose "Character code: $code is an invalid alias character."
$false
return
}
}
$true
}
}
}
The function supports two parameters, Alias and RemoveIllegalCharacters. In the Begin block we assign a series of numbers to a variable, $IllegalCharacters, using the range operator along with the plus operator (+) to combine a range with a list of elements in an array. These numbers represents the character codes an alias cannot contain.
In the Process block we check if the RemoveIllegalCharacters parameter has been specified. If it was specified, all invalid characters are removed and a fixed alias is returned. Otherwise the function just tests if the alias is valid and returns $true/$false respectively. Invalid characters are removed using the Replace operator. Since we don't know if each invalid character is a regular expression meta character we use the Escape method to convert it so that the regular expression engine will interpret any metacharacters that it may contain as character literals.
With the following command you can fix all invalid aliases on all mailbox objects:
Get-Mailbox –ResultSize Unlimited | Where-Object {-not (Test-ExchangeAlias -Alias $_.Alias)} | Foreach-Object {
$NewAlias = Test-ExchangeAlias -Alias $_.Alias -RemoveIllegalCharacters
$_ | Set-Mailbox –Alias $NewAlias
}
When running the above you’ll get the ‘inconsistent state’ error for each invalid alias mailbox object but if you issue the command again you’ll see that the error has gone and the Aliases have been fixed.
A few days ago, Devfarm Software the makers of PowerWF, has released their commercial PowerShell editor, PowerSE v2.5, as a free community script editor.
PowerSE is an advanced script editor and an interactive console packed with multiple features to help you develop and debug your PowerShell scripts. Now you can download a fully functional FREE version HERE (requires registration).
PowerSE features:
- Complete IntelliSense coverage of PowerShell, DotNet and WMI.
- Real time Syntax Checking instantly lets you see errors in your code and quickly jump to error lines to fix the problem.
- It has an XML editor.
- The ability to do a quick command. Example. Get-process. Then using the grid, you can select columns, rearrange the rows, and sort. Then generate the PowerShell, and insert it into your script. Much faster than typing all those commands.
- Integrated Help.
- Tabbed interface to edit multiple scripts.
- Integrated Console. If you hit F11, it will zoom into full-screen mode (F11 to zoom back). F12 will zoom the highlighted window to full-screen, and vice-versa. F1 full-screen help.
- Command History (combined with the console). So, you can do an interactive console session, to figure out what you need. Then using the Command History, insert those command and create a script. Sort of record/playback.
- Call Stack history, plus complete debugger.
- Search scripts in script repositories like: PoshCode and TechNet Script Center Repository.
Check out this video for a quick overview.
So, give it a spin to see how you like it. Thumbs up for making it free but there's one drawback, the product will expire in 60 days and to continue using it you will have to re-download it!