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
"start move Monitor.."
#Target DB Log Drive Threshold value to suspend moves.
#Target DB Log Drive Threshold value for auto resume suspend Mailbox Moves


#====Edit Recipient Details for Notification=====================

function GetDBFreeSpace {
  Param ($DB)
  $dbinfo = get-mailboxdatabase $DB
  $server = $dbinfo.server
  $DBDRiveS=Get-WmiObject -ComputerName $server -Class win32_volume | Select Capacity,FreeSpace,Label
  $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

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"
                  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) 
    "No Mailbox move in progress, Monitor will terminate.." 
 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
    {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)
      $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
   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) 

                $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
        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)

0..50 | % {sleep $(300/50); Write-Host "*" -n -f Yellow}    
} 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) 
     write-host "Cleaning up SourceDatabase:"$db
                    Add-Content -Value "Cleaning up SourceDatabase:$db" -Path $movemonlog
     CleanUp-Database $db
  write-host "No SourceDatabase found for Cleanup."
        Add-Content -Value "No SourceDatabase found for Cleanup." -Path $movemonlog
        if ($TargetDataBase)         
             foreach ($tdb in $TargetDataBase) 
                  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
                          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
       $suspendSub="Mailbox Move Monitor-Post Move Completion Tasks Status"
    Send-MailMessage -to $to -From $From -Subject $suspendSub -SmtpServer $smtp -Body (cat $movemonlog| Out-String)