Exchange Database growth is something that should be planned properly. Sometimes databases start to grow rapidly over the others, a proper Local Mailbox Redistribution should be performed to maintain the load.

This script EXMB-Planner will plan the mailbox redistribution across new databases. The EXMB-Planner ensures that the new database will not exceed a certain size limit. Also, the EXMB-Planner may create a redistribution plan based on the number used on each DB.

Download the Script from here, or read it at the end of this post.
Please rename the file to .ps1

Details:
As to Microsoft’s best practices, the Max EDB size should not go more than 2 TB on High Availability replicated database and up to 200 GB for the standalone non-replicated database. But in some cases when you have a large EDB, you might need to redistribute the Mailbox between other databases to reduce the size, for example, you have a 1.7 TB of Exchange Database, and want to redistribute the mailboxes in it to a smaller size 3×500 or maybe a group of 700GB.. whatever.

Some reason to make the database smaller is to easy the recovery time as the bigger the database the higher risk of breaching the RTO/RPO SLA, or maybe need to replicate the EBD over the WAN. but which user should be migrated and how to ensure that the total migrated users are filling the wanted database size not more, not less.

The script will not create or change anything on your Exchange database, the only thing it needs is to read the mailboxes only, and it will create a plan on a CSV which can help you in the migration process.

The Powershell script I wrote below will do the following:
– Read all the mailboxes inside the required database (Usermailbox – Archive mailbox).
Create a plan for a database with a certain size (RequireDBSizeInGB parameter), the script will calculate the number of mailboxes that should be migrated to a new database to reach the required size, so if the user sets the RequireDBSizeInGB parameter value to 500, the script will create a plan (CSV file) which include the users with a total mailbox of 500 GB or less.
Set a number of Mailboxes in a Database (MaxMBxPerDB parameter), so you can also redistribute the users from the named database to multiple databases, each database has a certain number of mailboxes, such as 100 mailbox per-database.
Support Archive Mailboxes (EXMBxArchiveLog), This parameter will fetch all Archive Mailboxes (not the primary only the archive) and also create a similar plan.
Combining the two options above: You can combine both parameters together if you want each database to have a certain number of users and at the same time don’t exceed a certain number of GB, you can both parameter as required. As users mailboxes size vary, from less than 1GB up to whatever you set in your policy. So the plan will be created accordingly.

Script usage:

Example 1, Prepare a plan to redistribute users from 1.7 TB EDB to multiple databases with a MAX size of each EDB not exceeding 700 GB

#Without using the MaxMBxPerDB parameter
#Without using the EXMBxArchiveLog parameter
.\New-EXuserDBMigration.ps1 -DatabaseName MyBigDBName -OutputFolder C:\PathToFolder -RequireDBSizeInGB 700 -MgmtPSFullUri "http://MyExchangeServer.FQDN/powershell" 

The Above script will create CSV files and how users should be redistributed.

The Files will be named as the following EXDB[N], these files contain the user’s mailboxes emailaddress property that met the required parameter, you can use these files to import them to the Exchange Server Migration Patch wizard.
Also, an Additional file will be created EX-Planfull, This file will include detailed information about the plan, you can review the file and confirm the calculation, such as the number of mailboxes size, match the requirement along with the database number.

Folder content
EX-Planfull Content

The console will show an output with basic information such as the number of required DB, Each database and how many Mailbox it should host and the Total size of the mailboxes in it.

Console Output

So the script is telling, if you create a new database and move all the users in one of the CSV to this database, the database should grow to the size set in the RequireDBSizeInGB Parameter. Please note that this excludes the Database and mailbox overhead size.

Example 2: Re-distribute users from 1 EX-DB to multiple databases, were the MAX EDB size should not exceed 700GB and the MAX number of Mailboxes in each EX-DB should not be more than 50 Mailbox Per-Database.

#Without using the -EXMBxArchiveLog Parameter.
.\ExchangeStorageSpace.ps1 -DatabaseName MyBigDBName -OutputFolder C:\PathToFolder -RequireDBSizeInGB 700 -MgmtPSFullUri "http://MyExchangeServer.FQDN/powershell"  -MaxMBxPerDB 50

The output of the above script after executing it on a ≈ 1.7TB DB is a collection is CSV files which include the emailaddress for the users in the EX-DB MyBigDBName, each file contains a list of 50 users (as it was passed in the parameter above -MaxMBxPerDB) and DB size is less than 700. If the total number of Mailbox size is above 700GB and the MaxMBxPerDB threshold did not reach, a new database plan (CSV) will be created.

Example 3

Using the EXMBxArchiveLog Parameter, will enable the script not only to get the Primary email address, but also the Archive Mailboxes inside the same database, the main concern is information inside this database, so if a user is hosted in MyBigDBName and his mailbox is in another database outside this database, this user archive wont show in the result. and if a user Archive is hosted in MyBigDBName and his Primary Mailbox hosted in another database, only the archive mailbox will show in the result, as we need to get the information inside this DB “MyBigDBName

.\ExchangeStorageSpace.ps1 -DatabaseName MyBigDBName -OutputFolder C:\PathToFolder -RequireDBSizeInGB 700 -MgmtPSFullUri "http://MyExchangeServer.FQDN/powershell"  -EXMBxArchiveLog
 <#
    .SYNOPSIS
        This script help you in planning the migration of users from 1 DB to other smaller database.
    .DESCRIPTION
        If you have a large Exchange Database and want to move the users to other database for load distribution, this script will help you in
        planning such migration as it will give you an idea about how many users should be migrated to a single database and how many database required, which can be controlled by parameter
    .PARAMETER $DatabaseName
        $DatabaseName: Required, The Exchange Database Name, as you can get it using Get-Mailboxdatabase

    .PARAMETER $OutputFolder
        $OutputFolder: Required, The Path to a Folder, NOT a file where the script will write the result to.

    .PARAMETER $RequireDBSizeInGB
         $RequireDBSizeInGB: How many GB of information the new database should hold, you can set this to any number above 50.

    .PARAMETER $MaxMBxPerDB
        $MaxMBxPerDB: Not Required, if you want to set a number of users per-Mailbox database, then you can use this parameter, default value is 50000, but you can limit the number 
        per mailbox database to be 100 or whatever as long as the value is above 10.

    .PARAMETER $EXMBxArchiveLog
        $EXMBxArchiveLog: Not Required, Type is Switch, so you only add it if you need it, This switch will enable the script to get similar list but for primary mailbox with its Archive mailbox.
        Note that enabling this option might significantly increase the execution time for the script, as the script will need to search all your exchange archive mailboxes for any
        mailbox archive located in the required database.

    .PARAMETER $MgmtPSFullUri
        $MgmtPSFullUri: Required, The URI to connect to MGMT Powershell URI to initial the session, usually its https://Exchange_Name_Space_URL/powershell ,or maybe http://servername_FQDN/Powershell
        You can find the URI through the following powershell command 
    (Get-PowerShellVirtualDirectory -Server MyServerName).InternalUrl.AbsoluteUri

    .OUTPUTS
       *************************Report********************
            Number of DB Required: XX
            Number of Archive DB required is XX
            *************************************
            Number of Mailbox on each Proposed Database

            Count : XX
            Sum   : XX

            *************************************
            Number of Archive Mailbox on each Proposed Database

            Count : XX
            Sum   : XX
         --------------------------
    .EXAMPLE
        .\New-EXuserDBMigration.ps1 -DatabaseName EXDatabaseName -OutputFolder C:\MyFolder -RequireDBSizeInGB 100 -MgmtPSFullUri "http://myserver.FQDN/powershell" -EXMBxArchiveLog
       
   .NOTES
        Feel free and let me know if you got any comment by sending me an email to farisnt@gmail.com
       
        
    #>


[cmdletbinding()]
param(
[parameter(mandatory=$true)]$DatabaseName,
[parameter(mandatory=$true)]$OutputFolder,
[parameter(mandatory=$true)]$RequireDBSizeInGB,
[parameter(mandatory=$true)]$MgmtPSFullUri,
[parameter(mandatory=$false)][int]$MaxMBxPerDB=50000,
[parameter(mandatory=$false)][switch]$EXMBxArchiveLog=$false

)

Function Get-ExDatabaseusage{
[cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]$DBName,
        [parameter(mandatory=$false)]$RequiredDBSize
        
       
)
$UsersResults=@()
[System.Collections.ArrayList]$ExchCmdLine=@()
[System.Collections.ArrayList]$ExchArchiveCmdLine=@()
[System.Collections.ArrayList]$ExchArchiveCmdLine=@()
$ExCMDWithArchive=""
        Write-Host "Getting users information, this might take few Minuts to complete... Please wait" -ForegroundColor Yellow
        Write-Host "Maybe during this we can check your facebook or take a coffee break :)" -ForegroundColor Yellow
        Write-Host "The process depend on the hardware and the speed of Exchange server response"-ForegroundColor Yellow

        $ReadMailboxFromDB=get-mailbox -Database $DBName
        foreach ($singleMailbox in $ReadMailboxFromDB){
        Write-Host "Getting " -NoNewline
        Write-Host $($singleMailbox) -NoNewline -ForegroundColor Green 
        Write-Host " Information..."
        $ExchCmdLine.Add(($singleMailbox.PrimarySmtpAddress | Get-MailboxStatistics |select DisplayName,TotalItemSize, @{N="EmailAddress";E={$singleMailbox.PrimarySmtpAddress}})) |Out-Null
        }

        if ($EXMBxArchiveLog){ 
       
        Write-Host "Enabling EXMBxArchiveLog, will increase the time the script will take to complete, so please wait..." -ForegroundColor Green
        Write-Host "Searching for Archive mailboxes in the required database..." -ForegroundColor Green
            $ReadMailboxFromDBWithArchive=get-mailbox 
            foreach ($singleMailboxwithArc in $ReadMailboxFromDBWithArchive){
                Write-Host "Getting Archive Info for " -NoNewline
                Write-Host $singleMailboxwithArc -NoNewline -ForegroundColor red
                Write-Host " if its available..."
                $ExCMDWithArchive=$singleMailboxwithArc.PrimarySmtpAddress | Get-MailboxStatistics -Archive -ErrorAction silentlycontinue|Where {$_.databasename -like $DBName} |select DisplayName,TotalItemSize, @{N="EmailAddress";E={$singleMailboxwithArc.PrimarySmtpAddress}}

                if ($ExCMDWithArchive -notlike $null){
                $ExchArchiveCmdLine.Add(($ExCMDWithArchive | where {$_.displayname -notlike $null})) |Out-Null
                
                }
        
           }
        }
        
    #### Get Value for TotalItemSize,prepare the format and convert it to Int
    
        $UsersResults=Prepare-thelist -UsersList $ExchCmdLine
    
    if ($ExchArchiveCmdLine){
         $ExchArchiveCmdLine=Prepare-thelist -UsersList $ExchArchiveCmdLine
         return $UsersResults,$ExchArchiveCmdLine
        }
  
      
return $UsersResults         


}

function Prepare-thelist {
[cmdletbinding()]
param(
[parameter(mandatory=$true)]$UsersList
)
$parsedResult=@()
Foreach ($singlemb in $UsersList){
        
        $formatedusers=New-Object PSObject 
        $formatedusers | Add-Member -NotePropertyName "User" -NotePropertyValue $singlemb.DisplayName
        [string]$Strval1=($singlemb.TotalItemSize.Value)
        $formatedusers | Add-Member -NotePropertyName "MailboxGB" -NotePropertyValue ([math]::Round(  ([int64](($Strval1.Substring($Strval1.IndexOf("(")+1)).Split(" ")[0]).Replace(",","")/1GB),3))
        $formatedusers | Add-Member -NotePropertyName "EmailAddress" -NotePropertyValue $singlemb.EmailAddress
        $parsedResult+=$formatedusers
        
}
        return $parsedResult
}
function Prepare-MyDBLog {
[cmdletbinding()]
param(
[parameter(mandatory=$true)]$UsersList,
[parameter(mandatory=$true)][int]$RequireDBSizeInGB,
[parameter(mandatory=$false)][int]$MaxMBxPerDB=50000

)

$Databases=@()
$i=1
$TotalNewDBSize=0
    foreach ($SingleUser in $UsersList){
    $databasename=New-Object psobject
    $databasename | Add-Member -NotePropertyName "DBName" -NotePropertyValue ""
    $databasename | Add-Member -NotePropertyName "UserMBX" -NotePropertyValue ""
    $databasename | Add-Member -NotePropertyName "UserMBXSize" -NotePropertyValue ""
    $databasename | Add-Member -NotePropertyName "EmailAddress" -NotePropertyValue ""
        
        if (($Databases| where {$_.DBName -match $i}).count -ge $MaxMBxPerDB){
            $TotalNewDBSize=$RequireDBSizeInGB +1
        }

           if (($SingleUser.MailboxGB + $TotalNewDBSize)-gt $RequireDBSizeInGB){
            $i++
            $TotalNewDBSize=0
           }

       $databasename.DBName ="$i"
       $databasename.UserMBX =$SingleUser.User
       $databasename.UserMBXSize =$SingleUser.MailboxGB
       $TotalNewDBSize=$TotalNewDBSize+$SingleUser.MailboxGB
       $databasename.Emailaddress=$SingleUser.EmailAddress
       $Databases+=$databasename
    }
return $Databases

}
$ErrorActionPreference="stop"
######Validation of Parameters ########
    try{

        Write-Host "Checking Parameter...Please wait"
        Write-Host "connecting to Exchange Server... Please wait" -ForegroundColor Yellow
            if (!(Get-PSSession | where {($_.ConfigurationName -like "microsoft.exchange") -and ($_.State -like "Opened")})){
            $ExchangeSession=New-PSSession -ConnectionUri $MgmtPSFullUri -ConfigurationName microsoft.exchange -ErrorAction Stop -AllowClobber
            Import-PSSession $ExchangeSession
            Write-Host "Connection is established and will start getting the result.. please wait"
            }

        Write-Host "Checking Database name...$($DatabaseName)"
        Get-MailboxDatabase -Identity  $DatabaseName -ErrorAction stop
        Write-Host ""
        Write-Host "Testing Output Folder"
        $temppath=Join-Path $OutputFolder -ChildPath  "tmpwrite.tmp"
        Add-Content -Path $temppath -Value "Writing test value"
        if ($RequireDBSizeInGB -lt 50){Write-Host "RequireDBSizeInGB should be bigger than 50"; return}
        if ($MaxMBxPerDB -lt 10){Write-Host "MaxBMxPerDB should be higher than 10"; return}
    }
    catch{
    Write-Host $_.exception.message -ForegroundColor Red
    return
    }



Write-Host "Validation is completed, lets start the fun :)" 
if (!($EXMBxArchiveLog)){
$AllUsersFromDB=Get-ExDatabaseusage -DBName $DatabaseName | Sort-Object MailboxGB
$TotalDBs=Prepare-MyDBLog -UsersList $AllUsersFromDB -RequireDBSizeInGB $RequireDBSizeInGB -MaxMBxPerDB $MaxMBxPerDB
}
Else{
$AllUsersFromDB,$AllUsersArchFromDB=Get-ExDatabaseusage -DBName $DatabaseName | Sort-Object MailboxGB
$TotalDBs=Prepare-MyDBLog -UsersList $AllUsersFromDB -RequireDBSizeInGB $RequireDBSizeInGB -MaxMBxPerDB $MaxMBxPerDB
$TotalArcDBs=Prepare-MyDBLog -UsersList $AllUsersArchFromDB -RequireDBSizeInGB $RequireDBSizeInGB -MaxMBxPerDB $MaxMBxPerDB
}

Write-Host "*************************Report********************" -ForegroundColor Green
Write-Host "Number of DB Required:" -NoNewline -ForegroundColor Green
Write-Host $TotalDBs[-1].DBName -ForegroundColor Red 
if ($EXMBxArchiveLog){Write-Host "Number of Archive DB required is" -ForegroundColor Green -NoNewline
     write-host $TotalArcDBs[-1].DBName -ForegroundColor red}

Write-Host "*************************************" -ForegroundColor Yellow
Write-Host "Number of Mailbox on each Proposed Database"
$TotalDBs | export-csv -Path (Join-Path -Path $OutputFolder -ChildPath "Ex-Planfull.csv") -NoTypeInformation -Force
for ($i=1;$i -le ([int]$TotalDBs[-1].DBName);$i++){
$TotalDBs| where {$_.DBName -match $i} | Measure-Object -Property UserMBXSize -Sum | select count,sum
$TotalDBs| where {$_.DBName -match $i} | select emailaddress |export-csv -Path (join-path -path $OutputFolder -childpath "\EXDB$i.csv") -NoTypeInformation |Out-Null
}

if ($EXMBxArchiveLog){
Write-Host "*************************************" -ForegroundColor Yellow
Write-Host "Number of Archive Mailbox on each Proposed Database"
$TotalArcDBs | export-csv -Path (Join-Path -Path $OutputFolder -ChildPath "ExArch-Planfull.csv") -NoTypeInformation -Force
for ($i=1;$i -le ([int]$TotalArcDBs[-1].DBName);$i++){
$TotalArcDBs| where {$_.DBName -match $i} | Measure-Object -Property UserMBXSize -Sum | select count,sum
$TotalArcDBs| where {$_.DBName -match $i} | select emailaddress |export-csv -Path (join-path -path $OutputFolder -childpath "\Arch-EXDB$i.csv") -NoTypeInformation |Out-Null
}
}

How about read these topics too

1/5 - (428 votes)