Writing

Building a Safe Lifecycle for Hybrid Windows Devices

How I automated the identification, quarantine, and removal of inactive devices across Active Directory, Microsoft Entra ID, and Intune

9 min read
Security Automation Published
Active DirectoryMicrosoft Entra IDMicrosoft IntunePowerShellFastAPIIdentity Security

Hybrid device management has a lifecycle problem.

A computer can be replaced, reinstalled, abandoned, or disconnected from the corporate environment, while its records remain active across Active Directory, Microsoft Entra ID, and Microsoft Intune. Over time, these stale objects reduce inventory accuracy, complicate investigations, and increase the number of identities and endpoints that administrators must review.

I built DeviceLifecycle to address this problem without treating deletion as a simple cleanup operation.

The project is a PowerShell-based automation that correlates device identities across Microsoft platforms, evaluates inactivity using multiple signals, and moves eligible devices through a controlled lifecycle. I also built DeviceLifecycle-API, a separate read-only extension that exposes reports and logs to authorized internal systems.

Source code:

The engineering problem

In a hybrid Microsoft environment, one physical computer may be represented by several records:

  • an Active Directory computer object;
  • a Microsoft Entra ID device object;
  • a Microsoft Intune managed-device record;
  • a Windows Autopilot registration;
  • locally stored lifecycle state, reports, and logs.

These records do not necessarily appear, update, or disappear at the same time.

An Intune record may be removed after a Retire action while the Active Directory object remains in quarantine. An Entra ID object may still exist after its on-premises computer has stopped communicating. A computer name may also be reused after reinstallation or hardware replacement.

This means that a safe lifecycle process cannot rely on a single timestamp or on the device name alone.

The automation must answer a more difficult question:

Is there enough consistent evidence to justify changing this device identity?

That question shaped the entire design.

Design principle: fail closed

The central rule of DeviceLifecycle is simple:

Automate routine cases, but stop when the evidence is incomplete or ambiguous.

A device is not modified merely because one source reports inactivity. Before an action is allowed, the automation verifies identity correlation, activity signals, exclusions, protection rules, and lifecycle state.

Missing or conflicting information produces a manual-review result instead of an automated action.

This fail-closed model is especially important because the project can disable and eventually delete identities from multiple administrative systems.

Architecture

I separated the solution into two repositories with different trust boundaries.

DeviceLifecycle

The main project owns the privileged workflow. It:

  • inventories devices from Active Directory, Entra ID, and Intune;
  • correlates identities across the platforms;
  • evaluates inactivity thresholds;
  • produces CSV reports and execution logs;
  • stores lifecycle state in JSON;
  • quarantines or removes eligible devices, depending on the configured mode;
  • supports recovery of quarantined devices.

DeviceLifecycle-API

The API is an optional extension. It:

  • reads the latest CSV report and execution log;
  • authenticates consumers with an API key;
  • exposes CSV, JSON, metadata, and log endpoints;
  • performs no lifecycle action;
  • does not contact Active Directory, Entra ID, Intune, or Microsoft Graph.

The separation keeps reporting consumers outside the control plane.

Active Directory ----\
Microsoft Entra ID ----> DeviceLifecycle ---> CSV reports
Microsoft Intune ----/                       Execution logs
                                             Persistent state
                                                    |
                                                    v
                                          DeviceLifecycle-API
                                                    |
                                  Dashboards, monitoring, audits,
                                    inventory and internal tools

If the API is unavailable, DeviceLifecycle continues operating normally. The lifecycle engine remains the authoritative producer of state and reports.

Correlating identities safely

Computer names are useful labels, but they are weak identity keys.

A device may be renamed, reinstalled, or replaced by another computer that receives the same standardized name. For this reason, DeviceLifecycle correlates records using identifiers shared between the platforms:

  1. The Active Directory computer SID is matched to the Entra ID onPremisesSecurityIdentifier.
  2. The Entra ID deviceId is matched to the Intune azureADDeviceId.
  3. Activity timestamps from the available systems are evaluated against the configured thresholds.

An action is allowed only when the correlation is unambiguous and the required activity signals support the same conclusion.

Records are sent to manual review when, for example:

  • an Entra ID or Intune match is missing;
  • multiple records match the same identifier;
  • a required timestamp is unavailable;
  • identifiers are inconsistent;
  • the device is protected or excluded;
  • Autopilot membership cannot be determined reliably.

Uncertainty is treated as a reason to stop, not as permission to continue.

A staged lifecycle instead of immediate deletion

The default workflow uses multiple stages.

1. Attention

After the configured attention threshold—75 inactive days by default—the device appears in the report.

No change is made. This stage gives administrators time to investigate devices that are approaching quarantine eligibility.

2. Quarantine candidate

After 90 inactive days across the required signals, the device may become a quarantine candidate.

When the Quarantine or Enforce mode is enabled, the automation can:

  • send a Retire command to Intune;
  • disable the computer account in Active Directory;
  • move the object to a dedicated quarantine organizational unit;
  • record identifiers and lifecycle timestamps in persistent state.

The JSON state file is essential because Intune may remove the managedDevice record after processing the retirement operation. The lifecycle must remain traceable even after a source record disappears.

3. Permanent removal

After the configured quarantine retention period—30 additional days by default—the device may become eligible for final removal.

In Enforce mode, the workflow can:

  • delete the Active Directory computer object;
  • remove any remaining Intune record;
  • trigger a Microsoft Entra Connect delta synchronization;
  • verify whether a residual Entra ID object remains;
  • remove the residual Entra object after an additional grace period.

This sequence creates several opportunities to detect mistakes before an irreversible action occurs.

A critical Entra Connect detail

The quarantine organizational unit must remain inside the Microsoft Entra Connect synchronization scope.

If the OU is excluded, moving a computer into quarantine may cause the corresponding Entra ID object to disappear immediately. That would bypass the intended retention period and change the behavior of the lifecycle.

This was an important design lesson: an operation that appears safe inside Active Directory may have an unintended cloud-side effect because of directory synchronization.

Automation must account for the complete system, not only the command being executed.

Progressive operating modes

DeviceLifecycle provides three operating modes.

ReportOnly

This is the default and safest mode.

The automation inventories and evaluates devices but performs no administrative change. The reports include outcomes such as:

  • attention candidates;
  • quarantine candidates;
  • missing Entra ID matches;
  • missing Intune matches;
  • ambiguous correlations;
  • missing activity timestamps;
  • records requiring manual review.

This mode should run long enough to validate assumptions against the real environment.

Quarantine

This mode enables reversible containment actions. It can retire the Intune record, disable the Active Directory account, and move the object to quarantine, but it does not perform final deletion.

Enforce

This mode enables the complete lifecycle, including permanent removal after the configured retention and grace periods.

Enforcement should only be enabled after reporting, quarantine, recovery procedures, permissions, synchronization behavior, and operational limits have been tested.

Safety controls

The safety mechanisms are part of the core design, not additional features.

The project includes:

  • ReportOnly as the default mode;
  • SID and GUID correlation instead of name-only matching;
  • manual review for incomplete or ambiguous records;
  • exclusion of Windows servers and domain controllers;
  • exclusion of Autopilot-registered devices;
  • exclusion of the automation host;
  • a dedicated Active Directory exception group;
  • detection of protection against accidental deletion;
  • a configurable maximum number of actions per run;
  • persistent JSON state;
  • CSV reports and execution logs;
  • delayed cleanup of residual Entra ID objects;
  • PowerShell -WhatIf support;
  • explicit validation, recovery, and uninstall procedures.

The scheduled task runs as NT AUTHORITY\SYSTEM. In Active Directory, it therefore operates through the computer account of the server hosting the automation.

Instead of assigning broad administrative privileges, permissions can be delegated only on the managed and quarantine organizational units. This follows the principle of least privilege and reduces the impact of a compromised automation host.

Recovery is deliberately manual

A quarantined device is not automatically restored merely because it communicates again.

A later Intune synchronization may only indicate that the device received the retirement command. It does not prove that the computer should return to production.

Recovery therefore requires an explicit administrative decision.

The recovery script can:

  • re-enable the Active Directory computer account;
  • move it back to the production OU;
  • remove lifecycle markers and quarantine state;
  • trigger directory synchronization;
  • support validation of hybrid join and Intune enrollment.

It also supports -WhatIf, allowing the recovery path to be reviewed before execution.

Automated quarantine and deliberate recovery provide a safer balance than silently reversing a security decision.

Reporting and operational state

Each execution produces structured evidence:

  • a current CSV report;
  • per-execution reports;
  • execution logs;
  • a persistent JSON state file.

The state file preserves identifiers and lifecycle timestamps between runs. This is necessary because the workflow itself may remove records from source systems.

The reports can be reviewed directly on the server or consumed through the optional API.

The read-only API extension

I created DeviceLifecycle-API with FastAPI and Uvicorn to make operational data available to internal dashboards and services without giving them filesystem access or lifecycle privileges.

The main endpoints are:

EndpointPurpose
/api/v1/healthService and source-file availability
/api/v1/metadataReport and log metadata
/api/v1/report.csvOriginal CSV report
/api/v1/reportCSV converted to JSON
/api/v1/log?lines=500Latest log lines
/api/v1/log/fileComplete current log

The API deliberately has no endpoints for quarantine, deletion, restoration, or device modification.

This prevents a reporting integration from becoming a privileged management interface.

API security model

The API is intended for internal use, but the internal network is not treated as inherently trusted.

Its controls include:

  • API-key authentication through X-API-Key;
  • randomly generated 64-character hexadecimal keys;
  • constant-time key comparison;
  • runtime secrets stored outside the repository;
  • Windows Firewall allowlisting by source address;
  • disabled Swagger, ReDoc, and OpenAPI interfaces in runtime;
  • rejection of client-controlled filesystem paths;
  • stable file reads to avoid partially updated responses;
  • Cache-Control: no-store responses;
  • configurable log-tail limits;
  • rotating request logs without API-key exposure.

Internal HTTP is the default transport. For routed or untrusted networks, the service should be placed behind an HTTPS reverse proxy.

What this project taught me

The hardest part of automation is not replacing commands with code. It is defining when software has enough evidence to make a decision safely.

A hybrid device lifecycle must account for:

  • eventual consistency;
  • missing and duplicated records;
  • synchronization delays;
  • reused computer names;
  • asynchronous retirement operations;
  • dependencies between on-premises and cloud systems.

A decision that is locally correct may still be unsafe when the complete environment is considered.

The project also reinforced the value of separating capabilities. DeviceLifecycle owns the privileged workflow. DeviceLifecycle-API provides operational visibility. Monitoring and reporting tools can consume data without receiving authority to change the environment.

The main principle I intend to carry into future security automation work is:

Reliable automation should make conservative decisions, preserve evidence, and stop when the available evidence is insufficient.

Source code

The complete projects and their installation documentation are available on GitHub: