SCRIPT LIBRARY · POWERSHELL
Trigger Intune Automatic Enrollment on a Windows Device with PowerShell
For the Entra-joined PC that never showed up in Intune. Check it's ready, kick off enrollment, and see why it failed if it does.
- What it does
- Checks the device's Entra join state and existing MDM enrollment, runs Windows' built-in automatic enrollment if it's needed, waits for the result, and returns it along with the matching events from the MDM log.
- Requires
- Windows PowerShell 5.1 or PowerShell 7
- A device that's Microsoft Entra joined or hybrid joined
- No modules
- Permissions
- Local administrator on the device. The user signing in needs an Intune license and has to be inside the MDM user scope.
- Runs on
- Windows 10 1903+ and Windows 11
- Tested
- Parse-checked and dry-run with mocked dsregcmd, registry and event log calls in PowerShell 7.4
Let's clear something up first, because the original version of this post got it wrong. There's no Graph call or cmdlet that reaches out and "adds" a Windows PC to Intune. Enrollment always happens on the device, which asks Intune to take it on. The old script used cmdlets that never existed, and I'd rather replace it than leave it up.
What you can do from PowerShell is poke the device into enrolling itself. Windows ships with the same mechanism the "Enable automatic MDM enrollment using default Azure AD credentials" Group Policy uses: deviceenroller.exe. On a device that's Entra joined or hybrid joined, it enrolls into Intune with the signed-in user's token. No prompts, no Settings app.
This script wraps that in the checks you'd otherwise do by hand. Is the device actually joined? Is it already enrolled? Did the enrollment work, and if not, what error did Windows log? It's what I reach for when a hybrid-joined PC has been sitting in Entra for a week and never showed up in Intune.
<#
.SYNOPSIS
Checks whether a Windows device is ready for Intune automatic enrollment, triggers it
if it isn't enrolled yet, and tells you what happened.
.DESCRIPTION
Reads the join state from dsregcmd, looks for an existing MDM enrollment in the registry,
and if the device is Entra joined (or hybrid joined) but not enrolled, runs the same
built-in command the "Enable automatic MDM enrollment" Group Policy uses. Then it waits
for the result and pulls the matching events from the MDM diagnostics log. Supports -WhatIf.
Run it elevated, on the device itself.
.PARAMETER UseDeviceCredential
Enroll with the device's credential instead of the signed-in user's. Needed when nobody
is signed in (for example, co-management scenarios).
.PARAMETER WaitSeconds
How long to wait for the enrollment to show up before giving up. Default: 120.
.EXAMPLE
.\Start-IntuneEnrollment.ps1 -WhatIf
.EXAMPLE
.\Start-IntuneEnrollment.ps1 -UseDeviceCredential -WaitSeconds 300
#>
#Requires -RunAsAdministrator
[CmdletBinding(SupportsShouldProcess)]
param(
[switch]$UseDeviceCredential,
[ValidateRange(0, 1800)]
[int]$WaitSeconds = 120
)
function Get-JoinState {
$state = @{}
foreach ($line in (& dsregcmd.exe /status)) {
if ($line -match '^\s*(\w+)\s*:\s*(.+?)\s*$') { $state[$matches[1]] = $matches[2] }
}
$state
}
function Get-MdmEnrollment {
# An Intune enrollment lives under this key with ProviderID "MS DM Server".
Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\Enrollments' -ErrorAction SilentlyContinue |
Get-ItemProperty -ErrorAction SilentlyContinue |
Where-Object { $_.ProviderID -eq 'MS DM Server' } |
Select-Object -First 1
}
$join = Get-JoinState
$existing = Get-MdmEnrollment
$status = [pscustomobject]@{
ComputerName = $env:COMPUTERNAME
EntraJoined = $join['AzureAdJoined'] -eq 'YES'
DomainJoined = $join['DomainJoined'] -eq 'YES'
Tenant = $join['TenantName']
EnrolledBefore = [bool]$existing
EnrolledAfter = [bool]$existing
EnrolledUpn = $existing.UPN
Result = $null
RecentEvents = $null
}
if ($existing) {
$status.Result = 'Already enrolled'
return $status
}
if (-not $status.EntraJoined) {
$status.Result = if ($status.DomainJoined) { 'Not ready: domain joined but not hybrid joined yet (check Entra Connect device sync)' } else { 'Not ready: device is not Entra joined' }
return $status
}
$argument = if ($UseDeviceCredential) { '/AutoEnrollMDMUsingAADDeviceCredential' } else { '/AutoEnrollMDM' }
if (-not $PSCmdlet.ShouldProcess($env:COMPUTERNAME, "deviceenroller.exe /c $argument")) {
$status.Result = 'WhatIf'
return $status
}
$started = Get-Date
& "$env:windir\System32\deviceenroller.exe" /c $argument
Write-Verbose "Enrollment requested with $argument. Waiting up to $WaitSeconds seconds."
$deadline = $started.AddSeconds($WaitSeconds)
do {
Start-Sleep -Seconds 10
$existing = Get-MdmEnrollment
} until ($existing -or (Get-Date) -ge $deadline)
# Event 75 = auto-enrollment succeeded, 76 = failed (the message carries the error code).
$status.RecentEvents = @(Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-DeviceManagement-Enterprise-Diagnostics-Provider/Admin'; Id = 75, 76; StartTime = $started } -ErrorAction SilentlyContinue |
ForEach-Object { '{0:HH:mm:ss} [{1}] {2}' -f $_.TimeCreated, $_.Id, $_.Message })
$status.EnrolledAfter = [bool]$existing
$status.EnrolledUpn = $existing.UPN
$status.Result = if ($existing) { 'Enrolled' } elseif ($status.RecentEvents -match '\[76\]') { 'Failed: see RecentEvents' } else { 'No result yet: check the device in Intune in a few minutes' }
$status
Parameters
| Parameter | Type | Default | What it's for |
|---|---|---|---|
-UseDeviceCredential | switch | — | Enroll with the device's own Entra credential instead of the signed-in user's. Use it when nobody's signed in, or for co-management. |
-WaitSeconds | int | 120 | How long to wait for the enrollment to appear before giving up. Anywhere from 0 to 1800. |
-WhatIf | switch | — | Run the readiness checks and show what it would do, without enrolling. |
Run it
Just check. Nothing gets enrolled.
.\Start-IntuneEnrollment.ps1 -WhatIfThe normal fix, with the user signed in.
.\Start-IntuneEnrollment.ps1No user around, so enroll with the device credential and give it longer.
.\Start-IntuneEnrollment.ps1 -UseDeviceCredential -WaitSeconds 300Just the evidence, for the ticket.
.\Start-IntuneEnrollment.ps1 | Select-Object -ExpandProperty RecentEventsWhat you'll see
ComputerName : PC-0142
EntraJoined : True
DomainJoined : True
Tenant : Contoso
EnrolledBefore : False
EnrolledAfter : False
EnrolledUpn :
Result : Failed: see RecentEvents
RecentEvents : {14:15:02 [76] Auto MDM Enroll: Failed (Unknown Win32 Error code: 0x8018002b)}
How it works
- Read the join state.
dsregcmd /statusis parsed into key/value pairs, so the script knows whether the device is Entra joined, domain joined, and which tenant it belongs to. - Look for an existing enrollment. Intune enrollments live under
HKLM:\SOFTWARE\Microsoft\Enrollmentswith aProviderIDofMS DM Server. If one's there, you're done, and it tells you which UPN enrolled it. - Stop early if it can't work. Not joined? It explains why (including the common "domain joined but hybrid join hasn't finished" case) instead of running something that's going to fail.
- Trigger enrollment.
deviceenroller.exe /c /AutoEnrollMDM, or/AutoEnrollMDMUsingAADDeviceCredentialwith the switch. It's the same built-in command the Group Policy schedules. - Wait, then collect evidence. It polls the registry for up to
-WaitSeconds, then pulls events 75 (success) and 76 (failure) from theDeviceManagement-Enterprise-Diagnostics-Provider/Adminlog. The failure event includes the error code, which is the thing you'll actually want to search for.
Take it further
- Use the Group Policy for the whole fleet. For hybrid-joined devices, the "Enable automatic MDM enrollment using default Azure AD credentials" setting does this on a schedule. The script is for the stragglers.
- Run it from your RMM or ConfigMgr. Its output is a single object, so it's easy to collect from many machines and filter for the ones that failed. Just remember the signed-in-user rule above.
- Brand-new devices? Skip all this and register them for Windows Autopilot instead. The next post in this series covers
Get-WindowsAutopilotInfo.
Things that'll trip you up
- Hybrid join has to finish first. A domain-joined PC isn't hybrid joined until Entra Connect has synced the computer object and the device has registered. If dsregcmd says AzureAdJoined is NO, enrollment can't work yet, and the script says so instead of trying.
- The MDM user scope decides a lot. In the Entra admin center, under Mobility (MDM and WIP), Microsoft Intune's MDM user scope has to include the user, either All or a group they're in. If it's set to None, every enrollment fails, and 0x8018002b is the error you'll usually see.
- Licensing and UPNs. The user needs an Intune license, and their UPN has to be on a verified domain. A UPN still ending in .local will fail every time.
- Device credential enrollment has limits. Devices enrolled with -UseDeviceCredential have no primary user, so Company Portal and user-targeted apps behave differently. It's meant for co-management and shared devices, not everyday laptops.
- Somebody has to be signed in. The default mode enrolls with the signed-in user's Entra token, so it needs a real user session on the device, ideally the person who'll own it. With nobody signed in it fails. That's the case -UseDeviceCredential is for.