Exchange 2013 Migration via Powershell script based upon search.

ems.jpg

ExchangeMigrationWeb.ps1Overview:

During a migration from 2010 to 2013 we were working on changing some of our e-mail retention policies.  We had implemented journaling through a Barracuda Message Archiver to retain our messages per company policy. Second to that, we also wanted to migrate our e-mail storage from our existing mentality of just letting people manage an unlimited "pot" of e-mail. This isn't very cost effective for one, second to that it doesn't make for an Exchange Environment that is easy to manage and project future costs.

Because of this we were going to finally put into place e-mail box quotas to force people to clean up their mailbox.  We already had retention policies in place, however our average mailbox size was still well over 2GB.  That being the case we decided to set a max size of 2GB in order to allow for the future projection of growth, and keep a relatively static cost regarding our high end storage that is hosting our DAG.

The first issue we ran into (Other then how to deal with lowering those over 2GB) was how to migrate forward while at the same point dealing with those boxes that were larger.   Even though we could look into exchange and get a list of all the mailboxes that are currently below the 2GB quota, to have to parse through the Migration Job Wizard and manually select all those users would be tedious.  So... a script is in order to handle this for us.

The "how":

Well even as great as Exchange is, it doesn't make it easy to accomplish this.  The "TotalItemSize" property that contains the full mailbox size is stored within the Get-MailboxStatistics CMDlet.  However the New-Migration, or New-MigrationBatch CMDlets require an e-mail address in order to process a migration, and that is NOT stored in the Get-MailboxStatistics CMDlet.  There are several "commonalities" between the various CMDlets, such as GUID, Display Name and so forth, however we decided to use DisplayName from Get-Mailbox.

Essentially what we did was run Get-MailboxStatistics with a filter based upon the TotalItemSize being less then 1.5GB and not already existing in the new databases.  We then ran the Get-Mailbox Command to return all mailbox DisplayNames, and compared the two files in order to build a text file that could then be ran to return all of the "PrimarySMTPAddress" from the Get-Mailbox command to have the correct information needed to do the migration batch file.

Below is a snippet of that code.  You will also notice that there was some triming and parsing of the file in order to translate from the output of the Get-MailboxStatistics to the format needed to run the loop to pull the e-mail addresses.

###     SET YOUR VARIABLES FOR THE SEARCH CRITERIA      ####
$ServerSearchVariable="*ex2013*"
$TotalItemSizeVariable="100MB"

###     SET YOUR VARIABLES FOR THE COMPARE and IMPORT      ####
$CompareFile="c:\temp\compare.txt"
$PrimarySMTP="C:\temp\PrimarySMTP.txt"
$MigrationEmails="C:\temp\MigrationEmails.txt"
###     Do the compare of MBStats based upon Total Item size set above and the server name variable
Write-Host -foregroundcolor Yellow "Running the compare to gather the list of users who will be part of this migration"
$MBStats=Get-Mailbox | Get-MailboxStatistics | Where-Object {$_.TotalItemSize -lt $TotalItemSizeVariable -and $_.ServerName -notlike "$ServerSearchVariable"} |Select-Object DisplayName
$MBName=Get-Mailbox | Select-Object DisplayName
$FileCompare=Compare-Object $MBStats $MBName -IncludeEqual
$FileCompare | Where-Object {$_.SideIndicator -like "=="} | Out-File $CompareFile
###  Here I am Trimming the file to get it ready for the comparison
Write-Host -foregroundcolor Yellow "Trimming and parsing file"
(Get-Content $CompareFile) | ForEach-Object {$_ -replace "@{DisplayName=", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace "}", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace "InputObject", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace "SideIndicator", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace "-----------   ", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace " --  ", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace " ==  ", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace " ", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ? {$_.trim() -ne "" } | Set-Content $CompareFile
###     Comparing the Get-MailboxStatistics search to the full list of e-mail addresses and returning PrimarySMTP to setup the text file for the migration
Write-Host -foregroundcolor Yellow Comparing the files and translating to e-mail addresses
$FinalCompare=Get-Content $CompareFile
Foreach ($line in $FinalCompare)
{
    $smtp=Get-Mailbox | Where-Object {$_.Name -eq "$line"} | Select-Object PrimarySmtpAddress
    Add-Content $PrimarySMTP $smtp
}
### Pruning File prior to import
Write-Host -foregroundcolor Yellow "Final Pruning"
(Get-Content $PrimarySMTP) | ForEach-Object {$_ -replace "@{PrimarySmtpAddress=", ""} | Set-Content $PrimarySMTP
(Get-Content $PrimarySMTP) | ForEach-Object {$_ -replace "}", ""} | Set-Content $PrimarySMTP

The above code basically gives you a list of E-Mail addresses based upon the search criteria you set and put it's into the proper format for the New-Migration CMDLet.  The file that is created will look like:

EMailAddress
user1@domain.com
user2@domain.com
user3@domain.com
user4@domain.com
...

Below is the rest of the script (Also Attached).   The first portion of it makes sure that the location of the temp files is clean on the off chance it wasn't prior.  The last portion not only starts the exchange migration, but also cleans up after itself.

#### Cleanup of Previous files if they existed 

    if (Test-Path C:\temp\compare.txt | Where-Object {$_ -eq "True"})
    {
        Remove-Item C:\temp\compare.txt
    }
    else
    {
        Write-Host -foregroundcolor Gray "Compare.txt didn't exist"
    }

    if (Test-Path C:\temp\PrimarySMTP.txt | Where-Object {$_ -eq "True"})
    {
        Remove-Item C:\temp\PrimarySMTP.txt
    }
    else
    {
        Write-Host -foregroundcolor Gray "PrimarySMTP.txt didn't exist"
    }

    if (Test-Path C:\temp\MigrationEmails.txt | Where-Object {$_ -eq "True"})
    {
        Remove-Item C:\temp\MigrationEmails.txt
    }
    else
    {
        Write-Host -foregroundcolor Gray "MigrationEmails.txt didn't exist"
    }

###     SET YOUR VARIABLES FOR THE SEARCH CRITERIA      ####

$ServerSearchVariable="*ex2013*"
$TotalItemSizeVariable="400MB"

###     SET YOUR VARIABLES FOR EXCHANGE ENVIRONMENT     ####
$ExchDB="EX2013-DAG1"
$MigrationName="Under 400 MBv2"

###     SET YOUR VARIABLES FOR THE COMPARE and IMPORT      ####

$CompareFile="c:\temp\compare.txt"
$PrimarySMTP="C:\temp\PrimarySMTP.txt"
$MigrationEmails="C:\temp\MigrationEmails.txt"

###     Do the compare of MBStats based upon Total Item size set above and the server name variable

Write-Host -foregroundcolor Yellow "Running the compare to gather the list of users who will be part of this migration"

$MBStats=Get-Mailbox | Get-MailboxStatistics | Where-Object {$_.TotalItemSize -lt $TotalItemSizeVariable -and $_.ServerName -notlike "$ServerSearchVariable"} |Select-Object DisplayName
$MBName=Get-Mailbox | Select-Object DisplayName
$FileCompare=Compare-Object $MBStats $MBName -IncludeEqual
$FileCompare | Where-Object {$_.SideIndicator -like "=="} | Out-File $CompareFile

###  Here I am Trimming the file to get it ready for the comparison

Write-Host -foregroundcolor Yellow "Trimming and parsing file"
(Get-Content $CompareFile) | ForEach-Object {$_ -replace "@{DisplayName=", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace "}", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace "InputObject", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace "SideIndicator", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace "-----------   ", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace " --  ", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace " ==  ", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ForEach-Object {$_ -replace " ", ""} | Set-Content $CompareFile
(Get-Content $CompareFile) | ? {$_.trim() -ne "" } | Set-Content $CompareFile

###     Comparing the Get-MailboxStatistics search to the full list of e-mail addresses and returning PrimarySMTP to setup the text file for the migration

Write-Host -foregroundcolor Yellow Comparing the files and translating to e-mail addresses
$FinalCompare=Get-Content $CompareFile
Foreach ($line in $FinalCompare)
{
    $smtp=Get-Mailbox | Where-Object {$_.Name -eq "$line"} | Select-Object PrimarySmtpAddress
    Add-Content $PrimarySMTP $smtp

}

### Pruning File prior to import
Write-Host -foregroundcolor Yellow "Final Pruning"
(Get-Content $PrimarySMTP) | ForEach-Object {$_ -replace "@{PrimarySmtpAddress=", ""} | Set-Content $PrimarySMTP
(Get-Content $PrimarySMTP) | ForEach-Object {$_ -replace "}", ""} | Set-Content $PrimarySMTP

###   SENDING NOTIFICATION MESSAGE
###   Setting Variables for the message   ###

$Smtp = "SMTP SERVER" 
$From = "noreply@DOMAIN.com" 
$CC=""
$BCC=""
$Subject = "Your E-Mail Box is Migrating"  
$Body = get-content C:\TEMP\content.html

#### Now send the email using \> Send-MailMessage  

### IF YOU NEED TO CC or BCC you can comment out the current Send-MailMessage Line and uncomment the one containing the CC and BCC arguments
# Send-MailMessage -SmtpServer $Smtp -To $To -From $From -CC $CC -BCC $BCC -Subject $Subject -Body "$Body" -BodyAsHtml -Priority high 

$NotificationPerson=Get-Content $PrimarySMTP
Foreach ($person in $NotificationPerson)
{
Send-MailMessage -SmtpServer $Smtp -To $person -From $From -Subject $Subject -Body "$Body" -BodyAsHtml -Priority high 

}

###  File pruned, need to added EMailAddress to format import file
Write-Host -foregroundcolor Yellow "Reformating Migration file"
 Add-Content -Path $MigrationEmails -Value EmailAddress
 Add-Content -Path $MigrationEmails -Value (Get-Content $PrimarySMTP)

###     BEGIN MIGRATION   ####
Write-Host -foregroundcolor Yellow "Adding Migration to Exchange 2013"
New-MigrationBatch -Name "$MigrationName" -CSVData ([System.IO.File]::ReadAllBytes("$MigrationEmails")) -Local -TargetDatabase $ExchDB -AutoStart -AutoComplete

Write-Host -foregroundcolor Yellow "##################################"

    if (Get-MigrationBatch -Identity "$MigrationName" | Where-Object {$_.Identity -like "$MigrationName"})
    {
        Write-Host -foregroundcolor Yellow "Migration Batch of $MigrationName has started"
    }
    else
    {
        Write-Host -foregroundcolor Yellow "$MigrationName did NOT START"
    }

Write-Host -foregroundcolor Yellow "##################################"
Write-Host -foregroundcolor Yellow "Cleaning Up Files"
Write-Host "Starting sleep to allow upload."
Start-Sleep 30

###   CLEANUP FILES

#Remove-Item $CompareFile
#Remove-Item $PrimarySMTP
#Remove-Item $MigrationEmails

#Write-Host -foregroundcolor Yellow "$CompareFile , $PrimarySMTP , and $MigrationEmails were removed"
Write-Host -foregroundcolor Yellow "COMPLETE"

ExchangeMigrationWeb.ps1