When Monday Morning Gets Interesting

Imagine this: you're sipping coffee on a calm Monday morning when suddenly Microsoft's new directive hits your inbox. Now you're scrambling to ensure no rogue AD writer hijacks your precious Entra roles. Don't panic—grab your PowerShell cape, and let's save your directory from impending doom.

Deadline Alert: Effective June 1, 2026, Microsoft will block hard-matching new Active Directory user objects to existing cloud users that hold an Entra role. If you run hybrid identity with Entra Connect or Cloud Sync, keep reading.

SyncJacking 101: What Is It?

Microsoft is tightening hybrid identity practices—and for a good reason. SyncJacking is a potential attack where an adversary with Active Directory write access could craft an on-premises user object that hard-matches to a privileged cloud account through Entra Connect synchronization.

In plain English: an attacker who can create or modify AD user objects could inject their account into your cloud directory, hijacking a privileged Entra account faster than you can say "Zero Trust." Not so funny when your Global Admin account suddenly starts offering free crypto on LinkedIn.

How It Works

  1. Attacker gains write access to on-premises Active Directory.
  2. Creates a new AD user with a mS-DS-ConsistencyGuid (ImmutableID) that matches a privileged cloud-only Entra user.
  3. Entra Connect syncs the object and hard-matches it to the existing cloud user.
  4. The attacker now controls the on-premises copy—and by extension, the privileged cloud account.

The fix: Microsoft's upcoming enforcement will block hard-matching for any cloud user that holds an Entra directory role, effectively shutting down this attack vector.

The Problem: Are You Already Exposed?

Before June 1 rolls around, you need to answer one critical question: do any of your Entra role holders have an onPremisesImmutableId set?

If they do, those accounts are synced from (or have been matched to) on-premises AD. That's exactly the scenario Microsoft is locking down. You need to audit both:

  • Permanently assigned roles — Users with always-on directory role assignments.
  • PIM-eligible roles — Users who can activate privileged roles through Privileged Identity Management.

Why both? The basic audit script from the original advisory only covers permanent assignments. PIM-eligible users are equally at risk—if someone can activate Global Admin, they're a target for SyncJacking too.

Script 1: Audit Permanent Role Assignments

Let's tackle the straightforward case first—accounts with permanently assigned Entra directory roles:

Required Graph Scopes (Delegated)

  • Directory.Read.All — read user properties including onPremisesImmutableId
  • RoleManagement.Read.Directory — read Entra directory role assignments
# Connect to Microsoft Graph API (delegated / interactive)
Connect-MgGraph -Scopes "Directory.Read.All", "RoleManagement.Read.Directory"

# Query all permanent role assignments and check for synced accounts
Get-MgRoleManagementDirectoryRoleAssignment -All | ForEach-Object {
    $user = Get-MgUser -UserId $_.PrincipalId `
        -Property "displayName,userPrincipalName,onPremisesImmutableId,onPremisesSyncEnabled" `
        -ErrorAction SilentlyContinue

    if ($user -and $user.onPremisesImmutableId) {
        [PSCustomObject]@{
            Name        = $user.displayName
            UPN         = $user.userPrincipalName
            ImmutableId = $user.onPremisesImmutableId
            SyncEnabled = $user.onPremisesSyncEnabled
        }
    }
} | Format-Table -AutoSize

Reading the output: No output means you're in the clear. Any results mean those synced accounts hold permanent Entra roles—investigate and remediate before June 1.

Script 2: Audit PIM-Eligible Assignments

Because why settle for 70% safety when you can have 100%? PIM-eligible roles are the gap most admins miss. If someone can activate Global Admin through PIM, they're just as much a SyncJacking target.

Required Graph Scopes (Delegated)

  • RoleEligibilitySchedule.Read.Directory — read PIM eligibility schedules
  • Directory.Read.All — read user properties

Why RoleEligibilitySchedule and not RoleAssignmentSchedule? The AssignmentSchedule cmdlet returns active assignments (which overlap with Script 1). The EligibilitySchedule cmdlet returns users who are eligible to activate a role through PIM but haven't activated it yet—that's the gap we need to close.

# Connect to Microsoft Graph API with PIM eligibility scopes
Connect-MgGraph -Scopes "RoleEligibilitySchedule.Read.Directory", "Directory.Read.All"

# Query all PIM-eligible role schedules (users who CAN activate a role)
$schedules = Get-MgRoleManagementDirectoryRoleEligibilitySchedule -All

$results = foreach ($schedule in $schedules) {
    $user = Get-MgUser -UserId $schedule.PrincipalId `
        -Property "displayName,userPrincipalName,onPremisesImmutableId,onPremisesSyncEnabled" `
        -ErrorAction SilentlyContinue

    if ($user -and $user.onPremisesImmutableId) {
        [PSCustomObject]@{
            Name        = $user.displayName
            UPN         = $user.userPrincipalName
            ImmutableId = $user.onPremisesImmutableId
            Role        = $schedule.RoleDefinitionId
            SyncEnabled = $user.onPremisesSyncEnabled
        }
    }
}

# Display in console
$results | Format-Table -AutoSize

# Export to CSV for audit trail
$results | Export-Csv -Path "PIM_Affected_Users.csv" -NoTypeInformation

Pro Tip: Resolve Friendly Role Names

The RoleDefinitionId output is a GUID. To get a human-readable role name, you can look it up with Get-MgRoleManagementDirectoryRoleDefinition and match on Id. Here's a quick snippet to replace the GUID with the display name:

# Fetch all role definitions for name lookup
$roleDefs = Get-MgRoleManagementDirectoryRoleDefinition -All

# Then in your output, replace:
#   Role = $schedule.RoleDefinitionId
# With:
#   Role = ($roleDefs | Where-Object { $_.Id -eq $schedule.RoleDefinitionId }).DisplayName

CSV glory: The script exports results to PIM_Affected_Users.csv—making audit reviews easier than remembering your cat's vaccination calendar.

Interpreting Your Results

No Output

You're clean. None of your Entra role holders are synced from on-prem AD. No action needed—enjoy your coffee.

Results Returned

These accounts have both an Entra role and an onPremisesImmutableId. Investigate each one before the June 1 deadline.

Remediation Options

  • Convert to cloud-only: If the account doesn't need to sync, remove the onPremisesImmutableId by moving it out of sync scope and converting it to a cloud-only account.
  • Remove the Entra role: If the account is legitimately synced and doesn't need the privileged role, remove the role assignment.
  • Use a dedicated cloud-only admin account: Best practice is to have privileged Entra roles assigned to cloud-only accounts, separate from synced identities.
  • Review PIM eligibility: Ensure PIM-eligible users are intentional and documented. Remove stale eligibility assignments.

Technical Notes

Things to Keep in Mind

  • Scopes are requested in-script: Each script's Connect-MgGraph -Scopes line already requests exactly the delegated permissions it needs. You'll be prompted for consent on first run if your app registration hasn't pre-consented them.
  • Admin consent may be required: Directory.Read.All, RoleManagement.Read.Directory, and RoleEligibilitySchedule.Read.Directory typically require tenant admin consent. If you're not a Global Admin, ask one to grant consent first.
  • API throttling: For large directories, the Graph API may throttle requests. The -All parameter handles pagination automatically, but if you have thousands of role assignments, expect the script to take a few minutes.
  • Service principals vs users: The PrincipalId on a role assignment can be a user, group, or service principal. The scripts use -ErrorAction SilentlyContinue to gracefully skip non-user principals.
  • Module requirements: You'll need Microsoft.Graph.Authentication, Microsoft.Graph.Users, and Microsoft.Graph.Identity.Governance. See install commands below.

Install the Required Modules

# Option A: Install the full Microsoft Graph SDK (all submodules)
Install-Module Microsoft.Graph -Scope CurrentUser

# Option B: Install only the submodules these scripts need
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser
Install-Module Microsoft.Graph.Users -Scope CurrentUser
Install-Module Microsoft.Graph.Identity.Governance -Scope CurrentUser

Wrap-Up: Protect Your Hybrid Identity

By running both scripts, you're not just protecting your Active Directory—you're ensuring no sync-enabled supervillain can hijack your infrastructure. Do it for your users. Do it for the compliance team. Do it for lunch breaks uninterrupted by a SOC email titled "Privileged Account Anomaly."

Your Action Checklist

  1. Run Script 1 to audit permanent Entra role assignments.
  2. Run Script 2 to catch PIM-eligible assignments.
  3. Investigate any accounts that appear in the results.
  4. Remediate before June 1, 2026.
  5. Consider adopting the best practice of using cloud-only admin accounts for privileged Entra roles.

Remember: the best time to audit your hybrid identity was yesterday. The second-best time is right now. Fire up that PowerShell console and let's keep your cloud fortress secure.

Stay safe out there, cloud ninjas.

Ofir Gavish

Ofir Gavish

Microsoft MVP | Senior Cloud Solutions Architect specializing in Microsoft 365, Azure, and enterprise identity security. When not hunting SyncJacking vectors, I enjoy documenting the journey for fellow cloud ninjas.

Share this article

Related Articles