Balancing the number of mailboxes across Exchange 2010 and 2007 databases

Introduction

In Exchange 2010, you now have the option to allow mailboxes to be automatically distributed across databases. However, the algorithm used simply randomly allocates the new mailbox to your chosen databases – rather than ensuring the mailbox count is balanced, and doesn’t do anything about re-distributing mailboxes if you add new databases.

To help with this, and of course to help with any situation where you want to balance the number of mailboxes across a set of databases, I’ve written a simple script that help with moving mailboxes to balance out your databases.

Using Generate-DBBalanceScript.ps1

There’s nothing too complicated about the script- it doesn’t balance based on mailbox size (a future version), but simply creates a script with Move-Mailbox or New-MoveRequest  commands that once complete, balances based on mailbox counts across the databases. You pass it the results of a Get-MailboxDatabase command, along with an output file that will contain the Mailbox move commands.

First of all, lets see it in action:

Of course, my example was the simplest – across all databases. Here’s a few examples including the one above, and some others that show how to drill down to databases on specific servers:

Example One – Generate a move file based on all Exchange 2010 Databases:

1
.\Generate-BalanceMoveRequests.ps1 -DBs (Get-MailboxDatabase) -OutputPowershellFile .\moves.ps1

Example Two – Generate a move file based on  Exchange 2010 Databases located on a single server “servername”:

1
2
$o=Get-MailboxDatabase -Server servername
.\Generate-BalanceMoveRequests.ps1 -DBs $o -OutputPowershellFile moves07.ps1 -Exchange2010:$false

Example Three – Generate a move file based on  Exchange 2007 Databases located on two servers, “serverone” and “servertwo”:

1
2
$o=Get-MailboxDatabase | where {$_.Server -eq "serverone" -or $_.Server -eq "servertwo"}
.\Generate-BalanceMoveRequests.ps1 -DBs $o -OutputPowershellFile moves07.ps1 -Exchange2010:$false
Download Generate-DBBalanceScript.ps1

You can download Generate-DBBalanceScript.ps1 here or view the script below.

Hope this helps!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<#
    .SYNOPSIS
    Generates a Powershell file containing Mailbox Move Cmdlets to help balance mailbox databases, based on mailbox count, not size.
    .DESCRIPTION
    Using a list of Databases, works out the average number of mailboxes there should be per DB, then generates move commands to run seperately.
   
    By Steve Goodman
    .PARAMETER DBs
    Databases to use
    The result of a Get-MailboxDatabase cmdlet. Eg. (Get-MailboxDatabase -Server name)
    .PARAMETER OutputPowershellFile
    The filename to write, for example C:\output.ps1 or .\output.ps1
    .PARAMETER Exchange2010
    By default, true. Set to $false to generate Exchagne2007 move-mailbox commands
    .EXAMPLE
    Generate a move file based on all Exchange 2010 Databases
     .\Generate-BalanceMoveRequests.ps1 -DBs (Get-MailboxDatabase) -OutputPowershellFile .\moves.ps1
    .EXAMPLE
    Generate a move file based on  Exchange 2007 Databases located on a single server "servername"
    $o=Get-MailboxDatabase -Server servername
    .\Generate-BalanceMoveRequests.ps1 -DBs $o -OutputPowershellFile moves07.ps1 -Exchange2010:$false
    .EXAMPLE
    Generate a move file based on  Exchange 2007 Databases located on two servers, "serverone" and "servertwo"
    $o=Get-MailboxDatabase | where {$_.Server -eq "serverone" -or $_.Server -eq "servertwo"}
    .\Generate-BalanceMoveRequests.ps1 -DBs $o -OutputPowershellFile moves07.ps1 -Exchange2010:$false
   
    #>

param(
    [parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false,HelpMessage="Mailbox database object")][array]$DBs,
    [parameter(Position=1,Mandatory=$true,ValueFromPipeline=$false,HelpMessage="Filename for output Powershell script")][string]$OutputPowershellFile,
    [parameter(Position=2,Mandatory=$false,ValueFromPipeline=$false,HelpMessage="Is this for Exchange 2010")][bool]$Exchange2010=$true
    )

# Check for Exchange cmdlets
if (!(Get-Command Get-MailboxDatabase -ErrorAction SilentlyContinue))
{
    throw "Exchange Cmdlets not available";
}
# Check input object
if ($DBs[0].GetType().Name -ne "MailboxDatabase")
{
    throw "Object is not an array of mailbox databases"
}
if ($DBs.Count -eq 1)
{
    throw "You can't balance a single database";
}

# Check file does not exist
if ((Test-Path $OutputPowershellFile))
{
    throw "File $($OutputPowershellFile) already exists";
}

# Initialise file
"# Mailbox Mail Powershell Script File, generated $(date)`r`n"|Out-File -FilePath $OutputPowershellFile -NoClobber
if (!(Test-Path $OutputPowershellFile))
{
    throw "Could not write to $($OutputPowershellFile)";
}

# Initialise Variables
[int]$TotalMailboxes=0; # Total Mailboxes
[int]$BalancedCount=0; # Balanced Number of Mailboxes per DB
[array]$DBCounters=@(); # Array with DB Id, Mailbox Count and Mailbox listing
[array]$UA_DBCounters=@(); # Under allocated list from above, populated later
[array]$OA_DBCounters=@(); # Over allocated list from above, populated later
[array]$PA_DBCounters=@(); # Perfectly allocated list from above, populated later
[string]$Output=""; # Variable to write output text to

# Gather initial mailbox counts
Write-Host "Gathering Mailbox Counts"
for ($i = 0; $i -lt $DBs.Count; $i++)
{
    Write-Progress -activity "Gathering Mailbox Counts" -status "Processing Database $($DBs[$i].Identity)" -PercentComplete (($i / $DBs.Count)  * 100)
    $Mailboxes = (Get-Mailbox  -Database $DBs[$i].Identity -ResultSize Unlimited | select Identity)
    $DBCounters=$DBCounters+1;
    $DBCounters[$DBCounters.Count-1] = New-Object Object
    $DBCounters[$DBCounters.Count-1] | Add-Member NoteProperty Database $DBs[$i].Identity    
    $DBCounters[$DBCounters.Count-1] | Add-Member NoteProperty Total $Mailboxes.Count
    $DBCounters[$DBCounters.Count-1] | Add-Member NoteProperty Mailboxes $Mailboxes
    $TotalMailboxes+=$Mailboxes.Count
}
Write-Progress -Activity "Gathering Mailbox Counts" -Completed -Status "Completed"
Write-Host "DB Counts are as follows:"
$DBCounters|Select Database,Total
$BalancedCount = $TotalMailboxes / $DBs.Count
Write-Host "Found $($TotalMailboxes) mailboxes across $($DBs.Count) databases. Aiming for $($BalancedCount) mailboxes per database."

Write-Host "Sorting Databases into over, under and perfectly allocated."
# Sort DBs into under-allocated, over-allocated and perfectly allocated
for ($i = 0; $i -lt $DBCounters.Count; $i++)
{
    Write-Progress -activity "Sorting Databases into over, under and perfectly allocated" -status "Processing Database $($DBCounters[$i].Database)" -PercentComplete (($i / $DBCounters.Count)  * 100)
    if ($DBCounters[$i].Total -lt $BalancedCount)
    {
        $UA_DBCounters=$UA_DBCounters+1;
        $UA_DBCounters[$UA_DBCounters.Count-1] = $DBCounters[$i];
    } elseif ($DBCounters[$i].Total -gt $BalancedCount) {
        $OA_DBCounters=$OA_DBCounters+1;
        $OA_DBCounters[$OA_DBCounters.Count-1] = $DBCounters[$i];
    } else {
        $PA_DBCounters=$PA_DBCounters+1;
        $PA_DBCounters[$PA_DBCounters.Count-1] = $DBCounters[$i];
    }
}
Write-Progress -activity "Sorting Databases into over, under and perfectly allocated" -Completed -Status "Completed"

Write-Host "Found $($OA_DBCounters.Count) over, $($UA_DBCounters.Count) under and $($PA_DBCounters.Count) perfectly allocated";
# Make move list
Write-Host "Generating Powershell File '$($OutputPowershellFile)' to Balance DBs."
for ($i = 0; $i -lt $OA_DBCounters.Count; $i++)
{
    Write-Progress -activity "Generating Powershell File '$($OutputPowershellFile)' to Balance DBs" -status "Processing Database $($OA_DBCounters[$i].Database)" -PercentComplete (($i / $OA_DBCounters.Count)  * 100)
    # Get the mailbox list
    [array]$OA_Mailboxes = $OA_DBCounters[$i].Mailboxes
    $UA_DBPointer=0;
    for ($j=$BalancedCount; $j -lt $OA_Mailboxes.Count; $j++)
    {
        # Move to next underallocated DB if required
        if ($UA_DBCounters[$UA_DBPointer].Total -ge $BalancedCount -and $UA_DBPointer -lt $UA_DBCounters.Count-1)
        {
                $UA_DBPointer++;
        }
        Write-Progress -activity "Generating move commands" -status "Processing $($OA_Mailboxes[$j].Identity)" -PercentComplete (($j / $OA_Mailboxes.Count)  * 100)
        # Generate a Powershell command for the move request
        if ($Exchange2010)
        {
            $Output+="New-MoveRequest -Identity '$($OA_Mailboxes[$j].Identity)' -TargetDatabase '$($UA_DBCounters[$UA_DBPointer].Database)' -Confirm:" + '$false' + "`r`n";
        } else {
            $Output+="Move-Mailbox -Identity '$($OA_Mailboxes[$j].Identity)' -TargetDatabase '$($UA_DBCounters[$UA_DBPointer].Database)'`r`n";
        }        
        $OA_DBCounters[$i].Total--; # Take one mailbox of the total on the overallocated DB
        $UA_DBCounters[$UA_DBPointer].Total++; # Add one mailbox tot the total of the current underallocated DB
       
       
    }
    Write-Progress -activity "Generating move commands" -Completed -Status "Completed"
}
Write-Progress -activity "Generating Powershell File '$($OutputPowershellFile)' to Balance DBs" -Completed -Status "Completed"
Write-Host "DB Counts will be as follows after executing $($OutputPowershellFile):"
$OA_DBCounters|Select Database,Total
$UA_DBCounters|Select Database,Total
$PA_DBCounters|Select Database,Total
Write-Host "Writing Powershell file '$($OutputPowershellFile)'"
$Output | Out-File -FilePath $OutputPowershellFile -NoClobber -Append

56 thoughts on “Balancing the number of mailboxes across Exchange 2010 and 2007 databases

  1. There is a bug !

    You have just to move “$UA_DBPointer=0;” before “for ($i = 0; $i -lt $OA_DBCounters.Count; $i++)”

    This will resolve the kek’s issue.

    Ben

  2. Hi Steve

    I am not sure if you are still monitoring this thread or not but I have a question to ask.

    In you examples above, if the script was to be run for all databases, does this look at the Active and Passive databases in a DAG or just the Active?

    If the above is true that the script only looks at the active databases, then I suppose that I would have to use Example 3, but is it possible to run this on Exchange 2010 or 2013?

  3. Pingback: Moving Mailboxes the easy way – flaphead.com

  4. Hi James, are you able to provide an example of how you overcame this? I’m currently having the same issue. I think i understand your workaround, just wanting to verify.

  5. Hi Steve,

    I do’nt know if you still monitor this, but this does exactly what I need it to do…except…it doesn’t 🙁

    I’m in the same boat as the bloke up above…our users have a parenthesis in their display name quite commonly, and it seems to be throwing an error in the move portion of the script. I don’t see anywhere how to overcome this…is it possible to use the samaccountname or mailnickname or even smtp address in the move file? How would i modify the code to accomplish such a thing?

    Thanks!

    James

    • Disregard, I figured it out!

      I added primarysmtp to the properties selected in the get-mailbox cmd, then when generating the move requests i changed .identity to .primarysmtpaddress. At this point, I still had an issue with a problem ” ‘ ” terminator, so i did a find\replace on the move file to replace all instances of ” ‘ ” with a blank space. Voila, problem solved!

  6. Hey there Steve. We have a single SBS server with 30 mailboxes on it. The script works for the first two sections but the Database Backup section is what I’m most interested in. Have you worked with anyone to get the script running on the SBS side of things?

    Site: Default-First-Site-Name Exchange Version Roles Installed OS Version OS Service Pack
    Mailboxes: 30 CAS EDGE HUB MBX UM
    SBSServer Exchange 2010 SP1 UR7 v2 30 Windows? Small Business Server 2011 Std Service Pack 1

  7. Has anyone figured out how to exclude the archive database (or any particular mailbox) from being part of the balancing? We have 3 databases, with 1 of the 3 being archival on slow storage. If I run this, I fear that my 300 mailboxes will balance across all 3 databases and lead to major performance issues for 1/3 of the users. I also don’t want archival mailboxes to be moved over to “fast storage” mailbox databases. I haven’t figured out how to exclude 1 database while including the other 2.

    Any help would be appreciated!

    • if you do $o = Get-MailboxDatabase| Where-object {$_.name -ne ‘Archive’}
      then
      .\Generate-BalanceMoveRequests.ps1 -DBs $o -OutputPowershellFile .\moves.ps1

      this will do all but the Archive database. I am running into the problem where im trying to exclude 2 databases and it wont work no matter how i run it.

      • Just figured my problem out

        o$ = Get-MailboxDatabase | Where-Object {$_.name -ne ‘Archive’ -and $_.name -ne ‘Recovery’}

        was trying to use -or not -and.

  8. Pingback: Balancing mailboxes across Exchange 2010 and 2007 databases « MidThought's

  9. Steve,

    Love the script, but I have a problem.

    Does it have a problem with space in the Identity? I’m getting the following error when I try to execute the moves.ps1 script. Is it possible to use the alias as the indentifier instead of the AD OU?

    [PS] C:\scripts>.\moves.ps1
    The string starting:
    At C:\scripts\moves.ps1:85 char:81
    + New-MoveRequest -Identity ‘mycompany.corp/Users/user name’ -TargetDatabase ‘MDB04 <<<< ' -Confirm:$false
    is missing the terminator: '.
    At C:\scripts\moves.ps1:85 char:98
    + New-MoveRequest -Identity ''mycompany.corp/Users/user name' -TargetDatabase 'MDB04' -Confirm:$false <<<<
    + CategoryInfo : ParserError: ( -Confirm:$false:String) [], ParseException
    + FullyQualifiedErrorId : TerminatorExpectedAtEndOfString

    Thanks for all the work you've done to make my Exchange tasks easier!

  10. I Have a large 130k 2010 environment that really needs the “Generate-BalancebySizeMoveRequests.ps1” script. we have about 3 tiers of MB sizes and use the random theory to distribute them over all 16 MBX servers and DB’s… would be awesome to use mailbox size in that distribution as well. like maybe setting some defaults that can be changed like (low<=50mb, med2048+) and distribute those equally across DB’s so that not all the <50mb ones are on one DB and the 2048+ ones are on another….

    anyways. just my +1 for this functionality

    Dave P

  11. Hey there Steve, I was wondering if you ever got a chance last year to write the Generate-BalancebySizeMoveRequests.ps1???? 🙂

    Chris

  12. Steve, do you know if a script can be created to rebalance the DAG in 2010?
    If I have 3 servers, 3 databases and have copies of each DB on each server, If I lose a server the DB all work but the third server is never used again when back online unless I manually balance them.

    • Hiya,

      A script is built into Exchange 2010 which performs this:

      Open Exchange Management Shell:

      cd $exscripts
      \RedistributeActiveDatabases.ps1 -DagName DAG -BalanceDbsByActivationPreference

      Steve

  13. Steve, did you ever update this script to balance mailboxes based on size? We’ve got to divide a single database into 5 and we want to balance the size of the database.

    • Hi Michael,

      I haven’t had a look at it yet, sadly it’s hard to come back to the older posts to add features as I have been pretty busy over the last few months.

      Steve

  14. Steve,

    Sorry I won’t bombard with anymore information after this. I reduced this down to 2 servers, old to new with 18 databases balancing to 24. Still strange results. Any thoughts?

    .\Generate-DBBalanceScript.ps1 -DBs $o -OutputPowershellFile .\moves9to4.2.txt
    Gathering Mailbox Counts
    DB Counts are as follows:

    Database Total
    ———— —–
    EXSRV09DB01 — 122
    EXSRV09DB02 — 122
    EXSRV09DB03 — 120
    EXSRV09DB04 — 118
    EXSRV09DB05 — 120
    EXSRV09DB06 — 109
    EXSRV09DB07 — 118
    EXSRV09DB08 — 115
    EXSRV09DB09 — 117
    EXSRV09DB10 — 112
    EXSRV09DB11 — 116
    EXSRV09DB12 — 106
    EXSRV09DB13 — 111
    EXSRV09DB14 — 99
    EXSRV09DB15 — 103
    EXSRV09DB16 — 108
    EXSRV09DB17 — 108
    EXSRV09DB18 — 103
    EXSRV04DB01 — 5
    EXSRV04DB02 — 5
    EXSRV04DB03 — 5
    EXSRV04DB04 — 5
    EXSRV04DB05 — 5
    EXSRV04DB06 — 5
    EXSRV04DB07 — 5
    EXSRV04DB08 — 5
    EXSRV04DB09 — 6
    EXSRV04DB10 — 5
    EXSRV04DB11 — 4
    EXSRV04DB12 — 5
    EXSRV04DB13 — 5
    EXSRV04DB14 — 4
    EXSRV04DB15 — 6
    EXSRV04DB16 — 3
    EXSRV04DB17 — 10
    EXSRV04DB21 — 10
    EXSRV04DB19 — 6
    EXSRV04DB23 — 11
    EXSRV04DB20 — 10
    EXSRV04DB22 — 3
    EXSRV04DB18 — 4
    EXSRV04DB24 — 3
    Found 2162 mailboxes across 42 databases. Aiming for 51 mailboxes per database.
    Sorting Databases into over, under and perfectly allocated.
    Found 18 over, 24 under and 0 perfectly allocated
    Generating Powershell File ‘.\moves9to4.2.txt’ to Balance DBs.
    DB Counts will be as follows after executing .\moves9to4.2.txt:
    EXSRV09DB01 — 51
    EXSRV09DB02 — 51
    EXSRV09DB03 — 51
    EXSRV09DB04 — 51
    EXSRV09DB05 — 51
    EXSRV09DB06 — 51
    EXSRV09DB07 — 51
    EXSRV09DB08 — 51
    EXSRV09DB09 — 51
    EXSRV09DB10 — 51
    EXSRV09DB11 — 51
    EXSRV09DB12 — 51
    EXSRV09DB13 — 51
    EXSRV09DB14 — 51
    EXSRV09DB15 — 51
    EXSRV09DB16 — 51
    EXSRV09DB17 — 51
    EXSRV09DB18 — 51
    EXSRV04DB01 — 51
    EXSRV04DB02 — 67
    EXSRV04DB03 — 67
    EXSRV04DB04 — 66
    EXSRV04DB05 — 65
    EXSRV04DB06 — 64
    EXSRV04DB07 — 64
    EXSRV04DB08 — 63
    EXSRV04DB09 — 62
    EXSRV04DB10 — 61
    EXSRV04DB11 — 61
    EXSRV04DB12 — 60
    EXSRV04DB13 — 59
    EXSRV04DB14 — 58
    EXSRV04DB15 — 57
    EXSRV04DB16 — 56
    EXSRV04DB17 — 55
    EXSRV04DB21 — 54
    EXSRV04DB19 — 52
    EXSRV04DB23 — 51
    EXSRV04DB20 — 41
    EXSRV04DB22 — 3
    EXSRV04DB18 — 4
    EXSRV04DB24 — 3
    Writing Powershell file ‘.\moves9to4.2.txt’

  15. Steve,

    I quarted it down to 72 databases and have the same strange results. The balacing is going against 3 servers, 2 old and one new. Results below:

    Found 5399 mailboxes across 72 databases. Aiming for 75 mailboxes per database.
    Sorting Databases into over, under and perfectly allocated.
    Found 48 over, 24 under and 0 perfectly allocated
    Generating Powershell File ‘.\moves1-2to6.txt’ to Balance DBs.
    DB Counts will be as follows after EXSRVecuting .\moves1-2to6.txt:
    EXSRV01DB01 — 75
    EXSRV01DB02 — 75
    EXSRV01DB03 — 75
    EXSRV01DB04 — 75
    EXSRV01DB06 — 75
    EXSRV01DB10 — 75
    EXSRV01DB12 — 75
    EXSRV01DB09 — 75
    EXSRV01DB07 — 75
    EXSRV01DB08 — 75
    EXSRV01DB05 — 75
    EXSRV01DB11 — 75
    EXSRV01DB19 — 75
    EXSRV01DB22 — 75
    EXSRV01DB15 — 75
    EXSRV01DB14 — 75
    EXSRV01DB23 — 75
    EXSRV01DB17 — 75
    EXSRV01DB16 — 75
    EXSRV01DB21 — 75
    EXSRV01DB20 — 75
    EXSRV01DB13 — 75
    EXSRV01DB18 — 75
    EXSRV01DB24 — 75
    EXSRV02DB01 — 75
    EXSRV02DB02 — 75
    EXSRV02DB03 — 75
    EXSRV02DB04 — 75
    EXSRV02DB05 — 75
    EXSRV02DB06 — 75
    EXSRV02DB07 — 75
    EXSRV02DB08 — 75
    EXSRV02DB09 — 75
    EXSRV02DB10 — 75
    EXSRV02DB11 — 75
    EXSRV02DB12 — 75
    EXSRV02DB13 — 75
    EXSRV02DB14 — 75
    EXSRV02DB15 — 75
    EXSRV02DB16 — 75
    EXSRV02DB17 — 75
    EXSRV02DB18 — 75
    EXSRV02DB19 — 75
    EXSRV02DB20 — 75
    EXSRV02DB21 — 75
    EXSRV02DB22 — 75
    EXSRV02DB23 — 75
    EXSRV02DB24 — 75
    EXSRV06DB01 — 75
    EXSRV06DB02 — 118
    EXSRV06DB03 — 116
    EXSRV06DB04 — 114
    EXSRV06DB05 — 111
    EXSRV06DB06 — 109
    EXSRV06DB09 — 107
    EXSRV06DB07 — 105
    EXSRV06DB08 — 103
    EXSRV06DB10 — 101
    EXSRV06DB11 — 98
    EXSRV06DB12 — 96
    EXSRV06DB13 — 93
    EXSRV06DB14 — 90
    EXSRV06DB16 — 88
    EXSRV06DB20 — 86
    EXSRV06DB17 — 83
    EXSRV06DB18 — 77
    EXSRV06DB19 — 27
    EXSRV06DB15
    EXSRV06DB21
    EXSRV06DB22 — 2
    EXSRV06DB23
    EXSRV06DB24
    Writing Powershell file ‘.\moves1-2to6.txt’

  16. Steve,

    Stretching my weak powershell skills to their limits I managed to do all the inclusions/exclusions and divide everything in half. Half of my existing servers coupled with half the new in each new set. Same results but only half as much 🙂 Same deal. It excludes most, not all of the new databases. We’ve done get-mailboxdatabase |fl side by side between working and non-working db’s and their identical. All databases were created with standard script as you can imagine what a chore it would be manually.

    Any ideas? Thanks for your help.

    Found 9636 mailboxes across 132 databases. Aiming for 73 mailboxes per database.
    Sorting Databases into over, under and perfectly allocated.
    Found 84 over, 48 under and 0 perfectly allocated
    Generating Powershell File ‘.\moves.ps1’ to Balance DBs.
    DB Counts will be as follows after executing .\moves.ps1

  17. Steve,

    Thanks for getting back to me. How would I limit? I am excluding a dozen resource account databases. I was trying to figure out a way to try and have it see half of our server and do the balancing against it only. I guess the challenge is how to omit some and include only others?

  18. Steve,

    This is a fine piece of work. I am so close to getting it working but keep hitting some limit. Below is output that I get from any Exchange 2010 server where I run this. We have a 12 node DAG that just got 4 new nodes introduced and I’m trying to balance those 19705 accounts across 12 servesr rather than the current 8. There are 96 new databases and the script targets 75 per db which makes sense. What is perplexing is that the file it generates 7103 New-MoveRequests but ignores 58 databases and as you can see below it overloads 36 db’s. Have I hit some limit? Any help would be appreciated.

    —————————————————————————————————————–
    Found 19705 mailboxes across 264 databases. Aiming for 75 mailboxes per database.
    Sorting Databases into over, under and perfectly allocated.
    Found 168 over, 96 under and 0 perfectly allocated
    Generating Powershell File ‘.\moves6.ps1’ to Balance DBs.
    DB Counts will be as follows after executing .\moves6.ps1:
    EXSRV09DB01 — 75
    EXSRV09DB02 — 75
    EXSRV09DB03 — 75
    EXSRV10DB01 — 75
    EXSRV10DB02 — 75
    EXSRV10DB03 — 75
    EXSRV11DB01 — 75
    EXSRV11DB02 — 75
    EXSRV11DB03 — 75
    EXSRV12DB01 — 75
    EXSRV12DB02 — 75
    EXSRV12DB03 — 75
    EXSRV01DB01 — 75
    EXSRV01DB02 — 75
    EXSRV01DB03 — 75
    EXSRV01DB04 — 75
    EXSRV09DB04 — 75
    EXSRV09DB05 — 75
    EXSRV09DB06 — 75
    EXSRV09DB07 — 75
    EXSRV09DB08 — 75
    EXSRV09DB09 — 75
    EXSRV10DB04 — 75
    EXSRV10DB05 — 75
    EXSRV10DB06 — 75
    EXSRV10DB07 — 75
    EXSRV10DB08 — 75
    EXSRV10DB09 — 75
    EXSRV11DB04 — 75
    EXSRV11DB07 — 75
    EXSRV11DB05 — 75
    EXSRV11DB06 — 75
    EXSRV11DB09 — 75
    EXSRV11DB08 — 75
    EXSRV12DB06 — 75
    EXSRV12DB05 — 75
    EXSRV12DB04 — 75
    EXSRV12DB07 — 75
    EXSRV12DB08 — 75
    EXSRV12DB09 — 75
    EXSRV01DB06 — 75
    EXSRV01DB10 — 75
    EXSRV01DB12 — 75
    EXSRV01DB09 — 75
    EXSRV01DB07 — 75
    EXSRV01DB08 — 75
    EXSRV01DB05 — 75
    EXSRV01DB11 — 75
    EXSRV09DB10 — 75
    EXSRV09DB11 — 75
    EXSRV09DB12 — 75
    EXSRV09DB13 — 75
    EXSRV09DB14 — 75
    EXSRV09DB15 — 75
    EXSRV09DB16 — 75
    EXSRV09DB17 — 75
    EXSRV09DB18 — 75
    EXSRV10DB10 — 75
    EXSRV10DB11 — 75
    EXSRV10DB12 — 75
    EXSRV10DB13 — 75
    EXSRV10DB14 — 75
    EXSRV10DB15 — 75
    EXSRV10DB16 — 75
    EXSRV10DB17 — 75
    EXSRV10DB18 — 75
    EXSRV11DB11 — 75
    EXSRV11DB18 — 75
    EXSRV11DB13 — 75
    EXSRV11DB12 — 75
    EXSRV11DB10 — 75
    EXSRV11DB16 — 75
    EXSRV11DB17 — 75
    EXSRV11DB15 — 75
    EXSRV11DB14 — 75
    EXSRV12DB10 — 75
    EXSRV12DB11 — 75
    EXSRV12DB12 — 75
    EXSRV12DB13 — 75
    EXSRV12DB14 — 75
    EXSRV12DB15 — 75
    EXSRV12DB16 — 75
    EXSRV12DB17 — 75
    EXSRV12DB18 — 75
    EXSRV01DB19 — 75
    EXSRV01DB22 — 75
    EXSRV01DB15 — 75
    EXSRV01DB14 — 75
    EXSRV01DB23 — 75
    EXSRV01DB17 — 75
    EXSRV01DB16 — 75
    EXSRV01DB21 — 75
    EXSRV01DB20 — 75
    EXSRV01DB13 — 75
    EXSRV01DB18 — 75
    EXSRV01DB24 — 75
    EXSRV02DB01 — 75
    EXSRV02DB02 — 75
    EXSRV02DB03 — 75
    EXSRV02DB04 — 75
    EXSRV02DB05 — 75
    EXSRV02DB06 — 75
    EXSRV02DB07 — 75
    EXSRV02DB08 — 75
    EXSRV02DB09 — 75
    EXSRV02DB10 — 75
    EXSRV02DB11 — 75
    EXSRV02DB12 — 75
    EXSRV02DB13 — 75
    EXSRV02DB14 — 75
    EXSRV02DB15 — 75
    EXSRV02DB16 — 75
    EXSRV02DB17 — 75
    EXSRV02DB18 — 75
    EXSRV02DB19 — 75
    EXSRV02DB20 — 75
    EXSRV02DB21 — 75
    EXSRV02DB22 — 75
    EXSRV02DB23 — 75
    EXSRV02DB24 — 75
    EXSRV03DB01 — 75
    EXSRV03DB02 — 75
    EXSRV03DB03 — 75
    EXSRV03DB04 — 75
    EXSRV03DB05 — 75
    EXSRV03DB06 — 75
    EXSRV03DB07 — 75
    EXSRV03DB08 — 75
    EXSRV03DB09 — 75
    EXSRV03DB10 — 75
    EXSRV03DB11 — 75
    EXSRV03DB12 — 75
    EXSRV03DB13 — 75
    EXSRV03DB14 — 75
    EXSRV03DB15 — 75
    EXSRV03DB16 — 75
    EXSRV03DB17 — 75
    EXSRV03DB18 — 75
    EXSRV03DB19 — 75
    EXSRV03DB20 — 75
    EXSRV03DB21 — 75
    EXSRV03DB23 — 75
    EXSRV03DB22 — 75
    EXSRV03DB24 — 75
    EXSRV08DB01 — 75
    EXSRV08DB02 — 75
    EXSRV08DB03 — 75
    EXSRV08DB04 — 75
    EXSRV08DB05 — 75
    EXSRV08DB06 — 75
    EXSRV08DB07 — 75
    EXSRV08DB08 — 75
    EXSRV08DB09 — 75
    EXSRV08DB10 — 75
    EXSRV08DB11 — 75
    EXSRV08DB12 — 75
    EXSRV08DB13 — 75
    EXSRV08DB14 — 75
    EXSRV08DB15 — 75
    EXSRV08DB16 — 75
    EXSRV08DB17 — 75
    EXSRV08DB18 — 75
    EXSRV08DB19 — 75
    EXSRV08DB20 — 75
    EXSRV08DB21 — 75
    EXSRV08DB22 — 75
    EXSRV08DB23 — 75
    EXSRV08DB24 — 75
    EXSRV04DB01 — 75
    EXSRV04DB02 — 240
    EXSRV04DB03 — 238
    EXSRV04DB04 — 237
    EXSRV04DB05 — 236
    EXSRV04DB06 — 234
    EXSRV04DB07 — 233
    EXSRV04DB08 — 231
    EXSRV04DB09 — 230
    EXSRV04DB10 — 227
    EXSRV04DB11 — 225
    EXSRV04DB12 — 223
    EXSRV04DB13 — 220
    EXSRV04DB14 — 218
    EXSRV04DB15 — 216
    EXSRV04DB16 — 214
    EXSRV04DB17 — 212
    EXSRV04DB18 — 210
    EXSRV04DB19 — 208
    EXSRV04DB20 — 206
    EXSRV04DB21 — 204
    EXSRV04DB22 — 200
    EXSRV04DB23 — 195
    EXSRV04DB24 — 191
    EXSRV05DB06 — 185
    EXSRV05DB02 — 180
    EXSRV05DB08 — 176
    EXSRV05DB09 — 173
    EXSRV05DB04 — 169
    EXSRV05DB03 — 161
    EXSRV05DB05 — 155
    EXSRV05DB07 — 150
    EXSRV05DB01 — 143
    EXSRV05DB14 — 132
    EXSRV05DB12 — 112
    EXSRV05DB11 — 106
    EXSRV05DB10 — 90
    EXSRV05DB15 — 50
    EXSRV05DB16
    EXSRV05DB17
    EXSRV05DB13
    EXSRV05DB18
    EXSRV05DB19
    EXSRV05DB20
    EXSRV05DB24
    EXSRV05DB21
    EXSRV05DB22
    EXSRV05DB23
    EXSRV07DB03
    EXSRV07DB01
    EXSRV07DB02
    EXSRV07DB04
    EXSRV07DB05
    EXSRV07DB06
    EXSRV07DB07
    EXSRV07DB08
    EXSRV07DB09
    EXSRV07DB10
    EXSRV07DB11
    EXSRV07DB12
    EXSRV07DB13
    EXSRV07DB14
    EXSRV07DB15
    EXSRV07DB16
    EXSRV07DB17
    EXSRV07DB18
    EXSRV07DB19
    EXSRV07DB20
    EXSRV07DB21
    EXSRV07DB22
    EXSRV07DB24
    EXSRV07DB23
    EXSRV06DB01
    EXSRV06DB02
    EXSRV06DB03
    EXSRV06DB04
    EXSRV06DB05
    EXSRV06DB06
    EXSRV06DB07
    EXSRV06DB08
    EXSRV06DB09
    EXSRV06DB10
    EXSRV06DB11
    EXSRV06DB12
    EXSRV06DB13
    EXSRV06DB14
    EXSRV06DB16
    EXSRV06DB20
    EXSRV06DB17
    EXSRV06DB18
    EXSRV06DB19
    EXSRV06DB15
    EXSRV06DB21
    EXSRV06DB22
    EXSRV06DB23
    EXSRV06DB24
    Writing Powershell file ‘.\moves.ps1’
    —————————————————————————————————————–

    • Hiya,

      I’ve not tried the script on that many mailboxes and wouldn’t recommend running that many move requests in one go (though, I must admit the script shouldn’t have went wrong).

      Do you have any better success if you limit the number of databases, perhaps balancing 1-10, 11, 20 etc?

      Steve

  19. Hi Steve,
    We have 6 databases in all, but they are in pairs. Just for an example say there are two on Solid state drives, two on Fiber channel 15K and 2 on SATA. What would I have to do to get your script to balance mailboxes across two server but just within two specific databases?

    In case that isn’t completely clear here is an example:
    Server 1 databases: SS-Server1-DB1, FC-Server1-DB1, SATA-Server1-DB1
    Server 2 databases: SS-Server2-DB1, FC-Server2-DB1, SATA-Server2-DB1

    I would like to balance the mailboxes between the two SS* databases, then the mailboxes between the two FC* databases, and finally the mailboxes between the two SATA* databases.

    Thanks for your assistance.
    Greg

  20. Hi Steve,

    Do you have any information on how to balance the databases based on mailbox size and only on a select list of databases? Thanks for all you do.

  21. Hi Steve,

    I did notice one issue when the script generates the list of moves. Currently it uses the Identity of each user which seems to the OU plus CN. We have some users with parenthesis in the name of the OU where their mailbox resides which causes the script to fail with the following error:

    The term ‘Sales’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spell
    ing of the name, or if a path was included, verify that the path is correct and try again.
    At C:\Tech\Scripts\PowerShell\dbmoves.ps1:41 char:83
    + New-MoveRequest -Identity ‘domain.com/Corp/Users/HQ (Sales <<<< )/Smith, Tom' -TargetDat
    abase 'DB15'
    + CategoryInfo : ObjectNotFound: (Sales:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    Is there any way to have it output the user name via PrimarySMTPAddress in the move script?

    Thanks,

    -Devon

    • I will have to do that soon, won’t I.. And take into account archive mailboxes, too. It is on my list – the new year has been pretty busy so far so just getting a handle again on the blog 🙂

      Steve

  22. Hi,

    thanks for your script . I tried and it work but there is a small issue while running moves.ps1 in exchange 2007 environments . so issue is that for every mailbox move it prompts for yes or No input. so can you help me with that so that all i can do without changing anything in moves.ps1. or there is any way to add -confirm :$false in each line while generating the script.

  23. Hi Steve,

    Great script! Is it possible to get the reverse, balance mailbox databases based on mailbox size not count for Exchange 2010?

    Cheers,
    Alan

    • Hi Alan,

      Glad you like it. I’d been thinking the same thing – it would be useful to balance based on size instead of mailbox numbers.

      Balancing on mailbox numbers has a basis in each DB’s underlying LUN being fully provisioned to be able to support full quotas for each mailbox. However another approach could be to add storage and additional DBs as mailboxes grow toward their quotas, and this would require re-balancing the databases based on size as you suggest.

      So – It’s added to my to-do list 🙂

      Steve

  24. Hi Steve,

    I enjoy reading your blog. I’m trying to come up with a script to do something similar to this one. Our helpdesk creates all new mailboxes in one SG (Staging) and then we spread them out among the other SG’s. to balance them. I would like to automate this task to move them to the least populated SG’s, similar to what you are doing with these scripts.

  25. Not really good with the scripting thing but i need to do this load balancing across top 2 exchange 2007 servers with 4 storage groups each. How would i achieve this using your scripts

    • Hi Lamont,

      You would use Example three from above:

      $o=Get-MailboxDatabase | where {$_.Server -eq “serverone” -or $_.Server -eq “servertwo”}
      .\Generate-BalanceMoveRequests.ps1 -DBs $o -OutputPowershellFile moves07.ps1 -Exchange2010:$false

      Also, if you are using Powershell V1 and not Powershell V2 you will need to do this:

      Change the first bit of the script up until “# Check for Exchange cmdlets” to:

      param($DBs,$OutputPowershellFile,$Exchange2010=$true)

      # Check for Exchange cmdlets

      Steve

  26. Hi Steve,
    Thanks again – this worked, although with a slight modification:

    I changed it to:

    $o=Get-MailboxDatabase -server mkt-mbx01 | where-object {$_.name -ne “Mailbox Database”}

    This excludes the first storage group’s mailbox DB successfully from the calculations for balancing the mailboxes.

    Thanks once again for all your help – I’m learning more from you about Powershell scripting than I have from numerous books!

    Sam

  27. Hi Steve,
    Sorry, one last question: Is there a way to tell the script to exclude one of the Storage Groups when balancing the mailboxes across the DBs? We need to exlude the First Storage Group completely as there is limited disk space available for this and it is only used for service mailboxes etc.

    Thanks again,
    Sam

    • Hi Sam,

      Would something like this as the bit to get the DBs do what you want?

      $o = Get-MailboxDatabase | Where {$_.Storagegroup -ne “The one to exclude”}

      Steve

  28. Hi Steve,
    It’s working!!! That did the trick, thanks!

    I’m planning to split the move script that’s generated into blocks of 100 mailboxes as we have around 1000 mailboxes to move (our old Exchange admin decided to tell the people creating mailboxes to create them all in one storage group for some reason leaving us with one SG with over 1800 mailboxes!!). Obviously we’ve got a fair amount of work to do to tell these users when we’ll be moving their mailboxes and to then schedule the appropriate scripts to run.

    Thanks so much for your help with this – it’s saved us a huge amount of work!
    Sam

  29. This script looks like it’ll sort out a number of problems we’re having with the distribution of our mailboxes across our storage groups. We’ve got a SCR clustered Exchange 2007 environment and I’ve tried running your script but it fails immediately with the following error:

    Unable to find type [parameter(Position=0,Mandatory=$true,ValueFromPipeline=$fa
    lse,HelpMessage=”Mailbox database object”)]: make sure that the assembly contai
    ning this type is loaded.

    To run the script I first typed:
    $o=Get-MailboxDatabase -Server servername
    followed by:
    .\Generate-BalanceMoveRequests.ps1 -DBs $o -OutputPowershellFile moves07.ps1 -Exchange2010:$false

    Apologies, I’m far from a powershell expert – more like a novice! Any help you could provide though would be great as this script is exactly the answer to our problems!

    Thanks in advance,
    Sam

      • Hi Steve,
        Many thanks! I’ll wait to hear from you – let me know if you need any more information about our environment etc. though.

        Sam

      • Hiya,

        It looks like it’s a Powershell 1.0 issue – as a quick hack try the following:

        Change the first bit of the script up until “# Check for Exchange cmdlets” to:

        param($DBs,$OutputPowershellFile,$Exchange2010=$true)

        # Check for Exchange cmdlets

        This takes out some of the get-help compatibility and prompting for values, but assuming you put the right values (like you did above) it looks like it works fine on Win 2003 Ent + Exchange 2007 SP3 + Powershell 1.0.

        Steve

Comments are closed.