Inactive user accounts can increase security and licensing risk when they are left enabled after employees change roles or leave an organization. This tutorial shows how to identify potentially inactive accounts in on-premises Active Directory Domain Services (AD DS), review their last sign-in information and export a report with PowerShell.
Important: Inactivity is an investigation criterion, not proof that an account is unused. LastLogonDate is derived from the replicated lastLogonTimestamp attribute and is intentionally approximate. Validate any account with its owner and your identity-management process before disabling or deleting it.
Before you begin
- Use a domain-joined management computer or Windows Server with the Active Directory PowerShell module installed (RSAT).
- Use an account with permission to read directory user attributes.
- Open Windows PowerShell or another compatible PowerShell session and run
Import-Module ActiveDirectory. - Run the commands in this article in read-only mode first. None of the reporting commands changes accounts.
1. Confirm the Active Directory module works
Import-Module ActiveDirectory
Get-ADDomain
Get-Command Get-ADUser, Search-ADAccount
If the module is unavailable, install the appropriate RSAT feature using Windows optional features or your Windows Server feature-management process. A cloud-only Microsoft Entra ID environment does not use these AD DS cmdlets.
2. List enabled users and their last logon dates
Get-ADUser -Filter 'Enabled -eq $true' -Properties LastLogonDate |
Select-Object Name, SamAccountName, Enabled, LastLogonDate |
Sort-Object LastLogonDate
LastLogonDate is a convenient calculated property, but it is not a precise, real-time last sign-in record. Accounts that have never signed in, or for which the timestamp has not been populated, may show a blank value.
Suggested real screenshot: PowerShell output with Name, SamAccountName and LastLogonDate columns. Blur actual usernames and domain information.
3. Find enabled accounts inactive for 90 days
Use a date threshold to create an initial review list:
$Cutoff = (Get-Date).AddDays(-90)
Get-ADUser -Filter 'Enabled -eq $true' -Properties LastLogonDate, Description |
Where-Object {
$_.LastLogonDate -and $_.LastLogonDate -lt $Cutoff
} |
Select-Object Name, SamAccountName, LastLogonDate, Description |
Sort-Object LastLogonDate
This version deliberately excludes blank dates so that “never logged on/unknown” accounts can be reviewed separately. Adjust 90 days to your organization’s policy; don’t assume it is an industry-mandated threshold.
4. Include accounts with no recorded logon
Get-ADUser -Filter 'Enabled -eq $true' -Properties LastLogonDate, WhenCreated |
Where-Object { -not $_.LastLogonDate } |
Select-Object Name, SamAccountName, WhenCreated |
Sort-Object WhenCreated
A blank LastLogonDate does not guarantee that an account has never authenticated. Investigate recently created accounts and attribute-replication behavior before drawing conclusions.
5. Use Search-ADAccount for a quick inactivity report
The Active Directory module also includes a cmdlet specifically for locating inactive accounts:
Search-ADAccount -AccountInactive -UsersOnly -TimeSpan 90.00:00:00 |
Where-Object Enabled |
Select-Object Name, SamAccountName, LastLogonDate
-TimeSpan 90.00:00:00 represents 90 days. Compare its results with your Get-ADUser report and review accounts with incomplete logon information. Neither approach is a substitute for precise sign-in auditing.
6. Limit the report to a specific organizational unit
Use -SearchBase to keep the report focused on the part of your directory you administer:
$OU = 'OU=Employees,DC=contoso,DC=com'
$Cutoff = (Get-Date).AddDays(-90)
Get-ADUser -SearchBase $OU -Filter 'Enabled -eq $true' -Properties LastLogonDate |
Where-Object { $_.LastLogonDate -and $_.LastLogonDate -lt $Cutoff } |
Select-Object Name, SamAccountName, DistinguishedName, LastLogonDate
Replace the sample distinguished name with the correct OU in your domain. Test the search base before treating an empty report as evidence that no accounts are inactive.
7. Export inactive accounts to CSV
$Cutoff = (Get-Date).AddDays(-90)
$Report = Get-ADUser -Filter 'Enabled -eq $true' -Properties LastLogonDate, WhenCreated, Description |
Where-Object { $_.LastLogonDate -and $_.LastLogonDate -lt $Cutoff } |
Select-Object Name, SamAccountName, LastLogonDate, WhenCreated, Description, DistinguishedName
$Report | Export-Csv -Path 'C:\Reports\Inactive-AD-Users.csv' -NoTypeInformation -Encoding UTF8
Create C:\Reports before running the export (for example, New-Item -ItemType Directory -Path 'C:\Reports' -Force). Treat the exported report as sensitive because it contains identity information; store and share it according to company policy.
Suggested real screenshot: Open the resulting CSV in Excel and show the column structure using anonymized test-account data.
8. Understand lastLogon vs. lastLogonTimestamp
| Attribute | What it means | Important limitation |
|---|---|---|
lastLogon |
Logon timestamp recorded by an individual domain controller | Not replicated; query all relevant domain controllers for the latest value |
lastLogonTimestamp |
Replicated timestamp intended to help identify stale accounts | Updated infrequently by design; not suitable for exact audit timing |
LastLogonDate |
PowerShell-friendly date derived from lastLogonTimestamp | Has the same approximate nature as the underlying attribute |
If you need the most recent lastLogon value for a user, query each relevant domain controller and compare the results. For detailed investigations, correlate domain-controller security logs and other approved identity telemetry instead of relying on one AD attribute.
9. Review accounts before taking action
Before considering any account disablement, check its owner, organizational unit, business purpose, group memberships, account type, logon history and whether it supports a service, scheduled task or integration. Exclude or separately review service accounts, privileged accounts, emergency accounts, temporary accounts and other exceptions. Coordinate with HR and system owners, document approvals and follow your organization’s access-review process.
Do not automate disabling or deleting accounts solely from the 90-day report. An inactive-looking account might still have a legitimate role, and logon attributes are not perfectly current.
Troubleshooting common errors
| Issue | What to check |
|---|---|
Get-ADUser is not recognized |
Install or enable RSAT Active Directory PowerShell tools and import the module |
| Cannot contact server or domain | Domain connectivity, DNS, VPN, firewall and domain-controller availability |
| Insufficient access | Directory read permissions and any delegated access restrictions |
| Blank last logon date | Attribute not populated or not available; don’t infer inactivity without further checks |
| Unexpected accounts in report | Search base, service accounts, replication lag and filtering criteria |
Frequently asked questions
Can I safely disable every account inactive for 90 days?
No. Review exceptions, account ownership, business purpose, data completeness and policy approvals before taking action.
Is LastLogonDate the exact last sign-in?
No. It is based on an approximate replicated timestamp, which is sufficient for some stale-account reviews but not exact forensic auditing.
Does this work for Microsoft Entra ID accounts?
These commands apply to on-premises Active Directory Domain Services. Microsoft Entra ID uses different sign-in reporting and APIs.
Related ElMajdal.net tutorials
For other Active Directory administrative tasks, see How to Enable Active Directory Recycle Bin and How to Find FSMO Roles Holders.
