Set-UpnWithMailAddress.ps1

<#PSScriptInfo
 
.VERSION 3.0
 
.GUID d043e608-c8b8-4336-ab34-01935201ac7a
 
.AUTHOR Aaron Guilmette
 
.COMPANYNAME Microsoft
 
.COPYRIGHT 2020
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI https://www.undocumented-features.com/2019/04/25/update-to-the-set-upnwithmailaddress-script/
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
.DESCRIPTION
Replace UserPrincipalName with Mail attribute.
 
.PRIVATEDATA
 
#>


<#
.SYNOPSIS
This script will replace the UPN of the target object with the value
in its mail attribute. This can be useful if parity between the
User Principal Name and email address is necessary (such as single
sign-on scenarios with Office 365).
 
.EXAMPLE
.\Set-UpnWithMailAddress.ps1 -TargetUser testuser@contoso.com
Replace UPN with mail attribute for user testuser@contoso.com.
 
.EXAMPLE
.\Set-UpnWithMailAddress.ps1 -TargetUser *
Replace UPN with mail attribute for all users.
 
.EXAMPLE
.\Set-UpnWithMailAddress.ps1 -TargetUser * -SearchBase "OU=Test,DC=contoso,DC=com" -LogFile Log.txt
Replace UPN with mail attribute for all users in organizational unit Test
and log results to log.txt
 
.EXAMPLE
.\Set-UpnWithMailAddress.ps1 -TargetUser * -OnlyMailboxes
Replace UPN with mail attribute for mailbox users only (exclude mail-
enabled users).
 
.PARAMETER ErrorLog
Specify logfile for errors. Default is <date>_UPNUpdateError.txt.
 
.PARAMETER Logfile
Specify logfile for operations. Default is <date>_UPNUpdate.txt
 
.PARAMETER OnlyMailboxes
Ignore MailUser objects, based on msExchRecipientTypeDetails value. If
the environment has MailUsers, it may not be desirable to overwrite the
UserPrincipalName with the external mail attribute, since the external
mail address will most likely be for a domain not bound to the current
organization.
 
.PARAMETER SearchBase
Set the BaseDN for the search query. Defaults to the DN of the current
domain.
 
.PARAMETER SearchScope
Set the search scope for Active Directory Operations.
 
.PARAMETER TargetUser
This parameter is used to identify the user or group of users on which
the attributes will be updated. Can be a single UPN or a wildcard to
select all users in current domain.
 
.LINK
http://aka.ms/thebookonit
 
.NOTES
 
All envrionments perform differently. Please test this code before using it
in production.
 
THIS CODE AND ANY ASSOCIATED INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK OF USE, INABILITY TO USE, OR RESULTS FROM THE USE OF
THIS CODE REMAINS WITH THE USER.
 
2020-04-21 Updated for PowerShell Gallery.
2019-04-25 Added objectGUID to log file.
            Reduced number of Get- calls to improve performance.
            Added on-screen progress meter for large batches.
2018-05-17 Updated logging parameters.
2016-08-30 Added -OnlyMailboxes parameter.
2014-08-02 Initial release.
#>


Param(
    [Parameter(Mandatory=$false,HelpMessage="Enter UPN for single user or * for all users")]
        [string]$TargetUser = "*",
    [Parameter(Mandatory=$false,HelpMessage="Only Mailboxes")]
        [switch]$OnlyMailboxes,
    [Parameter(Mandatory=$false,HelpMessage="Active Directory Base DN")]
        [string]$SearchBase = (Get-ADDomain).DistinguishedName,
    [Parameter(Mandatory=$false,HelpMessage="Active Directory Search Scope")]
        [ValidateSet("Base","OneLevel","Subtree")]
        [string]$SearchScope = "Subtree",
    [string]$Logfile = (Get-Date -Format yyyy-MM-dd) + "_UPNUpdate.txt",
    [string]$ErrorLog = (Get-Date -Format yyyy-MM-dd) + "_UPNUpdateError.txt"
    )
If (!(Get-Module ActiveDirectory))
    {
    Import-Module ActiveDirectory
    }

# Logging function
function Write-Log([string[]]$Message, [string]$LogFile = $Script:LogFile, [switch]$ConsoleOnly,[switch]$ConsoleOutput, [ValidateSet("SUCCESS", "INFO", "WARN", "ERROR", "DEBUG")][string]$LogLevel)
{
    If (!(Test-Path $LogFile)) { Out-File -Append -FilePath $LogFile -InputObject "`"DistinguishedName`",`"UPNBefore`",`"MailBefore`",`"UPNAfter`",`"MailAfter`",`"ObjectGuid`"" }
    $Message = $Message + $Input
    If (!$LogLevel) { $LogLevel = "INFO" }
    switch ($LogLevel)
    {
        SUCCESS { $Color = "Green" }
        INFO { $Color = "White" }
        WARN { $Color = "Yellow" }
        ERROR { $Color = "Red" }
        DEBUG { $Color = "Gray" }
    }
    if ($Message -ne $null -and $Message.Length -gt 0)
    {
        $TimeStamp = [System.DateTime]::Now.ToString("yyyy-MM-dd HH:mm:ss")
        
        if (!($ConsoleOnly))
        {
            if ($LogFile -ne $null -and $LogFile -ne [System.String]::Empty)
            {
                Out-File -Append -FilePath $LogFile -InputObject "$Message"
            }
        }
        if ($ConsoleOutput -eq $true)
        {
            Write-Host "[$TimeStamp] [$LogLevel] :: $Message" -ForegroundColor $Color
        }
    }
}

switch ($OnlyMailboxes)
{
    $true {
        Write-Log -ConsoleOutput -LogLevel INFO -Message "Processing mailbox objects." -ConsoleOnly
        [array]$TempUsers = Get-ADUser -LDAPFilter "(userPrincipalName=$($TargetUser))" -SearchBase $SearchBase -SearchScope $SearchScope -Properties mail, msExchRecipientTypeDetails | ? { $_.msExchRecipientTypeDetails -eq "1" }
    }
    $false {
        Write-Log -ConsoleOutput -LogLevel INFO -Message "Processing all object types." -ConsoleOnly
        [array]$TempUsers = Get-ADUser -LDAPFilter "(userPrincipalName=$($TargetUser))" -SearchBase $SearchBase -SearchScope $SearchScope -Properties mail
    }
}

$i = 1
$Users = @()
foreach ($obj in $TempUsers) { if (!($obj.Mail -match $obj.UserPrincipalName)) { $Users += $obj } }
Write-Log -ConsoleOutput -LogLevel INFO -Message "$($Users.Count) objects need to be updated." -ConsoleOnly
ForEach ($User in $Users) 
    {
    Write-Progress -Activity "Updating users [$($User.UserPrincipalName)]" -PercentComplete (($i / $Users.Count) * 100) -Status "$($Users.Count - $i) users remaining" -Id 1
    $objBefore = $User
    If ($objBefore.Mail)
        {
        If (!($objBefore.Mail -match $objBefore.UserPrincipalName))
        {
            Set-ADObject -Identity $User.DistinguishedName -Replace @{ userPrincipalName = $($User.mail) }
            $objAfter = Get-ADObject -Identity $User.DistinguishedName -Properties UserPrincipalName, Mail
            Write-Log -LogFile $Logfile -LogLevel INFO -Message "`"$($User.DistinguishedName)`",`"$($objBefore.UserPrincipalName)`",`"$($objBefore.mail)`",`"$($objAfter.UserPrincipalName)`",`"$($objAfter.Mail)`",`"$($User.objectGuid)`""
        }
    }
    Else
    {
        Write-Log -ConsoleOutput -LogLevel ERROR -Message "$($objBefore.UserPrincipalName) does not have a valid mail attribute." -LogFile $ErrorLog
    }
    $objBefore = $null
    $objAfter = $null
    $i++
    }