How To: Remove OAuth Credentials when removing Exchange

Update

04/09/2025

Microsoft have finally updated their documentation and now provide a script to remove the required key.

Script can be found here https://microsoft.github.io/CSS-Exchange/Hybrid/ConfigureExchangeHybridApplication/ its always recommended to download the latest version.

The command you will want to run is as follows.

.\ConfigureExchangeHybridApplication.ps1 -ResetFirstPartyServicePrincipalKeyCredentials

The problem

Picture the scene, Microsoft finally allow you to remove your last exchange server from your on premise environment and keep Extra Connect synchronising your user identities, you are most of the way through the steps and you get to the “Permanently shutting down your last Exchange Server” section, specifically step 5 where you have to remove the remove the service principal credentials created for OAuth and the commands just wont work.

The guide I’m referring too can be found here and the powershell commands in question are these…

$thumbprint = (Get-AuthConfig).CurrentCertificateThumbprint
$oAuthCert = (dir Cert:\LocalMachine\My) | where {$_.Thumbprint -match $thumbprint}
$certType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Cert
$certBytes = $oAuthCert.Export($certType)
$credValue = [System.Convert]::ToBase64String($certBytes)

Import-Module Microsoft.Graph.Applications
Connect-MgGraph -Scopes "Application.Read.All"
$ServiceName = "00000002-0000-0ff1-ce00-000000000000"
$p = Get-MgServicePrincipalByAppId -AppId $ServiceName
$keyId = (Get-MgServicePrincipal -ServicePrincipalId $p.Id).KeyCredentials $true | Where-Object {$_.Value -eq $credValue}).KeyId

The errors will start to be thrown when line 11 is executed. This line has a mix of the older MSOnline and the newer Graph cmdlets. The Graph cmdlet doesn’t have a property called ‘Value’, nor did it ever have property named that.

It seems someone or maybe event AI has tried to convert the commands over to the Graph Module and done it without testing.

For the curious the previous code was as follows…

$thumbprint = (Get-AuthConfig).CurrentCertificateThumbprint
$oAuthCert = (dir Cert:\LocalMachine\My) | where {$_.Thumbprint -match $thumbprint}
$certType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Cert
$certBytes = $oAuthCert.Export($certType)
$credValue = [System.Convert]::ToBase64String($certBytes)

Import-Module -Name MSOnline
Connect-MsolService
$ServiceName = "00000002-0000-0ff1-ce00-000000000000"
$p = Get-MsolServicePrincipal -ServicePrincipalName $ServiceName
$keyId = (Get-MsolServicePrincipalCredential -AppPrincipalId $p.AppPrincipalId -ReturnKeyValues $true | ?{$_.Value -eq $credValue}).KeyId

Pretty similar hey? So you can see where this error has come from.

So what should the code be now?

For that we have to find another property to match against. In this case we have the ‘customKeyIdentifier’ property. Now this property can be encoded to base64 which gives us the certificate thumbprint. This simplifies things slightly as we can get the thumbprint in one line of code.

We then need to get the apps service principal though the Graph Module and loop through all the keys to see which matches the thumbprint.

You might have multiple keys matching the thumbprint and should ensure that nothing is relying on them in your production environment before removing them, you have been warned!

Also the Remove-MgServicePrincipalKey requires a JWT signed by the private key we are retrying to remove. Firstly, you might not have that private key, secondly I have no worked out how to create that JWT yet (If you know, help me out please by letting me know in the comments), so we need to work around that by using the Update-MqServicePrincipal cmdlet instead.

Please remember the below code provided as is, use at own risk.

// 5a
$thumbprint = (Get-AuthConfig).CurrentCertificateThumbprint

// 5b / 5c
Import-Module Microsoft.Graph.Applications
Connect-MgGraph -Scopes "Application.ReadWrite.All"
$ServiceName = "00000002-0000-0ff1-ce00-000000000000"
$p = Get-MgServicePrincipalByAppId -AppId $ServiceName
$keycredentials = @()

Write-Host "Current Key Credentials"
$p.KeyCredentials

foreach ($key in $p.KeyCredentials) {
    if ($key.customKeyIdentifier -and ([System.Convert]::ToBase64String($key.customKeyIdentifier) -eq $thumbprint)) {
		Write-Host "Match - Removing key with KeyId: $($key.keyId)"
	}
	else
	{
		Write-Host "Keeping key with KeyId: $($key.keyId)"
		$keycredentials += $key
	}
}

Update-MgServicePrincipal -ServicePrincipalId $p.Id -KeyCredentials $keycredentials

If you know you want to remove all keys we can use the following code instead

// 5b / 5c
Import-Module Microsoft.Graph.Applications
Connect-MgGraph -Scopes "Application.ReadWrite.All"
$ServiceName = "00000002-0000-0ff1-ce00-000000000000"
$p = Get-MgServicePrincipalByAppId -AppId $ServiceName
$keycredentials = @()
Update-MgServicePrincipal -ServicePrincipalId $p.Id -KeyCredentials $keycredentials

Conclusion

Hopefully this blog post will be rendered useless when Microsoft finally update the documentation correctly. Until then I hope this allows you to continue with your last exchange server decommission.

Let me know in the comments if this has helped your organisation.

I’m an IT professional who has worked for distributers and several MSP’s for over 15 years. My main focus is on the Microsoft Cloud but have a deep knowledge in Exchange, Active Directory and core Windows infrastructure. I’m a lover of craft beer and going to live music events. I’m also on a mission to slowly make my home smarter.

You may also like

How To: Remove OAuth Credentials when removing Exchange
Removing the last Exchange server but OAuth service principal creds wont remove. Here's what’s wrong and how to fix it....
Yes No Choice Function in PowerShell
This article explores a PowerShell function that simplifies user prompts using the $host.ui.PromptForChoice method. By restricting input to predefined options, the function ensures accuracy and ease of use. It returns 0 for No and 1 for Yes, with customizable parameters for the prompt's title, message, and default selection....

Leave a Reply

Your email address will not be published. Required fields are marked *