Home > Uncategorized > Load Balance your Exchange Mailboxes across Databases on one or multiple servers.

Load Balance your Exchange Mailboxes across Databases on one or multiple servers.

A while ago i wrote a script to distribute Exchange mailboxes evenly across different mailbox databases on one server.

Recently, that customer installed an additional mailbox server.  So the script needed an update to distribute the mailboxes evenly across multiple servers.

Here is the explanation of the script:

Configuration/Installation
To install the application, just place the script and the config file in a folder of you choice.
You can run the script in foreground or schedule it.  To schedule it, create a task using the following command line:
PowerShell.exe -PSConsoleFile “C:\Program Files\Microsoft\Exchange Server\Bin\exshell.psc1” -Command “C:\DBBalancer\DBBalancer.ps1”

Make sure your account is at least an Exchange Server Administrator on the Mailbox Server.

The following values must be changed to reflect your environment
– $Global:intThresholdInGB:  The script will start to balance you databases if the difference between the biggest DB and the smallest DB exceeds this value.  This should be set to something like 5 GB.
– $Global:strMailboxServers:  The MailboxServers to balance.  The script can be run from every machine with Exchange 2007 Management Tools installed on, so it needs to know which Mailbox Server it needs to connect to.  The script will balance all DB’s accross all the mailbox servers in this list..

Processing
The script will move a RANDOM mailbox from the Biggest DB to the Smallest DB until all DB are within the Threshold limit.  So if you
set the Threshold to 5 GB, all DB’s will be equally loaded with a maximum difference of 5 GB.
Only one mailbox at a time will be moved to minimize user downtime.

Logging
You can follow the progress of the script in the Application Log.  Look for events of Source DBBalancer.
Every mailbox move will be listed in the eventviewer.

When all Mailbox Databases are balanced, the following will be displayed in the Event Viewer:
All Mailbox databases are balanced within the Threshold limit.

You can download the files or copy-paste them from below.

Download

Script file:

# DBBalancer Version 1.3

# Written by BoerLowie (sammybog<at>gmail.com)

#

# This script is used to maintain a balance between your different Exchange 2007 Mailbox Databases on different servers.

#

# Configuration/Installation

# --------------------------

#

# To install the application, just place the script and the config file in a folder of you choice.

#

# You can run the script in foreground or schedule it.  To schedule it, create a task using the following command line:

#

# PowerShell.exe -PSConsoleFile "C:\Program Files\Microsoft\Exchange Server\Bin\exshell.psc1" -Command "C:\DBBalancer\DBBalancer.ps1"

#

# Make sure your account is at least an Exchange Server Administrator on the Mailbox Server.

#

# The following values must be changed to reflect your environment

#  - $Global:intThresholdInGB:  The script will start to balance you databases if the difference between the biggest

#                               DB and the smallest DB exceeds this value.  This should be set to something like 5 GB.

#  - $Global:strMailboxServers:  The MailboxServers to balance.  The script can be run from every machine with Exchange 2007 Management Tools 

#                                installed on, so it needs to know which Mailbox Server it needs to connect to.  

#                                The script will balance all DB's accross all the mailbox servers in this list..

#

# Processing

# ---------- 

#

# The script will move a RANDOM mailbox from the Biggest DB to the Smallest DB until all DB are within the Threshold limit.  So if you

# set the Threshold to 5 GB, all DB's will be equally loaded with a maximum difference of 5 GB.

# Only one mailbox at a time will be moved to minimize user downtime.

#

# Logging

# -------

#

# You can follow the progress of the script in the Application Log.  Look for events of Source DBBalancer.

# Every mailbox move will be listed in the eventviewer.

#

# When all Mailbox Databases are balanced, the following will be displayed in the Event Viewer:

#  All Mailbox databases are balanced within the Threshold limit.

 

$ErrorActionPreference = "SilentlyContinue"

 

# CHANGE THESE VARIABLES TO REFLECT YOUR ENVIRONMENT

$Global:strMailboxServers = @("MailboxServer1", "MailboxServer2", "MailboxServer3")

$Global:intThresholdInGB = 5

 

$Global:strBiggestDB = ""

$Global:strSmallestDB = ""

$Global:blnExecute = $True

 

 

Function Start-Init

{

    $Global:objEventLog = New-Object System.Diagnostics.EventLog("Application")

    $Global:objEventLog.MachineName = "."

    $Global:objEventLog.Source = "DBBalancer"

    $Global:objEventLog.WriteEntry("Application Started." + [System.Environment]::NewLine + [System.Environment]::NewLine + `

        "Mailbox Servers :" + [System.String]::Join(",", $Global:strMailboxServers) + [System.Environment]::NewLine + `

        "Threshold in GB :" + $Global:intThresholdInGB)

}

 

Function Check-Prerequisites

{

    [System.Int] $lintCount = 0

    Foreach ($lobjMailboxServer in $Global:strMailboxServers)

    {

        $lintCount += (Get-MailboxDatabase -Server $lobjMailboxServer | Measure-Object).Count

    }

    If ($lintCount  -gt 1)

    {

        Return $true

    }

    Else

    {

        Return $false

    }

}

 

Function Get-BiggestandSmallestDB

{

    Process

    {

        $DBs = Get-MailboxDatabase | Where-Object {$Global:strMailboxServers -contains $_.Server}

        $blnFirstRun = $true

        foreach ($DB in $DBs)

        {

            $lonDBSize = (Get-MailboxStatistics -Database $DB.Identity | Measure-Object -Property TotalItemSize -Sum).Sum

            If ($blnFirstRun -eq $true)

            {

                $Global:strBiggestDB = $DB.ServerName + "\" + $DB.Name + ";" + $lonDBSize

                $Global:strSmallestDB = $DB.ServerName + "\" + $DB.Name + ";" + $lonDBSize

                $blnFirstRun = $false

            }

            Else

            {

                If ($lonDBSize -lt $strSmallestDB.Split(";")[1])

                {

                    $Global:strSmallestDB = $DB.ServerName + "\" + $DB.Name + ";" + $lonDBSize

                }

                If ($lonDBSize -gt $strBiggestDB.Split(";")[1])

                {

                    $Global:strBiggestDB = $DB.ServerName + "\" + $DB.Name + ";" + $lonDBSize

                }

            }

        }

    }

}

 

Function Check-Threshold

{

    If (([Long]$Global:strBiggestDB.Split(";")[1] - [Long]$Global:strSmallestDB.Split(";")[1]) -gt ($Global:intThresholdInGB * 1000000000))

    {

        Return $True

    }

    Else

    {

        Return $False

    }

}

 

Function Do-MainLoop

{

    While ($Global:blnExecute -eq $True)

    {

        Get-BiggestandSmallestDB

        If (Check-Threshold -eq $True)

        {

            [Long]$lonDataToMove = ([Long]$Global:strBiggestDB.Split(";")[1] - [Long]$Global:strSmallestDB.Split(";")[1]) / 2

            $Mailboxes = Get-Mailbox -Database $Global:strBiggestDB.Split(";")[0] -ResultSize "Unlimited"

            $intRandomMailbox = New-Object System.Random

            $intRandomMailbox = $intRandomMailbox.Next(0, ($Mailboxes.Count - 1))

            $Global:objEventLog.WriteEntry("Moving " + $Mailboxes[$intRandomMailbox] + " from database " + $Global:strBiggestDB.Split(";")[0] + `

                " to database " + $Global:strSmallestDB.Split(";")[0] + "." + [System.Environment]::NewLine + [System.Environment]::NewLine + `

                "The mailbox has a size of " + ((Get-MailboxStatistics -Identity $Mailboxes[$intRandomMailbox]).TotalItemSize) + "ytes.")

            Move-Mailbox -Identity $Mailboxes[$intRandomMailbox] -TargetDatabase $Global:strSmallestDB.Split(";")[0] -Confirm:$false    

        }

        Else

        {

            $Global:blnExecute = $False            

        }

    }

    Return $True

}

 

Start-Init

If (Check-Prerequisites -eq $true)

{

    If (Do-MainLoop -eq $True)

    {

        $Global:objEventLog.WriteEntry("All Mailbox databases are balanced within the Threshold limit.")

    }

}

Else

{

    $Global:objEventLog.WriteEntry("Prerequisites check failed.  You probably have only 1 Mailbox Database and therefor we can't move mailboxes around.")

}

Advertisements
Categories: Uncategorized
  1. Mohamed Ghouse
    October 14, 2011 at 20:29

    Hi,

    This document is great to solve lots of issue. Please let me know if this script can also be used for Exchange 2010 servers in DA. Because the architecture is completely different when compared to 2007.

    If not, please share the script with required changes. Thanks a lot.

    • October 19, 2011 at 07:30

      Hi Mohamed,

      to be honest i haven’t tried it on Exchange 2010 yet. I’ll see if i have some spare time to test it on 2010.

      gr

      • Sean T
        February 7, 2012 at 18:05

        Mostly looks like it will work with Exchange 2010 though there are a couple of things you can use instead in 2010. As an example you now have the attribute DatabaseSize for the Databases rather than ‘Summing’ the TotalItemSize, and instead of Move-Mailbox it’s New-MoveRequest – otherwise at first glance it looks like it should work.

        Personally I’d write the output of the moves to a Log file so you can keep a copy of them in case of restores rather than the Application Log (as that may well be set to Overwrite as needed) – and remember that when you move mailboxes you’re going to have to do a Full Database Backup too, so double check your backup strategy before kicking this to run every night šŸ™‚

        Otherwise it’s a nice script, great work

  2. M. Khavari
    December 30, 2011 at 00:58

    Thank you for the great document. Is there a way we can exclude databases from the load balancing mailboxes. I know you can disable automatic load balancing in Exchange but I am not sure if we can exclude databases in the script from this action.

    • December 30, 2011 at 09:38

      Sure, that shouldn’t be too hard.

      In the function Get-BiggestandSmallestDB, you have the following line:

      $DBs = Get-MailboxDatabase | Where-Object {$Global:strMailboxServers -contains $_.Server}

      This fetches all your mailbox databases on that mailbox server. Change this line to something like this to exclude a DB from it:

      $DBs = Get-MailboxDatabase | Where-Object {($Global:strMailboxServers -contains $_.Server) -and ($_.Name -ne “ExcludeDB”)}

      Replace “ExcludeDB” by the name of the DB to exclude. Ideally, use a variable that you fill in at the top of the script, this makes it easier to adapt it afterwards.

      gr

      • MAS
        September 11, 2012 at 11:25

        Hi, Can I exclude multiple database?

      • September 11, 2012 at 12:01

        Should be easy to accomplish.

        To do it quick and dirty: change every “Get-MailboxDatabase” cmdlet to exclude them. For example by using a “Where-Object {$_.Name -ne ‘…’}”.

  3. December 6, 2012 at 22:04

    Great looking script! Is there a way to only run it during a certain time period? Say a 6-8 hour time frame?

    • December 14, 2012 at 21:20

      Shouldn’t be that difficult.

      Store the time in a variable when the script starts:

      $dtNow = [DateTime]::Now

      Now before you start moving a mailbox, check if the current time is bigger than the starttime + 2 hours:

      If ([DateTime]::Now -gt $dtNow.AddHours(2))
      {
      Continue mailbox move
      }
      Else
      {
      End script
      }

      That should do it!

      ps: don’t try to ‘solve’ this by terminating the script after two hours in the Scheduled tasks properties, because that will give you a partially moved mailbox in the other database. Exchange won’t have an actual problem with it, but it will result in wasted storage…

  4. Ken
    September 20, 2013 at 16:37

    This is exactly the script I am looking for, however, I need it for Exchange 2010. Has anyone updated this to use with Exchange 2010?

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: