Checks/EntraID/ARG.ENT.014.ps1

Register-ArgusCheck @{
    Id          = 'ARG.ENT.014'
    Title       = 'Stale devices (no sign-in for more than {StaleDays} days)'
    Area        = 'EntraID'
    Category    = 'Device hygiene'
    Severity    = 'Low'
    Description = 'Devices whose `approximateLastSignInDateTime` is older than **{StaleDays}** days (or that never signed in and were registered more than {StaleDays} days ago). Both enabled and disabled devices are listed; disabled ones are usually ready to be deleted.'
    Rationale   = 'Stale devices make device inventories, Conditional Access reporting and licence counts unreliable.'
    Remediation = @{
        Steps      = "1. Disable stale devices first and wait a grace period (for example 30 days).`n2. Delete devices that nobody claimed.`n3. Before deleting Windows devices, make sure BitLocker recovery keys you still need are kept, and do not delete Windows Autopilot devices from Entra ID directly."
        PowerShell = "# Sample only - review before running. Disables one device.`nConnect-MgGraph -Scopes 'Device.ReadWrite.All'`nUpdate-MgDevice -DeviceId '<Id>' -AccountEnabled:`$false"
        PortalUrl  = 'https://entra.microsoft.com/'
    }
    References  = @(
        @{ Title = 'How to manage stale devices in Microsoft Entra ID'; Url = 'https://learn.microsoft.com/en-us/entra/identity/devices/manage-stale-devices' }
    )
    Requires    = @{
        Services    = @('Graph')
        GraphScopes = @('Device.Read.All')
        DataSets    = @('Devices')
    }
    Parameters  = @{ StaleDays = 180 }
    Tags        = @('Devices', 'Lifecycle')
    Test        = {
        param($Context)
        $threshold = $Context.Now.AddDays(-[int]$Context.Parameters.StaleDays)
        $devices = @($Context.Data['Devices'])
        $findings = foreach ($device in $devices) {
            $last = ConvertTo-ArgusUtcDate (Get-ArgusProperty $device 'approximateLastSignInDateTime')
            $registered = ConvertTo-ArgusUtcDate (Get-ArgusProperty $device 'registrationDateTime')
            $reference = if ($last) { $last } else { $registered }
            if (-not $reference -or $reference -ge $threshold) { continue }
            [pscustomobject]@{
                DeviceName      = Get-ArgusProperty $device 'displayName' ''
                OperatingSystem = (@((Get-ArgusProperty $device 'operatingSystem' ''), (Get-ArgusProperty $device 'operatingSystemVersion' '')) | Where-Object { $_ }) -join ' '
                TrustType       = Get-ArgusProperty $device 'trustType' ''
                Enabled         = [bool](Get-ArgusProperty $device 'accountEnabled' $false)
                LastSignIn      = Format-ArgusDate $last 'Never'
                DaysSinceSignIn = [int]($Context.Now - $reference).TotalDays
                Id              = Get-ArgusProperty $device 'id' ''
            }
        }
        $findings = @($findings | Sort-Object DaysSinceSignIn -Descending)
        $status = if ($findings.Count -gt 0) { 'Failed' } else { 'Passed' }
        New-ArgusResult -Status $status -Findings $findings -EvaluatedCount $devices.Count `
            -Summary '{AffectedCount} of {EvaluatedCount} devices have not signed in for {StaleDays}+ days.'
    }
}