Fair warning, this is a long blog-post. Give me a shout if I should split it into more parts.

I recently had a customer whom was in the process of merging. They were in fact three different companies that were becoming one.

This blog post will focus on the Office 365 services, and specifically how to migrate E-mail and files from Office 365 to Office 365.  

We will be using a third-party tool (Bittitan MigrationWiz) as it isn't feasible to do a manual migration considering the cost of a consultant and the time needed. In my old blog I covered Quest OnDemand Migration, and I am excited to show the procedure for Bittitan this time around.

Cost Analysis

At Bittitan Pricing we can quickly see the prices for the migration tool.
Pricing
If you only need to migrate E-mail choose Mailbox Migration Licenses, however if you also need to migrate the files of the users, choose User Migration Bundle.
Remember: Most likely you will need a bit of both, as some are just mailboxes, and some require both migration of mailboxes and files. We'll get to this in the mapping section.

Remember what I said about the cost of a consultant? A consultant in Norway costs around $160 USD per hour.
For 200 users, the UMG licenses will cost you $3000 USD. That's about 2.4 days worth of consultant-work. I would guarantee you that no consultant would manually migrate mailboxes and files of 200 users in that time.

Now, obviously most of it could be automated, but then you'd be looking at a package-price and voila we're back at start with Bittitan MigrationWiz

Scenario, and mapping everything that will be moved

Let's take a look at our scenario:
Office365--1--1
We have three companies total

  1. Main Company
    This tenant will remain, all users from the other companies will be merged in to this one. Their domain names will also be migrated to this tenant.
  2. Company 2
    This one is on hosted Exchange, and their personal files are made available through a fileshare.
  3. Company 3
    Company 3 is already on their own Office 365 tenant, and will also be migrated to Main Company.

For the migration, we need to do some mapping of the users in the different scenarios. Starting with Company 3, as Office 365 to Office 365 migration is easier considering you only need administrator-rights and Powershell modules.
Considering the length of this blog, I will post a Part 2 with a guide on how to migrate from Company 2 (Hosted Exchange). Also, OneDrive is not covered here, but the steps are very similar. I will also write about this in Part 2.

Main problem with Hosted Exchange is that the supplier of the service will most likely not give you admin-rights, and you need to ask for all changes in the environment. It's usually not a problem, but it is a question of time. Be sure to send off all information-requests on behalf of the users a long time before the actual migration.

Company 3 Migration

For mapping of the users, I like to use scripts. We want to know which users have a license and are active today. No point in wasting a license on a blocked user, and no point in wasting a MigrationWiz User Migration Bundle on a shared E-mail.

For mapping everything I could think of that can come in handy, I created this script:

#Log on to Office 365/Exchange Online
Import-Module MSOnline
$O365Cred = Get-Credential
$O365Session = New-PSSession –ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential $O365Cred -Authentication Basic -AllowRedirection
Import-PSSession $O365Session
Connect-MsolService –Credential $O365Cred

#Get all users (You can uncomment to get only licensed users)
$users = get-Msoluser -All #| Where-Object { $_.isLicensed -equals "TRUE" }
#Create an array, and then we fill it with information we want/need
$Mycol = @()
foreach ($user in $users)
 {
# $User.xxx is for Get-MSOLUser (Check Parameters)
# $mbx.xxx is for Get-Mailbox (Check Parameters)
# $mbxstat.xxx is for Get-MailboxStatistics (Check Parameters)
# $xtra.xxx is for Get-User (Check Parameters)
# If you want to add any parameters, just add them to the list and remember to add them to $MyCol as well :)

    $mbx = get-mailbox $user.UserPrincipalName 
    $mbxstat = get-mailboxstatistics $user.UserPrincipalName 
    $xtra = Get-User $user.UserPrincipalName 
    $MyObject = New-Object PSObject -Property @{
        userName = $user.UserPrincipalName
        userLicensed = $user.IsLicensed
        User_Microsoft365E3_License= [bool](($user.Licenses).Where({$_.AccountSkuId -like '*SPE_E3'})) 
        User_Microsoft365E5_License= [bool](($user.Licenses).Where({$_.AccountSkuId -like '*SPE_E5'}))
        User_Microsoft365F1_License= [bool](($user.Licenses).Where({$_.AccountSkuId -like '*SPE_F1'}))
        User_Office365_License= [bool](($user.Licenses).Where({$_.AccountSkuId -like '*ENTERPRISEPACK'}))
        User_EMS_License= [bool](($user.Licenses).Where({$_.AccountSkuId -like '*EMS'}))
        User_PowerBI_Standard_License= [bool](($user.Licenses).Where({$_.AccountSkuId -like '*Power_BI_STAND*'}))
        User_PowerBI_Pro_License= [bool](($user.Licenses).Where({$_.AccountSkuId -like '*Power_BI_Pro*'}))
        User_CRM_License= [bool](($user.Licenses).Where({$_.AccountSkuId -like '*CRM*'}))
        User_Visio_License= [bool](($user.Licenses).Where({$_.AccountSkuId -like '*VISIO*'}))
        User_Project_License= [bool](($user.Licenses).Where({$_.AccountSkuId -like '*PROJECT*'}))
        License_EMS = $false
        License_POWER_BI_ADDON = $true
        userDepartment = $user.Department
        userTitle = $user.Title
        userStreetAddress = $user.StreetAddress
        userState =  $user.State
        userCity = $user.City
        userCountry = $user.Country
        userDisplayName = $user.DisplayName
        userFirstName = $user.FirstName
        userLastName = $user.LastName
        userMobilePHone = $user.MobilePhone
        userOffice = $user.Office
        userPostalCode = $user.PostalCode
        userUsageLocation = $user.UsageLocation
        userAlternateEmail = $user.AlternateEmailAddresses -join " - "
        userPreferredLanguage = $user.PreferredLanguage
        userPrimarySMTP = $mbx.PrimarySMTPAddress
        userAlias = $mbx.Alias
        userRecipientType = $mbx.RecipientType
        userEmailAddresses = $mbx.EmailAddresses
        userIsMailboxEnabled = $mbx.IsMailboxEnabled
        userSamAccountName = $mbx.SamAccountName
        userLanguages = $mbx.Languages
        userDatabase = $mbx.Database
        userServerName = $mbx.ServerName
        userLastlogon = $mbxstat.lastlogontime
        userLastAccount = $mbxstat.lastloggedonuseraccount
        userItemSize = $mbxstat.totalitemsize
        userCount = $mbxstat.itemcount
        userManager = $xtra.Manager
        userCompany = $xtra.Company
        userCountryRegion = $xtra.CountryOrRegion
        userHomePhone = $xtra.HomePhone
        }
$Mycol += $MyObject
}
$Mycol |select UserPrimarySmtp,
                userFirstName, 
                userLastName, 
                userTitle, 
                userName,
                userLicensed,
                userDisplayName,
                userMobilePhone,
                userOffice, 
                userDepartment,
                userStreetAddress,
                userState,
                userCity,
                userCountry,
                userPostalCode,
                userUsageLocation,
                userAlternateEmail,
                userPreferredLanguage,
                userAlias,
                userRecipientType,
                userEmailAddresses,
                userLastLogon,userItemSize,
                userCount,userLastAccount,
                userIsMailboxEnabled,
                userSamAccountName,
                userLanguages,
                userDatabase,
                userServerName,
                userManager,
                usercompany,
                userCountryRegion,
                userHomePhone | export-csv -Encoding Unicode c:\temp\UserReports_$((Get-Date).ToString('MM-yyyy')).csv -NoTypeInformation

As you can see, I am not selecting all the objects I've grabbed from the users. This will vary for each time, so I have already pre-defined them in case I need to add them in another project.

Now we have a pretty detailed list of all our users. Wash the list with the relevant users to be migrated, and let's continue:
userlist-1
Note: If you want to keep the domain-name of the company you will be migrating, change the UserPrincipalName to *.tenantname.onmicrosoft.com in your washed list. We will change it after migrating the domain name.
If you want to create users based on the new domain-name of the Main Company, change the UserPrincipalName in your list to whatever *@domain.com

Creating users

We have a washed list, and we want to create users based on this list. The users also need a license. What do you do? Again Powershell to the rescue.
Be sure to log on to the correct tenant! In the previous segment, we used the old tenant, now we will use the main tenant to create our new users.

$users = Import-Csv -Path C:\temp\Userlist.csv -Encoding UTF8

$users | ForEach-Object {

New-MsolUser -UserPrincipalName $_.UserPrincipalName -DisplayName $_.DisplayName 
-City $_.city -UsageLocation $_.UsageLocation -Country $_.Country -Department $_.Department 
-FirstName $_.FirstName -LastName $_.LastName -Office $_.Office -MobilePhone $_.MobilePhone -Title $_.Title 
-PostalCode $_.PostalCode -State $_.State -StreetAddress $_.StreetAddress
Set-MsolUserLicense -UserPrincipalName $_.UserPrincipalName -AddLicenses "Contoso:Licensepack" #change to your licensepack
} 

Notice that the Manager-attribute is not on the list of parameters. You could easily set this after Contoso:licensepack:

Set-User $_.UserPrincipalName -Manager $_.Manager

The problem is, the Manager-attribute can only be added after your license is active, and the Exchange mailbox has been activated. Our script handles the activation, but that activation might take up to 24 hours, so you'd need to add a wait command.

I prefer to run a ForEach -> Add Manager the day after.

Bittitan MigrationWiz

Creating an account and Project

We have our users, we have our lists, and now is the time to create an account at Bittitan and actually migrate the data!

  1. Register a new user
    BittRegistration

  2. In the upper righthand corner click on Purchase
    BittPurchase

  3. Remember our list? You now know which users have only an Email-account, presumably you've also seen that some are shared mailboxes. You can now buy the appropriate amount of E-mail Migration and User Migration Bundle licenses.
    BittLicenses

  4. When your licenses are bought, go back to MigrationWiz, and in the upper righthand corner, click on Projects
    BittProjects

  5. Create a new Project
    BittProjects2

  6. Choose type, we will need to create a project for both E-mail and Documents. Let's start with E-Mail
    BittMigrate

  7. Create a Project Name and create a new Customer for the Project
    BittProjectName
    For the Customer, you only need to type in a DomainName and Name of the company
    BittProjectCustomer

  8. For each project we need a Source and a Destination, the Source will be where you will be migrating from
    BittProjectSource
    Write an understandable name, and create the Endpoint. This will be our connection to Office 365. Note: Administratorrights are needed if you do not want to use impersonation rights.
    BittProjectSourceEndpoint
    And for Source, we now have:
    BittProjectSourceNew

  9. Same procedure for the destination, this will of course be the Main Company/New tenant. The target:
    BittProjectDestination
    And for Destination, we now have:
    BittProjectDestinationNew-1

  10. Click on Save and go to summary

Adding users to Project, and starting the migration

Remember our extracted lists from the 'Old' tenant? Let's bulk add them to our MigrationWiz Project!

  1. Click on the + button in our newly created project, and then click on bulk add
    BittUsersBulk

  2. Download the Sample CSV-file, and open it
    BittUsersBulkSample
    Looks a bit empty..
    BittUsersBulkSampleEmpty

  3. Let's add some data. Note: You do not need to add more than Source E-mail and Destination E-Mail if you added an administrator during the Project- Source/Destination creation.
    BittUsersBulkSampleChange
    Note: Be sure to have the same user on both Source and Destination, be careful not to dump the emails of a user to another user!

  4. Go back to MigrationWiz and add the same file back, but now with our users:
    BittUsersBulkSampleFileAdd
    MigrationWiz will read the file and populate the rows
    BittUsersBulkSampleADd
    Click Save

Now, before you continue, we need to make sure that our users (with data) have the UMB license, or else they will consume a regular E-Mail Migration License!
Either select all, or the users that will also move Documents (OneDrive, Box, Dropbox, File Server, etc):
BittAddLicenseUMB
Note: This might take a while, so please give it 30 minutes. You can check by clicking on Start Trial Migration, and it will show you if the licenses are activated on the users:
BittStartTrialCheck

Migration-Time!

When the licenses have been applied to the users, it's time to migrate. Since we have been excellent on the pre-migration phase, we just need to click on some buttons:

First of all, you should always check out the advanced options, as you might want to change something here.
BittAdvanced
Whether you want to define how far E-mail should be synchronized, or maybe you want errors delivered to some users. Check out the explanation from Bittitan on what possibilities there are here
BittAdvancedOptions

For the migration itself, just click Play:

  1. Choose the users you want to migrate, and then click on the "Play" button to start a new migration, Full Migration
    BittStartMigration

  2. Click on Start Migration
    BittStartMigrationReal

  3. Grab a cup of coffee, and watch as MigrationWiz gathers the necessary information, and starts pushing it all to the new tenant.

  4. You might get some errors, just check the error message, fix the error and retry the mailbox. Most likely it's a typo or something.

Post-Migration

Now we need to do some cleanup.

  • If you're not taking the domain with you, you should disable the mailbox so the users can't still log on to their old E-mail/tenant.
    We should also create an autoreply that tells people that the old domain is not in use anymore, in a polite manner, and refer to the new E-mail address.
    And last but not least, the the users migrated should still get all E-mails sent to the previous Email address.

Again, we use Powershell, because doing this manually on even 10 users, would be a mundane task.

$users = Import-Csv -Path C:\temp\MigrationList.csv -Encoding UTF8

$users | ForEach-Object {

Set-MailboxAutoReplyConfiguration -Identity $_.SourceAddress -AutoReplyState Enabled 
-ExternalMessage "<H4>We've moved!</h4><br><hr><br>Hi,<br><br> We've moved to domain.com<br>This E-mail will be sent to my new E-mailaddress automatically, but please update the address to $($_.DestinationAddress)<br>Have a nice day!" 
-InternalMessage "<H4>We've moved!</h4><br><hr><br>Hi,<br><br> We've moved to domain.com<br>This E-mail will be sent to my new E-mailaddress automatically, but please update the address to $($_.DestinationAddress)<br>Have a nice day!" 

Set-Mailbox -Identity $_.SourceAddress -DeliverToMailboxAndForward $true -ForwardingSMTPAddress $_.DestinationAddress

Set-CASMailbox -Identity $_.SourceAddress -ImapEnabled $False -MAPIEnabled $False -OWAEnabled $False -EwsEnabled $False -PopEnabled $False -ActiveSyncEnabled $False 

} 

Explanation on the cmdlets

Set-MailboxAutoReplyConfiguration
Here we set an autoreply, and Powershell let's us use HTML. You can use any editor online to create a nice message, and copy/paste to the script. I've added ExternalMessage (For external users) and -Internal, but if you migrate everyone internally, there is no need to add the InternalMessage parameter.

Set-Mailbox
Here we create forwarding from the old address to the new. Remember to change $.SourceAddress and $.DestinationAddress to whatever you've defined in your userlist.

Set-CASMailbox
With Set-CASMailbox cmdlet and all the parameters set to False, we are blocking access to the old Exchange without deleting the mailboxes. It's an easy way to keep active mailboxes, without removing licenses or users. I usually recommend keeping them for three months minimum or until you've made completely sure that everything that was supposed to be migrated is migrated. Be careful not to remove the users immediately, as there might be something you have forgotten.

Finally, if you are taking the domain with you, you first need to remove the domain from the Source-tenant:

$UserCredential = Get-Credential
Connect-MsolService -Credential $UserCredential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session -AllowClobber
$testpath = test-path c:\temp; If ($testpath -eq $false) {new-item -type directory c:\temp}; $dataout = @();
Get-MsolUser -All | ? {$_.UserPrincipalName -match "Sourcedomain.com"} | % {Set-MsolUserPrincipalName -ObjectId $_.objectId -NewUserPrincipalName ($_.UserPrincipalName.Split(“@”)[0] + “@Sourcedomain.onmicrosoft.com”); $dataout += "$($_.UserPrincipalName)" ; $_.UserPrincipalName };$dataout | out-file c:\temp\UPNChangeOutput.txt

This will remove the domain on all users in the tenant, and you can check the output on users affected.
Now you can remove the domain completely, and register it on the new tenant.
If you want to add it as the UPN, you can use Set-msoluser and bulk change in a ForEach-loop. Or you can use Set-Mailbox to add the old domain as an alias, there are tons of scripts to help you do it on your users.

Congratulations on finishing the worlds longest blogpost!