Home > Exchange Server > Using Cmdlet Extension Agents to Cause Automatic Events to Occur in Exchange 2010 – Life Just Got Simpler!

Using Cmdlet Extension Agents to Cause Automatic Events to Occur in Exchange 2010 – Life Just Got Simpler!

In previous posts, I’ve shown how to automate some tasks like automatically sending a ‘welcome’ email to all new user accounts, automatically applying Messaging Records Management policies to new users, and other functions. This helps perform normally routine tasks, as well as providing for a level of interaction with the users that isn’t as readily available manually. It also helps reduce errors and maintain consistency.

The problem with these methods is that they rely on scheduled tasks. So, every four hours, send a message to the new users. That’s all fine and dandy when the account is provisioned for a user starting tomorrow or next week. But if the HR person is standing at your desk saying “the person is starting NOW”, there’s a gap. Additionally, as the environment gets bigger and bigger, the queries to find new users can take more time and more system resources. There must be a better way, and there IS!

A nearly undocumented Exchange 2010 feature called the cmdlet extension agents helps perform tasks automatically based on other commands running. Other Exchange notables have talked about some of the features, including Administrator Audit Logging. There are also cmdlet extension agents for OAB Resource Management, Provisioning Policy, Mailbox Resources Management, RUS, and even a Query Base DN agent. These can be seen by using the Get-CmdletExtensionAgent cmdlet. Today, we’ll focus on the Scripting Agent.

Picture this: a new user mailbox is created from ANY method that calls the new-mailbox cmdlet. This could be Exchange Management Console, Exchange Management Shell, or even some custom provisioning application. As soon as that cmdlet succeeds, the server immediately fires another event – say, sending the ‘welcome’ email. Or disabling POP3 and IMAP on the mailbox; or assigning an Exchange ActiveSync or retention policy. Very cool. But it gets even cooler when you realize that you can also trigger events BEFORE the cmdlet actually does much. Imagine, everytime the remove-mailbox cmdlet is run, an export-mailbox cmdlet is triggered and dumps the mailbox to .pst! It’s possible! Here’s how we can do thngs:

Open Notepad and paste the following:

<?xml version="1.0" encoding="utf-8" ?>
<Configuration version="1.0">
 <Feature Name="MailboxProvisioning" Cmdlets="new-mailbox">
  <ApiCall Name="OnComplete">
   if($succeeded)    {
    $newmailbox = $provisioningHandler.UserSpecifiedParameters["Name"]
    set-casmailbox $newmailbox -ImapEnabled $false
   }
  </ApiCall>
 </Feature>
</Configuration>

Save the file as ScriptingAgentConfig.xml in the \bin\CmdletExtensionAgents folder, which, by default is C:\Program Files\Microsoft\Exchange Server\V14\Bin\CmdletExtensionAgents

Let’s break down how this works. In our code, the highlighted sections are where we need to focus, Notice this line: <Feature Name=”MailboxProvisioning” Cmdlets=”new-mailbox”>. The “cmdlets” parameter dictates what cmdlets will fire our custom event. In this case, whenever new-mailbox is executed. We can define multiple cmdlets here, separating them with a comma.The very next line, <ApiCall Name=”OnComplete”> defines when during the new-mailbox process our custom event will fire. Here, it’s OnComplete, or after the new-mailbox command finishes. We can also use “validate”, to trigger an event after Exchange determines new-mailbox is a valid command and has all of the info it needs. “validate” is where we could export a mailbox during remove-mailbox, as it occurs after validation, but before the mailbox is actually removed.

For the sake of keeping things simple here, we’ll disable IMAP on a new mailbox as an example. In order to disable the IMAP feature, we normally run the set-casmailbox cmdlet (or use EMC, which just calls set-casmailbox), which requires a mailbox name. So, we use $newmailbox = $provisioningHandler.UserSpecifiedParameters["Name"] to assign the mailbox name used in the new-mailbox cmdlet to a variable, $newmailbox. After that, we simply run set-casmailbox $newmailbox -ImapEnabled $false like we would from EMS or a .ps1 script. That’s it!

Before we try out our new found feature, we need to enable the Cmdlet Extension Agent. Open an Exchange Management Shell window and use the following command:

Enable-CmdletExtensionAgent "Scripting Agent"

Now – using either EMS or EMC, create a new mailbox. Once it’s created, look at the Mailbox Features tab of the mailbox, and you’ll see that IMAP is disabled:

You can certainly perform multiple actions, if you’d like, by simply specifying each command on a new line. If you’d like to take complex actions after new-mailbox (or any cmdlet), you can also call an external script by placing the path and script name in the .xml file.

$newmailbox = $provisioningHandler.UserSpecifiedParameters["Name"]
c:\myscript.ps1 -name $newmailbox

Keep in mind that you can’t do things like write-host or dump other outputs to the console screen. If you’d like to look at some other samples, look at the ScriptingAgentConfig.sample file in the \CmdletExtensionAgents folder in Notepad.

As you can see, the possibilities here are endless. We can automate many tasks, and, with the power of PowerShell, perform many actions not available in the Exchange Management Console.

Caveats: If you have more than one server running Exchange 2010, you’ll need to enable the extension agent on each, and create the .xml file.

  1. jp
    January 31, 2012 at 6:13 am | #1

    Sir, I followed your steps but encontered the followint issue once creat a new mailbox account. Pls help , thanks in advance.

    摘要: 1 Project。1 FInished,0 failed。
    Time: 00:00:01

    qw1 Completed

    Alarm:
    具备索引 5 的 cmdlet 扩展代理在 OnComplete() 中引发了异常。异常情况为: Microsoft.Exchange.Provisioning.ProvisioningException: ScriptingAgent: 对 OnComplete API 调用 Scriptlet 时引发了异常: 由于在’JT-AD-CASHUB02.nanshan.com.cn’上找不到对象’qw1’,因此无法执行该操作。。 —> Microsoft.Exchange.Configuration.Tasks.ManagementObjectNotFoundException: 由于在’JT-AD-CASHUB02.nanshan.com.cn’上找不到对象’qw1’,因此无法执行该操作。
    在 Microsoft.Exchange.Configuration.Tasks.DataAccessTask`1.GetDataObject[TObject](IIdentityParameter id, IConfigDataProvider session, ObjectId rootID, OptionalIdentityData optionalData, Nullable`1 notFoundError, Nullable`1 multipleFoundError, ExchangeErrorCategory errorCategory)
    在 Microsoft.Exchange.Configuration.Tasks.RecipientTaskHelper.ResolveDataObject[TDataObject](IConfigDataProvider dataSession, IConfigDataProvider globalCatalogSession, ADServerSettings serverSettings, IIdentityParameter identity, ObjectId rootId, OptionalIdentityData optionalData, String domainController, CategorizedGetDataObjectDelegate getDataObjectHandler, TaskVerboseLoggingDelegate logHandler, ErrorLoggerDelegate errorHandler)
    在 Microsoft.Exchange.Configuration.Tasks.SetRecipientObjectTask`3.ResolveDataObject()
    在 Microsoft.Exchange.Management.RecipientTasks.SetCASMailboxBase`2.ResolveDataObject()
    在 Microsoft.Exchange.Management.RecipientTasks.SetCASMailbox.ResolveDataObject()
    在 Microsoft.Exchange.Configuration.Tasks.SetObjectWithIdentityTaskBase`3.PrepareDataObject()
    在 Microsoft.Exchange.Configuration.Tasks.SetRecipientObjectTask`3.PrepareDataObject()
    在 Microsoft.Exchange.Configuration.Tasks.SetTaskBase`1.InternalValidate()
    在 Microsoft.Exchange.Configuration.Tasks.SetObjectWithIdentityTaskBase`3.InternalValidate()
    在 Microsoft.Exchange.Configuration.Tasks.SetRecipientObjectTask`3.InternalValidate()
    在 Microsoft.Exchange.Management.RecipientTasks.SetCASMailboxBase`2.InternalValidate()
    在 Microsoft.Exchange.Management.RecipientTasks.SetCASMailbox.InternalValidate()
    在 Microsoft.Exchange.Configuration.Tasks.Task.ProcessRecord()
    — 内部异常堆栈跟踪的结尾 —
    在 Microsoft.Exchange.ProvisioningAgent.ScriptingAgentHandler.OnComplete(Boolean succeeded, Exception e)
    在 Microsoft.Exchange.Provisioning.ProvisioningLayer.OnComplete(Task task, Boolean succeeded, Exception exception)

    已完成的 Exchange 命令行管理程序命令:
    New-Mailbox -Name ‘qw1′ -Alias ‘qw1′ -UserPrincipalName ‘qw1@nanshan.com.cn’ -SamAccountName ‘qw1′ -FirstName ” -Initials ” -LastName ” -Password ‘System.Security.SecureString’ -ResetPasswordOnNextLogon $false

    已用时间: 00:00:01

  2. Aravind
    March 19, 2012 at 5:01 am | #2

    Hi All,

    Just a small comment.

    When you copy the above lines from the URL….

    ==> is causing some issues @ my Exchange servers.
    Receiving a crash with the Event-ID: 4999
    Watson report about to be sent for process id: 4600, with parameters: E12, c-RTL-AMD64, 14.02.0247.005, RemotePowershell, M.Exchange.ProvisioningAgent, M.E.P.ScriptingAgentConfiguration.Initialize, System.NullReferenceException, e29, 14.02.0247.005.
    ErrorReportingEnabled: False

    Solution:
    Remove the blank-space in the XMl file

    The contents should be:

    Cheers
    Aravind

    • patk7
      December 6, 2012 at 3:33 pm | #3

      I had the same error on my servers. My solution was the following.

      I started from the ScriptingAgentConfig.xml.sample file, copy/pasted the FILE into ScriptingAgentconfig.xml (the FILE, not the contents!). I then deleted sections i did not need, and modified what I needed. No more Watson error!

      Pat

  3. Ayanes
    July 3, 2014 at 2:45 pm | #4

    I added the following to my xml file so that the new welcome message is sent when mailbox is created:

    if($succeeded) {
    $newmailbox = $provisioningHandler.UserSpecifiedParameters["Name"]
    C:\Scripts\WelcomeMsg\Working-New-WelcomeEmail.ps1 -name $newmailbox
    }

    However the mailbox is created but I get the following warning:
    arning:
    The cmdlet extension agent with the index 5 has thrown an exception in OnComplete(). The exception is: Microsoft.Exchange.Provisioning.ProvisioningException: ScriptingAgent: Exception thrown while invoking scriptlet for OnComplete API: A parameter cannot be found that matches parameter name ‘name’.. —> System.Management.Automation.ParameterBindingException: A parameter cannot be found that matches parameter name ‘name’.
    at System.Management.Automation.CmdletParameterBinderController.VerifyArgumentsProcessed(ParameterBindingException originalBindingException)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
    at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
    at System.Management.Automation.CommandProcessor.BindCommandLineParameters(CommandParameterInternal[] parameters)
    at System.Management.Automation.CommandProcessor.Prepare(CommandParameterInternal[] parameters)
    at System.Management.Automation.CommandProcessorBase.DoPrepare(CommandParameterInternal[] parameters)
    at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
    at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input, Hashtable errorResults, Boolean enumerate)
    — End of inner exception stack trace —
    at Microsoft.Exchange.ProvisioningAgent.ScriptingAgentHandler.OnComplete(Boolean succeeded, Exception e)
    at Microsoft.Exchange.Provisioning.ProvisioningLayer.OnComplete(Task task, Boolean succeeded, Exception exception)

  1. July 31, 2012 at 9:30 pm | #1
  2. August 15, 2012 at 7:23 am | #2
  3. October 25, 2012 at 9:54 am | #3
  4. April 11, 2013 at 12:36 pm | #4
  5. January 16, 2014 at 10:45 am | #5
  6. April 17, 2014 at 11:56 am | #6

Leave a Reply