PowerShell Scripts to Force Password Change for All Users After a Security Incident

(Last updated on March 30, 2022)

After a confirmed or even suspected security breach it may be advised to have all users change their passwords. In this post we’ll review how to confirm if users have changed their passwords and how to force users to complete a password change in Active Directory.

You can manually check the pwdLastSet attribute on each user account to see when their password was last changed (you could also find this out by running a free scan with Specops Password Auditor and looking at password changed dates in the Password Age report). Use ADSIEdit or Active Directory Users and Computers (with advanced features enabled) to view the attribute directly:

When using PowerShell to pull the attribute, you’ll see that the attribute is actually saved in what is known as ‘filetime’ format, but you can also pull a constructed attribute called passwordLastSet which will be a DateTime PowerShell attribute (more on why that’s important in a bit).

Quick note on filetime: this is a timestamp stored as a count of the number of 100-nanosecond intervals that have elapsed since midnight on January 1, 1601 (UTC).

Also note if you see pwdLastSet attribute is 0 (i.e. ‘never’) and passwordLastSet is blank — this means the option that the user must change their password at next logon has been enabled. This is a bit of a drawback in Active Directory as we now have no way to know when the last password change was actually performed.

The passwordLastSet property as a DateTime object is important because we can now manipulate the date and compare it to other dates. So if I want to run a check to say, for example, was this user’s password changed within the past 24 hours, I can add 1 day to the passwordLastSet attribute and compare it to

$user.passwordLastSet.AddDays(1) -gt $(get-date)

If we change the threshold to 30 days for this user, the result is now true (in this example, the user’s password change was about 3 weeks ago as of this blog post):

We can run this same check against all users. We’ll also exclude disabled users and any users who are already required to change their password at next logon, and check for users whose passwords never expire and/or cannot change their own password:

$verbosepreference = "continue"  
 
## Construct a date X days ago, in this case 30 days:
$compareDateTime = $(get-date).AddDays(-30)

## Alternative Option -- compare against a specific datetime by uncommenting and modifying the date in the following line:
## $compareDateTime = get-date -date "2022-03-21 12:00"
 
write-verbose "Finding users whose passwords have not changed since $compareDateTime" 
  
Get-ADUser -filter { Enabled -eq $True } –Properties pwdLastSet,passwordLastSet,passwordNeverExpires,cannotChangePassword |   
    where { $_.passwordLastSet -lt $compareDateTime -and $_.pwdLastSet -ne 0 } |   
    Select-Object name,sAmAccountName,passwordLastSet,passwordNeverExpires,cannotChangePassword
                   

If we’d like to force all those users to change a password, we can run a script that does the same date comparison but then ticks the ‘user must change password at next logon’ option on all of them. Here we exclude any user with ‘passwordNeverExpires’ or ‘cannotChangePassword’ set to true, as you cannot require a password change on those users. We recommend a manual process to remediate these as these accounts are usually service accounts where a password change requires simultaneously updating the password in a service or application configuration.

$verbosepreference = "continue"  
 
## Construct a date X days ago, in this case 30 days:
$compareDateTime = $(get-date).AddDays(-30)

## Alternative Option -- compare against a specific datetime by uncommenting and modifying the date in the following line:
## $compareDateTime = get-date -date "2022-03-21 12:00"
 
write-verbose "Finding users whose passwords have not changed since $compareDateTime" 
  
$users = Get-ADUser -filter { Enabled -eq $True } –Properties pwdLastSet,passwordLastSet,passwordNeverExpires,cannotChangePassword |   
    where { $_.passwordLastSet -lt $compareDateTime -and $_.pwdLastSet -ne 0 -and $_.passwordNeverExpires -eq $false -and $_.cannotChangePassword -eq $false }

foreach ($user in $users) {
    $outObject = new-object -typename psobject
    $outobject | Add-member -MemberType NoteProperty -Name distinguishedName -Value $user.distinguishedname
    $outobject | Add-Member -MemberType NoteProperty -Name OldPasswordLastSet -value $user.passwordlastset
    set-aduser $user -ChangePasswordAtLogon:$true
    $outObject
} 

Tags: , ,

Written by

Darren Siegel

Product Specialist, Specops Software

More Articles
Back to Blog