/ PowerShell

Updating an Office 365 UserPrincipalName from one Federated Domain to another.

Problem

When the user object is being synced to the cloud service, you receive the following error message in the synchronization error report:
Unable to update this object in Microsoft Online Services, because the attribute FederatedUser.UserPrincipalName is not valid. Update the value in your local Active Directory

Solution

The corresponding support article from Microsoft describes the error and provides a working solution. However, its two steps, depends on inputting the tenancy domain (automate it!) and does not provide any error checking. Lets improve on that and have some fun :D

NB: We are using the Azure AD module as the MSOnline module will be deprecated at some point so converting your functions is a good idea :)

With the Azure AD module it is necessary to set a password profile. The function imports the system.web assembly to generate a random password on the fly. Don't Panic! The users password will be synced once changed to the federated domain.


Function Update-xAzureADUserUPN {
    <#
.SYNOPSIS
    Uses Set-AzureADUser cmdlet to set the user UserPrincipalName.
.DESCRIPTION
    When a users UserPrincipalName is updated within Active Directory, AAD Connect does not replicate the change.
    In order to update the change in Azure AD the UserPrincipalName must INTIALLY be set to a non-federated domain.

    The tenancy domain represents a non-federated domain and importantly CANNOT be federated. This function performs
    this two step process to update the users UserPrincipalName to reflect the change in Active Directory.

    With the Azure AD module it is necessary to set a password profile. The function imports the system.web assembly
    to generate a random password on the fly. Don't Panic! The users password will be synced once changed to the
    federated domain.

.EXAMPLE
    This example updates user Fakey McFakerson's UPN from
    [email protected] to [email protected],
    with a five second delay.

    Update-xAzureADUserUPN `
        -UserPrincipalName [email protected] `
        -NewUserPrincipalName [email protected] `
        -Delay 5

    RESULTS:
        ObjectID  : xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
        UPN-FROM: : [email protected]
        UPN-TO:   : [email protected]
        Status    : SUCCESS :)


.NOTES
    AUTHOR   : Steve Rackham
    DATE     : 2017-05-24 16:46 SteveR
    MODIFIED : 2017-08-14 09:22 SteveR

    #>

    [CmdletBinding(
        HelpUri = 'https://docs.microsoft.com/en-us/powershell/module/azuread/set-azureaduser?view=azureadps-2.0'
    )]
    param (
        # PARAMETER: USERPRINCIPALNAME: This represents the old UserPrincipalName as not replicated.
        [Parameter (
            Mandatory = $true,
            HelpMessage = "Enter the UserPrincipalName (UPN)",
            Position = 0)]
        [MailAddress]$UserPrincipalName,

        # PARAMETER: NEWUSERPRINCIPALNAME: This represents the current UserPrincipalName in Active Directory.
        [Parameter (
            Mandatory = $true,
            Position = 1)]
        [MailAddress]$NewUserPrincipalName,

        [Parameter(
            Mandatory = $false,
            HelpMessage = "Enter the UsageLocation (EG: NZ)"
        )]
        [String]$UsageLocation = "NZ",

        [Parameter(
            Mandatory = $true,
            HelpMessage = "Enter delay (in seconds)"
        )]
        [int]$Delay = 5

    ) # END PARAM

    # Create the reporting object.
    $userOutput = [pscustomObject] @{
        'Status'               = $null
        'ObjectID'             = $null
        'UserPrincipalName'    = $null
        'NewUserPrincipalName' = $null
    }  # END-PSCUSTOMOBJECT

    # Capture the user object and properties.
    try {
        Write-Host -NoNewline "CHECK USER: $($AzureADUser.UserPrincipalName)"
        $AzureADUser = Get-AzureADUser -ObjectId $UserPrincipalName -ErrorAction Stop
        Write-host " | SUCCESSFUL :)"

    }
    catch {

        Write-Host -ForegroundColor Yellow " | FAILURE :("
        Write-Warning $Error[0].Exception.Message

        $userOutput.Status = "ERROR! :("

        return $userOutput

    } # END TRY/CATCH

    # With the user captured we can reference the ObjectID for consistency,

    # SET USERPRINCIPALNAME TO NON-FEDERATED::---------------------------------
    try {
        # PASSWORD PROFILE:
        #  Create a Password Profile (TODO: Create small random password function)
        #  IMPORTANT! MAX 15 CHARS! This is a limitation within Office 365 apparently.

        $PasswordProfile = New-Object -TypeName Microsoft.Open.AzureAD.Model.PasswordProfile

        # Add the System Assembly to create the password on the fly.

        Add-Type -AssemblyName System.Web

        # Generate a password and capture to the password profile password value.
        # NB: (10, 5) translates to 10 alphanumeric and 5 non-alphanumeric characters.

        $PasswordProfile.Password = [System.Web.Security.Membership]::GeneratePassword(10, 5)
        Write-Debug "PASSWORD PROFILE: $($PasswordProfile.Password)"

        # NON FEDERATED DOMAIN:
        # Built the UPN suffix from the tenancy domain. This makes it portable against other tenancies without
        # hardcoding.
        $tenantDomain = (Get-AzureADTenantDetail).VerifieDDomains `
            | Where-Object {$_._Default -EQ $true } `
            | Select-Object Name

        Write-Host -ForegroundColor Cyan -NoNewline "NON-FEDERATED UPN: $NonFederatedUPN"
        $NonFederatedUPN = ($AzureADUser.UserprincipalName -split '@')[0] + '@' + $tenantDomain.Name
        Write-Host -ForegroundColor Cyan " | SUCCESSFUL :)"


        # SET UPN OPERATION: non-federated first.

        Set-AzureADUser -ObjectId $AzureADUser.ObjectId `
            -UserPrincipalName $NonFederatedUPN `
            -PasswordProfile $PasswordProfile `
            -ErrorAction Stop
        Write-Verbose "SET AZUREADUSER [$($AzureADUser.UserPrincipalName)]: TO: $($NonFederatedUPN) | SUCCESS :)"

    }
    catch {

        Write-Warning $Error[0].Exception.Message
        Write-Warning "SET AZUREADUSER [$($AzureADUser.UserPrincipalName)]: TO: $($NonFederatedUPN) | FAILURE :("

        $userOutput.Status = "ERROR! :("

    } # END TRY/CATCH

    # SET OPERATIONS COMPLETE. APPLY A DELAY TO ALLOW FOR CLOUD.
    Write-Verbose "SET OPERATIONS DELAY $Delay seconds..."
    $i = 0
    do {

        Write-Progress -Activity Sleeping -SecondsRemaining ($delay - $i) -PercentComplete ($i / $Delay * 100)
        Start-Sleep -Seconds 1
        $i++

    } until ($i -eq $Delay) # END DO-UNTIL.


    # SET USERPRINCIPALNAME TO NEW USERPRINCIPALNAME::-------------------------
    Try {
        Write-Host -ForegroundColor Cyan "SET AZUREADUSER [$($AzureADUser.UserPrincipalName)]: TO: $($NewUserPrincipalName)"
        Set-AzureADUser -ObjectId $AzureADUser.ObjectId -UserPrincipalName $NewUserPrincipalName -ErrorAction Stop
        Write-Host -ForegroundColor Cyan "| SUCCESS :)"

        # Capture the user again for reporting.
        $AzureADUser = Get-AzureADUser -ObjectId $NewUserPrincipalName -ErrorAction Stop

    }
    Catch {

        Write-Host -ForegroundColor Yellow "| FAILURE :("
        Write-Warning $Error[0].Exception.Message

        $userOutput.Status = "ERROR! :("

    } # END-TRY/CATCH/FINALLY

    if ($NewUserPrincipalName -eq $AzureADUser.userprincipalname) {$userOutput.Status = "SUCCESS :)"} # END-IF

    # BUILD AND OUTPUT THE REPORTING OBJECT::----------------------------------
    $userOutput = [pscustomObject] @{

        'Status'    = $userOutput.Status
        'ObjectID'  = $AzureADUser.ObjectID
        'UPN-FROM:' = $UserPrincipalName
        'UPN-TO:'   = $AzureADUser.UserprincipalName

    }  # END-PSCUSTOMOBJECT

    # OUTPUT:
    return $userOutput | Format-List

} # END-FUNCTION: Update-xAzureADUserUPN

And that is it. There are some additional elements to the function that should be interesting and give you some ideas on how to use in other functions. Hope this helps and inspires.
Happy coding...

Steve Rackham

Steve Rackham

By day, IT professional with advanced skills across a broad spectrum of technology stacks and enterprise platforms. By night, happiest learning while tinkering under the hood and writing :)

Read More
Updating an Office 365 UserPrincipalName from one Federated Domain to another.
Share this