• Home
  • PowerShell
  • How to check the last password change date in Active Directory using PowerShell

How to check the last password change date in Active Directory using PowerShell

Monitoring password changes is crucial for maintaining security in Active Directory (AD) environments. The Get-ADUser cmdlet provides access to password-related properties that help administrators track when users last updated their credentials. This information is vital for compliance audits, identifying accounts with outdated passwords, and generating password expiration reports. By leveraging PowerShell's capabilities with specific AD attributes, administrators can effectively monitor password hygiene across the organization.

Understanding PasswordLastSet and pwdLastSet

Active Directory stores password change information through two related but distinct characteristics:

  • pwdLastSet is the actual LDAP attribute stored in Active Directory. This attribute contains a 64-bit integer representing the number of 100-nanosecond intervals since January 1, 1601 (Windows FileTime format). The value automatically updates whenever a password change occurs, whether initiated by the user, an administrator, or through a password reset process.
  • PasswordLastSet is the PowerShell property exposed by the Get-ADUser cmdlet. When you query this property, PowerShell automatically converts the raw pwdLastSet value into a more readable DateTime object. This conversion simplifies working with the data in scripts and reports, eliminating the need for manual FileTime conversions.

Special cases and null values

The pwdLastSet attribute exhibits specific behaviors in certain scenarios:

  • Returns null for newly created accounts that haven't received an initial password.
  • Becomes null when the User must change password at the next logon option is enabled.
  • Gets set to 0 (zero) to force password expiration.
  • Updates to the current timestamp during any password change operation.

These special cases require careful handling in scripts to avoid errors and ensure accurate reporting.

PowerShell

Using the Get-ADUser command to get AD users' last password set date:

  1. Identify the domain that you want to retrieve the report from.
  2. Identify the LDAP properties you need to fetch the report.
  3. Write the script.
  4. Execute it in Windows PowerShell.
  5. The report will be exported i n the requested format.
  6. To obtain the report in a different format, modify the script accordingly to the needs of the user.
    Get-ADUser -identity robert.allen -properties PwdLastSet | sort Name | ft Name,@{Name='PwdLastSet';Expression={[DateTime]::FromFileTime($_.PwdLastSet)}}
ADManager Plus

Using ADManager Plus to get AD users' last password set date:

  1. Navigate to Reports > Password Reports > Password Changed Users.
  2. Select the required domain, the OU, and the desired time period to filter users.
  3. Click Generate.
  4. After the report generates, click Export As to download it in HTML, CSV, XLS, or PDF.

Example use cases and scripts

Example 1: Get the password last set date for a specific user.

Retrieve the date when a specific user last changed their password to verify compliance with password policies.

Get-ADUser -Identity "john.doe" -Properties PasswordLastSet | Select Name,PasswordLastSet

This command shows the exact date and time when the specified user's password was last changed.

Example 2: Find users who haven't changed their passwords in 90 days.

Create an AD user password age script to identify users with passwords older than 90 days.

Get-ADUser -Filter {PasswordLastSet -lt (Get-Date).AddDays(-90)} -Properties PasswordLastSet | Select Name,PasswordLastSet

This PowerShell last password change query helps identify security risks from stale passwords.

Example 3: Check when passwords were last changed AD for enabled accounts only.

Focus security reviews on active accounts by filtering for enabled users only.

Get-ADUser -Filter {Enabled -eq $true} -Properties PasswordLastSet,PasswordNeverExpires | Select Name,PasswordLastSet,PasswordNeverExpires

This command helps identify active accounts that may need password policy enforcement.

Supported parameters

Parameters Description
-Identity Specifies an AD user object by distinguished name, GUID, security identifier, or SAM account name.
-Filter Specifies a query string using PowerShell Expression Language to retrieve multiple objects based on PasswordLastSet.
-SearchBase Specifies the AD path to search for users with specific password change dates.
-Properties Must include PasswordLastSet to retrieve password change information (not returned by default).
-LDAPFilter Uses LDAP syntax for complex date-based password queries.
-ResultPageSize Controls pagination when retrieving large numbers of users for password reports.

Troubleshooting common password last set query issues

Error: PasswordLastSet property returns empty or null.

  • Cause: User has never set a password or property not specified in the query.
  • Solution: Include the property explicitly and handle null values:
    Get-ADUser -Filter * -Properties PasswordLastSet |
    Select Name,@{N='PasswordLastSet';E={if($_.PasswordLastSet){$_.PasswordLastSet}else{'Never Set'}}}

Error: Date shows a large number instead of a readable date.

  • Cause: PasswordLastSet is stored in FileTime format (Int64).
  • Solution: Convert to DateTime format:
    Get-ADUser -Identity "user" -Properties PasswordLastSet |
    Select Name,@{N='LastSet';E={[DateTime]::FromFileTime($_.PasswordLastSet)}}

Error: PowerShell password expiration report shows incorrect dates.

  • Cause: Not accounting for the domain's MaxPasswordAge policy.
  • Solution: Calculate expiration using domain policy:
    $maxPwdAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
    Get-ADUser -Filter * -Properties PasswordLastSet |
    Select Name,@{N='ExpiresOn';E={$_.PasswordLastSet.AddDays($maxPwdAge)}}

Error: Get-ADUser PasswordLastReset not working for fine-grained password policies.

  • Cause: Fine-grained password policies (FGPP) require different calculation.
  • Solution: Check for applicable Password Settings Object (PSO):
    Get-ADUser -Identity "user" -Properties PasswordLastSet,msDS-ResultantPSO |
    Select Name,PasswordLastSet,"msDS-ResultantPSO"

Best practices to avoid issues

  • Always specify -Properties parameter: PasswordLastSet is not returned by default.
  • Handle null values efficiently : Check for $null before date operations.
  • Test with small datasets first: Use -ResultSetSize to limit initial results.
  • Use proper date comparison: Always use DateTime objects, not strings.
  • Consider service accounts: Filter out or specially handle accounts with PasswordNeverExpires.

Limitations of using PowerShell to find the password last set date

  • Complex date calculations: Converting the PasswordLastSet value from FileTime format requires DateTime manipulation and can lead to errors with null values.
  • No automatic age calculation: You must manually calculate password age using date arithmetic for each user.
  • Limited visualization: You can't create charts or graphs showing password age distribution without additional modules.
  • Null value handling: Users who never set passwords return null, requiring special handling to avoid script errors.
  • Performance with large datasets: Retrieving Get-ADUse r PasswordLastSet for thousands of users can be slow without proper filtering.
  • No automated alerting: You can't send automatic notifications for password expirations without scheduled tasks.

Highlights of using ADManager Plus to get AD users' password age

  • Instant password age calculation: View automatically calculated password age in days without scripting.
  • Prebuilt password reports: Access dedicated reports for password expiry, never-expiring passwords, and recently changed passwords.
  • Compliance-ready reporting: Generate SOX, HIPAA, and PCI DSS compliant password audit reports instantly.
  • Bulk password management: Reset passwords for multiple users directly from report results.
  • Real-time password monitoring: Track password changes as they happen with preconfigured reports.
  • Export flexibility: Export password reports in multiple formats with formatted dates and calculated ages.

Find the password last set date of AD users with ADManager Plus

FAQs

PasswordLastSet is stored in Active Directory as a FileTime value (64-bit integer representing 100-nanosecond intervals since January 1, 1601). You need to convert it to a readable DateTime format:

Incorrect: Shows raw FileTime value

Get-ADUser -Identity "john.doe" -Properties PasswordLastSet | Select PasswordLastSet

Correct: Converts to readable date

Get-ADUser -Identity "john.doe" -Properties PasswordLastSet |
Select Name, @{Name='PasswordLastSet';Expression={[DateTime]::FromFileTime($_.PasswordLastSet)}}

For PowerShell 3.0 and above, the PasswordLastSet property is automatically converted to DateTime when using -Properties parameter correctly.

To create a PowerShell password expiration report, you need to combine the PasswordLastSet date with your domain's MaxPasswordAge policy:

To get the domain password policy:

$maxPwdAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge

To calculate expiration for all users:

Get-ADUser -Filter {Enabled -eq $true} -Properties PasswordLastSet, PasswordNeverExpires |
Select Name, PasswordLastSet,
@{Name='PasswordExpires';Expression={
if($_.PasswordNeverExpires -or $_.PasswordLastSet -eq $null) {
"Never"
} else {
$_.PasswordLastSet.AddDays($maxPwdAge.Days)
}
}},
@{Name='DaysUntilExpiry';Expression={
if($_.PasswordNeverExpires -or $_.PasswordLastSet -eq $null) {
"N/A"
} else {
(New-TimeSpan -Start (Get-Date) -End $_.PasswordLastSet.AddDays($maxPwdAge.Days)).Days
}
}} | Sort DaysUntilExpiry

Some accounts may have null PasswordLastSet values, indicating the password was never set or the account was created with User must change password at next logon option.

To find users with no password set:

Get-ADUser -Filter * -Properties PasswordLastSet |
Where-Object {$_.PasswordLastSet -eq $null} |
Select Name, SamAccountName, Enabled

To include proper handling in reports:

Get-ADUser -Filter * -Properties PasswordLastSet, whenCreated |
Select Name,
@{Name='PasswordStatus';Expression={
if($_.PasswordLastSet -eq $null) {
"Never Set (Created: $($_.whenCreated))"
} else {
"Last Changed: $($_.PasswordLastSet)"
}
}}

FGPPs override the default domain policy. You need to check for applicable PSOs.

To check if user has a specific PSO applied:

Get-ADUser -Identity "john.doe" -Properties PasswordLastSet, "msDS-ResultantPSO", "msDS-UserPasswordExpiryTimeComputed" |
Select Name, PasswordLastSet,
@{Name='PasswordPolicy';Expression={
if($_."msDS-ResultantPSO") {
(Get-ADObject $_."msDS-ResultantPSO" -Properties name).name
} else {
"Default Domain Policy"
}
}},
@{Name='ExpiryDate';Expression={
[DateTime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")
}}

To get all users with FGPPs and their expiration dates:

Get-ADUser -Filter * -Properties PasswordLastSet, "msDS-UserPasswordExpiryTimeComputed", "msDS-ResultantPSO" |
Where-Object {$_."msDS-ResultantPSO" -ne $null} |
Select Name, PasswordLastSet,
@{Name='ExpiryDate';Expression={[DateTime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}}

This discrepancy usually occurs due to time zone differences or how the tools calculate password age.

To ensure consistent time zone handling:

Get-ADUser -Identity "john.doe" -Properties PasswordLastSet |
Select Name,
@{Name='PasswordLastSet (UTC)';Expression={$_.PasswordLastSet.ToUniversalTime()}},
@{Name='PasswordLastSet (Local)';Expression={$_.PasswordLastSet.ToLocalTime()}},
@{Name='Password Age (Days)';Expression={
[Math]::Round(((Get-Date) - $_.PasswordLastSet).TotalDays, 2)
}}

For precise matching with Active Directory Users and Computers, use the same calculation method:

Get-ADUser -Identity "john.doe" -Properties PasswordLastSet, pwdLastSet |
Select Name,
@{Name='PasswordLastSet';Expression={$_.PasswordLastSet}},
@{Name='Exact Age';Expression={
$now = [DateTime]::Now
$age = $now - $_.PasswordLastSet
"$($age.Days) days, $($age.Hours) hours, $($age.Minutes) minutes"
}}

Create comprehensive AD user password age scripts for security audits across multiple organizational units.

To define target OUs:

$OUs = @(
"OU=Sales,DC=contoso,DC=com",
"OU=IT,DC=contoso,DC=com",
"OU=Finance,DC=contoso,DC=com"
)

To generate a comprehensive password audit report:

$Report = foreach ($OU in $OUs) {
Get-ADUser -Filter {Enabled -eq $true} -SearchBase $OU -Properties PasswordLastSet, PasswordNeverExpires,LastLogonDate |
Select @{Name='OU';Expression={$OU.Split(',')[0].Replace('OU=','')}},
Name,
SamAccountName,
@{Name='PasswordLastSet';Expression={
if($_.PasswordLastSet) {$_.PasswordLastSet} else {"Never"}
}},
@{Name='PasswordAge';Expression={
if($_.PasswordLastSet) {
[Math]::Round(((Get-Date) - $_.PasswordLastSet).TotalDays)
} else {"N/A"}
}},
PasswordNeverExpires,
LastLogonDate,
@{Name='Risk Level';Expression={
if($_.PasswordNeverExpires) {"High - Never Expires"}
elseif(-not $_.PasswordLastSet) {"High - Never Set"}
elseif(((Get-Date) - $_.PasswordLastSet).TotalDays -gt 180) {"High - Very Old"}
elseif(((Get-Date) - $_.PasswordLastSet).TotalDays -gt 90) {"Medium - Old"}
else {"Low"}
}}
}

To export the report with a timestamp:

$Report | Export-CSV "C:\PasswordAudit_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv" -NoTypeInformation
# Create summary statistics
$Report | Group-Object 'Risk Level' | Select Name, Count

Set up proactive monitoring with a PowerShell last password change script for upcoming expirations.

To get the default domain password policy:

$maxPwdAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
# Find users with passwords expiring within 7 days
$ExpiringUsers = Get-ADUser -Filter {Enabled -eq $true -and PasswordNeverExpires -eq $false} -Properties PasswordLastSet, EmailAddress, Manager |
Where-Object {$_.PasswordLastSet -ne $null} |
Select Name, EmailAddress,
@{Name='Manager';Expression={(Get-ADUser $_.Manager -ErrorAction SilentlyContinue).Name}},
PasswordLastSet,
@{Name='ExpiryDate';Expression={$_.PasswordLastSet.Add($maxPwdAge)}},
@{Name='DaysUntilExpiry';Expression={
($_.PasswordLastSet.Add($maxPwdAge) - (Get-Date)).Days
}} |
Where-Object {$_.DaysUntilExpiry -le 7 -and $_.DaysUntilExpiry -ge 0} |
Sort DaysUntilExpiry

To display the results:

$ExpiringUsers | Format-Table -AutoSize

PowerShell cannot directly access password history, but you can track password change frequency using AdminSDHolder or by comparing PasswordLastSet over time.

To check current password age and estimate change frequency:

Get-ADUser -Filter * -Properties PasswordLastSet, whenCreated |
Where-Object {$_.PasswordLastSet -ne $null} |
Select Name,
@{Name='AccountAge(Days)';Expression={((Get-Date) - $_.whenCreated).Days}},
@{Name='PasswordAge(Days)';Expression={((Get-Date) - $_.PasswordLastSet).Days}},
@{Name='EstimatedChangesPerYear';Expression={
$accountAge = ((Get-Date) - $_.whenCreated).Days
$passwordAge = ((Get-Date) - $_.PasswordLastSet).Days
if($accountAge -gt 365) {
[Math]::Round(365 / $passwordAge, 2)
} else {
"Account less than 1 year old"
}
}} |
Sort 'PasswordAge(Days)' -Descending

For parentheses or other special characters, use single quotes and escape as needed.

Note: For actual password history and detailed change tracking, consider using ADManager Plus, which maintains historical data.

The one-stop solution to Active Directory Management and Reporting
Email Download Link