## TL;DR
Run one Get-ADUser pass with LastLogonDate, flag accounts idle 90+ days, and export to CSV. LastLogonDate replicates across domain controllers, so a single query is audit-grade for stale-account reviews without touching every DC.

## The script
```powershell
Import-Module ActiveDirectory
$cutoff = (Get-Date).AddDays(-90)
Get-ADUser -Filter * -Properties LastLogonDate, Enabled, Department |
  Where-Object { $_.Enabled -eq $true } |
  Select-Object Name, SamAccountName, Department, LastLogonDate,
    @{Name="Stale"; Expression={ $_.LastLogonDate -lt $cutoff }} |
  Export-Csv -Path "ad-users-lastlogon.csv" -NoTypeInformation
```

## Steps
1. Run from a machine with the ActiveDirectory module (RSAT installed) using an account that can read AD. Expected: Import-Module ActiveDirectory succeeds with no error.
2. Save the script as Get-StaleADUsers.ps1 and run it. Expected: it finishes in under a minute for most domains and writes ad-users-lastlogon.csv.
3. Open the CSV: one row per enabled user with Name, SamAccountName, Department, LastLogonDate, and a Stale flag. Expected: row count roughly matches your enabled-user count.
4. Filter Stale = TRUE for the audit or disable-candidate list. Expected: the list matches the auditor's inactivity criteria; adjust the -90 cutoff if they use a different window.

## Use this when
- Auditors ask for a list of inactive accounts
- Planning a stale-account disable campaign
- Quarterly access reviews need evidence

## Not for this skill when
- Real-time logon monitoring (use a SIEM, not a script)
- Forensic-precision timestamps (LastLogonDate can lag replication by up to 14 days; fine for audits, not for forensics)

## Compatibility
- Windows PowerShell 5.1 or PowerShell 7 with the ActiveDirectory module
- Domain-joined workstation or a server with RSAT; read rights on AD are enough

## Variants
### Include disabled accounts too
Drop the Enabled filter and add Enabled to Select-Object. Useful when the auditor wants the full picture.
### Multiple domains in the forest
Wrap the Get-ADUser call in a loop over each domain with the -Server parameter, then merge the CSVs.
### Auditors want never-logged-on accounts
They show LastLogonDate empty. Filter for blank dates separately; they are usually stale service or test accounts.

## Why it happens
Every domain has accounts nobody disabled when people left. LastLogonDate is the cheapest reliable signal for finding them, and auditors accept it because the value replicates across domain controllers.

## Edge cases
- Service accounts that never log on interactively always look stale: exclude them by OU or naming convention.
- Users on long leave look stale: cross-check the HR roster before disabling.
- LastLogonDate can be blank for accounts created but never used: treat blank as stale, not as an error.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_qAtoeScg28Bo-tzHaWKy4w
