Detect Hybrid Entra ID Join Problems with PowerShell · CloudEngineerHub
Detection & Remediation9 min read4 views
Detecting Hybrid Entra ID Join Problems with PowerShell
Detect domain-joined devices that never completed hybrid Entra join and repair the local join trigger with a scoped, idempotent PowerShell remediation script.
C
CloudEngineerHub Team
Published: September 10, 2026· Last Updated: September 28, 2026·9 min read
Hybrid Entra join fails quietly. Devices stay domain joined, users log on as always, and nothing looks broken until conditional access starts blocking a handful of machines or Intune reports devices that never became compliant. The usual story is a subset of the fleet where the local half of hybrid join never completed or never retried after a transient failure. Windows provides the mechanism and the diagnosis in the same place: the Automatic-Device-Join scheduled task under Microsoft\Windows\Workplace Join, whose LastTaskResult records the outcome of the most recent join attempt, and dsregcmd /status, which reports whether the device is domain joined and Entra joined at the same time. This article gives you a paired PowerShell detection and remediation set for exactly that. The detection script classifies the device's join state and reads the task result; the remediation script reruns the task on devices that are domain joined but stuck, with strict guardrails so it never fights the upstream infrastructure that actually owns registration.
Visual Walkthrough
C
CloudEngineerHub Team
Author
The CloudEngineerHub editorial team — Microsoft endpoint management and cloud engineers writing hands-on guides on Intune, Azure, Entra ID, Autopilot, Defender and PowerShell. Every article is reviewed against current Microsoft documentation.
Exit 1 flips the device into the remediation queue for this package.
remediate
3
Remediation executes
Fix script runs silently as SYSTEM with a 5-minute budget.
verify
4
Re-run detection
The same detection script must now exit 0 — proof, not hope.
telemetry
5
Results report
Per-device outcome lands in Reports → Remediations for trend analysis.
6
Repeat on schedule
The loop continues — drift gets caught the day it appears.
Continuous cycle
Proactive Remediations — The Detect → Fix → Verify Loop. 1. Detection script runs: Scheduled (daily/hourly): exit 0 healthy, exit 1 unhealthy — nothing more. — 2. Verdict: unhealthy: Exit 1 flips the device into the remediation queue for this package. — 3. Remediation executes: Fix script runs silently as SYSTEM with a 5-minute budget. — 4. Re-run detection: The same detection script must now exit 0 — proof, not hope. — 5. Results report: Per-device outcome lands in Reports → Remediations for trend analysis. — 6. Repeat on schedule: The loop continues — drift gets caught the day it appears.
Symptoms
Hybrid join drift shows up indirectly, usually reported by people who manage Entra or Intune rather than the device itself:
Devices appear in Active Directory Users and Computers but not in the Entra admin center's device list.
Intune shows devices as unmanaged or missing, even though they are clearly on the corporate network.
Conditional access rules that require a hybrid joined or compliant device block specific machines only.
The device state view in dsregcmd /status shows AzureAdJoined : NO while DomainJoined : YES.
dsregcmd's troubleshooting section on those devices references a failed Automatic-Device-Join attempt with a non-zero result.
Possible Causes
The join task never triggered
The Automatic-Device-Join task fires on schedule and on specific events, but Group Policy that configures hybrid join may not reach the device, or the task's trigger conditions never evaluate true. A device where the task has never run cannot have joined.
The task runs but fails
A non-zero LastTaskResult means the last attempt failed. Common drivers are no line of sight to a domain controller, unreachable Entra authentication endpoints, or key length and TPM issues on the device. The result code tells you the attempt failed; the message in the task history and event logs tell you why.
Entra Connect is not syncing the device object
Hybrid join requires the device's AD object to be synchronized to Entra ID. If the device's OU is out of sync scope, or synchronization is paused, the local join attempt can succeed while the device still never registers.
SCP or tenant configuration problems
A missing or misconfigured service connection point in AD points join attempts at the wrong tenant. Devices then fail or, worse, register against a test tenant.
The device was cloned or captured mid-state
Images taken from a device that already had a device identity produce machines with a stale identity that never matches AD, and join attempts fail persistently.
Detection Script
The detection script is read-only and finishes in seconds. It runs in 64-bit SYSTEM context and does two things: parses dsregcmd /status for the AzureAdJoined and DomainJoined flags, and reads the Automatic-Device-Join task's state, last run time, and LastTaskResult through the ScheduledTasks cmdlets.
PowerShell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#Requires -RunAsAdministrator
[CmdletBinding()]
param()
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
<#
.SYNOPSIS
Detection script: checks hybrid Entra ID join state and the
Automatic-Device-Join scheduled task on a domain-joined Windows device.
.DESCRIPTION
Read-only. A hybrid joined device must report AzureAdJoined YES and
DomainJoined YES in dsregcmd /status. The local retry mechanism is the
'\Microsoft\Windows\Workplace Join\Automatic-Device-Join' scheduled task;
its LastTaskResult shows whether the last join attempt succeeded.
Exit 0 = hybrid join healthy (domain joined and Entra joined).
Exit 1 = domain joined but not Entra joined, with a failing or missing
Automatic-Device-Join task.
Exit 2 = topic not applicable (not domain joined) or the state is
inconclusive (task never ran, task running, or the task succeeds
while join state lags upstream).
Exit 3 = unexpected error.
#>$logDir = 'C:\ProgramData\Company\Logs'$logFile = Join-Path $logDir'HybridJoinDetection.log'$taskPath = '\Microsoft\Windows\Workplace Join\'$taskName = 'Automatic-Device-Join'# Documented Task Scheduler status codes that can appear in LastTaskResult.$schedTaskRunning = 267009 # 0x41301 SCHED_S_TASK_RUNNING - the task is currently running$schedTaskNeverRan = 267011 # 0x41303 SCHED_S_TASK_HAS_NOT_RUN - the task has not yet runfunction Write-DetectionLog {
param([Parameter(Mandatory)][string] $Level, [Parameter(Mandatory)][string] $Message)
if (-not (Test-Path $logDir)) { New-Item -Path$logDir -ItemType Directory -Force | Out-Null }
"$([DateTime]::UtcNow.ToString('o')) [$Level] $Message" | Out-File -FilePath$logFile -Append -Encoding utf8
}
function Get-DsRegStatusFieldMap {
# Runs dsregcmd /status and returns its key:value lines as a hashtable. # Spaces in field names are stripped so field naming differences across # Windows builds resolve to the same key.$map = @{}
$output = & "$env:SystemRoot\System32\dsregcmd.exe" /status 2>$nullforeach ($linein$output) {
if ($line -match'^\s*([A-Za-z][A-Za-z0-9 ]*?)\s*:\s*(.*?)\s*$') {
$map[($Matches[1] -replace'\s', '')] = $Matches[2]
}
}
return$map
}
try {
Write-DetectionLog 'INFO''Hybrid Entra join detection started.' # ---- Check 1: dsregcmd join flags ----$dsreg = Get-DsRegStatusFieldMap
$azureAdJoined = $dsreg['AzureAdJoined']
$domainJoined = $dsreg['DomainJoined']
Write-DetectionLog 'INFO'"dsregcmd: AzureAdJoined=$azureAdJoined DomainJoined=$domainJoined"if ($domainJoined -ne'YES') {
Write-DetectionLog 'WARN''Device is not AD domain joined - hybrid join detection is not applicable to this device.'
exit 2
}
$task = Get-ScheduledTask -TaskPath$taskPath -TaskName$taskName -ErrorAction SilentlyContinue
$taskInfo = $nullif ($null -ne$task) {
$taskInfo = Get-ScheduledTaskInfo -TaskName$taskName -TaskPath$taskPath -ErrorAction SilentlyContinue
}
# ---- Healthy hybrid join ----if ($azureAdJoined -eq'YES') {
$lastResult = 'unknown'if ($null -ne$taskInfo) { $lastResult = $taskInfo.LastTaskResult }
Write-DetectionLog 'INFO'"Hybrid join healthy (domain joined and Entra joined). Task LastTaskResult=$lastResult"
exit 0
}
# ---- Domain joined but not Entra joined: evaluate the join task ----if ($null -eq$task) {
Write-DetectionLog 'ERROR''Device is domain joined but not Entra joined, and the Automatic-Device-Join task is missing. The local join mechanism is broken - confirm hybrid join policy/GPO reaches the device.'
exit 1
}
$lastTaskResult = $taskInfo.LastTaskResult
$lastRunTime = $taskInfo.LastRunTime
Write-DetectionLog 'INFO'"Domain joined but not Entra joined. Task state=$($task.State) LastRunTime=$lastRunTime LastTaskResult=$lastTaskResult"if ($lastTaskResult -eq$schedTaskNeverRan) {
Write-DetectionLog 'WARN''Automatic-Device-Join has never run. Confirm the task trigger and that hybrid join configuration reaches the device.'
exit 2
}
if ($lastTaskResult -eq$schedTaskRunning) {
Write-DetectionLog 'WARN''Automatic-Device-Join is currently running - re-run detection after it completes.'
exit 2
}
if ($lastTaskResult -eq0) {
Write-DetectionLog 'WARN''Automatic-Device-Join reports success but the device is still not Entra joined. The cause is upstream: Entra Connect sync, domain controller connectivity, or device OU scoping.'
exit 2
}
Write-DetectionLog 'ERROR'"Automatic-Device-Join last completed with result $lastTaskResult - the hybrid join attempt is failing."
exit 1
}
catch {
Write-DetectionLog 'ERROR'"Unexpected failure: $($_.Exception.Message)"
exit 3
}
finally {
Write-DetectionLog 'INFO''Hybrid Entra join detection finished.'
}
Detection Logic
Join state pair. The script parses dsregcmd /status into a field map and evaluates both flags together. DomainJoined anything other than YES exits 2 because the hybrid join topic does not apply. AzureAdJoined YES with DomainJoined YES is the definition of healthy hybrid join, and the script logs the task result alongside for context.
Task presence. On a device that is domain joined but not Entra joined, the script reads Get-ScheduledTask for \Microsoft\Windows\Workplace Join\Automatic-Device-Join. A missing task means the local mechanism is gone, typically because policy never applied or the image was stripped; that exits 1 because no amount of retrying will help until policy is repaired.
LastTaskResult. A result of 0 means the last attempt succeeded. The script also recognizes two documented Task Scheduler status codes: 267009 (0x41301, the task is currently running) and 267011 (0x41303, the task has not yet run). Both exit 2, since the state is inconclusive rather than failed. Any other non-zero result exits 1: the attempt is failing, and the remediation or an infrastructure review is the next step.
Success-but-still-not-joined. A zero result on a device that is still not Entra joined exits 2 with a pointed message: the local mechanism works, and the cause is upstream, usually Entra Connect sync or connectivity. Treating that case as an error would send you fixing the wrong layer.
Remediation Script
The remediation is deliberately narrow. It confirms the device is domain joined and not yet Entra joined, confirms the task exists, and then triggers the join by starting Automatic-Device-Join, the operating system's own retry mechanism. If the task is already running it waits instead of starting a second instance, then polls until the task finishes or a timeout (600 seconds by default) is reached, and reports the final LastTaskResult. It never modifies group policy, never touches Entra Connect, never writes to Active Directory, and never attempts to fabricate a device identity. If the retry fails again, it exits 1 and says where to look: domain controller connectivity and sync state.
PowerShell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#Requires -RunAsAdministrator
[CmdletBinding()]
param(
# Maximum seconds to wait for the Automatic-Device-Join task to finish # after it has been started.
[ValidateRange(60, 1800)]
[int] $TaskCompletionTimeoutSeconds = 600
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
<#
.SYNOPSIS
Remediation script: retriggers hybrid Entra ID join by running the
Automatic-Device-Join scheduled task.
.DESCRIPTION
Scope (deliberately narrow):
- Only acts when the device is AD domain joined and NOT yet Entra joined.
- Starts the built-in '\Microsoft\Windows\Workplace Join\Automatic-Device-Join'
task (or waits when it is already running) and waits for completion.
- Never touches domain credentials, Entra Connect, group policy, or the
device object in Active Directory. Join retries depend on upstream
infrastructure (domain controller reachability, Entra Connect sync);
those problems are reported, not repaired, by this script.
Exit 0 = already hybrid joined, or the retry task completed successfully.
Exit 1 = the retry task ran and failed again (LastTaskResult non-zero).
Exit 2 = not applicable or not locally repairable (not domain joined, or
the scheduled task is missing).
Exit 3 = unexpected error.
#>$logDir = 'C:\ProgramData\Company\Logs'$logFile = Join-Path $logDir'HybridJoinRemediation.log'$taskPath = '\Microsoft\Windows\Workplace Join\'$taskName = 'Automatic-Device-Join'function Write-RemediationLog {
param([Parameter(Mandatory)][string] $Level, [Parameter(Mandatory)][string] $Message)
if (-not (Test-Path $logDir)) { New-Item -Path$logDir -ItemType Directory -Force | Out-Null }
"$([DateTime]::UtcNow.ToString('o')) [$Level] $Message" | Out-File -FilePath$logFile -Append -Encoding utf8
}
function Get-DsRegStatusFieldMap {
$map = @{}
$output = & "$env:SystemRoot\System32\dsregcmd.exe" /status 2>$nullforeach ($linein$output) {
if ($line -match'^\s*([A-Za-z][A-Za-z0-9 ]*?)\s*:\s*(.*?)\s*$') {
$map[($Matches[1] -replace'\s', '')] = $Matches[2]
}
}
return$map
}
try {
Write-RemediationLog 'INFO'"Hybrid join remediation started. TaskCompletionTimeoutSeconds=$TaskCompletionTimeoutSeconds" # ---- Verify the remediation is applicable ----$dsreg = Get-DsRegStatusFieldMap
$azureAdJoined = $dsreg['AzureAdJoined']
$domainJoined = $dsreg['DomainJoined']
Write-RemediationLog 'INFO'"dsregcmd: AzureAdJoined=$azureAdJoined DomainJoined=$domainJoined"if ($domainJoined -ne'YES') {
Write-RemediationLog 'WARN''Device is not AD domain joined - hybrid join remediation is not applicable.'
exit 2
}
if ($azureAdJoined -eq'YES') {
Write-RemediationLog 'INFO''Device is already Entra joined (hybrid joined) - no action needed.'
exit 0
}
$task = Get-ScheduledTask -TaskPath$taskPath -TaskName$taskName -ErrorAction SilentlyContinue
if ($null -eq$task) {
Write-RemediationLog 'ERROR''Automatic-Device-Join task not found. Restore the Workplace Join task through hybrid join policy/GPO first; this cannot be repaired locally.'
exit 2
}
# ---- Trigger the retry (or wait for the in-flight attempt) ----if ($task.State -eq'Running') {
Write-RemediationLog 'INFO''Task is already running - waiting for it to finish instead of starting a second instance.'
}
else {
Write-RemediationLog 'INFO'"Starting scheduled task '$taskName' to retry hybrid join."
Start-ScheduledTask -TaskName$taskName -TaskPath$taskPath -ErrorAction Stop
}
$deadline = (Get-Date).AddSeconds($TaskCompletionTimeoutSeconds)
do {
Start-Sleep -Seconds15$task = Get-ScheduledTask -TaskPath$taskPath -TaskName$taskName -ErrorAction Stop
Write-RemediationLog 'INFO'"Task state: $($task.State)"
} while ($task.State -eq'Running' -and (Get-Date) -lt$deadline)
if ($task.State -eq'Running') {
Write-RemediationLog 'WARN'"Task still running after $TaskCompletionTimeoutSeconds seconds. Join attempts can take several minutes on slow links; detection will confirm the outcome on the next run."
exit 0
}
$taskInfo = Get-ScheduledTaskInfo -TaskName$taskName -TaskPath$taskPath -ErrorAction Stop
Write-RemediationLog 'INFO'"Task finished. LastTaskResult=$($taskInfo.LastTaskResult) LastRunTime=$($taskInfo.LastRunTime)"if ($taskInfo.LastTaskResult -eq0) {
Write-RemediationLog 'INFO''Retry task completed successfully. Device registration may take additional time to surface as AzureAdJoined; the next detection run confirms.'
exit 0
}
Write-RemediationLog 'ERROR'"Retry task completed with failure result $($taskInfo.LastTaskResult). Investigate domain controller connectivity and Entra Connect sync state."
exit 1
}
catch {
Write-RemediationLog 'ERROR'"Remediation failed: $($_.Exception.Message)"
exit 3
}
finally {
Write-RemediationLog 'INFO''Hybrid join remediation finished.'
}
The design principle worth copying: the script triggers a retry that Windows already knows how to perform, and it reports rather than repairs upstream failures. That keeps the blast radius close to zero.
Deploying Through Intune
In the Microsoft Intune admin center, go to Devices → Scripts and remediations and select + Add on the Windows 10/11 platform tab.
Name the package Remediate - Hybrid Entra join retry and describe the blast radius: "starts the Automatic-Device-Join task on domain-joined devices that are not Entra joined".
On the Detection script tab, upload detect.ps1. Set Run script in 64-bit PowerShell: Yes and Run this script using signed-on credentials: No so it runs as SYSTEM.
On the Remediation script tab, upload remediate.ps1 with the same context settings. Leave Enforce script signature check: No unless you sign internal scripts.
Set Frequency to Daily while you are stabilizing hybrid join, then move to Weekly. Join retries are cheap, but there is no reason to loop them aggressively once the estate is healthy.
On Assignments, target a device group containing your domain-joined Windows fleet, ideally derived from an OU-based dynamic rule or a collection synced from Configuration Manager. Keep workgroup and Entra-joined-only devices out of scope.
Review and create.
Post-specific notes:
Devices must have line of sight to a domain controller for the retry to succeed; schedule the package so it runs while devices are on the corporate network, and expect remote VPN-only devices to lag.
Remediation runs as SYSTEM, which matches the task's own context, so no credential handling is involved.
If your remediation reports repeated failure results across many devices, stop and investigate Entra Connect before letting the loop continue.
Visual Walkthrough
The branch-by-branch path from "not joined" to a healthy PRT.
Device sync scope includes this OU? Sync health green?
3
Fix connectivity or scope
Allow device endpoints or widen the sync OU.
4
Re-run join
Task completes; dsregcmd flips AzureAdJoined to YES.
Hybrid Join Debug — dsregcmd Tells All. Gate: dsregcmd /status says? — Joined, no PRT: Validate SCP — Joined, no PRT: Cloud auth reachable — Joined, no PRT: Event 304/305 — Joined, no PRT: PRT acquired — Not joined: Check auto-join task — Not joined: AD Connect state — Not joined: Fix connectivity or scope — Not joined: Re-run join
Exit Codes
Intune treats any non-zero detection exit as remediation needed. Both scripts use the same convention:
Code
Meaning
0
Hybrid join healthy (detection), or already joined / retry triggered and completed successfully (remediation)
1
Domain joined but not Entra joined with a failing or missing task (detection), or the retry failed again (remediation)
2
Not domain joined (not applicable), task never ran or is running, task succeeds while join state lags, or the task is missing so a local fix is impossible
3
Unexpected script error - inspect the log
Logging
Detection writes to C:\ProgramData\Company\Logs\HybridJoinDetection.log and remediation to C:\ProgramData\Company\Logs\HybridJoinRemediation.log. Each line carries a UTC timestamp, a level, and a message; the dsregcmd flags, task state, last run time, and LastTaskResult are all logged on every run, which makes the file a tidy history of join attempts per device. Expect two to six lines per run; monthly rotation is more than enough. For deeper diagnosis, cross-reference the Microsoft-Windows-User Device Registration/Admin event log, where the join operation itself writes its errors.
Troubleshooting the Script
Exit 2 flood after first deployment. Most of it will be devices that are not domain joined or where the task has not run. Scope your assignments before assuming a script fault.
Remediation exits 3 immediately. Confirm 64-bit SYSTEM execution; Start-ScheduledTask also fails if task scheduler policy blocks it, so check for restrictions in your hardening baselines.
Task finishes but dsregcmd still shows NO. Expected in some cases: registration depends on Entra Connect sync cadence. Wait for the next detection run before judging.
Repeated non-zero LastTaskResult. Look at the task's history in Task Scheduler and the User Device Registration event log; the numeric result alone will not tell you the cause.
No log file. The script never ran: verify assignment, last check-in, and that no other package with the same name conflicts.
How to Validate the Fix
On a device with AzureAdJoined : NO and DomainJoined : YES, run the detection script manually in an elevated 64-bit console. Expect exit code 1 or 2 with the task result logged.
Run the remediation script manually. Expect log lines showing the task start, state polling, and a final LastTaskResult, then exit code 0 on success.
Run dsregcmd /status again after the task completes. AzureAdJoined may take a few minutes to flip to YES once registration succeeds.
Re-run detection. Expect exit code 0 with "Hybrid join healthy" in the log.
In the Entra admin center, confirm the device object appears under Devices, and in Intune confirm the device starts checking in.
Production Considerations
Context. Both scripts run as SYSTEM in 64-bit PowerShell; the scheduled task itself also runs in SYSTEM context, so no credentials are stored or passed anywhere.
Idempotency. Detection is read-only. Remediation checks state first and exits 0 immediately when the device is already hybrid joined; rerunning it simply retriggers Windows' own retry task.
Side effects. Starting Automatic-Device-Join performs a device registration attempt. There is no reboot, no service restart, and no interruption to signed-in users. Devices without domain controller line of sight will simply fail the attempt and keep their previous state.
Security review. The scripts contain no secrets and modify nothing beyond triggering a built-in task. Have your PowerShell automation SME and security team review them and validate the package in a test ring before broad production rollout, especially where hardening baselines restrict scheduled task control.
Scope discipline. Assign only to device groups that are supposed to be hybrid joined. On devices intentionally Entra joined or workgroup based, the detection exits 2 and adds noise without value.
Is starting the Automatic-Device-Join task safe to run fleet-wide?
Yes, within scope. The task is Windows' own retry mechanism for hybrid join, it runs as SYSTEM, and a failed attempt leaves the device unchanged. The only caution is assignment: point the package at domain-joined devices that should be hybrid joined, and stop looping if results show fleet-wide failures, which indicate an upstream cause.
The task reports success but dsregcmd still shows AzureAdJoined NO. Why?
Registration involves more than the local task: the device's AD object must be synchronized to Entra ID by Entra Connect, and the device must reach the Entra endpoints. Check sync scope for the device's OU and the synchronization schedule before suspecting the device itself.
Does hybrid join require a signed-in user?
No. The join is device-scoped and the Automatic-Device-Join task runs in SYSTEM context regardless of whether anyone is logged on. That is why running detection and remediation through Intune without signed-on credentials is the right configuration.
Comments
0No comments yet — be the first to share your thoughts.