Quantcast
Channel: Exchange Server 2010 forum
Viewing all articles
Browse latest Browse all 15005

Exchange 2010 Migration Script Memory Usage

$
0
0

We are migrating our users from Exchange 2007 to Exchange 2010. We have developed a script to do this.

Two questions:

  • Looking at the script, is there anything that could be done to improve it's performance?
  • We have found that it uses massive amounts of RAM on the Exchange CAS for small handfuls of users being migrated. Why would it be doing this?
Param (
	[Parameter(Position=0, Mandatory=$False, HelpMessage="The source server to move mailboxes from.")]
	[ValidateNotNullOrEmpty()]
	[String] $Source2007Server,
	[Parameter(Position=1, Mandatory=$False, HelpMessage="The source database to move mailboxes from in the format of `"Server\StorageGroup\Database`", e.g.: `"SERVER01\Storage Group 01\Database01`".")]
	[ValidateNotNullOrEmpty()]
	[String] $Source2007Database,
	[Parameter(Position=2, Mandatory=$False, HelpMessage="The file containing users to be moved.")]
	[ValidateNotNullOrEmpty()]
	[String] $SourceFile,
	[Parameter(Position=3, Mandatory=$False, HelpMessage="The destination server to move mailboxes to.")]
	[ValidateNotNullOrEmpty()]
	[String] $Target2010Server,

	[Parameter(Position=4, Mandatory=$False, HelpMessage="Specifies the number of bad items to skip if the move encounters corrupted items in the mailbox.")]
	[ValidateScript({$_ -ge 0})]
	[Int] $BadItemLimit = 0,

	[Parameter(Position=5, Mandatory=$False, HelpMessage="Specifies to suspend the migration when at the Ready To Complete stage.")]
	[Switch] $SuspendWhenReadyToComplete,
	[Parameter(Position=6, Mandatory=$False, HelpMessage="The maximum number of mailboxes to be moved.")]
	[ValidateNotNullOrEmpty()]
	[Int] $NumberOfMoves,
	[Parameter(Position=7, Mandatory=$False, HelpMessage="Specifies if error messages should be sent by e-mail.")]
	[Switch] $SendErrorsByEmail,
	[Parameter(Position=8, Mandatory=$False, HelpMessage="Specifies the batch name to be used.")]
	[String] $BatchName
	)
#
#
# Example Usage
#  & .\EX2010Migrate.ps1 -SourceFile:".\users.txt" -BadItemLimit:10 -SuspendWhenReadyToComplete:$False -BatchName:"TestBatch-1" -SendErrorsByEmail:$True
#

$WarningPreference = "SilentlyContinue"
#$TargetMRSServer = "server133.contoso.com"

Function SendWelcomeMsg 
{
	# Send Welcome Message
$msg = @"<p>Your mailbox was successfully moved to <strong><em>email</em></strong>, company’s new  enterprise email system powered by Exchange 2010. Please take a moment to drop us a note and provide your feedback by completing our survey: (**https://survey.contoso.com/surveys/?s=raLDUp1**) .</p><p>If you have any questions or concerns please email us at migrationss@contoso.com &nbsp; </p>

Changes of note that users experience after migration:
<ol> <li> The default view in Outlook Web Application (OWA) is Conversation Mode.  To change this, click on the arrow after Conversations by Date and uncheck the box next to ‘Conversations’ at the end of the drop-down list.</li><li>The autocomplete history of most frequently used email addresses has been cleared.  This is a known function of the upgrade to Exchange 2010.  History will be rebuilt as emails are sent and received. </li></ol> <p>No adverse effects are expected, however if you experience any issues after the migration, please use your normal support channel.</p><p>Additional information is available online:</p><ul> <li><p>Preferred email clients and client configurations are available here: (**http://it.contoso.com/email/clients**) </p></li><li><p>Technical support information: (**http://it.contoso.com/email/exchange-2010-release-notes/**)</p></li></ul><p>Thank you for your patience during this process.</p><p>&nbsp;&nbsp;&nbsp;<em>Email Upgrade Team</em><br>&nbsp;&nbsp;&nbsp;Website: (**http://it.contoso.com/email/exchange-2010**) </p>

NOTE: Embedded hyperlinks are disabled in this message. Please copy and paste the URL without the ** into your browser to view.

"@


If ($BatchName){
	foreach ($mbx2 in (Get-MoveRequest -ResultSize Unlimited -MoveStatus Completed -BatchName $BatchName)) {
		$MBXEmailAddress = (Get-Mailbox -Identity $mbx2.alias).PrimarySmtpAddress.ToString()
		Send-MailMessage -From $SmtpFrom -To $MBXEmailAddress -Subject "Welcome to Exchange 2010" -Body $msg -BodyAsHTML -SMTPserver $SmtpServer -DeliveryNotificationOption onFailure
	}
}
Else{
	foreach ($mbx2 in (Get-MoveRequest -ResultSize Unlimited -MoveStatus Completed)) {
		$MBXEmailAddress = (Get-Mailbox -Identity $mbx2.alias).PrimarySmtpAddress.ToString()
		Send-MailMessage -From $SmtpFrom -To $MBXEmailAddress -Subject "Welcome to Exchange 2010" -Body $msg -BodyAsHTML -SMTPserver $SmtpServer -DeliveryNotificationOption onFailure
	}
}
}
Function produceReport
{
	If ($BatchName) {
	# Use the Batch Name if available
	$moveRequests = (Get-MoveRequest -ResultSize Unlimited -BatchName $BatchName)|sort DisplayName
	}
	else {
	# Much slower but will ensure we only get the requests for this job 
	$moveRequests = (Get-MoveRequest -ResultSize Unlimited) | ? {(Get-Date (Get-MoveRequestStatistics $_).StartTimeStamp) -ge $global:startDate} | Sort DisplayName
	}
	$global:moveReport = "<HTML> <BODY>"

	# Header of the report (title and date)
	$global:moveReport = "<font size=""1"" face=""Arial,sans-serif""><h3>Mailbox Move Report</h3><h5>$((Get-Date).ToString())</h5></font>"

	# Header of the first table with the Status and Count columns
	$global:moveReport += "<table border=""0"" cellpadding=""3"" style=""font-size:8pt;font-family:Arial,sans-serif"">	<tr align=""center"" bgcolor=""#FFD700""><th>Status</th><th>Count</tr>"

	# Group all the move requests based on their status and populate the table
	$AlternateRow = 1
	ForEach ($move in ($moveRequests | Group Status))
	{
		$global:moveReport += "<tr"

		If ($AlternateRow)
		{
			# If it is an alternate row, we set the background color to grey
			$global:moveReport += " style=""background-color:#dddddd"""
			$AlternateRow = 0
		} Else
		{
			$AlternateRow = 1
		}

		$global:moveReport += "><td>$($move.Name)</td>"
		$global:moveReport += "<td align=""center"">$($move.Count)</td>"
		$global:moveReport += "</tr>";
	}


	# Check how many mailboxes didn't get moved
	$notMoved = $global:intCount - $moveRequests.Count
	If ($notMoved -gt 0)
	{
		# Write how many skipped mailboxes
		$global:moveReport += "<tr"
		If ($AlternateRow)
		{
			$global:moveReport += " style=""background-color:#dddddd"""
			$AlternateRow = 0
		} Else
		{
			$AlternateRow = 1
		}
		$global:moveReport += "><td>Skipped</td>"
		$global:moveReport += "<td align=""center"">$($notMoved)</td>"
		$global:moveReport += "</tr>";
	}

	$global:moveReport += "</table><br/>"


	# If no users were migrated, don't generate the rest of the report (which would be empty)
	If ($moveRequests.Count -eq $null) { $global:moveReport += "</body></html>"; Return }
	$global:moveReport += "<table border=""0"" cellpadding=""3"" style=""font-size:8pt;font-family:Arial,sans-serif"">	<tr align=""center"" bgcolor=""#FFD700""><th>User</th><th>Status</th><th>Item Count</th><th>Mailbox Size</th><th>Bad Items</th><th>Overall Duration</th></tr>"


	$AlternateRow = 1
	$moveRequests | ForEach {
		$global:moveReport += "<tr"
		If ($_.Status -match "Failed")
		{
			# If a mailbox move failed, we set the background color to Red
			$global:moveReport += " style=""background-color:#FF0000"""
		}
		Else
		{
			If ($AlternateRow)
			{
				$global:moveReport += " style=""background-color:#dddddd"""
				$AlternateRow = 0
			} Else
			{
				$AlternateRow = 1
			}
		}
		$global:moveReport += "><td>$($_.DisplayName)</td>"
		$global:moveReport += "<td align=""center"">$($_.Status)</td>"
		$global:moveReport += "<td align=""right"">$((Get-MoveRequestStatistics $_.Identity).TotalMailboxItemCount)</td>"
		$global:moveReport += "<td align=""right"">$((Get-MoveRequestStatistics $_.Identity).TotalMailboxSize)</td>"
		$global:moveReport += "<td align=""right"">$((Get-MoveRequestStatistics $_.Identity).BadItemsEncountered)</td>"
		$global:moveReport += "<td align=""right"">$((Get-MoveRequestStatistics $_.Identity).OverallDuration)</td>"
		$global:moveReport += "</tr>";
	}

	$global:moveReport += "</table><br/>"
	$global:moveReport += "</BODY></HTML>"
}


# Return the position of the smallest DB in the array
Function GetSmallestDB
{
	[Int] $intSmallestSize = -1
	
	# Go through the arrSizes array to get the smallest DB
	For ([Int] $x = 0; $x -lt $intNumberDBs; $x++)
	{
		If (($intSmallestSize -eq -1) -or ($arrDBSizes[$x, 1] -lt $intSmallestSize))
		{
			$intSmallestSize = $arrDBSizes[$x, 1]
			$intSmallestPos = $x
		}
	}

	# Return the position on arrDBSizes array of the smallest DB
	Return $intSmallestPos
}


Function printDBSizesArray
{
	For ([Int] $x = 0; $x -lt $intNumberDBs; $x++)
	{
		Write-Host $arrDBSizes[$x, 0]  $arrDBSizes[$x, 1]
	}
	Write-Host "`n"
}


Function moveUser ($user)
{
	# Get the smallest DB to move the user to and update the arrDBSizes array
	$intSmallestPos = GetSmallestDB
	$arrDBSizes[$intSmallestPos, 1] += (Get-MailboxStatistics $user).TotalItemSize.Value.ToMB()

	Write-Host "Moving $user to", $arrDBSizes[$intSmallestPos, 0]
	If ($BadItemLimit -gt 50)
	{
	If ($BatchName){
		New-MoveRequest -Identity $user -TargetDatabase $arrDBSizes[$intSmallestPos, 0] -BadItemLimit $BadItemLimit  -BatchName $BatchName -AcceptLargeDataLoss -SuspendWhenReadyToComplete:$SuspendWhenReadyToComplete -Confirm:$false -MRSServer $TargetMRSServer
	}
	Else {
		New-MoveRequest -Identity $user -TargetDatabase $arrDBSizes[$intSmallestPos, 0] -BadItemLimit $BadItemLimit -AcceptLargeDataLoss -SuspendWhenReadyToComplete:$SuspendWhenReadyToComplete -Confirm:$false -MRSServer $TargetMRSServer
	}
	} Else {
	If ($BatchName){
		New-MoveRequest -Identity $user -TargetDatabase $arrDBSizes[$intSmallestPos, 0] -BadItemLimit $BadItemLimit -BatchName $BatchName -SuspendWhenReadyToComplete:$SuspendWhenReadyToComplete -Confirm:$false -MRSServer $TargetMRSServer
	}
	Else {
		New-MoveRequest -Identity $user -TargetDatabase $arrDBSizes[$intSmallestPos, 0] -BadItemLimit $BadItemLimit -SuspendWhenReadyToComplete:$SuspendWhenReadyToComplete -Confirm:$false -MRSServer $TargetMRSServer

	}
	}

	# If we have already moved as many users as we wanted, then stop
	$global:intCount++
	If ($global:intCount -eq $NumberOfMoves) { finishMigration }

	Start-Sleep -S 1
}



Function finishMigration
{
	#Write-Host "`nFinal Result:`n" -ForegroundColor Green
	#printDBSizesArray
	$moveRequests = Get-MoveRequest -ResultSize Unlimited
	While (($moveRequests | ? {$_.Status -match "InProgress"}) -or ($moveRequests | ? {$_.Status -eq "Queued"}))
	{
		# Sleep for 5 minutes
		Start-Sleep -Seconds 300
		$moveRequests = Get-MoveRequest -ResultSize Unlimited
	}

	produceReport
	SendWelcomeMsg
	# We can also export the report to a file
	$dt = Get-Date -format "yyyyMMdd_hhmm"
	$global:moveReport | Out-File ".\movereports\move_report_$dt.html"



	Send-MailMessage -From $SmtpFrom -To $SmtpTo -Subject "Move Complete!" -Body $global:moveReport -BodyAsHTML -SMTPserver $SmtpServer -DeliveryNotificationOption onFailure
	Exit
}

Function sendError ([String] $strError)
{
	Write-Warning $strError
	If ($SendErrorsByEmail) { Send-MailMessage -From $SmtpFrom -To $SmtpTo -Subject "Move Error!" -Body $strError -SMTPserver $SmtpServer -DeliveryNotificationOption onFailure -Priority High }
	Exit
}


# Validate Input
If ($Source2007Database -and $Source2007Server)
{
	sendError "Please use only -Source2007Server or -Source2007Database, not both."
}

If ($Source2007Server)
{
	# Verify that the mentioned server is actually a 2007 Mailbox server and that there are mailboxes in it to be moved
	$2007server = Get-ExchangeServer $Source2007Server
	If (!$2007server.IsMailboxServer -or $2007server.AdminDisplayVersion -notmatch "Version 8")
	{
		sendError "`"$Source2007Server`" is not a valid Exchange 2007 Mailbox server!"
	}

	If ((Get-Mailbox -ResultSize Unlimited -Server $Source2007Server | Measure-Object).Count -eq 0)
	{
		sendError "There are no users in `"$Source2007Server`" server."
	}
}

If ($Source2007Database)
{
	If ($Source2007Database -notlike "*\*\*")
	{
		sendError "Please use `"Server\StorageGroup\Database`" format."
	}
	$2007DB = Get-MailboxDatabase $Source2007Database
	If ($2007DB.ExchangeVersion -notlike "*(8*")
	{
		sendError "`"$Source2007Database`" is not a valid Exchange 2007 database."
	}
	If ((Get-Mailbox -ResultSize Unlimited -Database $Source2007Database | Measure-Object).Count -eq 0)
	{
		sendError "There are no users in `"$Source2007Database`" database."
	}
}

If ($Target2010Server)
{
	$2010server = Get-ExchangeServer $Target2010Server
	If (!$2010server.IsMailboxServer -or $2010server.AdminDisplayVersion -notmatch "Version 14")
	{
		sendError "`"$Target2010Server`" is not a valid Exchange 2010 Mailbox server!"
	}
	$DBs = Get-MailboxDatabase -Server $Target2010Server -Status | ? {$_.IsExcludedFromProvisioning -eq $False -and $_.Mounted -eq $True -and $_.recovery -eq $false} | Sort Name
}
Else
{
	# If we don't specify a target 2010 mailbox server, then get all available databases in 2010
	$DBs = Get-MailboxDatabase -Status | ? {$_.IsExcludedFromProvisioning -eq $False -and $_.Mounted -eq $True -and $_.recovery -eq $false} | sort Name
}

# Check if we found any databases to move the users to
If ($DBs.Count -lt 1) { sendError "No suitable target databases were found!" }


# Initialize variables
[String] $global:moveReport = $null
[DateTime] $global:startDate = Get-Date
[Int] $global:intCount = 0
[Int] $intNumberDBs = $DBs.Count
$arrDBSizes = New-Object 'Object[,]' $intNumberDBs,2
[Int] $x = 0

$SmtpServer = "relay.contoso.com" #Enter FQDN of SMTP server
$SmtpFrom = "migrations@contoso.com" #Enter sender email address
$SmtpTo = "1@contoso.com","2@contoso.com","3@contoso.com" #Enter one or more recipient addresses in an array
#$SmtpCC = "email@contoso.com" #Enter one or more recipient addresses in an array
$SmtpSubject = “email Mailbox Quota Report” #Enter subject of message

# Get the sizes of all DBs that will be used for the move and save them into the array
ForEach ($db in $DBs)
{
	$arrDBSizes[$x, 0] = $db.Identity
	$arrDBSizes[$x, 1] = ($db.DatabaseSize - $db.AvailableNewMailboxSpace).ToMB()
	$x++
}


#printDBSizesArray


# The source is an Exchange 2007 server, so move all mailboxes found in the server
If ($Source2007Server)
{
	ForEach ($mbx in (Get-Mailbox -ResultSize Unlimited -Server $Source2007Server))
	{
		moveUser $mbx
	}
}


# The source is a specific DB, so move only mailboxes found in that DB
If ($Source2007Database)
{
	ForEach ($mbx in (Get-Mailbox -ResultSize Unlimited -Database $Source2007Database))
	{
		moveUser $mbx
	}
}


If ($SourceFile)
{
	# Read the users to move from a CSV file containing the alias of the users in a column named 'User'
	Import-Csv $SourceFile | foreach { moveUser $_.username }
}


# If the user didn't specify any source, then move all Exchange 2007 mailboxes
If (!$Source2007Server -and !$Source2007Database -and !$SourceFile)
{
	Write-host "Please enter parameters!"
	exit
#	ForEach ($mbx in (Get-MailboxServer | ? {$_.AdminDisplayVersion -match "version 8"} | Get-Mailbox -ResultSize Unlimited)) { moveUser $mbx }
}


finishMigration


Viewing all articles
Browse latest Browse all 15005

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>