Musings of Jason Jones

Category Archives: Exchange

In our scenario we had to keep creating new hires in the legacy domain so that we could get sidHistory, until we can say that we’re done and all things have been migrated.

I did it all in one script, but it’s a little big. I like it cause I did some new (to me) stuff like menus and consolidation. It’s what I wanted to do with the rest of the migration scripts, but just didn’t quite work right. If you’re migrating 100 people at once, you want to verify that everything in step 1 has worked correctly before going on to step 2.

Below is the script. I’ll try to explain as we go, but most of it is just re-doing of things we’ve done before.

This script also assumes that you still have your daily sync scripts going from source to target domain and that you’re waiting at least a day between new hire creation in legacy and migration to target. If not, you’ll need to run the prepare-mailboxmove script manually to create the MEU

If you’ve been following along with the process you’ll know I already covered mailbox moves. So you’re probably wondering what this post is about.

“Well, Jason,” you’re probably saying, “your process is nice enough and really saved me from having to write a lot of code, but what do I do for this users with huge mailboxes? Like, I have a few execs with 10GB mailboxes. I know I was never supposed to allow that, but, you know, they’re execs. And I have a tiny migration window.”

I’m glad you asked.

I found a nifty little switch on the mailbox move called “suspendwhenreadytocomplete”. Essentially what it does is kick off a mailbox move of a user into a temporary staging area (i.e you still have mailflow” and then when it gets to 95% it just stops. You could conceivably leave it there forever. Or days. Either one. The idea here is that that last 5% is the remaining mail items (and the changes) and then it flips the switch from MEU to mailbox and puts the mail attributes on the account in the target domain. Since I saw an average of about 20 minutes per 1GB of a mailbox during moves, I could work backward to see how much time it would take to move that mailbox and was able to kick it off early.

A couple huge caveats with this:

1. Only works from Exchange 2007 or 2010 to Exchange 2010.
2. You canNOT have Unified Messaging turned on for the user in the source domain when you start. Same as for a mailbox move. What this means is that the users won’t have voicemail until they’re actually migrated. There are ways around this using alternate names on your UM policy, but that didn’t work for us based on how we were changing things up.

This next one is pretty straightforward. We’re just creating a remote session to Exchange and then enabling Unified Messaging with the appropriate settings. Again, this is where our import file is super important as it has to have all the relevant information in it.

These next couple are very specific to our environment but I’m putting them out here for posterity. Both use the Quest AD Powershell tools, which are very powerful tools when it comes to object manipulation in AD. I suggest you go download and install them immediately – Quest

This first one sets an extended attribute on the user in the source domain so that we know they’ve been migrated. It’s more for a key for reports and stuff, but can be good info to have.

The second combines a couple of things. The first thing it does is set the displayname and UPN for the migrated user to go with our new standards (oh, did I mention we’re changing the UPN but leaving the SAM alone and changing the displayname?)

It also turns on ActiveSync for the users if they need it. Made the most sense for our scripts to put it here.

Perform a move mailbox (see below). The move mailbox converts the source account into a MEU on the source domain. This is needed for mailflow.

## Get date, set our transcript file, etc.
$date=get-date -format "yyyyMMdd"
$Tranoutput="d:\migration\Outputs\" + $date + "Mailboxmove.txt"
start-transcript -path $Tranoutput -append
##call include file
## This ensures that the variables are loaded, altho if you followed the previous article they already should be
. .\params.ps1
## Create Remote Powershell session to the Exchange 2010 server
## Greate for making sure all your command can be run from one place
$ExchSession=New-PSSession -ConfigurationName Microsoft.Exchange -connectionuri $ExchURI -credential $LocalCredentials
import-pssession $ExchSession|out-null
## Import the include file
$import=import-csv $ImportFile
## Do a move request for each mailbox using all of our variables
## Targetdeliverydomain is important to ensure mailflow. It should be set to a 3rd SMTP domain that is only being used by the 2010 environment
## This then gets set on the MEU in the source side
foreach ($item in $import){
New-MoveRequest -Identity $item.smtp -domaincontroller $TargetDC -RemoteLegacy -RemoteGlobalCatalog $SourceDC -RemoteCredential $RemoteCredentials -TargetDeliveryDomain $TargetDeliveryDomain -baditemlimit 50
}
## clean up after yourself and close your remote powershell session
remove-pssession $ExchSession
stop-transcript

The move mailbox ends up being the easiest part of this whole process. You can run powershell commands to check the status of the move request or just go into the Exchange console and check it there.

So let’s assume that in previous code I’ve pulled a list of all users and groups and now want to run some code daily to get new users and groups. You don’t want to pull the whole list every day, as this would get a little unwieldy. But if you just want to know what in AD has changed in the past day or past week or whatever, this is a handy snippet of code that can get you there.

Datetime is always a little tricky to manipulate, but it’s very powerful if you want to do comparisons.

$dayEnd = [datetime]::Today <== Here I’m just creating a new variable and want to set it to datetime. But specifying [datetime]::Today I’m telling it just give me monthy, day and year, so it would set the variable to 02262013 00:00:00, for example. Handy since I don’t want it to give a time of NOW.

$dayStart = $dayEnd.Adddays(-1) <== So take the previous variable and subtract 1 day from it (i.e. if $dayEnd was 02262013 00:00:00, $dayStart would be 02252013 00:00:00). If you wanted it to add 3 days to the starting variable you’d just do .Adddays(3). A minus sign is a negative here, obviously. So if you wanted the last week you’d do .Adddays (-7). If you didn’t care about comparing the 2 dates you could also set the initial variable to $day=[datetime]::Today.AddDays(-7) to set $day to a week ago

$results=@() <== Create and set an array into a variable

get-distributiongroup -resultsize unlimited|where-object {$_.whencreated -ge $daystart -and $_.whencreated -lt $dayend} <== I tried doing a -filter, but that didn’t work correctly, so doing a pipe into a where clause and key off of whencreated and greater than or equal to the $daystart and less than the $dayend.

In a previous post I covered how to do this with Distribution Lists. Now I want to do the same thing for user mailboxes, however the code ended up being a little bit different since I want to key off of Primary SMTP Address vs. just the name. Since the email address has an @ sign in it, the code changes because it exports differently.

This particular code snippet gets all Mailboxes in Exchange and dumps them to a file. Easily modifiable for other purposes if you need to:

$results+=”smtp” <== because of what I want to do with the export file later, I want a header in this file. This line just creates the initial header.

foreach ($item in $(get-mailbox -resultsize unlimited)){$results+=[string]$item.PrimarySMTPAddress} <== Several things going on in this line. I like combining statements into as little possible code as I can, so this one can be confusing if you don’t know how to look at it

foreach (blah in blah){blah} <==typical for each statement that does the {} for every item in the list

$item in $(get-mailbox -resultsizeunlimited) <== Pulls every mailbox with no limitations and puts them into the array

$results+=[string]$item.PrimarySMTPAddress <== Pulls the Primary SMTP attribute from every mailbox and adds it into the array defined above. Had to convert it to a string so that it would export in the correct format

$results|out-file filename.txt <== Outputs the results of the array into a textfile