Table of Contents
When you synchronize on-premises Active Directory users with Azure, Office 365, or InTune, the User Principal Name (UPN) is often used to identify the users. This means that all users that will be synchronized should have the userPrincipalName attribute assigned, and the values should be unique in the Forest.
Unfortunately, Active Directory does not enforce this. The userPrincipalName attribute is not mandatory. And while the Active Directory Users and Computers (ADUC) MMC will not allow you to assign a duplicate value, you can assign duplicates in code, such as scripts.
Note: Much of the PowerShell code on this web page has long lines. To prevent word wrapping, the back apostrophe character, "`", is used, which is the PowerShell line continuation character. This allows you to copy and paste the code in a text file with the .ps1 extension.
The PowerShell Get-ADUser and Get-ADComputer cmdlets expose the UserPrincipalName property. This property is the value of the userPrincipalName attribute of the Active Directory objects. The following features of the userPrincipalName attribute are relevant:
Any on-premises Active Directory users that will be synchronized with Office 365 or Azure should have a User Principal Name assigned. The following PowerShell script can be used to find all users with no value assigned to their userPrincipalName attribute in Active Directory:
Get-ADUser -LDAPFilter "(!(userPrincipalName=*))" | Select distinguishedName
The following dsquery command can be also used to find all users with no userPrincipalName assigned in Active Directory. It can be run at the command prompt of a domain controller, or any client with RSAT (Remote Server Administration Tools) installed. The following should be entered in one line at a command prompt, or in a batch file with .bat extension to run at a command prompt:
dsquery * -Filter "(&(objectCategory=person)(objectClass=user)(!(userPrincipalName=*)))" -Limit 0
If on-premises Active Directory users are to be successfully synchronized with Office 365 or Azure, they should have a unique User Principal Name. The following PowerShell script can be used to find all objects with duplicate userPrincipalName values in Active Directory:
# Script to find objects with duplicate userPrincipalName values.
# Version 1.0 - December 4, 2018
# Retrieve all objects with a UPN value assigned.
# Users and computers can have userPrincipalName.
$Objects = Get-ADObject -LDAPFilter "(userPrincipalName=*)" `
-Properties userPrincipalName
# Hash table of UPNs. The key is the UPN, the value is the DN of the object.
$UPNs = @{}
# Loop through the objects.
ForEach ($Object In $Objects)
{
$UPN = $Object.userPrincipalName
$DN = $Object.distinguishedName
# Check if this UPN has been seen already.
If ($UPNs.ContainsKey($UPN))
{
# Duplicate UPN.
"Duplicate UPN: $UPN"
$DN_Dupl = $UPNs[$UPN]
"DN: $DN_Dupl"
"DN: $DN"
}
Else
{
# Add this UPN to the hash table.
$UPNs.Add($UPN, $DN)
}
}
Errors are raised during synchronization if duplicate email addresses are found among any of these attributes: userPrincipalName, mail, msRTCSIP-PrimaryUserAddress, and proxyAddresses. The duplicates can be among any class of object in AD. For example, if the mail attribute of a user has a value that matches one of the proxyAddresses values of a group object, then the user will not synchronize properly. The following PowerShell script can be used to find all objects with duplicates among any of these attributes:
# FindDuplIDs.ps1
# Script to find objects with duplicates among the following AD attributes:
# userPrincipalName, mail, msRTCSIP-PrimaryUserAddress, proxyAddresses
# Version 1.0 - December 8, 2018
# Version 1.1 - March 1, 2019.
# Quote attribute with lDAPDisplayName msRTCSIP-PrimaryUserAddress.
# Retrieve all objects where any of the attributes are assigned values.
$Prop = @("userPrincipalName","mail","msRTCSIP-PrimaryUserAddress",`
"proxyAddresses")
$Filter = "(|(userPrincipalName=*)(mail=*)(msRTCSIP-PrimaryUserAddress=*)"`
"(proxyAddresses=*))"
$Objects = Get-ADObject -LDAPFilter $Filter -Properties $Prop
# Hash table of IDs. The key is the ID (the value of one of the 4 attributes),
# the value is the DN of the objects with the value (and the attribute names).
# The DNs in the value are separated by the "@" character. It is assumed that
# no distinguished names have this character.
$IDs = @{}
# Loop through the objects.
ForEach ($Object In $Objects)
{
# Retrieve attribute values.
$DN = $Object.distinguishedName
$UPN = $Object.userPrincipalName
$Mail = $Object.mail
$PrimAddr = $Object."msRTCSIP-PrimaryUserAddress"
$ProxyAddrs = $Object.proxyAddresses
# Check userPrincipalName.
If ($UPN)
{
# Check if this ID has been seen already.
If ($IDs.ContainsKey($UPN))
{
# Duplicate ID, append to value.
$IDs[$UPN] = $IDs[$UPN] + "@$DN `(UPN)"
}
Else
{
# Add this ID to the hash table.
$IDs.Add($UPN, "$DN `(UPN)")
}
}
# Check mail.
If ($Mail)
{
# Only consider value after any colon character.
$Mail = ($Mail.Split(":"))[-1]
# Check if this ID has been seen already.
If ($IDs.ContainsKey($Mail))
{
# Check for the current DN.
If ($IDs[$Mail] -Like "*$DN `(*")
{
# Add mail to the list of attributes for this DN.
$IDs[$Mail] = $IDs[$Mail].Replace("$DN `(","$DN `(mail,")
}
Else
{
# Duplicate ID, append to value.
$IDs[$Mail] = $IDs[$Mail] + "@$DN `(Mail)"
}
}
Else
{
# Add this ID to the hash table.
$IDs.Add($Mail, "$DN `(mail)")
}
}
# Check msRTCSIP-PrimaryUserAddress.
If ($PrimAddr)
{
# Only consider value after any colon character.
$PrimAddr = ($PrimAddr.Split(":"))[-1]
# Check if this ID has been seen already.
If ($IDs.ContainsKey($PrimAddr))
{
# Check for the current DN.
If ($IDs[$PrimAddr] -Like "*$DN `(*")
{
# Add msRTCSIP-PrimaryUserAddress to the list
# of attributes for this DN.
$IDs[$PrimAddr] = $IDs[$PrimAddr].`
Replace("$DN `(","$DN `(msRTCSIP-PrimaryUserAddress,")
}
Else
{
# Duplicate ID, append to value.
$IDs[$PrimAddr] = $IDs[$PrimAddr] `
+ "@$DN `(msRTCSIP-PrimaryUserAddress)"
}
}
Else
{
# Add this ID to the hash table.
$IDs.Add($PrimAddr, "$DN `(msRTCSIP-PrimaryUserAddress)")
}
}
# Check proxyAddresses.
If ($ProxyAddrs)
{
# Check each address in proxyAddresses.
ForEach ($Addr In $ProxyAddrs)
{
# Only consider value after any colon character.
$Addr = ($Addr.Split(":"))[-1]
# Check if this ID has been seen already.
If ($IDs.ContainsKey($Addr))
{
# Check for the current DN.
If ($IDs[$Addr] -Like "*$DN `(*")
{
# Add proxyAddresses to the list of attributes for this DN.
$IDs[$Addr] = `
$IDs[$Addr].Replace("$DN `(","$DN `(proxyAddresses,")
}
Else
{
# Duplicate ID, append to value.
$IDs[$Addr] = $IDs[$Addr] + "@$DN `(proxyAddresses)"
}
}
Else
{
# Add this ID to the hash table.
$IDs.Add($Addr, "$DN `(proxyAddresses)")
}
}
}
}
# Enumerate all IDs.
ForEach ($ID In $IDs.Keys)
{
$Values = $IDs[$ID].Split("@")
If ($Values.Count -gt 1)
{
"Duplicate ID: $ID"
ForEach ($Entry In $Values)
{
" $Entry"
}
}
}
The script can also be found here.
Find Duplicate Email Addresses among Several Attributes of any AD ObjectIt is also linked in the "Other Resources" section below.
Office 365 requires that the UserName portion of the User Principal Name (the string before the "@" character in the UPN) meet the following conditions:
The following PowerShell function returns $True if the UserName meets the conditions above.
#Function ValidateUserName ($Name)
{
# Function to validate the UserName portion of a UserPrincipalName,
# the string before the "@" character.
# Version 1.0 - December 4, 2018.
If ($Name.StartsWith(".") -Or $Name.StartsWith("-") -Or $Name.EndsWith(".") `
-Or $Name.EndsWith("-"))
{
Return $False
}
ElseIf ($Name.Contains(".."))
{
Return $False
}
Else
{
# Invalid characters: ~ ! # $ % ^ & * ( ) + = [ ] { } \ / | ; : " < > ? ,
# Reserved characters that must be escaped with the backslash
# in regular expressions:
# [ ] ( ) . \ ^ $ | ? * + { }
[regex]$Reg = "(\~|\!|\#|\$|\%|\^|\&|\*|\(|\)|\+|\=|\[|\]|\{|\}|\\|\/|\||\;|\:|`"|\<|\>|\?|\,)
If ($Reg.Matches($Name).Count -eq 0)
{
Return $True
}
Else
{
Return $False
}
}
}
The DNS domain name portion of the UPN, the string after the "@" character, should meet the following conditions:
The following PowerShell function returns $True if the DNS domain name meets the conditions above.
Function ValidateDomain ($DNSDomain)
{
# Function to validate the DNS Domain Name portion of a UserPrincipalName,
# the string after the "@" character.
# Reserved characters that must be escaped with
# the backslash in regular expressions:
# [ ] ( ) . \ ^ $ | ? * + { }
# Version 1.0 - December 4, 2018.
Try {
[regex]$Reg = "(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-zA-Z][-0-9a-zA-Z]*[0-9a-zA-Z]*\.)+[0-9a-zA-Z][-0-9a-zA-Z]{0,22}[0-9a-zA-Z]))$"
If ($Reg.Matches($DNSDomain).Count -eq 1)
{
Return $True
}
Else
{
Return $False
}
}
Catch {
Return $False
}
}
The two PowerShell functions described above can be combined in a script to validate User Principal Names. The following script also ensures that the UPN includes one and only one "@" character.
# ValidateUPN.ps1
# PowerShell script to validate userPrincipalName attribute of
# on-premises Active Directory users
# that will be synchronized with Office 365 or Azure Active Directory.
# Version 1.0 - December 4, 2018.
$UPNs = "David.Jones@proseware.com","d.j@server1.proseware.com",`
"jones@ms1.proseware.com","j.@server1.proseware.com",`
"j@server1.proseware.com9","js#internal@server1.proseware.com",`
"j_9@[129.126.118.1]","j..s@proseware.com",`
"js*@proseware.com","js@proseware..com",`
"js@proseware.com9","j.s@Server1.Proseware.com",`
"j\""s\""@proseware.com"
ForEach ($UPN In $UPNs)
{
"--UPN: $UPN"
$Parts = $UPN.Split("@")
Switch ($Parts.Count)
{
2 {
$UserName = $Parts[0]
$Domain = $Parts[1]
If (ValidateUserName $UserName)
{
" UserName OK"
}
Else
{
" UserName Bad"
}
If (ValidateDomain $Domain)
{
" Domain OK"
}
Else
{
" Domain Bad"
}
}
Default {
" UPN Bad"
}
}
}
The function definitions shown earlier (for ValidateUserName and ValidateDomain) must appear before the ForEach statement in the code above.
Finf Duplicate Email Addresses among Several Attributes of any AD Object (PowerShell)