Powershell Script for monitoring Bulk Mailbox Moves on Exchange Server

There are times when we have to move mailboxes in Bulk to maintain our Exchange Server environment for various reasons, that is the crucial activity for Exchange Admin as when we bulk move mailboxes, it generates a lot of transactions logs, which are required to be truncated to maintain the log drive space, so to overcome this situation we have options to enable circular logging, but in highly available environment where there are multiple database copies to be sync enabling circular logging alone doesn't help, as this process of CRCL slows the log truncation process and log drive may fill up pretty frequently.

So this script takes care of most of the mailbox moves related works.
below are functionality included in this scripts.

  • Monitors the Log Drive Space every 5 min and if size goes beyond 55% suspends the moves.
  • Resume the Mailbox move back when the available % is Above 90 
  • Resume Failed Moves.
  • Enable or Disable Circulate Logging on the DB automatically
  • Cleanup the Database post Moves to purge moved mailboxes
  • Cleanup Completed move Request
  • Notify Admins on action Taken by move Monitor tool.


# MoveMoniter-Advanced.ps1
# Author: Sunil Chauhan
# Email: Sunilkms@gmail.com
# This Script moniters Mailbox logdrive space and mailbox move and takes required `
#action suspened or resume the move automatically.
# Version 1.0 ::: Moniter Log drive space and if it less then 55% suspend moves.
# Version 1.5 ::: Target DB Circuler Logging check added, Enable or Disable if required.
# Version 2.5 ::: Auto Resume Feature Added for suspened and failed moves as well.
# Mailbox move will be resumed automatically when 90% space free in log drive.
# Version 3.5 ::: Less CPU overhead, only Queary Log space for DB which is beeing monitored.
# Database Size info added.
# Version 4.5 ::: Email Reporting Added.
# Version 5.5 ::: Post Move taskes Added, Auto disable circuler loggin if log drive free 90%
# Auto Clean Move Requests, Auto DB Cleanup to purge moved mailboxes
cls
"start move Monitor.."
#Target DB Log Drive Threshold value to suspend moves.
$DiskTh="55"
#Target DB Log Drive Threshold value for auto resume suspend Mailbox Moves
$sDiskTh="90"

$ExitCode="FORCE"

#====Edit Recipient Details for Notification=====================
$to="to@domain.com"
$From="from@domain.com"
$Smtp="SmtpServer"
$movemonlog="movemonlog.txt"
#================================================================

function GetDBFreeSpace {
Param ($DB)
$dbinfo = get-mailboxdatabase $DB
$server = $dbinfo.server
$EDB=$dbinfo.edbfilepath.pathname.split("\")[2]
$log=$dbinfo.logfolderpath.pathname.split("\")[2]
$DBDRiveS=Get-WmiObject -ComputerName $server -Class win32_volume | Select Capacity,FreeSpace,Label
$DBD=@()
$edbs=$DBDRiveS | ? {$_.Label -eq $edb } | Select Label, @{n="Capacity GB";E={[math]::round($_.Capacity / 1073741824)}},
@{n="FreeSpace GB";E={[math]::round($_.FreeSpace / 1073741824)}}, @{Name="Free(%)" `
;expression={[math]::round(((($_.FreeSpace / 1073741824)/($_.Capacity / 1073741824)) * 100),0)}}
$dbd += $edbs
$log=$DBDRiveS | ? {$_.Label -eq $log } | Select Label, @{n="Capacity GB";E={[math]::round($_.Capacity / 1073741824)}},
@{n="FreeSpace GB";E={[math]::round($_.FreeSpace / 1073741824)}}, @{Name="Free(%)"; `
expression={[math]::round(((($_.FreeSpace / 1073741824)/($_.Capacity / 1073741824)) * 100),0)}}
$dbd += $log
$dbd
}

function CleanUp-Database
{
param ($database)
$Mailboxes = Get-MailboxStatistics -Database $database | where {$_.DisconnectReason -eq SoftDeleted}
if ($mailboxes -eq $null)
{
Write-Host "no mailbox found in the db for cleanup"
}
else
{
Write-host "Mailbox found for cleanup:"$Mailboxes.Count -f cyan
Write-Host "cleaning..."
$Mailboxes | % {
Remove-StoreMailbox -Database $_.database -Identity $_.mailboxguid -MailboxState SoftDeleted -Confirm:$False
}
Write-Host "done."
}
}

Function CleanCompMoveRequest {
write-host "Getting Completed Mailbox Move Request"
$completed = Get-MoveRequest -MoveStatus Completed -ResultSize Unlimited
if ($completed) {
write-host "Found Completed move Request to remove:" $completed.count
$completed | Remove-MoveRequest -Confirm:$false
write-host "DOne" } else {"ALL clear, No completed Move Request found."}
}

DO {
Clear-Content $movemonlog
#ALL Queued Mailbox Move Request
$queued=Get-MoveRequest -MoveStatus Queued
#All inprogress Move Requests
$MoveRequest=Get-MoveRequest -moveStatus Inprogress -ResultSize Unlimited | ? {$_.TargetDatabase -notlike "*DU*"}
#All Suspend Move Requests
$SmoveRequest=Get-MoveRequest -moveStatus Suspended
#Get unique DB for Monitor
$DataBasetoMonitor=$moveRequest | Select TargetDatabase -Unique
$SdatabaseToMonitor=$smoveRequest | Select TargetDatabase -Unique

if ($DataBasetoMonitor -eq $null -and $SdatabaseToMonitor -eq $null)
{
$ExitCode="NORMAL"
"No Mailbox move in progress, Monitor will terminate.."
Break
}
Else
{
Write-Host "
Mailbox Move InProgress:" -n
Write-host "" $MoveRequest.Count -f cyan -n
Write-host " Queued:" -n
#Display Suspend Move info only if there are Suspend Moves
if ($sDataBasetoMonitor)
{
Write-Host "" $Queued.count -f Yellow -NoNewline
Write-host " Suspended:" -n
Write-host ""$SmoveRequest.count -ForegroundColor Yellow
}
else
{Write-Host "" $Queued.count -f Yellow}
if ($DataBasetoMonitor.count -gt 1) { $dtmc = $DataBasetoMonitor.count} else {$dtmc=1}
Write-Host "Total DB to monitor" $dtmc -f cyan
}
Add-Content -Value "Mailbox Move InProgress:$($MoveRequest.Count) Queued:$($Queued.count)" -Path $movemonlog
Add-Content -Value "Total DB to monitor:$dtmc" -Path $movemonlog


#Get the Logfiles disk space stats
foreach ($Sdb in $DataBasetoMonitor)
{
$db=($Sdb).TargetDatabase.Name
$ldn=$(Get-MailboxDatabase $db).Logfolderpath.PathName.split("\")[2]

#Circular Logging Status of Database
$CLstatus = (Get-MailboxDatabase $db).CircularLoggingEnabled
$CLvalueG = "Green"
$CLvalueR = "REd"
$CL = if ($clstatus -eq "true") { $CLvalueG } else {$CLvalueR}

""
Write-host "**************************************************"
Add-Content -Value "**************************************************" -Path $movemonlog
Write-Host $DB":" -f yellow -NoNewline
Add-Content -Value "$DB" -Path $movemonlog
write-host " Free Space in DB:" -n
Write-host $(GetDBFreespace $DB)[0].'Free(%)' -f cyan -NoNewline
Write-host "(%)"
Write-host "CircularLoggingEnabled: " -n
Write-host $CLstatus -f $cL
Add-Content -Value "Free Space in DB:$($(GetDBFreespace $DB)[0].'Free(%)') %" -Path $movemonlog

if ($clstatus -eq 0)
{
Write-host "Enabling Circular Logging for DB" $db
Set-MailboxDatabase $db -CircularLoggingEnabled:$True
Add-Content -Value "Enabling Circular Logging for DB:$db" -Path $movemonlog
}

$Move = Get-MoveRequest -TargetDatabase $db -ResultSize Unlimited
Add-Content -Value "Total Mailbox Move to Target DB:$($move.count)" -Path $movemonlog
$comp = $move | ? {$_.Status -like "Completed"}
$compinp = $move | ? {$_.Status -like "inprog*"}
$failed = $move | ? {$_.Status -like "Fail*"}
$SourceDB = $move | select SourceDatabase -Unique
Write-host "Source Database:" $SourceDB.SourceDatabase.Name
Add-Content -Value "Source Database:$($SourceDB.SourceDatabase.Name)" -Path $movemonlog
Write-host "MoveRequest Completed:" -n
Write-Host $($Comp.count)"" -f cyan -n
Add-Content -Value "MoveRequest Completed:$($Comp.count)" -Path $movemonlog
Write-Host "Inprogress:" -n
Add-Content -Value "Inprogress:$($compinp.count)" -Path $movemonlog

if ($failed) { Write-Host $($compinp.count) -n -f Cyan

Add-Content -Value "Failed:$($Failed.count)" -Path $movemonlog
Write-Host " Failed:" -n -f Cyan
Write-Host $($Failed.count) -f yellow

Write-Host "Resumeing Failed Moves.."
Add-Content -Value "Resumeing Failed Moves.." -Path $movemonlog
Get-MoveRequest -targetDatabase $db -moveStatus "Failed" | Resume-MoveRequest -Confirm:$false
} else {Write-Host $($compinp.count) -f Cyan}

$Free=$(GetDBFreespace $DB)[1].'Free(%)'

if ($free -lt $DiskTh) {
Add-Content -Value "Log drive space Free(%):$free" -Path $movemonlog
Write-Host "Log Drive space seems to be high for DB:$db" " Free(%):$Free" -f Yellow
Write-Host "Move Monitor will suspend Mailbox moves for:$db" -f Yellow
Add-Content -Value "Log Drive space seems to be high for DB:$db" -Path $movemonlog
Add-Content -Value "`nMove Monitor will suspend Mailbox moves for:$db" -Path $movemonlog
$TotMove=Get-MoveRequest -TargetDatabase $db -moveStatus Inprogress
Write-Host "Total Move in progress:"$($totMove.count)

#Suspend Mailbox Move
$TotMove | Suspend-MoveRequest -confirm:$false
$n=Get-MoveRequest -TargetDatabase $db -moveStatus Inprogress
$ns=
Write-Host "Move Suspended:"($totMove.count - $n.count)
Add-Content -Value "`nMove Suspended:$($totMove.count - $n.count)" -Path $movemonlog
Add-Content -Value "**************************************************" -Path $movemonlog

#Send Notification
$suspendSub="Mailbox Move Monitor-Move Suspended for:$DB"
Send-MailMessage -to $to -From $From -Subject $suspendSub -SmtpServer $smtp
} else {
Write-host "Log drive space Free(%):" -n
Write-host $Free -f Green
Add-Content -Value "Log drive space Free(%):$free" -Path $movemonlog
Write-host "**************************************************"
Add-Content -Value "**************************************************" -Path $movemonlog
}
}
if ($sDataBasetoMonitor)
{
Write-Host "**************************************************"
Write-Host "Suspened MoveRequest Found:"$SmoveRequest.count -ForegroundColor Cyan
Write-Host "Checking if MoveRequest can be Resume."
Add-Content -Value "Suspened MoveRequest Found:$($SmoveRequest.count)" -Path $movemonlog
Add-Content -Value "Checking if MoveRequest can be Resume." -Path $movemonlog
foreach ($susDB in $sDataBasetoMonitor)
{

$db=($Susdb).TargetDatabase.Name
$Free = $(GetDBFreespace $DB)[1].'Free(%)'

if ($free -gt $sDiskTh)
{
Add-Content -Value "$db" -Path $movemonlog
Write-Host "LogDrive space seems to be normal now for DB:" -n
Write-Host $db -f yellow -NoNewline
Write-Host " Free(%):" -NoNewline
write-host $Free -f Yellow
write-host "MoveMonitor will Resume Mailbox move for DB:"$db -f Green
Write-Host "**************************************************"
Add-Content -Value "LogDrive space seems to be normal now,LogDrive free(%):$free" -Path $movemonlog
Add-Content -Value "`nResuming Mailbox move Requests." -Path $movemonlog
Get-MoveRequest -TargetDatabase $DB -moveStatus Suspended | Resume-MoveRequest -Confirm:$false
Add-Content -Value "`nMailbox Move Has been Resumed." -Path $movemonlog
Add-Content -Value "**************************************************" -Path $movemonlog
$sub="MoveMoniter:Mailbox Move has been Resumed for:$DB"
Send-MailMessage -to $to -From $from -Subject $sub -SmtpServer $smtp
}
else{
write-host "DB size still high, MoveMonitor will auto resume Mailbox Moves when LogDrive has 90% Free Space."
write-host "Currently Free(%):" -n
Write-Host $Free -ForegroundColor Cyan
Write-Host "**************************************************"
Add-Content -Value "DB size still high, MoveMonitor will auto resume Mailbox Moves when LogDrive has 90% Free Space." -Path $movemonlog
Add-Content -Value "Currently Free(%):$free" -Path $movemonlog
Add-Content -Value "**************************************************" -Path $movemonlog
}
}
}
#Write progress for waiting time..
$suspendSub="Mailbox Move Monitor-status"
Send-MailMessage -to $to -From $From -Subject $suspendSub -SmtpServer $smtp -Body (cat movemonlog.txt | Out-String)

"SLEEP FOR 5 MIN"
0..50 | % {sleep $(300/50); Write-Host "*" -n -f Yellow}
cls
} until ($moveRequest -eq 0 -and $sDataBasetoMonitor -eq 0)

#post Move Completion Tasks

if ($ExitCode -eq "NORMAL")
{
write-host "PostMove Tasks Begins.."
write-host "Getting SourceDatabase for Cleanup."
Add-Content -Value "" -Path $movemonlog
Add-Content -Value "PostMove Tasks Begens.." -Path $movemonlog
$completedmoveRequest = Get-MoveRequest -moveStatus Completed -ResultSize Unlimited
$SourceDataBase = $completedmoveRequest | Select SourceDatabase -Unique
$TargetDataBase = $completedmoveRequest | Select TargetDatabase -Unique

if ($SourceDataBase)
{
foreach ($sdb in $SourceDataBase)
{
$db=($sdb).SourceDatabase.Name
write-host "Cleaning up SourceDatabase:"$db
Add-Content -Value "Cleaning up SourceDatabase:$db" -Path $movemonlog
CleanUp-Database $db
}
}
else{
write-host "No SourceDatabase found for Cleanup."
Add-Content -Value "No SourceDatabase found for Cleanup." -Path $movemonlog
}

if ($TargetDataBase)
{
foreach ($tdb in $TargetDataBase)
{
$db=($tdb).TargetDatabase.Name
write-host "Checking if Circular Logging can be disabled for DB"$db
Add-Content -Value "------------------------------------------------------------------------" -Path $movemonlog
Add-Content -Value "Checking if Circular Logging can be disabled for DB:$db" -Path $movemonlog
$Free=$(GetDBFreespace $DB)[1].'Free(%)'
if ($free -gt 80)
{
write-host "Circular Logging has been disabled for Target DB"$db
Set-MailboxDatabase $db -CircularLoggingEnabled:$false
Add-Content -Value "`nCircular Logging has been disabled for Target DB:$db" -Path $movemonlog
Add-Content -Value "------------------------------------------------------------------------" -Path $movemonlog
}
else
{
write-host "LogDrive Space is still less then 80% Circular Logging will be kept Enabled For the moment"-f yellow
Add-Content -Value "`nLogDrive Space is still less then 80%" -Path $movemonlog
Add-Content -Value "Circular Logging will be kept Enabled For the moment" -Path $movemonlog
}
}
}

write-host "Removing Completed Move Request."
Add-Content -Value "`nRemoved Completed Move Request" -Path $movemonlog
CleanCompMoveRequest
$suspendSub="Mailbox Move Monitor-Post Move Completion Tasks Status"
Send-MailMessage -to $to -From $From -Subject $suspendSub -SmtpServer $smtp -Body (cat $movemonlog| Out-String)
}

Comments