Wednesday, 5 March 2014

Exchange 2010/2013 mailbox quota automation

If you need to uniformly manage your Exchange 2010/2013 user quotas, you can set the database level quota defaults and these values would be used on all mailboxes residing in that database. And if you need more quota levels, for example "Normal users", "Power users", "VIP users", etc. you could create multiple databases and place users in the correct database. If you need to change a single user quota you would have to move user from one database to another which is not always the best solution. The other approach is to set per user quotas and have all types of users spread across all databases. It can be too cumbersome to manually set quotas for each new user or when quota level for an existing user changes so I created a way to automate this process which leverages Exchange Scripting Agent cmdlet extension.

The idea is to automatically set quota values based on a single property passed to the Set-Mailbox cmdlet. Let's pick CustomAttribute10 for this purpose. If you type something like:

Set-Mailbox "Jon Doe" -CustomAtrribute10 "Quota-PowerUsers"

Exchange Scripting Agent cmdlet extension would read that value and automatically set the following properties for that mailbox:
IssueWarningQuota=2000MB
ProhibitSendQuota=2100MB
ProhibitSendReceiveQuota=2200MB

Additionaly it would set UseDatabaseQuotaDefaults=$false to ignore database level quota values.

To set the quota for multiple mailboxes you could use something like this:

Import-Csv .\users.csv | foreach{Get-Mailbox $_.Alias | Set-Mailbox -CustomAttribute10 "Quota-VIPUsers"}

The good thing about using the Scripting Agent is that if you use Exchange Administration Console to set the CustomAttribute10 property, it would still run the cmdlet extension and execute the custom logic built into the script. If you have a helpdesk person not comfortable with Powershell you can instruct him to use the GUI instead.

So, here is a Scripting Agent cmdlet extension that leverages UpdateAffectedIConfigurable API call that reads the properties set by an administrator that called the Set-Mailbox cmdlet with CustomAttribute10 parameter and then fills in the additional Set-Mailbox properties that set the quota values based on the administrator input. After the UpdateAffectedIConfigurable API call is finished, the Validate API is called to check whether administrator has entered the correct quota string in CustomAttribute10 property. If the input is not correct it cancels the execution of Set-Mailbox.

You need to save this script in ScriptingAgentConfig.xml and copy it to the <installation path>\V15\Bin\CmdletExtensionAgents folder on every Exchange 2013 server in your organization. I haven't tested the script on Exchange 2010 but everything should work the same.

<?xml version="1.0" encoding="utf-8" ?>

<Configuration version="1.0">


    <Feature Name="MailboxQuotaProvisioning" Cmdlets="set-mailbox">
        <ApiCall Name="UpdateAffectedIConfigurable">
            #parameter list:
            #param([ProvisioningHandler]$provisioningHandler, [IConfigurable]$writableIConfigurable)

            #WriteToLogFile($provisioningHandler.UserSpecifiedParameters["CustomAttribute10"]);

            if($provisioningHandler.UserSpecifiedParameters["CustomAttribute10"] -eq $null)
            {
                #Administrator did not set the quota, exit
                #WriteToLogFile("The administrator did not set the quota");
                return $true;
            }
           
            #WriteToLogFile($provisioningHandler.TaskName);
           
            $quota = $provisioningHandler.UserSpecifiedParameters["CustomAttribute10"];
            if($quota -eq "Quota-PowerUsers"){
                $writableIConfigurable.UseDatabaseQuotaDefaults = $false;
                $writableIConfigurable.IssueWarningQuota = "5600MB";
                $writableIConfigurable.ProhibitSendQuota = "6000MB";
                $writableIConfigurable.ProhibitSendReceiveQuota = "6100MB";
            }
            elseif($quota -eq "Quota-VIPUsers"){
                $writableIConfigurable.UseDatabaseQuotaDefaults = $false;
                $writableIConfigurable.IssueWarningQuota = "8600MB";
                $writableIConfigurable.ProhibitSendQuota = "9000MB";
                $writableIConfigurable.ProhibitSendReceiveQuota = "9100MB";
            }
            elseif($quota -eq "Quota-VIPUsersUnlimited"){
                $writableIConfigurable.UseDatabaseQuotaDefaults = $false;
                $writableIConfigurable.IssueWarningQuota = "unlimited";
                $writableIConfigurable.ProhibitSendQuota = "unlimited";
                $writableIConfigurable.ProhibitSendReceiveQuota = "unlimited";
            }
            elseif($quota -eq "Quota-NormalUsers"){
                $writableIConfigurable.UseDatabaseQuotaDefaults = $true;
                $writableIConfigurable.IssueWarningQuota = "unlimited";
                $writableIConfigurable.ProhibitSendQuota = "unlimited";
                $writableIConfigurable.ProhibitSendReceiveQuota = "unlimited";
                $writableIConfigurable.CustomAttribute10 = $null;
            }

            return $true;

        </ApiCall>

        <ApiCall Name="Validate">
            #parameter list:
            #param([ProvisioningHandler]$provisioningHandler, [IConfigurable]$readOnlyIConfigurable)

            if($provisioningHandler.UserSpecifiedParameters["CustomAttribute10"] -eq $null)
            {
                return $null;
            }

            $quotaArray = "Quota-PowerUsers","Quota-VIPUsers","Quota-VIPUsersUnlimited","Quota-NormalUsers"

            $quota = $provisioningHandler.UserSpecifiedParameters["CustomAttribute10"];
            if($quotaArray -notcontains $quota){
                  New-Object -type Microsoft.Exchange.Data.ProvisioningValidationError -argumentlist ("Quota string not recognized. Must be one of $quotaArray", [Microsoft.Exchange.Data.ExchangeErrorCategory]::Client )               
            }

        </ApiCall>

    </Feature>

    <Common>
    function WriteToLogFile
    {
        param([string]$stringToWrite)
        Add-Content c:\Handler.log $stringToWrite
    }      
  
    </Common>

</Configuration>