Powershell : Tackling UPN and Email Address Mismatches in Exchange


Every Exchange Administrator needs to maintain consistency between User Principal Names (UPNs) and email addresses (ProxyAddress attribute) is critical for proper identity management. This post details my journey in identifying and resolving discrepancies between UPNs and email addresses in Exchange.

The normal scenario is this:

UPN: lee.croucher@bythepowerofgreyskull.com
Mail : lee.croucher@bythepowerofgreyskull.com


I notice we had an issue where this was true:

UPN: lee.croucher@bythepowerofgreyskull.com
Mail : na_68443@<tenantid>.onmicrosoft.com

This was caused by a mailbox that was present on the mailbox or the mail  attribute being set to something other than the authorised accepted domains from an Exchange point of  view, if this is the case then the "ProxyAddress" attribute will fall back to the default domain which for Exchange Online is <tenantid>.onmicrosoft.com

The Process

This was a simple process to understand this involved taking the mailboxes that had incorrect  "ProxyAddress" attributes due to a disconnected mailbox when the mailbox was a remote mailbox from Exchanges point of view and its was deleted locally, to fix this you need to creating that mailbox once again but do not migrate the mailbox to Exchange Online.

When the "mail" attribute is fixes you can simply disconnect the local mailbox from Exchange on-premises. 

The Challenge

I recently encountered an issue where many users had mismatches between their UPN and their primary email address. This inconsistency created problems with authentication, mail routing, and user experience. The task seemed straightforward: find all mismatches and create or update mailboxes to align with the UPNs.

However, like many seemingly simple Exchange tasks, this proved more complex than anticipated and this post outlined how these addresses are correct to match the UPN.

Initial Investigation: Identifying Mismatches

The first step was to identify users with mismatches between their UPN and email address. I created a PowerShell script to query a specific OU and compare these values:

# Import required modules
Import-Module ActiveDirectory
Import-Module ExchangeOnlineManagement

# Define the OU path
$OUPath = "OU=Users,DC=bear,DC=local"

# Get current script directory for log file placement
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
$LogFile = Join-Path -Path $ScriptPath -ChildPath "UPN_Email_Mismatch_$
(Get-Date -Format 'yyyyMMdd_HHmmss').log"

# Function to write to log file
function Write-Log {
    param (
        [string]$Message
    )
    
    Write-Host $Message
    Add-Content -Path $LogFile -Value $Message
}

# Get users from the specified OU
$adUsers = Get-ADUser -Filter * -SearchBase $OUPath -Properties UserPrincipalName, Enabled
$totalUsers = $adUsers.Count
Write-Log "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Found $totalUsers users in the 
specified OU"

# Create log file header
Write-Log "UPN and Email Address Mismatch Report"
Write-Log "Generated on: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Log "OU: $OUPath"
Write-Log "----------------------------------------"
Write-Log "UPN`tEmail Address`tDisplay Name"
Write-Log "----------------------------------------"

# Process each user to find mismatches
$mismatchCount = 0
foreach ($user in $adUsers) {
    # Get the mailbox
    $mailbox = Get-Mailbox -Identity $user.UserPrincipalName -ErrorAction SilentlyContinue
    
    if ($mailbox) {
        $upn = $user.UserPrincipalName
        $email = $mailbox.PrimarySmtpAddress
        
        # Check if UPN and email address match
        if ($upn -ne $email) {
            $mismatchCount++
            $logEntry = "$upn`t$email`t$($mailbox.DisplayName)"
            Write-Log $logEntry
        }
    }
}

# Write summary
Write-Log "----------------------------------------"
Write-Log "Summary:"
Write-Log "Total users processed: $totalUsers"
Write-Log "Total mismatches found: $mismatchCount"
Write-Log "----------------------------------------"

# Clean up
Remove-PSSession $ExchangeSession

This script generated a log file with all users whose UPN differed from their primary email address which can be a problem if your SAML application uses the "mail" attribute to sign people into the application.

Fixing the mismatched "mail" attributes

Next we need to fix those records and that required another script that was run from the Exchange Management shell, this needed to read the file created with the previous script, this would be used in the next script as a parameter as you can see below:
[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)]
    [string]$LogFilePath,
    
    [Parameter(Mandatory=$false)]
    [switch]$DisableConfirm
)
Find the UPN value from the log file

The parameters -LogFilePath would allow you to set the path to the log file created in the previous script this would be handled by this part of the script that would look at that log file and extract the UPN from the corrrect places in that file: 
# Read the log file and extract UPNs
try {
    Write-Host "Reading UPNs from log file..."
    $logContent = Get-Content -Path $LogFilePath -Raw
    
    # Extract UPNs using regex pattern matching
    # Look for lines that start with an email address pattern followed by a tab
    $upnMatches = [regex]::Matches($logContent, '(?m)^([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]
    +\.[A-Za-z]{2,})\t')
    
    if ($upnMatches.Count -eq 0) {
        Write-Host "No UPNs found in the log file. Make sure the format is correct."
        -ForegroundColor Red
        exit
    }
    
    # Extract unique UPNs from matches
    $upns = @()
    foreach ($match in $upnMatches) {
        $upn = $match.Groups[1].Value
        if ($upn -match '@bythepowerofgreyskull\.com$' -and $upn -notmatch '^Summary:' 
        -and $upn -notmatch '^Total') {
            $upns += $upn
        }
    }
    
    $totalUpns = $upns.Count
    Write-Log "Found $totalUpns UPNs in the log file"
}
catch {
    $errorMessage = $_.Exception.Message
    Write-Host "Error reading log file - $errorMessage" -ForegroundColor Red
    exit
}

Testing for Potential Conflicts

Before blindly updating email addresses, I needed to understand if there would be conflicts. My initial thought was to check for proxy address conflicts across the domain:

# For each mismatch, check if the UPN exists as a proxy address elsewhere
foreach ($mismatchUser in $mismatchUsers) {
    $upn = $mismatchUser.UPN
    
    # Search for accounts where this UPN exists as a proxy address
    $filter = "proxyAddresses -like '*:$upn'"
    $conflictingUsers = Get-ADUser -Filter $filter -Properties UserPrincipalName,
     mail, proxyAddresses, Enabled
    
    if ($conflictingUsers) {
        # Found conflicts - this UPN is already in use as a proxy address
        foreach ($conflictUser in $conflictingUsers) {
            # Skip if it's the same user
            if ($conflictUser.UserPrincipalName -eq $upn) {
                continue
            }
            
            $accountType = if ($conflictUser.Enabled) { "Enabled" } else { "Disabled" }
            Write-Log "$upn`tConflict detected`tAddress in use by: $($conflictUser.
            SamAccountName) (Account Status: $accountType)"
        }
    }
}

This approach had limitations - it was slow and didn't accurately predict all potential conflicts that Exchange might encounter.

Using Exchange to "fill in the blanks"

I realized Exchange itself would provide the most accurate information about conflicts. Instead of trying to predict them, I decided to directly attempt mailbox creation with the UPN as the email address and capture any errors:

try {
    # Attempt to create/update the mailbox
    $result = Enable-Mailbox -Identity $adUser.SamAccountName -Alias $alias 
    -Database "UserMBX1" -ErrorAction Stop
    
    # Now set the primary SMTP address
    Set-Mailbox -Identity $adUser.SamAccountName -PrimarySmtpAddress $upn 
    -EmailAddressPolicyEnabled $false
    
    Write-Log "$upn`tSuccess`tMailbox created successfully"
} 
catch {
    $errorMessage = $_.Exception.Message
    
    # Check if error is due to a proxy address conflict
    if ($errorMessage -match "The proxy address '.*' is already being used by") {
        Write-Log "$upn`tError`t$errorMessage"
        
        # Extract conflicting account info
        if ($errorMessage -match "The proxy address '.*' is already being used by 
        the proxy addresses or LegacyExchangeDN of '([^']+)'") {
            $conflictingAccount = $Matches[1]
            Write-Log "Conflicting Account: $conflictingAccount"
        }
    }
}

This approach immediately revealed conflicts and provided specific information about which accounts had conflicting addresses.

Unexpected Challenge: The WindowsEmailAddress Problem

While implementing the direct approach, I ran into an unexpected challenge. Many users were failing with this error:

Could not convert property WindowsEmailAddress to type SmtpAddress. Error while 
converting string 'n/a' to result type Microsoft.Exchange.Data.SmtpAddress: 
The email address "n/a" isn't correct.

This occurred because some user accounts had invalid placeholder values ('n/a') in their email attributes.

Simply updating the AD mail attribute didn't solve the problem because Exchange was caching or referencing this value from another location.

The key insights that made this solution work:

  1. Update AD attributes first - Ensure the mail attribute matches the UPN before attempting mailbox creation
  2. Use a two-step process - First create the mailbox with minimal parameters, then set the primary SMTP address
  3. Have fallback strategies - For WindowsEmailAddress errors, using the mail-enable then convert approach worked well
  4. Always verify results - Exchange might report success but fail to create a mailbox, or report an error but still create one
  5. Detailed logging - Capture all errors and conflicts for further analysis
Previous Post Next Post

نموذج الاتصال