Archive for category Exchange 2010

Script to make UPN match email in On Premises and Office 365

I was recently performing an Office 365 migration from On-Premises Exchange and needed to change the UPN’s to match the email address.

One would assume that Dirsync would update the Office 365 environment for unlicensed users, but for whatever reason in this environment this proccess does not work. After spending some time on the phone with Microsoft Support it was determined it would be best to do one of the following:

1. Use the SetUPN VBS script to change the UPN’s locally in AD then Disable and Enable Dirsync (potentially taking up to 72 hours where no migrations or testing would be done)
2. Write a script to change the UPN in both local AD and Office 365

I chose to go with the second path to avoid the extra time. I was not able to find any current scripts out there to do this so I wrote one from scratch to handle it, sharing this for anyone else who may end up in the same boat. The script is powershell based and you will need to update a few portions to match your environment. As always USE AT YOUR OWN RISK and Test in a lab first.

 

# *************************************************************************************************
# * Script to change UPN to match email address for both On Premises AD and Microsoft Online *
# * Prior to running this make sure you have added the UPN suffixes to your local active director *
# * Author: Bryan Sprowls (bsprowls at gmail.com) *
# * Author Date: 8/24/2012 *
# * Logging functions by Axel Kara *
# *************************************************************************************************

Import-Module msonline

$LogFile = “C:\UPNTool\log.log”

$o365Cred = Get-Credential

Connect-MsolService -credential $o365Cred

# Edit this line to match your organization including the @
$CloudDomain = “@domain.onmicrosoft.com”

# Edit this line to match your organization
$FederatedDomains = “Domain 1.com”, “Domain 2.com”, “Domain 3.com”, “domain 4.net”

# LDAP query for users with mail attribute set
$SrchrFilter = “(&(objectclass=user)(mail=*))”

# Setup our directory search and base on RootDSE
$Srchr = New-Object system.DirectoryServices.DirectorySearcher(“LDAP://RootDSE”)
$Srchr.PageSize = 1000
$Srchr.Filter = $SrchrFilter
$Srchr.SearchScope = “Subtree”

$props = “name”, “distinguishedname”, “mail”, “userprincipalname”

foreach ($prop in $props) {$Srchr.PropertiesToLoad.Add($prop) | Out-Null}

# Execute our search
$SrchrResults = $Srchr.FindAll()

foreach ($Result in $SrchrResults) {

$ADUser = $Result.Properties

$UserDN = $ADUser.distinguishedname

# Dont do this for users in an OU for disabled users – this can be removed if not needed for your environment
if ($UserDN -notlike “*Disabled*”) {

$OldUPN = [string]$ADUser.userprincipalname
$NewUPN = [string]$ADUser.mail
$InterumUPN = $NewUPN.Split(“@”)[0] + $CloudDomain
$NewUPNDomain = $NewUPN.split(“@”)[1]

# only run the changes if the UPN domain is a federated domain
if ($FederatedDomains -contains $NewUPNDomain){

try {

Write-Host $InterumUPN
Write-Host $NewUPNDomain

# change UPN in AD
$user = system.DirectoryServices.DirectoryEntry(“LDAP://$UserDN”)
$user.psbase.invokeset(‘userprincipalname’,$NewUPN)
$user.psbase.commitchanges()

# Change office 365 old UPN to interum UPN
Set-MsolUserPrincipalName -UserPrincipalName $OldUPN -NewUserPrincipalName $InterumUPN

# Wait a second for execution
Start-Sleep -Seconds 1

# Change office 365 interum UPN to new UPN
Set-MsolUserPrincipalName -UserPrincipalName $InterumUPN -NewUserPrincipalName $NewUPN

}
Catch {

write-log -msg $_

}
}

}

}

function Write-Log($Msg, [System.Boolean]$LogTime=$true){
#
# .SYNOPSIS
# Creates a log entry
# .DESCRIPTION
# By default a time stamp will be logged too. This can be
# disabled with the -LogTime $false parameter
# .NOTES
# Author: Axel Kara, axel.kara@gmx.de
# .EXAMPLE
# Write-Log -Msg ‘Log entry created successfull.’ [-LogTime $false]
#
if($LogTime){
$date = Get-Date -format dd.MM.yyyy
$time = Get-Date -format HH:mm:ss
Add-Content -Path $LogFile -Value ($date + ” ” + $time + ” ” + $Msg)
}
else{
Add-Content -Path $LogFile -Value $Msg
}
}

function Initialize-LogFile($File, [System.Boolean]$reset=$false){
#
# .SYNOPSIS
# Initializes the log file
# .DESCRIPTION
# Creates the log file header
# Creates the folder structure on local drives if necessary
# Resets existing log if used with -reset $true
# .NOTES
# Author: Axel Kara, axel.kara@gmx.de
# .EXAMPLE
# Initialize-LogFile -File ‘C:\Logging\events.log’ [-reset $true]
#
try{
#Check if file exists
if(Test-Path -Path $File){
#Check if file should be reset
if($reset){
Clear-Content $File -ErrorAction SilentlyContinue
}
}
else{
#Check if file is a local file
if($File.Substring(1,1) -eq ‘:’){
#Check if drive exists
$driveInfo = [System.IO.DriveInfo]($File)
if($driveInfo.IsReady -eq $false){
Write-Log -Msg ($driveInfo.Name + ” not ready.”)
}

#Create folder structure if necessary
$Dir = [System.IO.Path]::GetDirectoryName($File)
if(([System.IO.Directory]::Exists($Dir)) -eq $false){
$objDir = [System.IO.Directory]::CreateDirectory($Dir)
Write-Log -Msg ($Dir + ” created.”)
}
}
}
#Write header
Write-Log -LogTime $false -Msg $LogSeparator
Write-Log -LogTime $false -Msg (((Get-ScriptName).PadRight($LogSeparator.Length – (” Version ” + $Version).Length,” “)) + ” Version ” + $Version)
Write-Log -LogTime $false -Msg $LogSeparator
}
catch{
Write-Log -Msg $_
}
}

 

Leave a comment

Script to balance exchange 2010 mailboxes across databases

I’ve modified a script that I used quite frequently for exchange 2007 to load balance mailboxes across databases to work on exchange 2010. Figured i would share the script

I plan to add the following enhancements so keep your eyes pealed for updates:

1. Add some logic to determining what mailbox to move as opposed to a random selection

2. ability to exclude databases


# Based on DBBalancer script originally written by BoerLowie (sammyboggmail.com)
# Rewritten to work on Exchange 2010 by Bryan Sprowls (bsprowlsgmail.com)
#
# This script is used to maintain a balance between your different Exchange 2010 Mailbox Databases.
#
# Configuration
# -------------
#
# Make sure your account is at least an Exchange Server Administrator on the Mailbox Server.
#
# The following values must be changed to reflect your environment
# - $Global:intThresholdInGB: The script will start to balance you databases if the difference between the biggest
# DB and the smallest DB exceeds this value. This should be set to something like 5 GB.
# Processing
# ----------
#
# The script will move a RANDOM mailbox from the Biggest DB to the Smallest DB until all DB are within the Threshold limit. So if you
# set the Threshold to 5 GB, all DB's will be equally loaded with a maximum difference of 5 GB.
# Only one mailbox at a time will be moved to minimize user downtime.
#
# Logging
# -------
#
# You can follow the progress of the script in the Application Log. Look for events of Source DBBalancer.
# Every mailbox move will be listed in the eventviewer.
#
# When all Mailbox Databases are balanced, the following will be displayed in the Event Viewer:
# All Mailbox databases are balanced within the Threshold limit.

$ErrorActionPreference = "SilentlyContinue"

# CHANGE THESE VARIABLES TO REFLECT YOUR ENVIRONMENT
$Global:intThresholdInGB = 1

# Do not change these variables
$Global:strBiggestDB = ""
$Global:strSmallestDB = ""
$Global:blnExecute = $True

Function Start-Init
{
$Global:objEventLog = New-Object System.Diagnostics.EventLog("Application")
$Global:objEventLog.MachineName = "."
$Global:objEventLog.Source = "DBBalancer"
$Global:objEventLog.WriteEntry("Application Started." + [System.Environment]::NewLine + [System.Environment]::NewLine + `
"Threshold in GB :" + $Global:intThresholdInGB)
}

Function Check-Prerequisites
{
[System.Int] $lintCount = 0

$lintCount += (Get-MailboxDatabase | Measure-Object).Count

If ($lintCount -gt 1)
{
Return $true
}
Else
{
Return $false
}
}

Function Get-BiggestandSmallestDB
{
Process
{
$DBs = Get-MailboxDatabase
$blnFirstRun = $true
foreach ($DB in $DBs)
{
$lonDBSize = (Get-MailboxDatabase $DB.Identity -status).DatabaseSize.ToMB() - (Get-MailboxDatabase $DB.Identity -status).AvailableNewMailboxSpace.ToMB()
If ($blnFirstRun -eq $true)
{
$Global:strBiggestDB = $DB.Name + ";" + $lonDBSize
$Global:strSmallestDB = $DB.Name + ";" + $lonDBSize
$blnFirstRun = $false
}
Else
{
If ($lonDBSize -lt $strSmallestDB.Split(";")[1])
{
$Global:strSmallestDB = $DB.Name + ";" + $lonDBSize
}
If ($lonDBSize -gt $strBiggestDB.Split(";")[1])
{
$Global:strBiggestDB = $DB.Name + ";" + $lonDBSize
}
}
}
}
}

Function Check-Threshold
{
If (([Long]$Global:strBiggestDB.Split(";")[1] - [Long]$Global:strSmallestDB.Split(";")[1]) -gt ($Global:intThresholdInGB * 1024))
{
Return $True
}
Else
{
Return $False
}
}

Function Do-MainLoop
{
While ($Global:blnExecute -eq $True)
{
Get-BiggestandSmallestDB
If (Check-Threshold -eq $True)
{
[Long]$lonDataToMove = ([Long]$Global:strBiggestDB.Split(";")[1] - [Long]$Global:strSmallestDB.Split(";")[1]) / 2
$Mailboxes = Get-Mailbox -Database $Global:strBiggestDB.Split(";")[0] -ResultSize "Unlimited" | Where-Object {$_.DisplayName -notlike "zz*"}
$intRandomMailbox = New-Object System.Random
$intRandomMailbox = $intRandomMailbox.Next(0, ($Mailboxes.Count - 1))
$Global:objEventLog.WriteEntry("Moving " + $Mailboxes[$intRandomMailbox] + " from database " + $Global:strBiggestDB.Split(";")[0] + `
" to database " + $Global:strSmallestDB.Split(";")[0] + "." + [System.Environment]::NewLine + [System.Environment]::NewLine + `
"The mailbox has a size of " + ((Get-MailboxStatistics -Identity $Mailboxes[$intRandomMailbox]).TotalItemSize))
get-moverequest | remove-moverequest -confirm:$false
new-moverequest -Identity $Mailboxes[$intRandomMailbox] -TargetDatabase $Global:strSmallestDB.Split(";")[0] -Confirm:$false

while ((get-moverequest -identity $Mailboxes[$intRandomMailbox]).Status -ne "Completed")
{
Start-Sleep -Seconds 20
}

}
Else
{
$Global:blnExecute = $False
}
}
Return $True
}

Start-Init
If (Check-Prerequisites -eq $true)
{
If (Do-MainLoop -eq $True)
{
$Global:objEventLog.WriteEntry("All Mailbox databases are balanced within the Threshold limit.")
}
}
Else
{
$Global:objEventLog.WriteEntry("Prerequisites check failed. You probably have only 1 Mailbox Database and therefor we can't move mailboxes around.")
}

, , ,

13 Comments

Upgrading Exchange 2003 FE/BE to Exchange 2010 (The environment) Part II

I’ve made a few Visio drawings to represent the environment at different stages in the migration. The first diagram below shows the legacy Exchange 2003 environment:

 

Diagram 1: Legacy Environment

As you can see it is a fairly straight forward setup. There is a 2003 Domain Controller/Global Catalog, 2 Exchange 2003 servers in a Front-End/Back-End configuration and a Windows 7 client running Outlook 2010.

The Forest and Domain functional levels will be set to Server 2003. It is important to note that prior to preparing the environment for Exchange 2010 the functional levels must be 2003 or higher. You will not be able to move forward if the functional level is any lower. This also means that you cannot have any Server 2000 or NT4 Domain controllers in the environment.

After preparing the environment for Exchange 2010 we will begin to introduce a few Exchange 2010 servers. When deploying the servers they will need to be deployed in a certain order. The proper order to introduce Exchange 2010 servers into a legacy exchange environment is the following:

  1. 1.       Client Access Server
  2. 2.       Hub Transport Server
  3. 3.       Unified Messaging Server
  4. 4.       Mailbox Server

There will be some configuration changes that need to be made but we will start getting into the technical discussions in the next parts.

Below is a drawing of what the intermediate environment will look like before we start moving users to the new environment.

 

Diagram 2: Intermediate Environment

Once we’ve completed any configurations we can start moving users using the Exchange 2010 new-moverequest cmdlet. Since we are moving from 2003 to 2010 we will be unable to online moves which means users cannot be in their mailbox while the move is in progress.

After all of the users have been moved we can begin to decommission the Exchange 2003 environment. And we may also decide to take advantage of the High Availability functions in Exchange 2010 so we will implement a DAG and a Load Balanced CAS Array. Our final environment will look like Diagram 3.

 

Diagram 3: Final Environment

 

Leave a comment

Upgrading Exchange 2003 FE/BE to Exchange 2010 (Intro)

In the next few posts I am going to walk through a couple options for upgrading from Exchange 2003 to Exchange 2010. The main purpose for this is for me to keep things straight as I work towards the MCITP: Exchange 2010 and ultimately I hope to go for MCM/MCA. The first part of this post is to just lay out high level what’s going to be done.

The first migration approach will be standing up new exchange servers in the same forest and moving users over time. The second migration approach will be installing new exchange server in a new forest to be used as a resource forest then moving users over.

I will not be walking through installation screens as I’m sure most people can figure those out. Also I will be using PowerShell in all areas that it is feasible.

Leave a comment