<#
.SYNOPSIS
Gets all Defender onboarded devices NOT enrolled in Intune.
Filter by platform: "Windows11", "iOS", "Android", "macOS", "" (blank = all)
.REQUIREMENTS
Install-Module Microsoft.Graph -Scope CurrentUser
#>
# ?? Configuration
$ExportCSV = $true
$FilterPlatform = "Windows11" # Change to "iOS", "Android", "macOS" or "" for all
$ExportPath = ".\DefenderNotInIntune_${FilterPlatform}_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
# ?? Login
Write-Host "Opening browser for login..." -ForegroundColor Cyan
Connect-MgGraph -Scopes @(
"DeviceManagementManagedDevices.Read.All",
"SecurityEvents.Read.All",
"ThreatHunting.Read.All"
) -NoWelcome
Write-Host " Logged in as : $((Get-MgContext).Account)" -ForegroundColor Green
Write-Host " Tenant : $((Get-MgContext).TenantId)" -ForegroundColor Green
# ?? Step 1: Get all Defender machines via Advanced Hunting
Write-Host "`nFetching Defender onboarded devices via Advanced Hunting..." -ForegroundColor Cyan
$mdeLookup = @{}
$huntingBody = @{
Query = @"
DeviceInfo
| where isnotempty(AadDeviceId)
| summarize arg_max(Timestamp, *) by AadDeviceId
| project AadDeviceId, DeviceName, OSPlatform, OSVersion, OnboardingStatus, LoggedOnUsers
"@
} | ConvertTo-Json
try {
$huntingResponse = Invoke-MgGraphRequest `
-Uri "https://graph.microsoft.com/v1.0/security/runHuntingQuery" `
-Method POST `
-Body $huntingBody `
-ContentType "application/json" `
-ErrorAction Stop
foreach ($row in $huntingResponse.results) {
if ($row.AadDeviceId) {
# Extract last logged on user from LoggedOnUsers JSON array
$lastUser = $null
if ($row.LoggedOnUsers) {
try {
$users = $row.LoggedOnUsers | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($users -and $users.Count -gt 0) {
$lastUser = "$($users[0].DomainName)\$($users[0].UserName)"
}
} catch {
$lastUser = $row.LoggedOnUsers
}
}
$mdeLookup[$row.AadDeviceId.ToLower()] = [PSCustomObject]@{
DeviceName = $row.DeviceName
OSPlatform = $row.OSPlatform
OSVersion = $row.OSVersion
OnboardingStatus = $row.OnboardingStatus
LastLoggedOnUser = $lastUser
}
}
}
Write-Host " Found $($mdeLookup.Count) Defender onboarded devices" -ForegroundColor Green
}
catch {
Write-Error "Advanced Hunting failed: $_"
Disconnect-MgGraph
exit 1
}
if ($mdeLookup.Count -eq 0) {
Write-Error "No Defender devices returned. Check ThreatHunting.Read.All is consented."
Disconnect-MgGraph
exit 1
}
# ?? Step 2: Get all Intune enrolled device AAD IDs
Write-Host "Fetching Intune enrolled devices..." -ForegroundColor Cyan
$intuneAadIds = [System.Collections.Generic.HashSet[string]]::new()
$uri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?" +
"`$select=azureADDeviceId&`$top=999"
do {
$response = Invoke-MgGraphRequest -Uri $uri -Method GET
foreach ($device in $response.value) {
if ($device.azureADDeviceId) {
$null = $intuneAadIds.Add($device.azureADDeviceId.ToLower())
}
}
$uri = $response.'@odata.nextLink'
} while ($uri)
Write-Host " Found $($intuneAadIds.Count) Intune devices" -ForegroundColor Green
# ?? Step 3: Find Defender devices NOT in Intune
Write-Host "`nComparing..." -ForegroundColor Cyan
$results = foreach ($aadId in $mdeLookup.Keys) {
if ($aadId -notin $intuneAadIds) {
$defender = $mdeLookup[$aadId]
[PSCustomObject]@{
DeviceName = $defender.DeviceName
OSPlatform = $defender.OSPlatform
OSVersion = $defender.OSVersion
OnboardingStatus = $defender.OnboardingStatus
LastLoggedOnUser = if ($defender.OSPlatform -like "Windows*") { $defender.LastLoggedOnUser } else { "N/A" }
AzureADDeviceId = $aadId
}
}
}
# ?? Apply platform filter
if ($FilterPlatform) {
$filtered = $results | Where-Object { $_.OSPlatform -eq $FilterPlatform }
} else {
$filtered = $results
}
# ?? Output
Write-Host "`n================================================" -ForegroundColor Yellow
Write-Host " Defender onboarded devices : $($mdeLookup.Count)" -ForegroundColor White
Write-Host " Intune enrolled devices : $($intuneAadIds.Count)" -ForegroundColor White
Write-Host " In Defender NOT in Intune : $($results.Count)" -ForegroundColor Red
if ($FilterPlatform) {
Write-Host " Filtered ($FilterPlatform) : $($filtered.Count)" -ForegroundColor Yellow
}
Write-Host "================================================" -ForegroundColor Yellow
$filtered | Format-Table DeviceName, OSPlatform, OSVersion, LastLoggedOnUser, AzureADDeviceId -AutoSize
if ($ExportCSV -and $filtered.Count -gt 0) {
$filtered | Export-Csv -Path $ExportPath -NoTypeInformation -Encoding UTF8
Write-Host "`nExported to: $ExportPath" -ForegroundColor Green
} elseif ($filtered.Count -eq 0) {
Write-Host "`nNo devices found for platform: $FilterPlatform" -ForegroundColor Green
}
Write-Host "`nBreakdown by OS (all platforms):" -ForegroundColor Cyan
$results | Group-Object OSPlatform | Sort-Object Count -Descending | Format-Table Name, Count -AutoSize
Disconnect-MgGraph