PowerShell

Outlook – The primary account cannot be removed unless it is the only account in the profile

Last few months I'm responsible for the migration of Office 365 to Office 365. While doing so, we came into a situation where users have their old mailbox as Primary Account and new Mailbox as their secondary account. This is a quite common scenario that people are running into and something that is expected. Usually, my recommendation is: Please create a new profile for user and topic is closed. It's also quite easy to achieve this in an automated way where you delete all profiles and Outlook just goes with autodiscovery adding new account as required. That's how I have always done this till now. My Client has gone thru setting up 1000+ users with their second account in Outlook and deleting a whole profile, recreating would cause lots of downloading of emails from Office 365 that my Client wanted to avoid.

What now?

That's fine, let's try to delete the primary email and keep the new one. Easier said than done!

The primary account cannot be removed unless it is the only account in the profile. You must remove all other Exchange accounts before removing the primary account.

I would lie if I didn't expect it. I did expect it, and I knew I've had quite a ride in the next few days finding out what is what in Outlook profile, and finally having a solution that is fully automated.

PowerShell for the rescue

Before we dive into it please be aware I've done limited testing to this. I will try to update this post after more testing is done. If you have more knowledge on what is what in Outlook I'm more than open for help. Currently, a module like, version of the code is available on GitHub so feel free to jump in on it and help out. Keep in mind that I've only tested this on Office (Outlook) 2016 and Windows 10. Please report if you run this on anything else and it fails or works!

function Backup-RegistryPath {
    param(
        [string] $Key,
        [string] $BackupPath = "$($env:USERPROFILE)\Desktop",
        [string] $BackupName
    )

    $Date = Get-Date
    $FileName = "$BackupName-$($Date.Year)-$($Date.Month)-$($Date.Day).$($Date.Hour).$($Date.Minute).$($Date.Second).reg"

    $BackupPlace = "$BackupPath\$FileName"

    if (Test-Path -Path $BackupPlace) {
        return $null
    } else {
        try {
            if (Test-Path Registry::$Key) {
                $Registry = Start-MyProgram -Program 'reg.exe' -cmdArgList "export", "$Key", "$BackupPlace"
                return $BackupPlace
            }
        } catch {

            return $null
        }
    }
}
function Convert-BinaryToHex {
    param(
        [alias('Bin')][Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true)][Byte[]]$Binary
    )
    if ($null -eq $Binary) {
        return
    }
    # assume pipeline input if we don't have an array (surely there must be a better way)
    if ($Binary.Length -eq 1) {
        $Binary = @($input)
    }
    $Return = -join ($Binary |  foreach { "{0:X2}" -f $_ })
    Write-Output $Return
}
function Convert-BinaryToString {
    param(
        [alias('Bin')]
        [Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true)]
        [Byte[]]$Binary
    )
    if ($null -ne $Binary) {
        return [System.Text.Encoding]::Unicode.GetString($Binary)
    }
}
function Find-OutlookKeys {
    param(
        $OutlookVersion = '2016',
        $EmailFind
    )
    $MainKey = [ordered] @{
        'Outlook 2016' = 'HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles'
        'Outlook 2013' = 'HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Outlook\Profiles'
    }


    $AllData = foreach ($OutlookVersion in $MainKey.Keys) {
        $OutlookRegistryKey = $MainKey.$OutlookVersion

        if ($OutlookVersion -eq 'Outlook 2016') {
            $SearchValue = '001f6641'
        } elseif ($OutlookVersion -eq 'Outlook 2013') {
            $SearchValue = '001f662b'
        } else {
            Exit
        }
        <#
Property      : {001f300a, 001f3d13, 00033e03, 00033009...}
PSPath        : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles\Nowy\cb755c91ea7e0b4c97fca67db4f0486b
PSParentPath  : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles\Nowy
PSChildName   : cb755c91ea7e0b4c97fca67db4f0486b
PSDrive       : HKCU
PSProvider    : Microsoft.PowerShell.Core\Registry
PSIsContainer : True
SubKeyCount   : 0
View          : Default
Handle        : Microsoft.Win32.SafeHandles.SafeRegistryHandle
ValueCount    : 20
Name          : HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles\Nowy\cb755c91ea7e0b4c97fca67db4f0486b
#>

        $RegistryKey = Get-ChildItem -Path "Registry::$OutlookRegistryKey" -Recurse
        $Special = $RegistryKey | Where-Object { $_.Property -eq $SearchValue } #| Select -Last 1 *
        $Keys = foreach ($S in $Special) {
            #$Special.PSPath
            $Path = "$($S.Name)"
            <#

    001f6641     : {83, 0, 77, 0...}
    PSPath       : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles\Nowy\0666d1f4813a9a4ba2d9462100225710
    PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles\Nowy
    PSChildName  : 0666d1f4813a9a4ba2d9462100225710
    PSProvider   : Microsoft.PowerShell.Core\Registry
    #>
            $MyValue = Get-ItemProperty -Path Registry::$Path -Name $SearchValue
            #$Email = [System.Text.Encoding]::Unicode.GetString( )
            $Email = Convert-BinaryToString ($MyValue.$SearchValue)

            $ParentPath = $S.PSParentPath
            $ChildName = $S.PSChildName
            #Write-Color "Path ", $ParentPath, " will delete value ", $ChildName, ' email ', $Email -Color White, Yellow, White, Yellow

            if ($ChildName -ne 'GroupsStore') {
                [PsCustomObject] @{
                    OutlookVersion  = $OutlookVersion
                    Profile         = ($ParentPath -split '\\')[-1]
                    ProfilePath     = $ParentPath
                    RegistryKeyName = $ChildName
                    RegistryKey     = "$ParentPath\$ChildName"
                    Email           = ($Email -replace 'SMTP:', '').ToLower()
                }
            }
            <#
            if ($Email -like "*$EmailFind*") {
                $ParentPath = $S.PSParentPath
                $ChildName = $S.PSChildName
                #$Path
                #Write-Color "ParentPath ", $ParentPath, " Path: ", $Path -Color White, Yellow, White, Yellow
                if ($ChildName -ne 'GroupsStore') {
                    #Write-Color "Path ", $ParentPath, " will delete value ", $ChildName -Color White, Yellow, White, Yellow
                    # Remove-Item -Path "$ParentPath\$ChildName" -Recurse
                }
            }
            #>
            #break
        }
        #Search-Registry -KeyName '001f6641' -Recurse $Path
        return $Keys
    }
    return $AllData
}
function Start-MyProgram {
    [CmdletBinding()]
    param (
        [string] $Program,
        [string[]] $CmdArgList
    )
    return & $Program $CmdArgList
}
function Start-OutlookProfile {
    param(
        [string] $RemoveAccount,
        [string] $PrimaryAccount,
        [string] $BackupPath = "$($env:USERPROFILE)\Desktop",
        [switch] $GUI,
        [switch] $DisplayProgress,
        [switch] $NoBackup,
        [switch] $DebugOutput,
        [switch] $WhatIf
    )


    $MainKey = [ordered] @{
        'Outlook 2016' = 'HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles'
        'Outlook 2013' = 'HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Outlook\Profiles'
    }

    $AllData = foreach ($OutlookVersion in $MainKey.Keys) {
        $OutlookRegistryKey = $MainKey.$OutlookVersion

        # Find All Outlook Profiles
        $OutlookProfiles = Get-ChildItem -path Registry::$OutlookRegistryKey -ErrorAction SilentlyContinue | Select *
        if ($null -eq $OutlookProfiles) {
            continue
        }


        $i = 1
        $Outlooks = foreach ($Outlook in $OutlookProfiles) {
            [pscustomobject] @{
                Number      = $i

                ProfileName = $Outlook.PSChildName
                ParentPath  = $Outlook.PSParentPath
                Path        = $Outlook.pSPath
            }
            $i++
        }

        # Loop thru one or more Outlook Profiles
        foreach ($OutlookProfile in $Outlooks) {

            $ProfilePath = $OutlookProfile.Path


            # default section - this is the key storing Default Value
            $DefaultKey = '0a0d020000000000c000000000000046'
            $RegKeyDefault = "$ProfilePath\$DefaultKey"

            $DefaultValue = (Get-ItemProperty -Path $RegKeyDefault -Name '01023d15' -ErrorAction SilentlyContinue).'01023d15'
            if ($null -ne $DefaultValue) {
                $DefaultValueHexServiceUID = Convert-BinaryToHex -Bin $DefaultValue

                if ($DisplayProgress) {
                    Write-Color -Text '[i] ', 'Default Registry Key: ', $RegKeyDefault -Color Blue, White, Green, White
                    Write-Color -Text '[i] ', 'Binary Value ', $DefaultValue -Color Blue, White, Green, White
                    Write-Color -Text '[i] ', 'Hex version ', $DefaultValueHexServiceUID -Color Blue, White, Green, White -LinesAfter 1
                }
            }

            # Scan each account in Outlook Profile
            $Array = foreach ($Outlook in $OutlookProfile) {
                $ProfilePath = $Outlook.Path
                $SpecialKey = '9375CFF0413111d3B88A00104B2A6676'
                $SubKeyProfile = "$ProfilePath\$SpecialKey"

                $OneProfile = Get-ChildItem -Path $SubKeyProfile


                $RootProfile = Get-ItemProperty -Path $SubKeyProfile
                $Val1 = $RootProfile.'{ED475418-B0D6-11D2-8C3B-00104B2A6676}'
                $Val2 = $RootProfile.'{ED475419-B0D6-11D2-8C3B-00104B2A6676}'
                $Val3 = $RootProfile.'{ED475420-B0D6-11D2-8C3B-00104B2A6676}'
                #Convert-BinaryTohex -Binary $Val1
                #Convert-BinaryTohex -Binary $Val2
                #Convert-BinaryTohex -Binary $Val3

                if ($DisplayProgress) {
                    Write-Color '[i] ', 'Profile path ', $ProfilePath -Color Blue, White, Yellow
                }


                foreach ($One in $OneProfile) {

                    $AccountName = (Get-ItemProperty -Path Registry::$One -Name 'Account Name' -ErrorAction SilentlyContinu).'Account Name'
                    $ServiceUID = (Get-ItemProperty -Path Registry::$One -Name 'Service UID' -ErrorAction SilentlyContinu).'Service UID'
                    $HexServiceUID = Convert-BinaryToHex -Binary $ServiceUID

                    $PreferencesUID = (Get-ItemProperty -Path Registry::$One -Name 'Preferences UID' -ErrorAction SilentlyContinue).'Preferences UID'
                    $HexPreferencesUID = Convert-BinaryToHex -Binary $PreferencesUID

                    $XPProviderUID = (Get-ItemProperty -Path Registry::$One -Name 'XP Provider UID' -ErrorAction SilentlyContinue).'XP Provider UID'
                    $HexXPProviderUID = Convert-BinaryToHex -Binary $XPProviderUID

                    if ($DisplayProgress) {
                        Write-Color '[Account] ', 'Name ', $AccountName, ' Service UID ', $ServiceUID, ' Hex version ', $HexServiceUID -Color Blue, White, Yellow, White, Yellow, WHite, Yellow
                    }
                    $RegKey = "$ProfilePath\$HexServiceUID"

                    # Find Service UID of account that will be used or is already set as Primary Account
                    $MyValue = (Get-ItemProperty -Path $RegKey -Name '01023d15' -ErrorAction SilentlyContinue).'01023d15'
                    $MyValueHexServiceUID = Convert-BinaryToHex -Binary $MyValue

                    if ($DisplayProgress) {
                        Write-Color '[Account Update] ', 'Key: ', $RegKey, ' Primary Service UID ', $MyValue, ' Hex ', $MyValueHexServiceUID -Color Blue, White, Green, White, Green, White, Green
                    }
                    [PsCustomobject] @{
                        OutlookVersion     = $OutlookVersion
                        Profile            = $OutlookProfile.ProfileName
                        ProfileNumber      = $One.PSChildName
                        AccountName        = $AccountName
                        #ServiceUIDBefore  = $ServiceUID
                        ServiceUID         = $HexServiceUID
                        #PrimaryServiceUIDBefore = $MyValue
                        RequiredServiceUID = $MyValueHexServiceUID
                        #PreferencesUIDBefore = $PreferencesUID
                        PreferencesUID     = $HexPreferencesUID

                        XPProviderUID      = $HexXPProviderUID
                        ProfilePath        = $ProfilePath

                    }


                }
            }
            if ($DisplayProgress) {
                Write-Color -LinesAfter 1
            }
            $Array
        }
    }
    <#
    if (-not $GUI) {
        # Assing all profiles
        $OutlookProfiles = $Outlooks
    } else {
        # Show GUI
        $Line = '==================================='
        do {
            Clear-Host
            Write-Color $line -LinesBefore 1
            Write-Color 'Outlook Profile Fixer' -C Green -StartTab 1
            Write-Color $line

            foreach ($Outlook in $Outlooks) {
                Write-Color -Text $Outlook.Number, ' - Profile Name: ', $Outlook.ProfileName -Color Yellow, White, Green
            }
            Write-Color '0', ' - ', 'Quit' -Color Yellow, White, Green -LinesAfter 1

            $Input = Read-Host 'Select'
            If ($Input -eq 0) {
                Exit
            } elseif ($Outlooks.Number -contains $Input) {
                break
            } else {
                Write-Color 'Wrong choice.', ' Press any key to restart!' -Color Red, Yellow -LinesBefore 1
                [void][System.Console]::ReadKey($true)
            }
        } while ($Input -ne '0')
        Clear-Host
        $OutlookProfiles = $Outlooks[$Input - 1]
        # End Gui
    }
    #>

    if (-not $NoBackup) {
        # Backup
        $Backups = foreach ($OutlookVersion in $MainKey.Keys) {
            $OutlookRegistryKey = $MainKey.$OutlookVersion

            [string] $BackupName = "$OutlookVersion-RegistryProfile"
            # Make registry Backup
            #Write-Color "[i] ", 'Backup of Key ', $OutlookRegistryKey, ' to ', $BackupPath -Color Blue, White, Yellow, White, Yellow
            $Backup = Backup-RegistryPath -Key $OutlookRegistryKey -BackupPath $BackupPath -BackupName $BackupName
            if ($null -ne $Backup) {
                #try {
                #    Write-Color "[i] ", "Backup of Outlook profiles made to ", $Backup -Color Blue, White, Yellow -LinesAfter 1
                #} catch {
                    Write-Color "[i] Backup of Outlook profiles made to $Backup"
                #}
            } else {

            }
        }

    }

    foreach ($Mail in $AllData) {
        if ($Mail.ProfilePath) {
            if ($Mail.PreferencesUID -and $Mail.XPProviderUID -and $Mail.PreferencesUID -and $Mail.ProfileNumber) {
                # Check if user wants to remove any account
                if ($RemoveAccount) {
                    if ($Mail.AccountName -match $RemoveAccount) {

                        $Keys = @(
                            "$($Mail.ProfilePath)\$($Mail.PreferencesUID)"
                            "$($Mail.ProfilePath)\$($Mail.ServiceUID)"
                            "$($Mail.ProfilePath)\$($Mail.XPProviderUID)"
                            "$($Mail.ProfilePath)\9375CFF0413111d3B88A00104B2A6676\$($Mail.ProfileNumber)"
                        )

                        foreach ($Key in $Keys) {
                            #try {
                            #    Write-Color '[i] ', "Removing key ", $Key -Color Blue, White, Yellow
                            #} catch {
                                Write-host "[i] Removing key $Key"
                            #}
                            if (-Not $WhatIf) {
                                Remove-Item -Path $Key -Confirm:$false #-WhatIf
                            }
                        }

                    }
                }
                if ($PrimaryAccount) {
                    if ($Mail.AccountName -match $PrimaryAccount) {
                        $Default = "$($Mail.ProfilePath)\0a0d020000000000c000000000000046"
                        if ($Mail.RequiredServiceUID) {
                            #Try {
                            #    Write-Color "[i] ", "Setting default profile ", $Default, ' with ', $Mail.RequiredServiceUID -Color Blue, White, Yellow, White, Green
                            #} catch {
                                Write-Host "[i] Setting default profile $Default with $($Mail.RequiredServiceUID)"
                            #}
                            if (-not $WhatIf) {
                                Set-ItemProperty -Path $Default -Name '01023d15' -Value $Mail.MyValue -Type Binary #-WhatIf
                            }
                        }

                        [int] $PrimaryProfile = $Mail.ProfileNumber
                        [byte[]] $ByteArray = @($PrimaryProfile, 0, 0, 0)
                        $SubValue = "$($Mail.ProfilePath)\9375CFF0413111d3B88A00104B2A6676"
                        #Try {
                        #    Write-Color "[i] ", "Setting default profile ", $SubValue, ' in ', "{ED475418-B0D6-11D2-8C3B-00104B2A6676}", ' with ', $ByteArray -Color Blue, White, Yellow, White, Green, White, Yellow
                        #} catch {
                            Write-Color "[i] Setting default profile $SubValue in {ED475418-B0D6-11D2-8C3B-00104B2A6676} with $ByteArray"
                        #}
                        if (-not $WhatIf) {
                            Set-ItemProperty -Path $SubValue -Name "{ED475418-B0D6-11D2-8C3B-00104B2A6676}" -Value $ByteArray -Type Binary #-WhatIf
                        }
                    }
                }
            }
        }
    }

    # Remove leftovers
    $LeftOvers = Find-OutlookKeys
    foreach ($Left in $LeftOvers) {
        # Check if user wants to remove any account
        if ($RemoveAccount) {
            if ($Left.Email -match "$RemoveAccount") {
                #try {
                #    Write-Color '[i] ', 'Removing leftovers key ', $Left.RegistryKey -Color Blue, White, Yellow
                #} catch {
                    Write-Host "[i] Removing leftovers key $($Left.RegistryKey)"
                #}
                if (-not $WhatIf) {
                    Remove-Item -Path $Left.RegistryKey -Recurse -Confirm:$False
                }
            }
        }
    }
    if ($DebugOutput) {
        $AllData
        $LeftOvers
    }
}
Backup your profiles

Following command is your standard function. With or without WhatIf your profiles will be backed up. Please test this first. See if your .reg file was created. If it was, please check it's content. Make sure everything is there. The good thing about this little script is that you can quickly revert its changes and try again by adding back backup to a registry.

Start-OutlookProfile 
Set your Outlook Email as Primary Account with PowerShell

This one makes sure your account is set as primary. Keep in mind I'm using WhatIf that means it won't be executed until you remove it. I'm also using only domain due to how Office 365 can have multiple accounts belonging to the same domain (shared accounts, groups and so on). While in this case it most likely doesn't matter, in the later case it will.

Start-OutlookProfile -PrimaryAccount 'domain.pl' -WhatIf

Remove Primary Email Account with PowerShell

This one actually removes the whole account from Outlook. I'm specifically using domain instead of full email address due to Office 365 groups, shared mailboxes and so on. This makes sure everything attached to it gets removed as well.

Start-OutlookProfile -RemoveDomain 'domain.pl' -WhatIf

Remove Primary Email, Set secondary email as primary with PowerShell

This one is a combination of both. Remove your secondary account, make your secondary account primary. Of course please notice there is WhatIf.

Start-OutlookProfile -PrimaryAccount 'domain.pl' -RemoveAccount 'otherdomain.com' -WhatIf

Debug Mode

Finally, this is your code to check what it found. Keep in mind that it goes thru all profiles.

Start-OutlookProfile -DebugOutput

This post was last modified on 17 maja, 2022 15:18

Przemyslaw Klys

System Architect with over 14 years of experience in the IT field. Skilled, among others, in Active Directory, Microsoft Exchange and Office 365. Profoundly interested in PowerShell. Software geek.

Share
Published by
Przemyslaw Klys

Recent Posts

Upgrade Azure Active Directory Connect fails with unexpected error

Today, I made the decision to upgrade my test environment and update the version of…

2 miesiące ago

Mastering Active Directory Hygiene: Automating Stale Computer Cleanup with CleanupMonster

Have you ever looked at your Active Directory and wondered, "Why do I still have…

3 miesiące ago

Active Directory Replication Summary to your Email or Microsoft Teams

Active Directory replication is a critical process that ensures the consistent and up-to-date state of…

7 miesięcy ago

Syncing Global Address List (GAL) to personal contacts and between Office 365 tenants with PowerShell

Hey there! Today, I wanted to introduce you to one of the small but excellent…

12 miesięcy ago

Active Directory Health Check using Microsoft Entra Connect Health Service

Active Directory (AD) is crucial in managing identities and resources within an organization. Ensuring its…

1 rok ago

Seamless HTML Report Creation: Harness the Power of Markdown with PSWriteHTML PowerShell Module

In today's digital age, the ability to create compelling and informative HTML reports and documents…

1 rok ago