Script to get list of Machines not enrolled into Intune but enrolled into Defender

<#
.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
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...