Identity Azure

Microsoft Entra Connect Sync Deep Dive: Designing Hybrid Identity with PHS, PTA, and Seamless SSO

Hybrid identity lives or dies on the sync engine that projects on-premises Active Directory into Microsoft Entra ID. Get the authentication method, the sourceAnchor, and the sync rule precedence right and it runs for years untouched; get them wrong and you are doing a tenant-wide re-sync at 2 a.m.

Naming note: the product formerly known as Azure AD Connect is now Microsoft Entra Connect Sync. The installed component, run profiles, and the ADSync PowerShell module are unchanged; only the branding moved.

1. Architecture and choosing your authentication method

Entra Connect Sync synchronizes objects (users, groups, contacts, devices) from one or more AD forests into a single Entra tenant. Authentication — how a user’s password is actually validated — is a separate decision layered on top of sync. You have three options:

Method Where password is validated On-prem dependency at sign-in Best for
Password Hash Sync (PHS) In the cloud, against a synced hash-of-a-hash None — survives an on-prem outage The default for almost everyone
Pass-through Authentication (PTA) On-prem DC, via lightweight agents Requires a live agent + DC “Passwords never leave the building” mandates
Federation (AD FS) On-prem AD FS farm Requires the whole AD FS estate Smart-card/3rd-party MFA, legacy claims rules

Principal’s take: default to PHS. Even if you run PTA or federation, enable PHS as a backstop so that leaked-credential detection (Entra ID Protection) works and you have an instant failover if your agents or AD FS go down. Microsoft’s own guidance is that PHS should be on unless a hard compliance rule forbids it.

PHS does not sync the cleartext or even the NTLM hash to the cloud — it syncs a salted PBKDF2/SHA-256 hash of the NTLM hash, which is not reversible to anything usable against on-prem. PTA keeps validation on-prem but introduces a runtime dependency you must make redundant (see Step 6).

Seamless SSO is orthogonal to all three: it silently signs in domain-joined, corporate-network users with Kerberos so they skip the username/password prompt. It pairs with PHS or PTA (not needed with federation, which handles SSO itself).

2. Pre-flight: UPN, sourceAnchor, and connectivity

Most failed deployments fail here, before installation.

Verify UPN suffixes are routable

Every user’s on-prem userPrincipalName suffix must be a verified custom domain in Entra. The classic trap is users with a non-routable .local UPN suffix. Add a routable suffix in AD and re-stamp users before you sync.

# On a DC: add a routable UPN suffix to the forest
Get-ADForest | Format-List UPNSuffixes
Set-ADForest -Identity contoso.local -UPNSuffixes @{Add="contoso.com"}

# Re-stamp users from .local to the routable suffix
Get-ADUser -Filter "UserPrincipalName -like '*@contoso.local'" -SearchBase "OU=Staff,DC=contoso,DC=local" |
  ForEach-Object {
    $new = ($_.UserPrincipalName -replace '@contoso\.local$','@contoso.com')
    Set-ADUser $_ -UserPrincipalName $new
  }

Choose your sourceAnchor wisely

The sourceAnchor is the immutable key linking an on-prem object to its cloud object. Historically people used objectGUID, but that is not preserved if you ever migrate a user to a new forest. Modern installs default to ms-DS-ConsistencyGuid: the wizard writes objectGUID into ms-DS-ConsistencyGuid on first sync, then uses that attribute thereafter. This makes cross-forest migration and disaster recovery dramatically easier.

Decide sourceAnchor once. Changing it after objects exist in the cloud requires deleting and re-matching every synced object. Confirm what is in use:

# On the Connect server
Get-ADSyncGlobalSettings |
  Select-Object -ExpandProperty Parameters |
  Where-Object { $_.Name -like '*SourceAnchor*' }
# Expect: Microsoft.SynchronizationOption.SourceAnchorAttribute = mS-DS-ConsistencyGuid

Connectivity and prerequisites

3. Custom installation walkthrough

Use Customize (not Express) for any real environment so you control connectors, filtering, and optional features.

Entra Connect → Customize → Install required components
  [x] Use an existing service account  -> gMSA: contoso\svc-aadsync$
  [x] Specify custom sync groups (ADSyncAdmins, Operators, Browse, PasswordSet)

User sign-in:
  (o) Password Hash Synchronization
  [x] Enable single sign-on (Seamless SSO)

Connect your directories:
  + Add AD forest  -> account with Replicating Directory Changes (for PHS)

Domain/OU filtering:
  (o) Sync selected organizational units
      [x] OU=Staff   [x] OU=ServiceAccounts   [ ] OU=Disabled  [ ] OU=Computers-Lab

Optional features:
  [x] Password hash synchronization
  [x] Password writeback        (requires Entra ID P1)
  [ ] Group writeback
  [x] ms-DS-ConsistencyGuid as the source anchor

OU filtering (above) is the coarse, recommended filter — only sync the OUs that hold real identities. For finer control you can add attribute-based filtering via an inbound sync rule that sets cloudFiltered = True for objects matching a condition (for example, a custom extensionAttribute15 flag). Filter out what you do not need; a smaller metaverse is faster and safer.

Do not check Start the synchronization process on the final page of a fresh production install if you have any doubt about filtering. Install in a state where you can inspect the connector space first, then enable the scheduler.

4. How the sync engine actually works

Internally the engine moves data through three logical stores. Understanding them is the difference between guessing and diagnosing.

 AD (source)        CONNECTOR SPACE         METAVERSE        CONNECTOR SPACE      Entra (target)
   objects   --->   (AD connector,   --->   (single,   --->  (Entra connector, --->  objects
                     staging)               authoritative)    staging)
                  [ import  ]            [ inbound  ]       [ outbound ]        [ export ]
                  [ profiles ]          [ sync rules]       [ sync rules]       [ profiles]

A Full Import / Full Sync re-reads everything and re-evaluates all rules — you run it only after changing sync rules or filtering, because it is expensive. The default scheduler runs a delta cycle every 30 minutes.

# Inspect and control the scheduler
Get-ADSyncScheduler                       # NextSyncCyclePolicyType, interval, enabled?
Start-ADSyncSyncCycle -PolicyType Delta   # force a delta cycle now
Start-ADSyncSyncCycle -PolicyType Initial # full import + full sync + export (after rule changes)
Set-ADSyncScheduler -SyncCycleEnabled $false   # pause (e.g., during maintenance)

5. Writing custom sync rules without breaking precedence

Sync rules are evaluated by precedencelower number wins. The product ships dozens of default rules occupying precedence values from 0 upward (commonly into the 100s). The cardinal rule:

Never edit a default (Microsoft-authored) rule, and never put a custom rule at a precedence that the product reserves. Author your custom rules at precedence >= 100 below the standard defaults — practically, leave a gap (e.g., start custom rules in the 50–99 band) only if you have audited what is already there. On upgrade, Microsoft can recreate default rules, silently discarding edits you made to them.

The supported pattern to change a default behavior is disable-and-clone:

  1. Open Synchronization Rules Editor.
  2. Find the default inbound rule (e.g., In from AD - User Join), click Disable (do not delete — the engine recreates deleted defaults).
  3. Clone it, give it a higher precedence number than the original, and edit the clone.

A common real-world need: stop overwriting the cloud mail attribute for a subset of users. Done as a scoped inbound transformation rule:

Synchronization Rules Editor -> Inbound -> Add new rule
  Name:            In from AD - Custom mail flow
  Connected System: contoso.local
  CS Object Type:   user        MV Object Type: person
  Link Type:        Join
  Precedence:       90          (above the default user rules you cloned/disabled)

  Scoping filter (group):
     department  EQUAL  "Contractors"

  Transformations:
     FlowType  Target            Source / Expression
     Constant  cloudFiltered     False
     Direct    mail              mail
     Expression accountEnabled   IIF([userAccountControl] BAND 2 = 0, True, False)

On those expressions: cloudFiltered drives whether an object is exported (set True to filter it out), and the accountEnabled expression is how the default rules read the disabled bit (0x2, ACCOUNTDISABLE) out of userAccountControl so disabled AD accounts arrive disabled in Entra. Always export your rule set before and after changes so you can diff and roll back:

# Document the current rule set as code (review in source control)
Get-ADSyncRule | Sort-Object Precedence |
  Select-Object Precedence, Name, Direction, Disabled |
  Format-Table -AutoSize

# Full, restorable export of every rule
Get-ADSyncRule | ConvertTo-Json -Depth 10 |
  Out-File C:\ADSync\sync-rules-backup.json

6. Seamless SSO and PTA agents with redundancy

Pass-through Authentication runs through lightweight PTA Authentication Agents. The Connect server installs the first agent; you must install at least two more on separate servers so a single host outage does not block all sign-ins. Three agents total (the one on the Connect server plus two standalone) is the practical minimum for production.

Download "Microsoft Entra Connect Authentication Agent" -> install on 2+ extra
domain-joined servers (NOT the Connect server). Each registers itself to the tenant.

Verify in portal:
  Entra admin center -> Identity -> Hybrid management -> Microsoft Entra Connect
    -> Connect Sync / Pass-through authentication  -> agents = Active (>= 3)

Seamless SSO works by creating a computer account named AZUREADSSOACC in AD and sharing its Kerberos decryption key with Entra. The critical, frequently-missed operational task is rolling the AZUREADSSOACC Kerberos key at least every 30 days — Microsoft explicitly recommends this. Treat it as a scheduled job.

# On the Connect server, from the Seamless SSO module folder
Import-Module 'C:\Program Files\Microsoft Azure Active Directory Connect\AzureADSSO.psd1'
New-AzureADSSOAuthenticationContext         # prompts for a Hybrid Identity Admin
Get-AzureADSSOStatus | ConvertFrom-Json     # which domains have Seamless SSO enabled

# Roll the Kerberos decryption key (do this >= every 30 days)
$creds = Get-Credential                     # on-prem Domain Admin for AZUREADSSOACC
Update-AzureADSSOForest -OnPremCredentials $creds

Client side, Seamless SSO requires the Entra URLs (https://autologon.microsoftazuread-sso.com and https://aadg.windows.net.nsatc.net) to be in the browser’s Intranet zone, typically pushed via Group Policy. Without that, Kerberos silent sign-in falls back to a prompt.

7. High availability: staging mode and upgrades

Entra Connect is active/passive, not active/active. You run a second server in staging mode: it imports and runs sync rules (so its metaverse is warm and current) but does not export to Entra and does not do password writeback. This is your DR box, your safe place to test rule changes, and the target for swing upgrades.

# Confirm a server's role
(Get-ADSyncScheduler).StagingModeEnabled   # True on the standby box

# Failover: promote staging to active, then demote the old primary
#  Old primary:  Set-ADSyncScheduler -StagingModeEnabled $true
#  New primary:  Set-ADSyncScheduler -StagingModeEnabled $false

Run a failover drill quarterly. Because both servers must use the same sourceAnchor and the same connector/filtering configuration, the safest way to keep them identical is to export the active config and import it on staging:

# On the active server: snapshot full configuration
Get-ADSyncServerConfiguration -Path C:\ADSyncExport

# (then import that snapshot when building/refreshing the staging server)

Upgrade strategy: prefer automatic upgrade for in-place minor updates (Get-ADSyncAutoUpgrade shows the state). For major version jumps, do a swing migration: stand up the new version on the staging server, validate its metaverse and a pending-export preview against production, then flip staging to active. This gives you an instant rollback — just flip back. Entra Connect builds expire (older builds get deprecated and eventually blocked), so do not let the version drift; treat staying current as part of the operational baseline.

Enterprise scenario

A retail platform team ran PTA with three agents and Seamless SSO across two AD forests. Sign-ins started failing intermittently for one business unit after an acquisition forest was onboarded — but only for users whose accounts had been migrated between OUs. Root cause: a previous admin had edited the default In from AD - User Join rule in place to flow a custom employeeId into extensionAttribute2. A minor auto-upgrade silently recreated that default rule, dropping the edit, so cloudFiltered logic downstream stopped scoping correctly and the migrated objects got cloudFiltered = True on the next full sync — they were deleted from the tenant, breaking their PTA sign-in entirely.

The fix was the supported disable-and-clone pattern plus codifying rules in source control so an upgrade can never resurrect a silent default over a hand edit:

# Disable the Microsoft default, clone at a higher precedence, own the change
$def = Get-ADSyncRule | ? { $_.Name -eq 'In from AD - User Join' }
Disable-ADSyncRule -Identifier $def.Identifier

# Snapshot rules as code; diff this in CI on every Connect upgrade
Get-ADSyncRule | Sort-Object Precedence |
  Select-Object Precedence, Name, Direction, Disabled, Identifier |
  Export-Csv C:\ADSync\rules-baseline.csv -NoTypeInformation

They also moved the employeeId transformation into a cloned inbound rule at precedence 90, re-ran Start-ADSyncSyncCycle -PolicyType Initial, and added a pre/post-upgrade Get-ADSyncRule diff to their change runbook. Lesson: any edit to a Microsoft-authored rule is a time bomb that detonates on the next upgrade — clone, never edit in place, and treat the rule set as version-controlled config.

Verify

Confirm the deployment end to end before you call it done.

# 1. Engine healthy, scheduler enabled, last cycle clean
Get-ADSyncScheduler
Get-ADSyncConnectorRunStatus            # should be idle, not stuck "Running"

# 2. No export errors and a sane pending-export count
Get-ADSyncConnectorStatistics -ConnectorName "<tenant> - AAD"
#    -> watch ExportAdd/Update/Delete; investigate any *Error counts

# 3. PHS is actually flowing
$c = Get-ADSyncConnector | ? { $_.Type -eq 'Extensible2' }
Get-ADSyncAADPasswordSyncConfiguration -SourceConnector $c.Name   # Enabled = True

# 4. Seamless SSO / PTA enabled for the right domains / agents Active
Get-AzureADSSOStatus | ConvertFrom-Json

Then validate from the user’s side: a synced user signs in to https://myapps.microsoft.com; on a domain-joined corp machine they should not be prompted (Seamless SSO), and a password changed on-prem should authenticate in the cloud within the sync interval (PHS) or immediately (PTA). In the portal, check Connect Health (or the Connect blade) for agent status and sync errors.

Checklist

Pitfalls and next steps

Next, wire Connect Health alerts to your incident channel, enable password writeback so self-service password reset round-trips to AD, and layer Conditional Access on top so that how identities sync and how they are allowed to sign in are governed as one coherent design.

Entra IDHybrid IdentityEntra ConnectPHSPTA

Comments

// part 1 of 2 · Entra Hybrid Identity Sync

Keep Reading