Public/AD/Update-ADDomainName.ps1

function Update-ADDomainName {
    <#
    .SYNOPSIS
        Changes the domain name for all users in Active Directory and configures email forwarding.
 
    .DESCRIPTION
        The Update-ADDomainName function handles the domain name change process in Active Directory.
        When an organization changes its domain name, this function performs the following tasks:
         
        1. Sets the old email address as a secondary SMTP proxy address to maintain email continuity
        2. Changes all user principal names (UPNs) from the old domain to the new domain
        3. Sets the new email address as the primary SMTP address
 
        This ensures a smooth transition during a domain name change while maintaining email
        functionality throughout the process.
 
    .PARAMETER OldDomain
        Specifies the old domain name (e.g., "contoso.com").
        This is the domain being replaced.
 
    .PARAMETER NewDomain
        Specifies the new domain name (e.g., "contoso.org").
        This is the domain that will replace the old domain.
 
    .PARAMETER UserFilter
        Optional filter to specify which users to process.
        By default, processes all users with UPNs containing the old domain.
 
    .PARAMETER WhatIf
        Shows what would happen if the cmdlet runs.
        The cmdlet doesn't run.
 
    .PARAMETER Confirm
        Prompts you for confirmation before running the cmdlet.
 
    .EXAMPLE
        Update-ADDomainName -OldDomain "old-domain.com" -NewDomain "new-domain.com"
         
        Updates all users' domain from old-domain.com to new-domain.com, preserving email continuity.
 
    .EXAMPLE
        Update-ADDomainName -OldDomain "contoso.local" -NewDomain "contoso.com" -UserFilter "Department -eq 'IT'"
         
        Updates only IT department users from contoso.local to contoso.com domain.
 
    .NOTES
        - Requires the Active Directory PowerShell module
        - Requires Domain Admin privileges
        - Should be run on a domain controller or system with proper permissions
        - Internet connection may be required if Azure AD sync is enabled
        - You should first register the new domain in local AD or Azure AD
         
        Author: Michiel VH
    #>

    
    [CmdletBinding(SupportsShouldProcess=$true)]
    param (
        [Parameter(Mandatory=$true, Position=0)]
        [string]$OldDomain,
        
        [Parameter(Mandatory=$true, Position=1)]
        [string]$NewDomain,
        
        [Parameter()]
        [string]$UserFilter
    )

    begin {
        # Check for AD module
        if (-not (Get-Module -Name ActiveDirectory -ListAvailable)) {
            Write-Error "This function requires the Active Directory module. Please install RSAT and try again."
            return
        }

        # Ensure domain names are properly formatted
        if (-not $OldDomain.StartsWith("@")) { $OldDomain = "@$OldDomain" }
        if (-not $NewDomain.StartsWith("@")) { $NewDomain = "@$NewDomain" }

        # Create default filter if not provided
        if (-not $UserFilter) {
            $domainFilter = $OldDomain.TrimStart('@')
            $UserFilter = "UserPrincipalName -like '*$domainFilter'"
        }

        Write-Host "Starting domain name change from $($OldDomain.TrimStart('@')) to $($NewDomain.TrimStart('@'))" -ForegroundColor Cyan
        Write-Host "This process will:" -ForegroundColor Cyan
        Write-Host " 1. Set proxy addresses for all users to maintain email continuity" -ForegroundColor Yellow
        Write-Host " 2. Change user principal names (UPNs) to the new domain" -ForegroundColor Yellow 
        Write-Host " 3. Set the new email address as primary SMTP address" -ForegroundColor Yellow

        $processedUsers = @()
    }

    process {
        # Get users from AD based on filter
        Write-Verbose "Retrieving users from Active Directory with filter: $UserFilter"
        $users = Get-ADUser -Filter $UserFilter -Properties SamAccountName, UserPrincipalName, ProxyAddresses

        if (-not $users -or $users.Count -eq 0) {
            Write-Warning "No users found matching filter: $UserFilter"
            return
        }

        Write-Host "Found $($users.Count) users to process" -ForegroundColor Cyan

        # STEP 1: Set proxy addresses for old email addresses
        Write-Host "`nSTEP 1: Setting secondary SMTP proxy addresses for old email addresses..." -ForegroundColor Cyan
        foreach ($user in $users) {
            $SamAccountName = $user.SamAccountName
            $UserPrincipalName = $user.UserPrincipalName
            $displayInfo = "$SamAccountName ($UserPrincipalName)"
            
            if ($PSCmdlet.ShouldProcess($displayInfo, "Add proxy address $UserPrincipalName")) {
                try {
                    Write-Verbose "Adding proxy address: smtp:$UserPrincipalName"
                    Set-ADUser $SamAccountName -Add @{proxyAddresses="smtp:$UserPrincipalName"} -ErrorAction Stop
                    Write-Host " [OK] Added proxy address for $displayInfo" -ForegroundColor Green
                    $processedUsers += $user
                } catch {
                    Write-Error " [ERROR] Failed to add proxy address for $displayInfo : $_"
                }
            }
        }
        
        # STEP 2: Change UPN for all users
        Write-Host "`nSTEP 2: Updating User Principal Names to new domain..." -ForegroundColor Cyan
        $localUsers = Get-ADUser -Filter "$UserFilter" -Properties userPrincipalName
        
        foreach ($user in $localUsers) {
            $SamAccountName = $user.SamAccountName
            $oldUPN = $user.UserPrincipalName
            $newUPN = $oldUPN.Replace($OldDomain, $NewDomain)
            $displayInfo = "$SamAccountName ($oldUPN -> $newUPN)"
            
            if ($PSCmdlet.ShouldProcess($displayInfo, "Update UPN")) {
                try {
                    Set-ADUser -Identity $user -UserPrincipalName $newUPN -ErrorAction Stop
                    Write-Host " [OK] Updated UPN for $SamAccountName" -ForegroundColor Green
                } catch {
                    Write-Error " [ERROR] Failed to update UPN for $displayInfo : $_"
                }
            }
        }
        
        # STEP 3: Set new email addresses as primary SMTP
        Write-Host "`nSTEP 3: Setting new email addresses as primary SMTP addresses..." -ForegroundColor Cyan
        $updatedUsers = Get-ADUser -Filter $UserFilter -Properties SamAccountName, UserPrincipalName
        
        foreach ($user in $updatedUsers) {
            $SamAccountName = $user.SamAccountName
            $UserPrincipalName = $user.UserPrincipalName
            $displayInfo = "$SamAccountName ($UserPrincipalName)"
            
            if ($PSCmdlet.ShouldProcess($displayInfo, "Set primary SMTP address")) {
                try {
                    Write-Verbose "Setting primary SMTP address: SMTP:$UserPrincipalName"
                    Set-ADUser $SamAccountName -Add @{proxyAddresses="SMTP:$UserPrincipalName"} -ErrorAction Stop
                    Write-Host " [OK] Set primary SMTP address for $displayInfo" -ForegroundColor Green
                } catch {
                    Write-Error " [ERROR] Failed to set primary SMTP address for $displayInfo : $_"
                }
            }
        }
    }

    end {
        Write-Host "`nDomain Name Change Summary:" -ForegroundColor Cyan
        Write-Host " Old Domain: $($OldDomain.TrimStart('@'))" -ForegroundColor Yellow
        Write-Host " New Domain: $($NewDomain.TrimStart('@'))" -ForegroundColor Green
        Write-Host " Users processed: $($processedUsers.Count)" -ForegroundColor Cyan
        
        Write-Host "`nNext Steps:" -ForegroundColor Magenta
        Write-Host " 1. Run Get-ADUser -Filter * | Sort-Object Name | Format-Table Name, UserPrincipalName to verify changes" -ForegroundColor White
        Write-Host " 2. Synchronize changes with Azure AD if applicable" -ForegroundColor White
        Write-Host " 3. Test email flow for both old and new domain addresses" -ForegroundColor White
    }
}