When managing a Windows domain, administrators often need to identify inactive computers for cleanup or security audits. A common requirement is finding workstations (excluding servers) that have had user logins within a specific timeframe - in this case, the last 30 days.
Several factors make this more complex than a simple AD query:
- Need to filter by computer objects only
- Must distinguish workstations from servers
- Requires accurate date comparison logic
- Should handle large domains efficiently
Here's the improved PowerShell script that addresses all requirements:
# Get current date threshold
$dateThreshold = (Get-Date).AddDays(-30)
# Query AD with proper filtering
$computers = Get-ADComputer -Filter {
OperatingSystem -notlike "*server*" -and
LastLogonDate -ge $dateThreshold
} -Properties OperatingSystem, LastLogonDate |
Select-Object Name, OperatingSystem, LastLogonDate
# Export results
$computers | Export-Csv -Path "C:\Temp\ActiveComputers.csv" -NoTypeInformation
For large domains, consider these enhancements:
# Add server OS exclusion list
$serverOSPatterns = @("*server*","*datacenter*","*enterprise*")
# Use LDAP filter for better performance
$ldapFilter = "(&(objectCategory=computer)(!(operatingSystem=*server*))(lastLogonTimestamp>={0}))" -f
[DateTime]::Now.AddDays(-30).ToFileTime()
Get-ADComputer -LDAPFilter $ldapFilter -Properties LastLogonDate |
Where-Object { $_.LastLogonDate -ge $dateThreshold }
If you need more granular control over the OS filtering:
# Define workstation OS patterns
$workstationOS = @("Windows 10*","Windows 11*")
Get-ADComputer -Filter * -Properties OperatingSystem, LastLogonDate |
Where-Object {
$_.OperatingSystem -match ($workstationOS -join "|") -and
$_.LastLogonDate -ge $dateThreshold
}
For global organizations, account for timezone differences in LastLogonDate:
$utcThreshold = (Get-Date).ToUniversalTime().AddDays(-30)
Get-ADComputer -Filter { LastLogonDate -ge $utcThreshold } -Properties LastLogonDate
As an admin managing a Windows domain, one of my recurring tasks is identifying which workstations are actually being used. Servers typically show consistent activity, but workstations might sit idle for months after employees depart. Here's how I solved this with PowerShell.
The key property we need is LastLogonDate, which represents the most recent authentication timestamp. Unlike LastLogon, this value gets replicated across domain controllers.
# Basic query to examine properties
Get-ADComputer -Filter * -Properties LastLogonDate,OperatingSystem |
Select-Object Name,OperatingSystem,LastLogonDate |
Format-Table -AutoSize
To find machines logged into within 30 days, we'll use PowerShell's datetime comparison:
$cutoffDate = (Get-Date).AddDays(-30)
Get-ADComputer -Filter {OperatingSystem -like "*Workstation*" -and LastLogonDate -ge $cutoffDate}
-Properties LastLogonDate,OperatingSystem |
Select-Object Name,OperatingSystem,LastLogonDate |
Export-Csv -Path "C:\Temp\ActiveWorkstations.csv" -NoTypeInformation
For large domains, these improvements help:
- Add
-ResultPageSize 1000for better performance - Use
-SearchBaseto limit to specific OUs - Include error handling for stale computers
try {
$activeComputers = Get-ADComputer -Filter {
OperatingSystem -like "*Workstation*" -and
Enabled -eq $true -and
LastLogonDate -ge $cutoffDate
} -Properties * -ErrorAction Stop
$activeComputers | Export-Csv -Path "C:\reports\active_workstations.csv" -NoTypeInformation
}
catch {
Write-Error "AD query failed: $_"
}
For environments where computer accounts rotate passwords regularly:
$active = Get-ADComputer -Filter {
pwdLastSet -ge $cutoffDate.ToFileTime()
} -Properties pwdLastSet
Create a scheduled task to run weekly:
$action = New-ScheduledTaskAction -Execute "PowerShell.exe"
-Argument "-NoProfile -File C:\scripts\check_active_workstations.ps1"
$trigger = New-ScheduledTaskTrigger -Weekly -At 3am
Register-ScheduledTask -TaskName "AD Workstation Activity Report"
-Action $action -Trigger $trigger -RunLevel Highest