A quick scope note first, because there are two products with confusingly similar names. This post is about classic Microsoft Entra Connect Sync — the older, heavyweight on-premises sync engine formerly called Azure AD Connect, the one with a full SQL database, the Synchronization Service Manager desktop tool, and the ADSync PowerShell module. It is not about modern Entra Cloud Sync, the lightweight agent Microsoft is steering everyone toward. The sibling post on this blog, Your Entra Cloud Sync Job Has Been Failing Quietly for Three Weeks, covers the modern engine, and it can lean on a clean Microsoft Graph API to read job health. Classic Connect Sync has no such surface. That difference is the entire reason this post needs a different approach.
Here is the situation this is really about. Your team did the modern thing: your primary forest moved to Entra Cloud Sync, and you built exactly the Graph-based monitoring from that sibling post, and the Cloud Sync dashboard is a reassuring wall of green. But there is a second directory — an acquired company, a legacy resource forest, a domain nobody wants to touch — and it still syncs through a classic Entra Connect Sync server that predates the Cloud Sync rollout. Nobody monitors that box, because there is no dashboard for it in the place everyone looks. And one quiet afternoon, it stops exporting.
The problem — imports keep running, the portal looks fine, and nothing reaches Entra ID
An engineer RDPs into that ageing Connect Sync server to check a setting, opens the Microsoft Entra Connect wizard, clicks through a couple of screens, and closes it. Or a scheduled version upgrade half-completes and rolls back. Or, months ago, someone stood the box up as a standby during a migration and ticked staging mode, intending to promote it later, and then the project ended and the ticket was closed. Any of these can leave the server in a state where the scheduler is still running — still importing from Active Directory, still running synchronisation — but no longer exporting anything to Entra ID.
Nothing about this looks broken from the cloud side. The tenant is healthy. Sign-ins work. Licensing is untouched. The Cloud Sync dashboard for your primary forest is still green, because that is a completely different engine. Meanwhile, in the legacy domain, a manager gets a new job title in HR, it flows into Active Directory that night, Connect Sync imports it, synchronises it internally… and then it sits there. It is never written up to Entra ID, because exports are not running. Three weeks later a helpdesk ticket lands: "Why does the org chart in Teams still show my old title?" and the agent who picks it up has no idea that an on-premises sync engine two acquisitions removed from their daily work has quietly gone read-only.
ADSync PowerShell module. Unlike Cloud Sync, there is no documented Graph job-status endpoint to query, script, or alert on centrally. If you want to know whether Connect Sync is genuinely exporting, you have to ask the server itself. That constraint shapes everything below — it is why this audit runs locally on the sync server rather than against Graph.Why it happens — the scheduler is always running, but "running" doesn't mean "exporting"
Microsoft Entra Connect Sync drives synchronisation through a built-in scheduler that, by default, runs a cycle every 30 minutes. The critical thing to understand is a line straight from Microsoft's documentation: "The scheduler itself is always running, but it can be configured to only run one or none of these tasks." A synchronisation cycle is three phases — import, sync, export — and the scheduler can be in a state where the first two happen and the third does not. That is exactly the failure that hides so well: the process is alive, the server is busy, CPU graphs look normal, and yet the one phase that actually changes anything in Entra ID is switched off.
You read the scheduler's real state with a single cmdlet, Get-ADSyncScheduler. Four of its properties are where silent failures live:
- StagingModeEnabled — per Microsoft, when staging mode is enabled "it suppresses the exports from running but still run import and synchronization." A server left in staging mode is the single most common quiet failure: it looks completely busy and healthy, and it exports nothing.
- SyncCycleEnabled — "Indicates if the scheduler is running the import, sync, and export processes as part of its operation." It is commonly set to
$falseduring a configuration change (filtering, rule edits) and then forgotten. - SchedulerSuspended — "Set by Connect during an upgrade to temporarily block the scheduler from running." An upgrade that fails or is interrupted can leave the server suspended, so no cycle runs at all.
- CurrentlyEffectiveSyncCycleInterval — the schedule actually in effect. If someone stretched
CustomizedSyncCycleIntervalto something absurd, changes trickle through slowly enough to look stuck.
There is a second class of failure that happens even when exports are running: the export completes, but with errors. A connector account that lost a permission after an AD delegation change, a duplicate proxyAddresses value, an object that fails validation — each produces a run that finishes with a result like completed-export-errors rather than success. Those errors pile up in the Operations tab of the Synchronization Service Manager, which is precisely the tool nobody opens on a server nobody monitors. The diagram below is the whole problem in one picture.
How to verify — one command on the server, before you trust any script
Before running any audit tooling, prove the check by hand on one server you understand. RDP to the Entra Connect Sync server, open an elevated Windows PowerShell session, and run two cmdlets. If Get-ADSyncScheduler is not recognised, load the module first with Import-Module ADSync — Microsoft notes this is needed on a domain controller or a server with a higher PowerShell restriction level than default.
That one highlighted line is the whole story. The scheduler is enabled, maintenance is enabled, the interval is the healthy default of 30 minutes, the next cycle is scheduled — everything screams "healthy" except StagingModeEnabled : True, which quietly means this server has not written a single change to Entra ID since it entered that state. You can confirm the engine is genuinely ticking over with Get-ADSyncConnectorRunStatus, which returns nothing when the engine is idle and the running connector's name when a cycle is in progress.
The second thing to check by hand is the run history, because a server can be exporting and still failing. In the Synchronization Service Manager, the Operations tab is where every run and its result is listed. This is what a connector that is exporting with errors looks like — the run finished, but not with success:
| Connector | Profile | Status | Sync Errors |
|---|---|---|---|
| contoso-legacy.local | Delta Import | success | 0 |
| contoso-legacy.local | Delta Sync | success | 0 |
| contoso.com – Microsoft Entra ID | Export | completed-export-errors | 3 |
Get-ADSyncScheduler, Get-ADSyncConnectorRunStatus, and the Operations tab only report state. None of them change the scheduler, promote the server, or touch a single object. The companion script below holds to the same rule: it audits and reports, and it never remediates.The fix — Get-EntraConnectSyncHealth.ps1
Manually opening PowerShell and the Operations tab on every legacy Connect Sync server, on every check-in, does not scale and does not survive being forgotten. The companion script does the same two checks programmatically and returns a clear exit code you can wire into your own scheduled task or monitoring. Because classic Connect Sync has no Graph surface, this script runs locally on the sync server itself, using the ADSync module. It needs that module present (it installs with Entra Connect Sync) and an elevated, local-administrator session. There is no tenant authentication and no Graph scope, because there is nothing in Graph to authenticate to.
Download Get-EntraConnectSyncHealth.ps1 from the Imran76Awan/Daily-Tasks repository on GitHub — no sign-in required. It is read-only and cannot change anything in your tenant — but always validate any script in your own environment before you rely on it.
The script does two things and reports. First it reads Get-ADSyncScheduler and flags a disabled sync cycle, a suspended scheduler, a server in staging mode (unless you pass -AllowStagingMode for a deliberate standby), and an over-long effective interval. Then it walks each connector from Get-ADSyncConnector and inspects the recent runs from Get-ADSyncRunProfileResult, flagging any run whose Result is not success. Here is the scheduler-check core:
The connector check is where a naive script quietly lies to you. Get-ADSyncRunProfileResult returns run objects whose status lives on a Result property; the script reads that property defensively, and if the object shape is not what it expects it raises an error and refuses to declare the server healthy rather than guessing. Anything that is not the literal string success is surfaced, with results containing error treated as high severity. Two switches tune it for real estates: -AllowStagingMode tells it that a given box is a deliberate, documented standby so staging mode is expected rather than a finding, and -LookbackRuns controls how many recent runs per connector it inspects. -ExportCsv writes every finding to a timestamped CSV, so you have evidence to attach to a change ticket rather than a console screenful you have to retype.
Run it on the drifted server and it reads the scheduler, walks the connectors, and prints a verdict. On the box that slipped into staging mode and is also carrying export errors, it returns exit code 2:
The exit codes are deliberately simple so you can act on them from a scheduled task or a monitoring probe: 0 means healthy with no findings, 2 means findings were detected, and 1 is reserved for the script failing to do its job — not elevated, the ADSync module missing, or a query throwing. That last distinction matters. The script never prints a clean bill of health after a query failed; if it could not read the scheduler or a connector's runs, it says so and exits 1 rather than misleading you with a reassuring "0 findings". A monitoring script that can go quiet in exactly the situation you built it to catch is worse than no script at all.
Proof it worked — disable staging, run a full sync, and confirm the actual object
Remediation for the staging-mode case is not a PowerShell one-liner — it is done through the Microsoft Entra Connect wizard's "Configure staging mode" flow, unticking staging mode after confirming no other server is actively syncing that forest (only one active Connect Sync server is supported per configuration). Because this box sat in staging longer than the 7-day window, the safe follow-up is a full cycle rather than a delta, which you trigger from PowerShell:
As with the Cloud Sync sibling post, a green run is necessary but not sufficient. The scheduler being healthy tells you the engine is exporting again; it does not by itself prove the specific object that was stuck — the manager's title change — actually made it across. Close the loop by checking that object directly in Entra ID:
Get-EntraConnectSyncHealth.ps1 as a daily Windows Task on each legacy Connect Sync server and act on exit code 2, and pair it with Microsoft Entra Connect Health for sync, the native service that alerts on sync errors and, from client version 2.5.79.0 and later, produces an object-level synchronization error report categorised by cause (duplicate attribute, data mismatch, and so on). The script is your on-server, no-dependencies point check; Connect Health is your tenant-side alerting. Until this forest is migrated to Cloud Sync, run both.References
- Microsoft Entra Connect Sync: Scheduler (Get-ADSyncScheduler, SyncCycleEnabled, StagingModeEnabled, SchedulerSuspended, the 30-minute default and 7-day rule) — Microsoft Learn
- Microsoft Entra Connect Sync: Operational tasks and staging mode ("active for import and synchronization, but it doesn't run any exports") — Microsoft Learn
- Microsoft Entra Connect: ADSync PowerShell Reference (Get-ADSyncRunProfileResult, Get-ADSyncRunStepResult, Get-ADSyncScheduler) — Microsoft Learn
- Using Microsoft Entra Connect Health with sync (object-level synchronization error report) — Microsoft Learn
- Hardening updates for Microsoft Entra Connect Sync (version 2.5.79.0 required by 30 September 2026) — Microsoft Learn
- Sander Berkouwer (Microsoft MVP): An overview of Azure AD Connect's PowerShell Modules and Cmdlets — dirteam.com (supplementary)