Multi-Threading Powershell script to Check and Repair numerous Exchange databases

For a global customer with terrabytes of Exchange 2013 data, I recently wrote a multi-threading powershell script I’d like to share with everyone.

The use case was an integrity check of the database backups prior to Exchange maintenance. First we have to commit all log files to a large number of databases, then run a surface or deep check on these databases before we can be relatively sure a restore won’t fail.

All database backups were mounted to mountpoints in a root folder on a windows server. Each database had it’s own subfolder in the root folder, the logfiles were in the same folder as the .edb file.

ESEUtil is our tool of choice when running integrity checks on Exchange databases. The tool has to be installed on the same server, the executable path is then referenced in the script.


  • Backed up databases are mounted to mount points
  • The root folder contains a seperate folder for each database
  • The .chk file and transaction logs are in the same folder as the database (.edb) they belong to
  • ESEUtil is installed on the server

Once completed, we’re ready to configure the script. The script is multi-threading, which means it can check several databases at once.  I recommend using CPU’s-1 threads. Note that ESEUtil can use up a LOT of memory.

Script configuration

$DBPath      = “D:\DATABASES”  #This is the path to the folder on your machine where you mounted each database to subfolder (example: D:\DATASES\DB01)

$LogFile     = “D:\DATABASES\Log.txt” #Results will be written here, including errors ESEUtil spills out

$ESEUtil     = ‘D:\ESEutil\eseutil.exe’ #Path to ESEUtil.exe

$maxThreads  = 4 #Number of concurrent checks to run

$deepscan = $true #Set to $False if you only want to run a /mh instead of /g after commit

Running the script

The script is now ready to run, right click EDBChecker_V0.3.ps1 and choose Run with Powershell. You’ll see a window, it’ll take some time to load and you’ll see a progress bar come up. Checking databases with /g can take a very long time (hours) per database, you can monitor eseutil with your task manager. Once databases have been checked, a line will be written to the log file.  Once repaired, another line will be written and the job engine will move on to the next database.

The script


Note: EdbChecker_V0.3Thread.ps1 is a required file and should be in the same folder as EdbChecker_V0.3.ps1, but should not be run.


And for advanced users, if you want to be really cool and let your Offline drives in drive manager automatically get mapped to a mountpoint, so you don’t have to manually assign a mountpoint to each disk, you may modify this snippet:

$root = "D:\mountpoint\"

foreach($disk in Get-Disk | where-object {$_.AllocatedSize /1GB -gt 2450}){
Set-Disk -Number $disk.Number -IsOffline $False

Set-Disk -Number $disk.Number -IsReadonly $False

Get-Partition -DiskNumber $disk.Number | where-object {$_.DriveLetter} | % {
$drive = “$($_.DriveLetter):”
remove-partitionaccesspath -disknumber $disk.Number -partitionnumber $_.PartitionNumber -accesspath $drive

Get-Partition -DiskNumber $disk.Number | where-object {$_.Size /1GB -gt 2450} | % {
$foldername = “$($disk.Number)$($_.PartitionNumber)”
$foldername = “$($root)$($foldername)”
New-Item $foldername -type directory
add-partitionaccesspath -disknumber $disk.Number -partitionnumber $_.PartitionNumber -accesspath $foldername

You’ll have to modify the where-object clauses to select the correct disk/partition in your situation. In my situation they were over 2450GB in size, and all other disks weren’t.

Notify of

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Inline Feedbacks
View all comments