Hard match takeover in Entra Connect: the June 2026 block and what it leaves open
Apr 15, 2026 · Samantha Kloos-KilkensFrom 1 June 2026 Microsoft Entra ID refuses to hard-match a new Active Directory user onto an existing cloud-managed user that holds a Microsoft Entra role. That closes the most direct route to what Semperis called SyncJacking, but only for one specific group of accounts, and the tenant settings that block cloud object takeover entirely have been available for a long time. This post covers how matching actually works, how the takeover runs, how to find the exposed admin accounts in your tenant today, and what I would switch on before June.
Photo: Renato Leal on Unsplash
How hard match and soft match work
Without a hit, Entra tries a soft match on the primary SMTP address, and on userPrincipalName when SoftMatchOnUpn is enabled (once enabled it can't be turned off). Soft match only considers cloud objects that don't have an immutableId yet, and after a soft match the sourceAnchor is written to the cloud object, so from then on it's a hard match target. Both kinds of matching only happen for objects that arrive as new.
The asymmetry that matters here: soft matching onto a cloud user with an admin role has been blocked for years. That's the Existing Admin Role Conflict in the sync error reference. Hard match had no enforced equivalent until the June 2026 change. The InvalidHardMatch section of the same article already describes a hard match blocked for users with privileged roles, but Microsoft's announcement says enforcement only starts on 1 June, so as far as I can tell, until then the role on the target didn't stop a hard match if you had the immutableId.
How the takeover works
- The attacker controls an AD identity that can create a user, or write ms-DS-ConsistencyGuid on an existing one, in an OU that is in sync scope. Delegated OU admins, helpdesk groups and old provisioning service accounts typically have exactly that right.
- They get hold of the target's onPremisesImmutableId. It's a property on the user object, not a secret, and for a formerly synced admin it's the Base64 form of a GUID that may still be sitting on an object in AD.
- They write the decoded bytes into ms-DS-ConsistencyGuid on their object and give it a password they know.
- At the next sync cycle (every 30 minutes by default for Connect Sync, every few minutes for Cloud Sync) the object is exported as new, Entra hard-matches it to the cloud admin and converts that account to synced. With password hash sync the attacker's hash is written to the account; with pass-through authentication or federation, authentication goes to AD, where the attacker already owns the password.
- The role assignments stay where they were, on the same object. What's left between the attacker and an admin session is Conditional Access and the MFA methods registered on the account.
Cloud-only admin accounts that still carry an onPremisesImmutableId
- It was synced once and later converted by disabling directory synchronization on the tenant, or it went through the old trick of removing it from sync scope and restoring it from the recycle bin. Both leave the anchor in place. The second route leaves a user with OnPremisesSyncEnabled False that Microsoft still considers a synced object, explicitly available to be matched again, which makes it an even easier target.
- It was created in a federated domain, where Microsoft Graph requires onPremisesImmutableId when you create the user.
- It went through the new user Source of Authority transfer, which makes a synced user cloud-managed and is designed so that the AD object can take it back over on rollback.
- A migration tool or a script stamped it at some point.
Finding admin accounts with an anchor
Connect-MgGraph -Scopes "RoleManagement.Read.Directory", "Directory.Read.All"
$roleNames = @{}
Get-MgRoleManagementDirectoryRoleDefinition -All |
ForEach-Object { $roleNames[$_.Id] = $_.DisplayName }
$grants = @(
Get-MgRoleManagementDirectoryRoleAssignment -All |
Select-Object PrincipalId, RoleDefinitionId, @{ n = 'Kind'; e = { 'Active' } }
Get-MgRoleManagementDirectoryRoleEligibilitySchedule -All |
Select-Object PrincipalId, RoleDefinitionId, @{ n = 'Kind'; e = { 'Eligible' } }
)
# Role-assignable groups: report their members
$expanded = foreach ($g in $grants) {
$obj = Get-MgDirectoryObject -DirectoryObjectId $g.PrincipalId
if ($obj.AdditionalProperties['@odata.type'] -eq '#microsoft.graph.group') {
Get-MgGroupMember -GroupId $g.PrincipalId -All | ForEach-Object {
[pscustomobject]@{ PrincipalId = $_.Id; RoleDefinitionId = $g.RoleDefinitionId; Kind = "$($g.Kind) via group" }
}
}
else { $g }
}
$expanded | Group-Object PrincipalId | ForEach-Object {
$u = Get-MgUser -UserId $_.Name -Property Id, UserPrincipalName, OnPremisesImmutableId, OnPremisesSyncEnabled -ErrorAction SilentlyContinue
if ($u.OnPremisesImmutableId) {
[pscustomobject]@{
UserPrincipalName = $u.UserPrincipalName
OnPremisesSyncEnabled = $u.OnPremisesSyncEnabled
OnPremisesImmutableId = $u.OnPremisesImmutableId
Roles = ($_.Group | ForEach-Object { "$($roleNames[$_.RoleDefinitionId]) ($($_.Kind))" } | Sort-Object -Unique) -join '; '
}
}
} | Format-Table -AutoSize
For a cloud-managed admin that has no reason to keep its anchor, clear it. ADSyncTools 2.5.0 and later include Get-ADSyncToolsOnPremisesAttribute and Clear-ADSyncToolsOnPremisesAttribute for exactly this, for example
Clear-ADSyncToolsOnPremisesAttribute -Id <objectId> -onPremisesImmutableId. Don't do this for users in a federated domain that still depend on the immutableId as their claim. Before clearing, I'd also check on the AD side whether any object still carries that anchor, because an object outside sync scope with an admin's anchor on it can be used for a takeover as soon as someone moves it into a synced OU: $immutableId = '<onPremisesImmutableId from the report>'
$bytes = [Convert]::FromBase64String($immutableId)
$ldap = ($bytes | ForEach-Object { '\{0:x2}' -f $_ }) -join ''
Get-ADObject -LDAPFilter "(mS-DS-ConsistencyGuid=$ldap)" -IncludeDeletedObjects -Properties whenChanged
What the June change covers and what it doesn't
Reading the announcement next to the InvalidHardMatch section, I'd plan around these gaps:
- Accounts without an Entra role that are still powerful: Owners on Azure subscriptions, owners of app registrations with high Graph permissions, holders of Exchange or Intune RBAC roles. None of these are Entra roles.
- PIM-eligible accounts and role assignments through role-assignable groups. Both texts talk about roles that are assigned or held, so I'm not assuming eligibility or group membership counts.
- Roles that aren't labelled privileged: What's new says "Microsoft Entra roles", the troubleshooting article says "privileged roles", and I'd plan for the narrower reading.
PATCH https://graph.microsoft.com/beta/users/{id} and body {"onPremisesObjectIdentifier": null}, which needs User-OnPremisesSyncBehavior.ReadWrite.All plus Global Administrator or Hybrid Identity Administrator. Tenant settings you can switch on yourself
Connect-MgGraph -Scopes "OnPremDirectorySynchronization.ReadWrite.All"
$OnPremSync = Get-MgDirectoryOnPremiseSynchronization
$OnPremSync.Features | Select-Object BlockCloudObjectTakeoverThroughHardMatchEnabled, BlockSoftMatchEnabled
$OnPremSync.Features.BlockCloudObjectTakeoverThroughHardMatchEnabled = $true
$OnPremSync.Features.BlockSoftMatchEnabled = $true
Update-MgDirectoryOnPremiseSynchronization `
-OnPremisesDirectorySynchronizationId $OnPremSync.Id `
-Features $OnPremSync.Features
These flags are about cloud objects. Semperis reported that their synced-account variant still worked with hard match takeover blocked, which fits: that variant remaps an object that is already synced, and that is what the July OnPremisesObjectIdentifier enforcement addresses. Until then, the new audit events are your detection. In Log Analytics:
AuditLogs
| where OperationName in ("Update user", "Set DirSyncEnabled flag")
| where tostring(TargetResources) has_any ("OnPremisesObjectIdentifier", "DirSyncEnabled")
| project TimeGenerated, InitiatedBy, TargetResources
Run the inventory script this week, clear the anchor on every cloud-managed role holder that doesn't need one (emergency access accounts first), and switch on blockCloudObjectTakeoverThroughHardMatchEnabled and blockSoftMatchEnabled before 1 June.
Update April 2026: A day after this post, Microsoft changed the rollback steps for user source of authority: they now start by disabling blockCloudObjectTakeoverThroughHardMatchEnabled. So the flag I recommend above also blocks rolling back a source of authority transfer until you switch it off. Put that step in the same runbook, so nobody switches the protection off for a rollback and forgets to switch it back on.
Update July 2026: Since 1 July 2026 Entra ID also blocks a hard match when the target cloud user already has onPremisesObjectIdentifier set or is eligible for a privileged role, not only when a role is assigned, and the check is enforced in the cloud for both Connect Sync and Cloud Sync. Cloud-managed accounts with writeback enabled are excluded, and allowOnPremUpdateOfOnPremisesObjectIdentifierEnabled is available as a temporary bypass during remediation. See Troubleshoot InvalidHardMatch errors.
Sources
- Microsoft Entra Connect: When you have an existing tenant (hard match vs. soft match)
- Understanding errors during Microsoft Entra synchronization (Existing Admin Role Conflict, InvalidHardMatch)
- Microsoft Entra Connect: Design concepts (ms-DS-ConsistencyGuid as sourceAnchor)
- What's new for Microsoft Entra
- MC1262584: blocking hard match for cloud users with Microsoft Entra roles
- MC1263280: OnPremisesObjectIdentifier enforcement for synced users
- Configure user Source of Authority
- Clear on-premises attributes from migrated users (ADSyncTools)
- Microsoft Graph: onPremisesDirectorySynchronizationFeature resource type
- Set-EntraDirSyncFeature
- Semperis: SyncJacking, Azure AD account takeover