Active Directory Stale Account Report: PowerShell Script Generator
Stale accounts are an easy way into a network and a common audit finding. This generator builds a read-only PowerShell script that lists enabled users and computers that have not signed in recently and accounts whose passwords never expire.
Fill in
Setup
# Requires the ActiveDirectory module (RSAT). Run as a user with read access to AD. Import-Module ActiveDirectory $days = 90 $base = "OU=Plant,DC=example,DC=local" $outDir = "C:\Reports\AD" $stamp = Get-Date -Format "yyyyMMdd" New-Item -ItemType Directory -Force -Path $outDir | Out-Null
Inactive users
$users = Search-ADAccount -UsersOnly -AccountInactive -TimeSpan ([TimeSpan]::FromDays($days)) -SearchBase $base |
Where-Object { $_.Enabled -eq $true } |
Get-ADUser -Properties LastLogonDate, PasswordLastSet, Department, Manager, Description |
Select-Object SamAccountName, Name, Department, Manager, LastLogonDate, PasswordLastSet, Description
$users | Export-Csv -NoTypeInformation -Path (Join-Path $outDir "inactive_users_$stamp.csv")
Write-Host ("Inactive enabled users: " + $users.Count)Inactive computers
$computers = Search-ADAccount -ComputersOnly -AccountInactive -TimeSpan ([TimeSpan]::FromDays($days)) -SearchBase $base |
Where-Object { $_.Enabled -eq $true } |
Get-ADComputer -Properties LastLogonDate, OperatingSystem, Description |
Select-Object Name, OperatingSystem, LastLogonDate, Description
$computers | Export-Csv -NoTypeInformation -Path (Join-Path $outDir "inactive_computers_$stamp.csv")
Write-Host ("Inactive enabled computers: " + $computers.Count)Passwords that never expire
$noExpiry = Search-ADAccount -UsersOnly -PasswordNeverExpires -SearchBase $base |
Where-Object { $_.Enabled -eq $true } |
Get-ADUser -Properties PasswordLastSet, Description |
Select-Object SamAccountName, Name, PasswordLastSet, Description
$noExpiry | Export-Csv -NoTypeInformation -Path (Join-Path $outDir "password_never_expires_$stamp.csv")
Write-Host ("Password never expires: " + $noExpiry.Count)Optional: disable after review (leave commented)
# Review the CSV with managers first. Remove -WhatIf only after approval.
# $users | ForEach-Object { Disable-ADAccount -Identity $_.SamAccountName -WhatIf }LastLogonDate comes from the lastLogonTimestamp attribute, which replicates with a delay of up to 14 days by default, so treat results as approximate and verify before disabling. Service accounts and break glass accounts will appear in these lists and must be reviewed rather than disabled automatically. Run in a test OU first.
ERP and AD access review
Want the AD and ERP user lists compared automatically? Optional follow up.
Optional. The tool and every export work without this. No spam.
How to use the script
Set the inactivity threshold, the organisational unit to search and an output folder, then copy the script into PowerShell on a machine with the Active Directory module. It writes CSV files that you can send to department managers for confirmation. The disable step is commented out and uses -WhatIf, so nothing changes until you decide.
- Start with 90 days and tighten later
- Send the CSV to line managers for sign-off
- Keep the output as evidence for the access review
- Review service accounts separately
What the cmdlets do
Search-ADAccount with -AccountInactive and -TimeSpan returns accounts that have not logged on for that period. It uses the replicated lastLogonTimestamp attribute, which is efficient but not exact. The script filters to enabled accounts, so disabled ones do not add noise, and then pulls extra properties with Get-ADUser or Get-ADComputer. -PasswordNeverExpires lists accounts with the matching flag set, which are usually service accounts or legacy exceptions.
Using the results in a review
Group users by department and manager, then ask each manager to confirm that the person still needs access. For ERP systems, repeat the exercise inside the ERP, because an AD account can be disabled while an ERP user with local authentication stays active. Contractors and seasonal workers are the usual exceptions to track with an end date.
Common mistakes
Disabling service accounts that only appear idle because they use cached tickets, deleting accounts without a retention period and relying on a single run instead of a recurring schedule are common. Schedule the report monthly, move disabled accounts to a holding OU for a period, and delete only after the business confirms nothing depends on them.
Frequently Asked Questions
How do I find inactive users in Active Directory?
Use Search-ADAccount -UsersOnly -AccountInactive -TimeSpan with the number of days, for example 90.00:00:00. It reads the lastLogonTimestamp attribute. Filter to enabled accounts and review service accounts before taking action.
Is LastLogonDate accurate?
It is based on lastLogonTimestamp, which only updates when the value is more than about 14 days old by default. It is accurate enough for 60 to 90 day reviews but not for exact last sign-in times.
Should I delete or disable stale accounts?
Disable first, move to a holding OU and record the date. Delete after a retention period once owners confirm nothing depends on the account. Keep an audit trail of who approved each action.
Does this cover my ERP users?
No. It covers Active Directory only. ERP systems often keep their own users and roles, so run a separate review there, such as a list of users with no recent login in the ERP audit tables.
Netray can connect AD review output to your ERP user and role tables so access reviews cover both systems in one report.
Related Tools
Plant Ransomware Response Readiness Checklist
A practical checklist to test whether your plant can detect, contain and recover from ransomware across IT, ERP and OT.
ERP OperationsManufacturing IT Change Request and CAB Template
A change request form built for plants, with production impact, maintenance window, rollback plan and approval lines for IT, operations and quality.
Industry TemplatesIndustrial Protocol and Plant IT TCP/UDP Ports Reference
A searchable table of default ports for industrial protocols and the IT services a plant depends on, built for firewall rule requests and conduit reviews.
ERP OperationsManufacturing IT Resilience Maturity Assessment
Answer ten questions about backups, redundancy, monitoring, patching and recovery to see how resilient your plant IT is and what to fix first.
More Developer Tools tools
See all- SAP Sales Order ABAP SELECT Generator (VBAK, VBAP, VBFA)
- SAP Sales Order Create BAPI ABAP Generator (BAPI_SALESORDER_CREATEFROMDAT2)
- SAP ABAP Open Items SELECT Generator
- SAP ACDOCA SQL Query Generator
- SAP QM Inspection Lot ABAP SELECT Generator
- SAP Quality Notification Pareto SQL Query Generator
- SAP QM Usage Decision BAPI Skeleton
- ABAP Custom Exception Class Generator
- ABAP Unit Test Class Generator