PowerShell find Email Addresses in AD Users and Contacts

Recently we had a user from one of our global offices saying they're unable to receive emails from our staff due to duplicate ProxyAddresses in our AD. IDFix was not useful, PowerShell was.

Recently we had a user from one of our global offices saying they're unable to receive emails from one of our staff. It turned out that the user's address in our 365 tenancy was showing up just with their @mail.on.microsoft.com address, so no mail was ever being delivered from us to them.

We've got about 20,000 synced contacts from our global offices in our AD, and often we receive various synchronization errors from Office365 due to duplicate ProxyAddresses. The IDFix tool Microsoft released is helpful when you've got a well maintained AD infrastructure that doesn't turn up 25,000 errors/warnings when you query trying to find a particular broken object. When you do have that, it's not easy to isolate the object you're trying to fix.

PowerShell does make it easy to figure out. This is another command that I've got loaded into my PowerShell Profile that helps out when dealing with this particular issue, or when finding out what account someone aliased to [email protected] to, since they clearly didn't do it to the actual help account.

function CrashCorp_FindEmailAddress(){
	<# 
	.SYNOPSIS
		Searches AD Objects for a particular email address.
	.DESCRIPTION
		Finds AD Object to which the email address is attached. Greps through all user accounts and contacts, so it may take a while.
		
		Returns the object's name, DN, all email addresses and their logon name if it is a user account.
		
		The logon name is /generally/ the same as the user email's local-part, but not always. 
		
		This function also returns contacts, and can include all gloabl contacts wth the IncludeGlobalContacts switch.
	.PARAMETER EmailAddress
		Required, string. Can be either the full address or just the name. Works against all CrashCorp domains. 
	.PARAMETER IncludeGlobalContacts
		Optional, switch. If specified will return results that match contacts synced from IH. Otherwise will only include local AD objects. 
	.EXAMPLE
		CrashCorp_FindEmailAddress [email protected]
		
		Returns Roland Deschain's information.
	.EXAMPLE
		CrashCorp_FindEmailAddress support
		
		Retrurns information about all CrashCorp Canada accounts which have the word "support" in their email address.
	.EXAMPLE
		CrashCorp_FindEmailAddress support -IncludeGlobalContacts
		
		Returns information about all CrashCorp Canada accounts, and contacts synced from Global which have the word "support" in their email address.
	#>
	[CmdletBinding()]
	Param(
		[Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)]
		[string]$EmailAddress,
		[switch]$IncludeGlobalContacts
	)
	
	# Format email address for search.
	$Email = "smtp:*$EmailAddress*"
	
	# Search through all AD user accounts ProxyAddresses to find the string.
	$UnfilteredADObjects = Get-ADObject -Properties SAMAccountName,ProxyAddresses -f {ProxyAddresses -like $Email}
	
	$ADObjects = @()
	if ($IncludeGlobalContacts){$ADObjects = $UnfilteredADObjects
	}else{
		$ADObjects = $UnfilteredADObjects | where-object {$_.DistinguishedName -notlike "*OU=GlobalContacts*"}
	}
	
	# Build list of objects to return.
	$UsersWithAddresses = @()
	foreach ($ADObject in $ADObjects){
		$UserDN = $ADObject.DistinguishedName
		$UserName = $ADObject.Name
		$UserSAM = $ADObject.SAMAccountName
		$ProxyAddresses = ""
		# Get all their email addresses
		foreach ($ProxyAddress in $ADObject.ProxyAddresses){
			if ($ProxyAddress -Like "smtp*"){
				$ProxyAddresses += $ProxyAddress + "`n"
			}
		}
		# Build a nice little object to cleanly format output
		$Properties = @{'ObjectDN'=$UserDN;"Object's Name"=$UserName;'Logon Name'=$UserSAM;'ProxyAddresses'=$ProxyAddresses.Trim()}
		$UserWithAddresses = New-Object -TypeName PSObject -Prop $Properties
		$UsersWithAddresses += $UserWithAddresses
	}
	return $UsersWithAddresses | fl
}