Script: Set-UPN-O365-PSMTP.ps1 – Sets UPNs on-premises and in Office 365


11/4/15: Posted script to TechNet Script Gallery: https://gallery.technet.microsoft.com/This-script-will-set-the-590a0907. Also updated code below and renamed the script to “Set-UPN-O365-PSMTP.ps1”

I’m working with a client who is migrating to Office 365 and we ran into the issue where users’ UPNs do not match their primary SMTP address, nor was it included as an SMTP address on their mailboxes.  In older, and maybe some current versions, of Android & iPhone devices if the user’s UPN didn’t match their primary SMTP address Autodiscover would fail. The user would then be prompted to put in the server name and login info.

With Office 365 the users must login with their UPN (be default), so its extra important that their UPN is their e-mail address. For my current client this was the issue we had to solve before we migrated to O365, once we migrate the users would need to login with their UPN. But their UPN was <samaccountname>@company.com and their Email Address Policy (EAP) in Exchange did not include this. Therefore, it would be confusing to users to tell them to login with their current UPN. In addition, since DirSync was setup months ago their UPN was already set in Office 365. With DirSync setup and a Hybrid configuration UPN updates made on-premises are not replicated to Azure AD (Office 365 AD). So I created the script below to take the PrimarySMTPAddress of a mailbox and set it as the UPN on-premises and in the cloud. I also worked my client to start using a create user script, like the one I posted here: https://blog.jasonsherry.net/2013/07/08/create-mailbox/.

  • This script was recently created, and with all of my scripts posted to my the blog code may not be updated, but they are updated on on my scripts website (http://izzy.org/Scripts).
  • If you find bugs or have questions comment below

Usage: ./Set-UPN-O365-PSMTP.ps1 <filter> [<SearchBase>]

  • Where <Filter>, required, can be any filter supported by the Get-AdUser cmdlet, see this article for examples.
  • Where [<SearchBase>], optional, can be the path to an OU to limit the results of the search
  • Example: ./Set-UPN-O365-PSMTP.ps1 * -SearchBase “OU=US,DC=Company,DC=Com”
    • This would return all users under the US OU

 Required Changes

  1. Install MS Online Services Sign-In Assistant -> http://www.microsoft.com/en-us/download/details.aspx?id=41950
  2. Install Windows Azure PowerShell  -> http://go.microsoft.com/fwlink/p/?linkid=236297
  3. $LocalDomain = “COMPANY”
    • Used to display the domain being updated
    • I might eliminate this in a future version by getting this attribute from the AD
  4. $UPNSuffix = “company.com”
    • Used to fill in the default login to O365
  5. $MakeChanges = $False
    • If set to the default of $False changes will only be logged to the screen and Set-UPN-O365.log file
  6. $UpdateO365 = $True
    • If set to $False changes will not be made to Office 365, just logged. $MakeChanges must also be set to $True for changes to be made to O365.

# WARNING: FOR EXAMPLE, NON-PRODUCTION USE 
# For more details see http://izzy.org/scripts/Warning.htm
#
# This script will set the on-premises UPN and Office 365 UPN value for a user
# to their PrimarySMTPAddress. DirSync/Azsure AD Sync will not sync UPN changes
# from on-premises to Office 365. So this script connects to O365 to make the 
# change directly.
#
# Source: http://izzy.org/scripts/O365/Set-UPN-O365-PSMTP.ps1
# Created 7/11/2014 | Last Updated 8/18/2015
#	8/22/14: Added checking for SamAccountName@UPNSuffix for O365 account, added more error handling, and more logging info
# 8/18/15: Renamed from Set-UPN-O365 to Set-UPN-O365-PSMTP.ps1
#
# Usage: .\Set-UPN-O365-PSMTP.ps1 <Filter> [<SearchBase>]
# Where: <Filter> is the filter to be used with the Get-ADUser command
#				 <SearchBase> is the optional OU to run the script against
#				 <SearchScope> controls the search level, defaults to [Subtree]
# Examples:	.\Set-UPN-O365-PSMTP.ps1 * -SearchBase "CN=Users,DC=Company,DC=Com"
#						.\Set-UPN-O365-PSMTP.ps1 * -SearchBase "OU=Sales,DC=Company,DC=Com"
#

param(
	[Parameter(Mandatory = $true)]
	[String]$Filter,
	[String]$SearchBase,
	[String]$SearchScope
)

$LocalDomain = "COMPANY"
$UPNSuffix = "company.com"

# Requires MS Online Services Sign-In Assistant -> http://www.microsoft.com/en-us/download/details.aspx?id=41950
# Requires above, Windows Azure PowerShell required to update Office 365 -> http://go.microsoft.com/fwlink/p/?linkid=236297
# For Filter examples see: http://blogs.msdn.com/b/adpowershell/archive/2009/04/14/active-directory-powershell-advanced-filter-part-ii.aspx

$MakeChanges = $False
$UpdateO365 = $True
$LogFile = "Set-UPN-O365-PSMTP.log"

If (!$MakeChanges) {Write-Host "MakeChange is set to False, changes will not be saved" -ForegroundColor Yellow}
If (!$UpdateO365) {Write-Host "UpdateO365 is set to False, changes will not be saved to Office 365" -ForegroundColor Yellow}

Import-module ActiveDirectory
If ($UpdateO365) {
	Import-Module MSOnline
	If (!$Global:O365Credentials) {
		write-output "Enter credentials for an org admin account in Office 365."
		$Global:O365Credentials = $host.ui.PromptForCredential("Need Office 365 credentials", "Please enter your user name and password.", "$env:username@$UPNSuffix", "UPN")}
	connect-msolservice -credential $Global:O365Credentials
}

If (!$SearchBase) {$SearchBase = $(Get-ADDomain).DistinguishedName}
If (!$SearchScope) {$SearchScope = "Subtree"}

Write-Host "Getting users under [$SearchBase] with a Filter of [$Filter]`n" -ForegroundColor Green
$Users = Get-ADUser -SearchScope $SearchScope -SearchBase "$SearchBase" -Filter $Filter -Properties mail
#$Users = Get-ADUser -SearchScope $SearchScope -SearchBase "$SearchBase" -Filter {sAMAccountName -eq "DChoudhu"} -Properties mail

"Local account: $env:username,Office 365 Account: $($Global:O365Credentials.UserName),Started: $(Get-Date -f "MM/dd/yyyy HH:mm:ss"),Information" | Out-File -Append $LogFile
"Filter: $Filter, SearchScope: $SearchScope, SearchBase: $SearchBase, Information" | Out-File -Append $LogFile

$Users | ForEach {
	$ADUser = $_
	If ($($ADUser.Mail)) {
		$PrimarySmtpAddress = $ADUser.Mail
		$SamAccountName = $ADUser.SamAccountName
		$CurrentUPN = $ADUser.UserPrincipalName
		Write-Host "Updating: $LocalDomain\$SamAccountName | Local UPN: [$CurrentUPN]" -ForegroundColor Cyan
		$ErrorInfo = ""
		$Updated = $False
		
		If ($ADUser.UserPrincipalName -ne $PrimarySmtpAddress) {
			Write-Host "  Local UPN: $CurrentUPN | New UPN: $PrimarySmtpAddress" -ForegroundColor Cyan
#			"$SamAccountName, $PrimarySmtpAddress, $CurrentUPN, Information, Local user updated" | Out-File $LogFile -Append
			If ($MakeChanges) {Set-ADUser $ADUser.DistinguishedName -UserPrincipalName $PrimarySmtpAddress}
			$Updated = $True
		}
		Else {Write-Host "  Local UPN already matches"  -ForegroundColor Green}
		If ($UpdateO365) {
			
			$O365User = $Null
			$LegacyUPN = "$($SamAccountName)@$($UPNSuffix)"
			$error.clear()
			$O365UPN = $CurrentUPN
			Try {$O365User = Get-MsolUser -UserPrincipalName $CurrentUPN -ErrorAction Stop}
			Catch {
				If ($_.Exception.Message -like "*User Not Found*") {
					$ErrorInfo = "O365 user not found UPN: [$CurrentUPN]"
					"$SamAccountName, $PrimarySmtpAddress, $CurrentUPN, Error, O365 user not found current UPN" | Out-File $LogFile -Append
					Write-Host "    User with UPN of [$CurrentUPN] was not found.`n    Searching using legacy UPN format: [$LegacyUPN]." -ForegroundColor Yellow
				}
				Else {
					write-error $("TRAPPED: " + $_.Exception.GetType().FullName)
					write-error $("TRAPPED: " + $_.Exception.Message)
				}
			}
			$error.clear()
			If (!$O365User) {
				$O365UPN = $LegacyUPN
				Try {$O365User = Get-MsolUser -UserPrincipalName $LegacyUPN -ErrorAction Stop}
				Catch {
					If ($_.Exception.Message -like "*User Not Found*") {
						$ErrorInfo = "O365 user not found Legacy UPN"
						"$SamAccountName, $PrimarySmtpAddress, $LegacyUPN, Error, $ErrorInfo" | Out-File $LogFile -Append
						Write-Host "    User with UPN of [$LegacyUPN] was not found and will not be updated." -ForegroundColor Red
					}
					Else {
						write-error $("TRAPPED: " + $_.Exception.GetType().FullName)
						write-error $("TRAPPED: " + $_.Exception.Message)				
					}
				}
			}
			If ($O365User) {
				$ErrorInfo = ""
				If ($O365User.UserPrincipalName -ne $PrimarySmtpAddress) {
					Write-Host "  O365 UPN: $O365UPN | New UPN: $PrimarySmtpAddress" -ForegroundColor Cyan
					$Updated = $True
					$error.clear()
					Try { If ($MakeChanges) {Set-MsolUserPrincipalName -UserPrincipalName $O365UPN -NewUserPrincipalName $PrimarySmtpAddress -ErrorAction Stop}}
					Catch {
						$Updated = $False
						write-error $("TRAPPED: " + $_.Exception.GetType().FullName)
						write-error $("TRAPPED: " + $_.Exception.Message)
						"$SamAccountName, $PrimarySmtpAddress, $CurrentUPN, Error, Type: $($_.Exception.GetType().FullName)" | Out-File $LogFile -Append
					}
				}
				Else {Write-Host "  Office 365 UPN already matches"  -ForegroundColor Green}
				
			}
		} # If ($UpdateO365)
		If (!$ErrorInfo) {If ($Updated) {$ErrorInfo = "Updated"}Else {$ErrorInfo = "Already matched"}}
		"$SamAccountName, $PrimarySmtpAddress, $CurrentUPN, Information, $ErrorInfo" | Out-File $LogFile -Append
	} # IF Mail
} #ForEach

About Jason Sherry

I am a ~30 year Exchange consultant and expert. I currently work for Commvault as a Solutions Specialist for Microsoft Infrastructure For more info see my resume at: http://resume.jasonsherry.org
This entry was posted in Exchange, Microsoft, Script, Technical and tagged , , . Bookmark the permalink.

1 Response to Script: Set-UPN-O365-PSMTP.ps1 – Sets UPNs on-premises and in Office 365

  1. Pingback: Weekly IT Newsletter – August 11-15, 2014 | Just a Lync Guy

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s