MVP - Most Valuable Professional

Just can't get enough of IT

This blog is about mostly anything in IT. But the primary focuses are Microsoft technologies like Exchange Server, Microsoft 365, Microsoft Teams, and Cloud Security.

There are three different ways to configure new Exchange user mailboxes after these have been created.

  • The classic manual administrator approach (keeps your job safe, but is it fun?)
  • The workflow based approach using some kind of IDM workflow solution (keeps the IDM consultant's job safe)
  • The scripting agent approach by extending the Exchange cmdlets (keeps your job safe, is fun, keeps you in control and get's you more free time)

The Exchange cmdlet extension is controlled by a scripting agent configuration file and a organizational setting to enable/disable the scripting agent.


A scripting agent configuration file sample (ScriptingAgentConfig.xml.sample) is located in

  • $exinstall\Bin\CmdletExtensionAgents

The sample needs to be renamed to ScriptingAgentConfig.xml, to be picked up the PowerShell engine.

As always, a slight reminder: Test any modification in a test environment first, before you use the extension in a production environment.

After succesfull testing and deployment, you need to enable the scripting agent using

Enable-CmdletExtensionAgent "Scripting Agent"


Even thought that you can extend mostly any Exchange cmdlet, this example covers the extension of the New-Mailbox and Enable-Mailbox cmdlets in a multi domain and multi AD site environment.

This extension disables the following CAS mailbox settings, after a new mailbox has been created:

  • ActiveSync
  • IMAP4
  • POP3
  • MAPI over HTTP

What does the example do?

  • Extension is named MailboxProvisioning and handles the cmdlets New-Mailbox and Enable-Mailbox
  • Is called on trigger OnComplete
    • The extension code is called after the original cmdlet has finished
  • Code is executed, if the original cmdlet was successfully finished
  • Code is executed, if the mailbox created is not an archive
  • A slight delay of 10 seconds ensures that domain controller activities have been finished
    • Can be adjusted or even removed, depending on your environment
  • Try to fetch at least on of three user parameters to identify the user mailbox
    • Checking for Identity, Name, Alias
  • Fetch a list of all domain controllers in the current AD site where the Exchange server is located
  • Iterate through the list of domain controllers and try to fetch the new CAS mailbox
    • If fetched, remember the domain controller's FQDN
  • Change the CAS mailbox settings as needed and use the remembered domain controller as DC to write to


<?xml version="1.0" encoding="utf-8" ?>
  <Configuration version="1.0">
	<Feature Name="MailboxProvisioning" Cmdlets="New-Mailbox,Enable-Mailbox">
		<ApiCall Name="OnComplete">
			If ($succeeded) {
				if (!($provisioningHandler.UserSpecifiedParameters.Archive -eq $true)) {
					# delay execution for 10 seconds, adjust as needed
					Start-Sleep -s 10
					# validate parameters to use a not null parameter
					if ($provisioningHandler.UserSpecifiedParameters["Identity"] -ne $null) {
						$user = $provisioningHandler.UserSpecifiedParameters["Identity"].ToString()
					elseif ($provisioningHandler.UserSpecifiedParameters["Name"] -ne $null) {
						$user = $provisioningHandler.UserSpecifiedParameters["Name"].ToString()
					else {
						$user = $provisioningHandler.UserSpecifiedParameters["Alias"].ToString()
					# view entire forest in a multi domain environment
					Set-AdServerSettings -ViewEntireForest:$true
					# fetch domain controllers in AD site}
					$server = Get-ExchangeServer $env:computername
					$DCs = Get-DomainController | ?{$_.adsite -eq $}
					$CasMailbox = $null
					foreach($d in $DCs) {
						while($CasMailbox -eq $null) {
							# find a valid domain controller having the updated user object
							$CasMailbox = Get-CASMailbox $user -DomainController $d.dnshostname -ErrorAction SilentlyContinue
							# fetch DCs FQDN
							$WriteDC = $d.DnsHostName
					try {
						# set CAS features as needed
						Set-CasMailbox $user -ActiveSyncEnabled:$false -ImapEnabled:$false -PopEnabled:$false -MapiHttpEnabled:$false -DomainController $WriteDC -ErrorAction SilentlyContinue
					catch {}


After adding the PowerShell code to the ScriptingAgentConfig.xml file, the file needs to be distributed across all Exchange servers. For distribution of the scripting agent configuration file I personally recommend Paul Cunningham's PowerShell script.

Be aware of the fact, that the scripting agent Xml is being validated using a strict schema validation. The scripting agent Xml is case sensitive, as noted here.


Read More »
Last updated: 2018-01-16

Exchange Server 2013Exchange Server 2016Description

The community script Update-CASMailbox simplifies the process for enabling or disabling protocols for Exchange mailbox access. Active Directory security groups are used to enable or disable a protocol for the group members.


Your Active Directory contains a security group named Exchange_POP_enabled which contains all mailbox users requiring POP3 access to be enabled.

You can use the following command to have POP3 enabled for all members of the given security group.

.\Update-CAS-Mailbox.ps1 -POP -FeatureEnabled $true -GroupName Exchange_POP_enabled

The script does not disable the POP3 for all non-members, as this might not be required as all new mailboxes have POP3 disabled anyway. If there is such a requirement, just let me know.

The following protocols are currently supported:

  • POP
  • IMAP
  • Outlook on the Web (OWA)
  • ActiveSync




You need assistance with your Exchange Server setup? You have questions about your Exchange Server infrastructure and going hybrid with Office 365? You are interested in what Exchange Server 2016 has to offer for your environment?

Read More »

The new community script Get-Diskspace helps to fetch disk volume information from a single server or across multiple servers.

Currently the script supports a command line switch to gather disk volume information across all Exchange servers in your environment.

The following screenshot shows the command line output

Screenshot command line output

The following screenshot shows the html email

Screenshot html email

# Get disk information from computer MYSERVER
.\Get-Diskpace.ps1 -ComputerName MYSERVER

# Get disk information from computer MYSERVER in MB

.\Get-Diskpace.ps1 -ComputerName MYSERVER -Unit MB

# Get disk information from all Exchange servers and send html email

.\Get-Diskpace.ps1 -AllExchangeServer -SendMail -MailFrom -MailTo -MailServer





You need assistance with your Exchange Server setup? You have questions about your Exchange Server infrastructure and going hybrid? You are interested in what Exchange Server 2016 has to offer for your environment?

Contact me at
Follow at

Read More »

At a recent troubleshooting case I was wondering why the pipeline tracing target directory remained empty after enabling the Exchange 2013 CU12 transport pipeline tracing using

Set-TransportService -PipelineTracingSenderAddress -PipelineTracingEnabled $true

In this case the sender address itself was

In past scenarios the email address to trace was copied from the original message and therefore this issue never occured.

After heading down the road on why no trace messages got logged in the pipeline tracing folder, and enabling and disabling the feature several times across multiple servers, the sender address made it's way into the cmdlet via Copy&Paste. And voilá... transport started tracing messages.

Set-TransportService -PipelineTracingSenderAddress -PipelineTracingEnabled $true

The TechNet article on pipeline tracing does not state anything about the fact that the email address attribute is case sensitive.

If you want to enable or disable pipeline tracing across multiple Exchange 2013 servers, you might want to use the following one-liners:

# One liner to activate Pipeline Tracing on multiple Exchange 2013 servers in a co-ex scenario
Get-ExchangeServer | ?{$_.AdminDisplayVersion -ilike "*15*"} | Get-TransportService | Set-TransportService -PipelineTracingEnabled $true -PipelineTracingSenderAddress

# One liner to deactive Pipeline Tracing across multiple Exchange 2013 servers
Get-ExchangeServer | ?{$_.AdminDisplayVersion -ilike "*15*"} | Get-TransportService | Set-TransportService -PipelineTracingEnabled $false -PipelineTracingSenderAddress $null

Not mention that the output stored in the pipeline tracing folders might be sensitive, as all data is stored in a readable format.



You need assistance with your Exchange Server setup? You have questions about your Exchange Server infrastructure and going hybrid? You are interested in what Exchange Server 2016 has to offer for your environment?

Contact me at
Follow at

Read More »

The JET Blue Story

The Exchange Server product used the Extensible Storage Engine (ESE, aka JET Blue) to store data for decades. The story of the JET Blue (in contrast to JET Red which is used for Access database) can be read here ( In the acient days of data storage the ESE database was the best choice for storing mostly unstructured data with many dynamic properties.

The Messaging API (MAPI) had been developed in the 1990s to provide programmers with a set of unified interface for easier message exchange. The MAPI documentation at TechNet has been replaced by the current Outlook 2013 MAPI Reference. In todays world it is not easy to find reliable ressource about the original MAPI implementation. The only printed resource is Inside Mapi (Microsoft Programming Series) , ISBN 978-1572313125 , which has been published in 1996.

At Ignite 2015 Ross Smith VI joked about moving the Exchange storage engine to SQL. Back in the day with Exchange 2013 in production and Exchange 2016 coming, this was true. But Ross laid the tracks for the evolution of Exchange.

Exchange Storage Engine Joke at Ignite

But it seems that the Exchange Product Team realized that in today's world with heavily standardized communication and less dynamic requirements than in the 1990s the days of JET blue are over. At the same time SQL Server evolved to mature database solution, capable of handling big data. The question was, if it can store SharePoint data, why not Exchange data. After twenty years of Exchange Server using the good ole ESE engine it was time to move on.

The SQL scripts that are used by Exchange to configure SQL are loacted in $exbin\SQL


	[IsWellKnownProperty] [bit] NOT NULL,





Exchange Server goes SQL

The current Exchange 2016 CU2 Preview supports an undocumented registry key to activate SQL Server support for Exchange. Personally I do not know, if this was supposed to be officially included in a public realease. So maybe the SQL support was made available by error and is already removed from the most current build again.

The famous SqueakyLobster registry key in has been used in Exchange 5.5 to troubleshoot performance issues. The new "Lobster" key is used to activate hidden code in Exchange Server product. The name of the key is LobsterMapiDB.

This key activates support for Exchange modern storage. Without this key you won't be able to move mailboxes from ESE legacy storage to SQL modern storage.

It is assumed that a SQL Server 2014 instance is available. A SQL Server 2014 Express edition is sufficient for testing purposes.

Any changes to configurations or the registry should be validated in a test environment first. Never try this in production right away.

The high level steps required to activate SQL support for Exchange 2016 are:

  • Create a configuration file to provide the SQL connection string
  • Create a SQL server login for the Exchange Trusted Subsystem security group
  • Add a registry key to the local Exchange Server registry
  • Restart Information Store Service (MSExchangeIS)
  • Execute PowerShell script to migrate mailboxes to SQL

The detailed steps are:

  • Create a new config file named Microsoft.Exchange.Data.SQL.exe.config in $exinstall/bin
		<?xml version="1.0" encoding="utf-8" ?>
		<!-- Exchange SQL Configuration - preliminary support -->
		<!-- %MAILBOXDATABASENAME% will be replaced by Exchange -->
		<!-- More information -->
			<sectionGroup name="SqlMapiProviderGroup" type="Microsoft.Exchange.Data.SQL.SqlMapiProviderGroup, Microsoft.Exchange.Data.Common, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
				<section name="SqlMapiProviderSection" type="Microsoft.Exchange.Data.SQL.SqlMapiProviderGroup, Microsoft.Exchange.Data.Common, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
				<gcServer enabled="True" />
				<generatePublisherEvidence="False" />
				<add key="MigrateMailboxesAutomatically" value="false" /> <!-- Not yet supported -->
				<add key="AllowJETBlueCoexistence" value="true" /> <!-- Allows for SQL/ESE Coexistence in DAG -->
				<add key="PerDatabaseMaxSize" value="1GB" />
				<add key="VerboseLoggingEnabled" value="False" />
					<add name="LobsterMapiDB"
					connectionString="Data Source=SERVERNAME\INSTANCE;Initial Catalog=%MAILBOXDATABASENAME%;Integrated Security=True;MultipleActiveResultSets=True" />
  • Create a SQL login for Exchange Trusted Subsystem
  • Create a new DWORD named LobsterMapiDB in
    and set the data value to 1

    LobsterMapiDB RegistryKey
  • Restart Exchange Information Store

    Restart-Service MSExchangeIS
  • Use Move-MailboxToModernStorage.ps1 script to move selected mailboxes to modern storage

More can be found here:



Enjoy Exchange for the next 20 years...

Read More »