HomeNewsletterCommunityToolsArchiveBlogToday's NewsAboutQuick Links Subscribe free
← Back to Blog
Scripts & Tools Windows Hello for BusinessIntuneProactive RemediationsPowerShellWHfBSecurity

Deploy Free WHfB Health Scripts to Intune: Six Checks, Five Auto-Repairs, Zero Helpdesk Calls

IA
Imran Awan
6 July 2026

Windows Hello for Business is one of the best security improvements you can deploy across your estate — but it breaks silently, in ways that are genuinely hard to diagnose. A user opens their laptop, taps their PIN, and gets a generic "Something went wrong" screen. They call the helpdesk. The helpdesk resets the PIN. It works. Until it doesn't again, two weeks later, on a different device. This post shows you how to stop that cycle by deploying an Intune Proactive Remediation pair that detects and repairs WHfB automatically, before the user ever notices.

Download the scripts — GitHub

Both scripts are free and open source. Download them from the Imran76Awan/WHFB repository on GitHub — no sign-in required.

Detect-WHfB.ps1 Remediate-WHfB.ps1 View full repo →

What actually breaks in Windows Hello for Business

WHfB has four moving parts that all need to be healthy at the same time. When any one of them drifts, authentication fails:

NGC Key (the credential)

The Windows Hello key stored in C:\Windows\ServiceProfiles\LocalService\AppData\Local\Microsoft\Ngc\. Tied to the user and device. If it gets corrupted or the folder permissions break, WHfB cannot function.

PRT (the SSO token)

The Primary Refresh Token is what gives users seamless SSO to Azure AD resources. WHfB relies on a valid PRT. If the PRT expires or the WAM token broker loses its session, sign-in fails even if the NGC key itself is healthy.

WHfB Services

Four Windows services underpin the stack: KeyIso (CNG Key Isolation), NgcSvc, NgcCtnrSvc, and TokenBroker (Web Account Manager). Any of them in Stopped state = broken authentication.

TPM + Policy

TPM 2.0 must be present, enabled, and ready. WHfB policy must not be explicitly disabled in the registry. BIOS updates can reset TPM ownership. GPO conflicts can set Enabled=0 and silently block provisioning.

How to verify — run dsregcmd /status

Before you deploy anything, run this on an affected device and look at these specific fields. This is the exact output the detection script parses.

Command Prompt (Administrator)
C:\> dsregcmd /status
| Device State
+----------------------------------------------------------------------+
  AzureAdJoined : YES
  DomainJoined : YES
  DeviceId : ████████-████-████-████-████████████
| User State
+----------------------------------------------------------------------+
  NgcSet : YES
  NgcKeyId : ████████████████████████████████████████
  AzureAdPrt : YES
  AzureAdPrtUpdateTime : 2026/07/06 08:41:23
  WamDefaultSet : YES

A broken device shows AzureAdPrt : NO or NgcSet : NO. That is your trigger to remediate.

What broken WHfB looks like in Event Viewer

Before deploying the detection script, you can confirm the failure by looking at two event logs. Open Event Viewer and navigate to Applications and Services Logs → Microsoft → Windows → User Device Registration → Admin. A device with a missing PRT shows Event ID 104:

Error — Event ID 104 — User Device Registration Microsoft-Windows-User Device Registration/Admin
Level: Error
Source: User Device Registration
Event ID: 104
Message: Automatic registration failed at join phase. Exit code: Unknown HResult Error code: 0xcaa2000c
This means the device could not obtain a PRT. WAM returned 0xcaa2000c — authentication token request denied, usually because the token broker service lost its session state.

For NGC key failures, check Applications and Services Logs → Microsoft → Windows → HelloForBusiness → Operational. Event ID 302 tells you WHfB provisioning was attempted and failed:

Warning — Event ID 302 — HelloForBusiness/Operational Microsoft-Windows-HelloForBusiness/Operational
Level: Warning
Source: HelloForBusiness
Event ID: 302
Message: Windows Hello for Business provisioning failed. NGC key creation failed. Error: NTE_BAD_FLAGS (0x80090009)
0x80090009 = the key container is in a bad state. This is the NGC folder corruption pattern — the key store exists but is inaccessible. Step 5 of the remediation script fixes this.
Gotcha: These Event Viewer logs are often empty if the device has never attempted WHfB provisioning — for example, a device where the NGC key was manually deleted. In that case dsregcmd /status is more reliable than Event Viewer for confirming the broken state.

The Detection Script — Detect-WHfB.ps1

The detection script runs 6 checks. It exits 0 (compliant, no action) or 1 (non-compliant, trigger remediation). It can run as SYSTEM or as the logged-on user — PRT and NGC checks are more accurate in user context, so schedule it to run as the logged-on user when possible.

Detect-WHfB.ps1 — Intune detection output
[08:41:22] [INFO ] Device: DESKTOP-██████ | User: DOMAIN\username
[08:41:22] [INFO ] dsregcmd parsed 47 values
 
[08:41:22] [PASS ] Join status : Hybrid Azure AD Joined [████████-████-████-████-████████████]
[08:41:22] [PASS ] PRT : Present (updated: 2026/07/06 08:41:23)
[08:41:22] [PASS ] NGC/WHfB key : Provisioned [KeyId: ████████████████████████████████████████]
[08:41:22] [PASS ] TPM : Present, enabled and ready (Owned: True)
[08:41:22] [PASS ] TPM version : 2.0, Level 0, rev 1.16
[08:41:22] [PASS ] Service [KeyIso] : Running
[08:41:22] [PASS ] Service [NgcSvc] : Running
[08:41:22] [PASS ] Service [NgcCtnrSvc] : Running
[08:41:22] [PASS ] Service [TokenBroker] : Running
[08:41:22] [PASS ] WHfB Policy : Policy key present in registry
 
[08:41:22] [PASS ] --- RESULT ---
[08:41:22] [PASS ] COMPLIANT - WHfB is fully provisioned and healthy
[08:41:22] [PASS ] Checks: Join=OK, PRT=OK, NGC=OK, TPM=OK, Services=OK, Policy=OK
Exit code: 0 — Intune marks device Compliant, no remediation triggered

The script uses a simple dsregcmd /status parser that converts each Key : Value line into a hashtable — no external module required, works on every Windows 10/11 device:

Detect-WHfB.ps1 — dsregcmd parser
function Get-DsregStatus {
    $raw = & dsregcmd.exe /status 2>&1
    $map = @{}
    foreach ($line in $raw) {
        if ($line -match '^s+([A-Za-z0-9_]+)s*:s*(.+)$') {
            $map[$Matches[1].Trim()] = $Matches[2].Trim()
        }
    }
    return $map
}
# Usage
$dsreg = Get-DsregStatus
$prtPresent = $dsreg['AzureAdPrt'] -eq 'YES'
$ngcProvisioned = $dsreg['NgcSet'] -eq 'YES'

For the policy check, the script checks three different registry locations because different versions of Windows and different GPO/MDM paths write to different keys:

Registry Editor — WHfB policy paths (all three checked)
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Policies\PassportForWork
Enabled REG_DWORD 0x00000001 ← Enabled via Group Policy
HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\PassportForWork
Enabled REG_DWORD 0x00000001
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PolicyManager\current\device\PassportForWork
Enabled REG_DWORD 0x00000001 ← Written by Intune MDM
If any path has Enabled = 0x00000000, WHfB is disabled by policy → detection exits 1

The Remediation Script — Remediate-WHfB.ps1

The remediation script runs as SYSTEM (the default Intune context) and works through five repair steps in order. It logs everything to C:\ProgramData\WHfB-Repair\WHfB-Remediate_{computername}_{timestamp}.log. The NGC cleanup step only runs if the NGC key is actually missing or broken — healthy keys are never touched.

5-Step Remediation Sequence
1
Restart WHfB Services
Sets KeyIso, NgcSvc, NgcCtnrSvc, TokenBroker to Automatic start and starts them. Safe — no data loss, instant.
2
Refresh Primary Refresh Token
Runs dsregcmd /refreshprt as the logged-on user via a scheduled task, then falls back to dsregcmd /forcerecovery as SYSTEM if that fails.
3
Hybrid AAD Re-registration
Only runs if the device is domain-joined but not AAD-joined (failed hybrid join). Triggers \Microsoft\Windows\Workplace Join\Automatic-Device-Join.
4
Trigger Intune MDM Sync
3 methods in parallel: COM EnterpriseMgmt tasks, CIM MDM_Client.UpdateSession(), and deviceenroller /AutoEnrollMDM. Re-applies WHfB policy from Intune.
5
Clean Stale NGC Keys CONDITIONAL
Only runs if NgcSet=NO or KeyId is missing/ERROR. Uses takeown + icacls to take ownership of the NGC folder, clears stale keys, runs dsregcmd /cleanupaccounts, then triggers the UserTask-Roam scheduled task to reprovision.

The clever part: running user-context commands from SYSTEM

Here is the problem with PRT refresh: dsregcmd /refreshprt only works in user context — it reaches the WAM token broker, which is session-bound. But Intune remediations run as SYSTEM. If you call dsregcmd /refreshprt from SYSTEM it silently does nothing.

The solution is Invoke-AsLoggedOnUser — a function that registers a one-time scheduled task under the logged-on user's principal, fires it 3 seconds later, waits 15 seconds, then cleans it up:

Remediate-WHfB.ps1 — Invoke-AsLoggedOnUser
function Invoke-AsLoggedOnUser {
    param([string]$TaskName, [string]$Execute, [string]$Argument = '')
 
    $loggedOnUser = Get-LoggedOnUser # Win32_ComputerSystem.UserName or query session
 
    $action = New-ScheduledTaskAction -Execute $Execute -Argument $Argument
    $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddSeconds(3)
    $principal = New-ScheduledTaskPrincipal -UserId $loggedOnUser `
                                   -LogonType Interactive -RunLevel Limited
    $settings = New-ScheduledTaskSettingsSet `
        -ExecutionTimeLimit (New-TimeSpan -Minutes 3) `
        -DeleteExpiredTaskAfter (New-TimeSpan -Minutes 5)
 
    Register-ScheduledTask -TaskName $TaskName -Action $action `
        -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null
 
    Start-Sleep -Seconds 15 # wait for dsregcmd /refreshprt to complete
    Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue
}
# Called from Repair-PRT:
$taskName = "WHfB-PRT-Refresh-$timestamp"
Invoke-AsLoggedOnUser -TaskName $taskName -Execute 'dsregcmd.exe' -Argument '/refreshprt'
Why this works: The scheduled task runs under LogonType Interactive with the logged-on user as principal. Windows Task Scheduler injects it into the user's active session, giving it access to the WAM token broker that handles Azure AD authentication. The task fires in user context even though the script registering it is running as SYSTEM. This is the same pattern used by SCCM/ConfigMgr for user-context deployments.

Proof it worked — live remediation output

Here is the actual output from running this on a device with a broken PRT and no NGC key. Pre-remediation state: AzureAdPrt=NO, NgcSet=NO. Post-remediation: both restored without a reboot.

Remediate-WHfB.ps1 — Full run output
[09:14:01] [INFO ] WHfB Intune Remediation Script started
[09:14:01] [INFO ] Computer : DESKTOP-██████
[09:14:01] [INFO ] User : SYSTEM
[09:14:01] [INFO ] Log file : C:\ProgramData\WHfB-Repair\WHfB-Remediate_██████_20260706_091401.log
 
[09:14:01] [INFO ] ============================================================
[09:14:01] [INFO ] Pre-Remediation State
[09:14:01] [INFO ] ============================================================
[09:14:02] [INFO ] AzureAdJoined : YES
[09:14:02] [INFO ] DomainJoined : YES
[09:14:02] [INFO ] AzureAdPrt : NO
[09:14:02] [INFO ] NgcSet : NO
[09:14:02] [INFO ] NgcKeyId :
[09:14:02] [INFO ] WamDefaultSet : NO
 
[09:14:02] [INFO ] ============================================================
[09:14:02] [INFO ] Step 1: Restart WHfB Required Services
[09:14:02] [INFO ] ============================================================
[09:14:02] [OK ] CNG Key Isolation (KeyIso): Already running
[09:14:02] [WARN ] NGC Cryptographic Svc (NgcSvc): Stopped — starting...
[09:14:03] [OK ] NGC Cryptographic Svc (NgcSvc): Started successfully
[09:14:03] [WARN ] NGC Container Svc (NgcCtnrSvc): Stopped — starting...
[09:14:04] [OK ] NGC Container Svc (NgcCtnrSvc): Started successfully
[09:14:04] [OK ] Web Account Manager (TokenBroker): Already running
 
[09:14:04] [INFO ] ============================================================
[09:14:04] [INFO ] Step 2: Refresh Primary Refresh Token (PRT)
[09:14:04] [INFO ] ============================================================
[09:14:04] [ACTION] Triggering dsregcmd /refreshprt as logged-on user...
[09:14:04] [ACTION] Scheduling [dsregcmd.exe /refreshprt] as user [DOMAIN\username]
[09:14:22] [OK ] User-context task completed: WHfB-PRT-Refresh-20260706_091401
[09:14:22] [OK ] PRT acquired successfully via WAM refresh
 
[09:14:22] [INFO ] ============================================================
[09:14:22] [INFO ] Step 3: Hybrid AAD Join Re-registration
[09:14:22] [INFO ] ============================================================
[09:14:22] [INFO ] Device is AAD-joined — skipping Hybrid re-registration
 
[09:14:22] [INFO ] ============================================================
[09:14:22] [INFO ] Step 4: Trigger Intune MDM Sync
[09:14:22] [INFO ] ============================================================
[09:14:22] [ACTION] Triggered MDM task: Schedule #1
[09:14:22] [ACTION] Triggered MDM task: Schedule #3
[09:14:23] [OK ] MDM sync triggered via CIM MDM_Client
[09:14:23] [OK ] deviceenroller /AutoEnrollMDM triggered
 
[09:14:23] [INFO ] ============================================================
[09:14:23] [INFO ] Step 5: Clean Stale NGC Keys and Retrigger WHfB Provisioning
[09:14:23] [INFO ] ============================================================
[09:14:23] [ACTION] NGC key missing or broken — running NGC cleanup
[09:14:23] [ACTION] Clearing stale NGC key store at: C:\Windows\ServiceProfiles\LocalService\AppData\Local\Microsoft\Ngc
[09:14:24] [OK ] NGC key store cleared
[09:14:24] [ACTION] Running dsregcmd /cleanupaccounts...
[09:14:25] [ACTION] Triggering NGC UserTask-Roam provisioning task...
[09:14:25] [OK ] NGC provisioning task triggered
[09:14:26] [OK ] Restarted: NgcSvc
[09:14:26] [OK ] Restarted: NgcCtnrSvc
 
[09:14:26] [INFO ] ============================================================
[09:14:26] [INFO ] Post-Remediation State
[09:14:26] [INFO ] ============================================================
[09:14:27] [INFO ] AzureAdJoined : YES
[09:14:27] [INFO ] AzureAdPrt : YES
[09:14:27] [INFO ] NgcSet : YES
[09:14:27] [INFO ] WamDefaultSet : YES
 
[09:14:27] [OK ] Remediation succeeded - PRT present and NGC provisioned
[09:14:27] [INFO ] Remediation script completed - log: C:\ProgramData\WHfB-Repair\...
Exit code: 0 — Intune marks Remediation as Succeeded
Do not clear the TPM to fix WHfB. Clearing the TPM in BIOS or via Clear-Tpm will invalidate your BitLocker protectors on every encrypted drive on the device. If you do not have the BitLocker recovery key saved in Entra ID or AD before you clear it, the drive becomes unrecoverable. The remediation script deliberately never touches the TPM. If TPM health is the actual failure, it needs a BIOS-level fix — not a script.

Deploy to Intune — step by step

Intune Admin Center — Proactive Remediation setup
Devices Scripts and remediations Proactive remediations + Create script package
  1. Name: WHfB Health Monitor
  2. Detection script: Upload Detect-WHfB.ps1 — Run as logged-on credentials: Yes — Run with 64-bit PowerShell: Yes
  3. Remediation script: Upload Remediate-WHfB.ps1 — Run as logged-on credentials: No (run as SYSTEM) — Run with 64-bit PowerShell: Yes
  4. Schedule: Every 1 hour (or Daily — detection is lightweight)
  5. Scope: Assign to your All Windows Hello for Business users group
Detection context vs. remediation context: Run the detection script as the logged-on user so it can check the user's PRT and NGC key accurately. Run the remediation script as SYSTEM so it has the elevation needed to restart services, take ownership of the NGC folder, and register scheduled tasks. Getting this the wrong way around is the most common deployment mistake.

Reading the logs after remediation

Every remediation run writes a timestamped log to C:\ProgramData\WHfB-Repair\. Open it from a remote session or collect it via Intune device diagnostics. The format is easy to scan — look for [FAIL] or [WARN] entries first, then check whether the Post-Remediation State section shows PRT and NGC as YES.

PowerShell — read and search the latest log file
# Get the most recent log on a device
$latest = Get-ChildItem 'C:\ProgramData\WHfB-Repair' -Filter '*.log' |
    Sort-Object LastWriteTime -Descending | Select-Object -First 1
 
# Show only non-INFO lines (warnings, failures, actions, results)
Get-Content $latest.FullName | Where-Object { $_ -notmatch '[INFO ]' }
 
# Quick pass/fail check — did remediation succeed?
Select-String -Path $latest.FullName -Pattern 'Remediation (succeeded|failed|completed)'

Microsoft documentation references

Download these scripts free: Both Detect-WHfB.ps1 and Remediate-WHfB.ps1 are part of the open-source EndpointIQ toolkit on GitHub. The remediation covers every common WHfB failure mode — PRT expiry, NGC corruption, stopped services, failed hybrid join — without touching the TPM or BitLocker. Safe to deploy broadly, not just to devices you already know are broken.
Share this post
LinkedIn X / Twitter Reddit Bluesky

More from EndpointWeekly

Technical Guide
Silently Fix a Missing Primary Refresh Token with Intune…
No PRT means no passwordless. The device looks healthy in Intune, compliance shows green,…
Security
Secure Boot Certificate Update 2026: Fix Non-Compliant Devices…
3,052 devices showing 'Not Up to Date' in your Secure Boot Status Report? Here is the…
Security
Which Windows Hello Gesture Did They Actually Use? Face,…
Entra only ever says "Windows Hello for Business" — never whether someone used face,…