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.
POWERSHELL
# 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)
 }