Refactor description to clarify device removal process and reporting for stale Entra ID and Intune-managed devices

This commit is contained in:
Petr Štěpán
2026-07-19 21:29:12 +02:00
parent 5a652dbbe2
commit f2f0537bb9
+4 -74
View File
@@ -4,80 +4,10 @@
longer than a given number of days. longer than a given number of days.
.DESCRIPTION .DESCRIPTION
Connects to Microsoft Graph and evaluates staleness in two passes: Connects to Microsoft Graph, finds stale Entra ID and Intune-managed devices,
and removes the matching device records after checking their last sign-in and
1. Entra ID devices, cross-referenced with their matching Intune managed last sync activity. It also reports devices that cannot be deleted, devices with
device (if any), matched via the device's Azure AD device ID. "Last no reliable activity signal, and whether an Autopilot registration still exists.
activity" is the more recent of the Intune managed device's
LastSyncDateTime (last MDM check-in) and the Entra ID device's
ApproximateLastSignInDateTime (last sign-in with the device identity). A
device is considered stale only when BOTH available signals are older than
the -InactiveDays threshold, to minimize the risk of removing a device that
is active under one signal but temporarily quiet under the other. When
stale, the script removes the Intune managed device object (if any) and
then the Entra ID device object itself.
2. Intune managed devices that have NO matching Entra ID device object at all
(e.g. the Entra ID device was already deleted manually, or sync between
Intune and Entra ID is broken). These are invisible to pass 1, since it is
driven from the Entra ID device list. For these, staleness is judged purely
by LastSyncDateTime, and only the Intune managed device object is removed
(there is no Entra ID device object to remove).
Devices with no usable activity signal at all cannot be judged reliably and are
reported as 'Unknown' rather than removed. Every row in the output has a 'Source'
column ('EntraID' or 'Intune') so it's clear which pass found it and what was
actually evaluated/removed. When a removal attempt fails, the 'ErrorDetail' column
holds the underlying error message(s) so the cause is visible directly in the
report, without needing to correlate back to the -Warning output. The 'TrustType'
column (Entra ID rows only - 'AzureAd' cloud joined, 'ServerAd' hybrid joined,
'Workplace' registered/BYOD) helps explain permission-related removal failures,
since deleting a Workplace-registered device can require different rights than
deleting a joined one. The 'AutopilotRegistered' column is purely informational -
it flags devices that still have an active Windows Autopilot device identity, in
case that turns out to be relevant to a removal failure, but it does not change
whether removal is attempted. This lookup is best-effort: the Autopilot/Intune
enrollment backend can return transient errors (e.g. HTTP 500) independently of
the rest of the script; if it fails, the column shows 'Unknown' for every device
instead of aborting the whole run.
The output only lists devices that were removed, attempted-but-failed, skipped
(e.g. under -WhatIf), or flagged as 'Unknown' for manual review - healthy, still
active devices are deliberately left out of the report to keep it focused on what
needs attention. Use -Verbose to see how many active devices were excluded.
This script reads Windows Autopilot device identities only to populate the
'AutopilotRegistered' column above - it never modifies or removes an Autopilot
registration. The hardware hash entry is always left in place, so a device can
always be re-provisioned via Autopilot later, even after its Entra ID / Intune
objects have been cleaned up (when that succeeds).
Hybrid Azure AD joined devices (TrustType 'ServerAd') are never removed - their
Entra ID device object is synced from on-premises Active Directory, and Entra ID
rejects deleting it directly, so removal must happen by deleting/disabling the
on-prem computer object and letting Entra Connect sync the deletion. A hybrid
device is only mentioned (via -Verbose, not in the output) when it would
otherwise have been flagged stale and removed - active hybrid devices are not
called out at all, same as any other active device.
Because these deletions are effectively irreversible, run with -WhatIf first to
preview exactly what would be removed - each candidate device is still reported
individually. For a real run, instead of prompting once per device/operation, the
script asks for a single upfront confirmation ("This will permanently remove ...
Continue?") before touching anything; declining aborts the entire run with nothing
removed. Pass -Confirm:$false to skip even that single prompt (e.g. for unattended/
scheduled execution).
Requires an account with sufficient Entra ID / Intune role assignments (e.g. Cloud
Device Administrator + Intune Administrator, or Global Administrator) and the
following Microsoft Graph delegated scopes:
Device.Read.All
Directory.ReadWrite.All
DeviceManagementManagedDevices.ReadWrite.All
DeviceManagementServiceConfig.ReadWrite.All (read-only use: listing Autopilot
device identities for the 'AutopilotRegistered' column; no Graph permission
model exists for read-only access to this resource, so ReadWrite.All is the
minimum available scope even though this script never writes to it)
.PARAMETER InactiveDays .PARAMETER InactiveDays
Number of days of inactivity after which a device is considered stale. Defaults to Number of days of inactivity after which a device is considered stale. Defaults to