When working with Active Directory user management, a common task is identifying accounts with non-expiring passwords. While the Search-ADAccount -PasswordNeverExpires
command seems straightforward, many administrators report incomplete results due to several technical factors.
For comprehensive results, we need to examine the actual userAccountControl
attribute where this setting is stored:
Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires |
Select-Object Name, SamAccountName, DistinguishedName
To scan all domains in your forest, use this enhanced script:
$Domains = (Get-ADForest).Domains
$Results = foreach ($Domain in $Domains) {
Get-ADUser -Server $Domain -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires, PasswordLastSet |
Select-Object Name, SamAccountName, DistinguishedName, PasswordLastSet, @{
Name = 'Domain'; Expression = {$Domain}
}
}
$Results | Export-CSV "NeverExpirePasswords_$(Get-Date -Format yyyyMMdd).csv" -NoTypeInformation
The original Search-ADAccount
cmdlet has several constraints:
- Default 1000 result limit (controlled by
-ResultPageSize
) - Only searches the current domain by default
- Doesn't expose all relevant attributes for reporting
For more granular control, combine filters:
Get-ADUser -Filter {Enabled -eq $true -and PasswordNeverExpires -eq $true} -Properties * |
Where-Object {$_.PasswordLastSet -lt (Get-Date).AddDays(-365)} |
Select-Object Name, SamAccountName, EmailAddress, Department
For large environments, implement parallel processing:
$Domains = (Get-ADForest).Domains
$UsersWithNeverExpire = $Domains | ForEach-Object -Parallel {
Import-Module ActiveDirectory
Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Server $_ -Properties PasswordNeverExpires
} -ThrottleLimit 5
When you identify these accounts, consider:
- Reviewing service account requirements
- Documenting business justifications
- Implementing periodic review workflows
- Setting alerting for newly configured never-expire flags
The default Search-ADAccount -PasswordNeverExpires
cmdlet often returns incomplete results because:
- It only searches the current domain by default
- Has a default result size limit of 1000 objects
- May miss objects in certain organizational units
For a complete forest-wide search, use this enhanced PowerShell script:
# Import Active Directory module
Import-Module ActiveDirectory
# Set unlimited result size
$adSearcher = New-Object DirectoryServices.DirectorySearcher
$adSearcher.PageSize = 50000
# Search all domains in forest
$forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$domains = $forest.Domains
foreach ($domain in $domains) {
Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Server $domain.Name -Properties PasswordNeverExpires |
Select-Object Name, SamAccountName, DistinguishedName, Enabled |
Export-Csv "NeverExpire_$($domain.Name).csv" -NoTypeInformation -Encoding UTF8
}
For enterprises with thousands of users, consider this optimized LDAP query:
$LDAPFilter = "(&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=65536))"
Get-ADUser -LDAPFilter $LDAPFilter -Properties * |
Where-Object {$_.Enabled -eq $true} |
Select-Object Name, SamAccountName, UserPrincipalName
To confirm the accuracy of your results:
- Check a sample of returned accounts manually in AD Users and Computers
- Compare against known service accounts that typically have this setting
- Validate the count against your organization's password policy exceptions
Create a scheduled task with this script to monitor changes:
# Weekly audit script
$DateStamp = Get-Date -Format "yyyyMMdd"
$ReportPath = "C:\ADReports\PasswordAudit_$DateStamp.html"
Search-ADAccount -PasswordNeverExpires |
ConvertTo-Html -Title "Password Never Expires Report" |
Out-File $ReportPath
# Email notification
Send-MailMessage -Attachments $ReportPath -Subject "Weekly AD Password Audit" -Body "See attached report"
- Service accounts should be handled separately from user accounts
- Review results with your security team before making changes
- Document all exceptions for compliance purposes