Long story short, I was answering a question around hard-matching user objects earlier today and wanted to include an article with detailed instructions on how the process works. Interestingly, all the articles I could find online seem to leverage the now deprecated MSOnline/AzureAD modules, and while these should still do the job, better solutions are available now. So, here’s a quick and dirty article on how to perform hard-match via the Graph API methods or the corresponding Graph SDK for PowerShell cmdlets.
First, a very quick introduction for those that are unaware how hard-match works. In a nutshell, the process “generates” an unique identifier for the on-premises user object and stamps it against the “matching” cloud object. Back in the day, the objectGUID property was used for this, with the mS-DS-ConsistencyGuid one taking over few years ago. Either way, we have a GUID value, which we need to transform and “stamp” on the cloud object’s ImmutableId property. Effectively, you are the one deciding which two objects to link together.
To generate the corresponding immutable ID value, we take the object’s GUID from AD and convert it to Base64-encoded string. This operation can of course easily be performed via PowerShell, and in turn we can leverage the AD cmdlets in order to fetch the value from the user’s object to streamline the process. Here are some examples:
#Generate ImmutableID for given AD user
[system.convert]::ToBase64String((Get-ADUser pesho -Properties mS-DS-ConsistencyGuid)."mS-DS-ConsistencyGuid")
LspMdPCr8k2xp6yXApVuqA==
#Generate ImmutableIDs for all AD users</pre>
Get-ADUser -Filter * | select UserPrincipalName,ObjectGUID,@{n="ImmutableID";e={[System.Convert]::ToBase64String($_.ObjectGUID.tobytearray())} } | Export-Csv -nti C:\temp\immutableID.csv
The resulting string will looks something like “LspMdPCr8k2xp6yXApVuqA==”. Once we have it, all we need to do is to add its value to the corresponding user object within Entra ID. When using the good old MSOnline or Azure AD PowerShell modules, the corresponding property is ImmutableId. When using the Graph methods however, you will have to instead use the OnPremisesImmutableId property. Here are some examples:
#Update the OnPremisesImmutableId for a given user directly Update-MgUser -UserId user@domain.com -OnPremisesImmutableId "LspMdPCr8k2xp6yXApVuqA==" Get-MgUser -UserId shared2021@michev.info -Property OnPremisesImmutableId | select OnPremisesImmutableId OnPremisesImmutableId --------------------- LspMdPCr8k2xp6yXApVuqA==
Of course, doing things one at a time is boring and prone to copy/paste errors. So here is an example that leverages sample CSV file, such as the one we exported above. We have a column UserPrincipalName to designate the user (the UPN of the users will most likely not match if you are resorting to hard-match, but it’s just an example after all), the Entra ID user that is, and a column ImmutableID to designate the value we want to stamp on the OnPremisesImmutableId property.
#Bulk update ImmutableID based on a CSV file
$var = Import-CSV C:\temp\immutableID.csv
$var | % { Update-MgUser -UserId $_.UserPrincipalName -OnPremisesImmutableId $_.ImmutableId }
For the sake of completeness, here are also the “raw” Graph API methods you can leverage for the same operation. Basically, a PATCH operation against the /users/{userId} endpoint, simple stuff:
PATCH https://graph.microsoft.com/v1.0/users/user@domain.com
{
"OnPremisesImmutableId": "xc/mL6kUzEqfKPAZV7Zd+g=="
}
And that’s it. Wait for the sync to run (or force it) and the Entra ID user should now be “linked” to the on-premises one. Happy “matching”!

Hey i Want change the Immutable Id with HTTP Graph in powershell with Entra Application – How?
Update-MgUser -UserId -OnPremisesImmutableId “”
We had a requirement for moving few users from one AD forest to another while both forests were synced to same O365 tenant. So, we were trying to retain the same office 365 account for the users post migration.
It worked when I logged in via my global admin account and run the cmdlets (Update-MgUser) to update the onpremisesimmutableid but failed with any less privileged account.
If you want this to be run as an unattended script with Graph API Application Permissions, consider assigning the User.ReadWrite.All API permissions to the AzureAD/EntraID Application and add the application service principal as a member of the Global Administrator Role (consider the risks prior to assignment).
I setup a self-signed certificate which was uploaded to the azure application and was installed only on the secure windows server with limited access where the script was scheduled.
I have tried using the User administrator and some other privileged role but didn’t work with already synced accounts. Somehow delegated permissions and user admin permissions are able to update the onpremisesimmutableid for cloud only objects but fail for objects which are already synced via another directory, or I would guess even in the same directory (in certain special cases).
This is very much appreciated. I had more problems with Graph PowerShell modules than any of the commands needed to do the job.
Thanks a ton! They retired MSolService a couple months ago and I had no idea how to do it. Graph worked great.