/build/static/layout/Breadcrumb_cap_w.png

Track User Password Expiration using Active Directory

When I worked the help desk, a fair number of tickets were related to problems with a user's password. Often it had expired and they were having issues resetting it and needed to logon immediately. If only they hadn't waited until the last minute! Keeping on top of user passwords is terrific help desk practice. Knowing when a user's password is going to expire can help you head off a call. Or you may simply need to audit passwords occasionally to identify potential password or security vulnerabilities.
One of the most important password checks you can make is to identify users with non-expiring passwords. I'm assuming this is the exception rather than the rule in your organization. One easy way to identify these accounts is with the Active Directory Users and Computers management console. If you install the Remote Server Administration Tools (RSAT) on your Windows 7 desktop you can accomplish this from your desk and never have to logon to a domain controller.
At the top of the navigation pane you should see an entry for Saved Queries. Right-click and choose New - Query. In the dialogue box enter a name and description. The query defaults to searching the domain root but if you want to limit it to a specific OU, click Browse and navigate to the OU you want to search.
'
Figure 1
Next, click the Define Query button. This will bring up a dialog box that you can use to create all sorts of queries. On the Users tab under Common Queries you should see a check box for Non-Expiring passwords. That's what we want. Check it.
'
Figure 2
Click OK twice and after a moment or two you should get results.
'
Figure 3'
You can manage the users in the results panel like any other user account. By the way, the query you created only works on the current machine. If there are other administrators who need to see this information, they don't have to re-invent the wheel. Right click on your query and choose Export Query Definition. Save the query to an XML file. The other administrators simply need to Import the saved query on their desktop.
If you have a domain controller that supports the Microsoft Active Directory PowerShell provider, that is to say it must be running the Active Directory Web Services service, you have even more options.
What Are You Talking About?
'
Microsoft Windows Server 2008 R2 introduced a new approach for managing Active Directory. Any R2 domain controller now runs an Active Directory web service for remote management. Microsoft also released a set of PowerShell cmdlets and a provider for connecting to this service and administering your domain. These cmdlets are also available on Windows 7 when you install the Remote Server Administration Tools and turn on the Active Directory feature. If you do not have an R2 domain controller, you can freely download the Active Directory Management Gateway service from Microsoft and install it on Windows 2003 and later domain controllers. Be aware that installation will require a reboot. But once installed, you can use the PowerShell features from Windows 7 to manage your legacy domains.
'
I'll assume you have RSAT installed on Windows 7. Go to Administrative Tools ' Active Directory Administrative Center. This is a new management console for managing via the AD web service. Click the link for your domain or navigate your way to the OU you want to manage. Now we can start filtering.
Because I typically only care about enabled accounts, let's first make sure we get them. Click Add Criteria and then choose 'Users with disabled/enabled accounts'. Also check 'Users whose password has expiration date/no expiration date' and click Add. These settings are toggles so you might need to change them. If the disable/enable filter is set to disable, click the link and select enabled. If you want to search the entire domain then use the Global Search link. The settings are the same. After you have made your choices, click Search. You should get a result like Figure 4.
'
Figure 4
'
You can manage individual accounts by double clicking them or using the Task pane. I recommend clicking the diskette icon and saving the query for the next time you open the management console. Unfortunately, I have not found a good way to share queries or filters among administrators. The best solution I have found is to click the Convert to LDAP, copy the query and paste it into a text file. Other administrators can paste the query into to a new session and then save their copy locally.
Another query you might find helpful is enabled accounts with expired passwords. Click Clear to start over and add the filtering criteria
The last query that I find especially helpful is identifying users whose password will expire in the next X number of days. Imagine coming in to work on Monday morning and getting a list of users with passwords set to expire the upcoming week. You could take pro-active measures to ensure a smooth update which should make everybody happy.
Clear any search criteria. Add criteria for disabled/enabled accounts and 'User with a password expiring in a given number of days.' Verify you are searching for enabled accounts and enter in a value for the number of days. I'll say it is Monday so I'll enter 5. Within moments I can see who I need to work with this week.
'
Figure 5'
Unfortunately, there is no way to print, export or otherwise save this information. However, the Administrative Center is actually sitting on top of PowerShell so can bypass the GUI and retrieve information directly from a PowerShell session.
First, open a PowerShell window and import the Active Directory module.
PS C:\> Import-Module ActiveDirectory
The user object has a number of password related properties that you can search on. Let's look at the reporting we've done in graphical tools and see how to accomplish the same thing in Windows PowerShell. First, let's find all the enabled accounts with non-expiring passwords.
PS C:\> get-aduser -filter {PasswordNeverExpires -eq $True -AND Enabled -eq $True} | Select Distinguishedname
Distinguishedname
-----------------
CN=Administrator,CN=Users,DC=jdhlab,DC=local
CN=Jeff,CN=Users,DC=jdhlab,DC=local
CN=Roy G. Biv,OU=Executive,OU=Employees,DC=jdhlab,DC=local
CN=Sunny Day,OU=Customer Service,OU=Employees,DC=jdhlab,DC=local
CN=Sherwood McNeal,OU=Executive,OU=Employees,DC=jdhlab,DC=local
CN=Aldo Shebby,OU=IT,OU=Employees,DC=jdhlab,DC=local
CN=Yuko Himmelspach,OU=Customer Service,OU=Employees,DC=jdhlab,DC=local
CN=Jim Shortz,OU=Legal,OU=Employees,DC=jdhlab,DC=local
CN=DA_Hicks,OU=Employees,DC=jdhlab,DC=local

I could have selected any number of properties to display. In fact one useful property is PasswordLastSet. Here's a revised command:
PS C:\> get-aduser -filter {PasswordNeverExpires -eq $True -AND Enabled -eq $True} -properties PasswordLastSet | Sort PasswordLastSet | Select Distinguishedname,PasswordLastSet
Distinguishedname'''''''''''''''''''''''''''''''''''''''''' PasswordLastSet
-----------------'''''''''''''''''''''''''''''''''''''''''' ---------------
CN=Administrator,CN=Users,DC=jdhlab,...'''''''''''''' 1/24/2010 5:21:49 PM
CN=Jeff,CN=Users,DC=jdhlab,DC=local'''''''''''''''''' 3/10/2010 10:37:08 AM
CN=Roy G. Biv,OU=Executive,OU=Employees,DC=jdhlab,... 3/22/2010 3:04:19 PM
CN=Aldo Shebby,OU=IT,OU=Employees,DC=jdhlab,...'''''' 6/23/2010 7:34:21 AM
CN=DA_Hicks,OU=Employees,DC=jdhlab,DC=local'''''''''' 7/12/2010 1:30:09 PM
CN=Sherwood McNeal,OU=Executive,OU=Employees,DC...''' 7/22/2010 10:57:26 AM
CN=Sunny Day,OU=Customer Service,OU=Employees,DC=j... 7/22/2010 4:11:47 PM
CN=Jim Shortz,OU=Legal,OU=Employees,DC=jdhlab...''''' 8/4/2010 10:44:28 AM
CN=Yuko Himmelspach,OU=Customer Service,OU=Employe... 3/6/2011 10:06:08 AM
I could easily send this to a printer or save to a text file. By the way, this command is searching the entire domain, but I can use the 'SearchBase parameter to limit my query to a specific organizational unit.
Next, let's find enabled accounts with but expired passwords. Perhaps these users have been on vacation and or these are accounts no longer in use in which case they should probably be disabled or deleted.
PS C:\> get-aduser -filter {Enabled -eq $True} -properties passwordExpired | where {$_.PasswordExpired} | measure-object
Count''' : 2853
Average' :
Sum''''' :
Maximum' :
Minimum' :
Property :
The PasswordExpired property is calculated by the cmdlet and is not an actual object property in Active Directory. Therefore I can't make it part of my filter with Get-ADUser, but I can filter using Where-Object. As you can see I have a lot of enabled accounts with expired passwords in my test domain. I hope your number is much, much smaller. But let's say you decide to err on the side of caution and disable these accounts until they can be investigated. This is an easy command.
PS C:\> get-aduser -filter {Enabled -eq $True} -properties passwordExpired | where {$_.passwordExpired} | Disable-ADAccount
The Disable-ADAccount cmdlet does not write anything to the pipeline unless you use 'Passthru, but this would in fact disable all the accounts.
How about taking this a step further and preparing a report that shows all enabled accounts, when their password was last, if it has expired and the password age.
PS C:\> Get-ADUser -filter {Enabled -eq $True -AND PasswordNeverExpires -eq $False} -properties * | Sort PasswordLastSet | Select Name,PasswordLastSet,PasswordExpired,@{Name="PasswordAge";Expression={(Get-Date)-$_.PasswordLastSet}}
Name'''''''''''' PasswordLastSet''''' PasswordExpired PasswordAge
----'''''''''''' ---------------''''' --------------- -----------
Jacek Jelitto''' 3/6/2011 10:04:55 AM'''''''''' False 38.05:23:24.7644101
Belinda Newman'' 3/6/2011 10:04:56 AM'''''''''' False 38.05:23:24.6130429
Davis Knellinger 4/10/2011 2:48:19 PM'''''''''' False 3.00:40:01.3041942
Ben Smith''''''' 4/13/2011 3:44:19 AM'''''''''' False 11:44:01.4135976
'
In my filter I'm getting all enabled accounts with passwords that can expire. The accounts are sorted on the PasswordLastSet property and then I'm selecting a subset of properties. The hash table defines a custom property called PasswordAge that is calculated by subtracting the password last set value from the current date and time. The result is a timespan object. The oldest accounts in this list are 38 days old.
Which leads us back to the last task: find accounts with passwords that will expire in the next X number of days. Conceptually, this is rather straightforward. First determine the maximum password age. This value can be found with the Get-ADDefaultDomainPasswordPolicy cmdlet.
PS C:\> Get-ADDefaultDomainPasswordPolicy
ComplexityEnabled'''''''''' : True
DistinguishedName'''''''''' : DC=jdhlab,DC=local
LockoutDuration'''''''''''' : 00:30:00
LockoutObservationWindow''' : 00:30:00
LockoutThreshold''''''''''' : 7
MaxPasswordAge''''''''''''' : 40.00:00:00
MinPasswordAge''''''''''''' : 1.00:00:00
MinPasswordLength'''''''''' : 7
objectClass'''''''''''''''' : {domainDNS}
objectGuid''''''''''''''''' : 9db48ea5-9dea-43c9-9301-f2262b244ce2
PasswordHistoryCount''''''' : 12
ReversibleEncryptionEnabled : False
The MaxPasswordAge property is a time span object. In my domain, it is 40 days.
PS C:\> (Get-ADDefaultDomainPasswordPolicy).MaxPasswordage.Days
40

Next I need to find accounts with passwords set between 40 days ago, my maximum password age and those set in the following X number of days. This is exactly what the query in the Active Directory Administrative Center is doing. To make life easier, you can download a PowerShell script here. The script is called Get-PasswordWillExpire.ps1. The script takes a single parameter value that represents the number of days. In other words, the script will return a list of user accounts that will expire in X number of days. The script uses the Microsoft Active Directory provider and by default searches for users in the entire domain whose password will expire in the next 5 days. But the script accepts parameters of 'Next and 'SearchBase so you can customize the search. The SearchBase must be the distinguishedname of a container or OU like 'OU=Employees,DC=JDHLab,DC=Local'. Be sure to enclose it in quotes. When I run the script I will get output like Figure 6.
'
Figure 6
Personally I have to admit this is one of the most useful PowerShell tools I've ever put together. I can incorporate the script into other processes such as using the email address from the account to send the user a message reminding them to change their password. Or get the user's manager and send her a list of employees who need to change passwords.
The primary advantage in using a command line tool like PowerShell is that it opens up all sorts of management and automation possibilities. You could schedule a task to run Monday morning before you get to work that prepares a list of accounts about to expire during the week and email them to yourself or your team. You could create an HTML file and post it to your internal help desk web site. You could print out a list of accounts and task someone to track down and verify the accounts are still active.
If you don't have a domain controller with the Active Directory Web Service, you can still use PowerShell to gather password information. I won't go into great detail but I wrote a script called Get-ADSIPasswordAge.ps1 which you can also download here. This script connects to the current user's domain, finds enabled accounts, and writes a subset of user account properties to the pipeline. Here's how you might use it.
PS C:\> $data=C:\Scripts\Get-ADSIPasswordAge.ps1
Connecting to JDHLAB.LOCAL
Getting Users
Filtering for Enabled accounts

The $data variable holds the results from the script like this:
Samaccount''''' : E.Muster
Name''''''''''' : Ella Muster
PasswordExpired : False
PasswordLastSet : 4/10/2011 2:48:40 PM
PasswordAge'''' : 3.05:55:10
Samaccount''''' : A.Brechner
Name''''''''''' : Augustine Brechner
PasswordExpired : False
PasswordLastSet : 3/6/2011 11:05:23 AM
PasswordAge'''' : 38.09:38:10

These are objects so you can slice and dice the data however you need.
Everything I've discussed assumes you are using a single password policy in your domain. Microsoft introduced the concept of fine grained password policies with Windows Server 2008 which adds a level of complexity for determining when a user's password will expire. Discussion of fine grained password policies is beyond what I have space for in this article.
If you'd like to explore more ways to use PowerShell with Active Directory, including coverage of fine grained password policies, you might be interested in a copy of Managing Active Directory with Windows PowerShell: TFM 2nd ed. (SAPIEN Press 2011).
Of course, you are not limited to what I've discussed in this article. There are plenty of third party solutions for managing user passwords that you might want to investigate. You can find command line and graphical tools. But in any event, getting a handle on user passwords can help take a bite out of your help desk work load.

Comments

  • A very informative article !
    Suffering with the same concern and receiving several tickets from my users to reset their expired passwords and unlock the accounts, I started using Lepide AD self service tool i.e., (http://www.lepide.com/active-directory-self-service/) that is very helpful in my hectic work-station. It sends automatic password expiration notification email within given time line so that they can change their password before it expired. It is equipped with several comprehensive features that also allow users to reset their passwords without any help of Admin or help-desk. - andrewcamary 9 years ago
  • I have gone the path of sending user emails to tell them their passwords are expiring. In XP and below a user would get hit with a nice dialog box in the center of the screen when they were nearing the password change policy notification, however in Windows 7 this notification got moved down to the very ignorable balloon tips on the systray.

    I have a GPO to start warning users in 14 days about a password change and I also have this Powershell script setup on a server to email the users once they are down to 7 days. I set this to run every night at 12:01. This has cut down the password reset issue by 80%.

    This script requires that powershell can execute scripts via the scheduler and also the Active Directory module. Just change the first 3 variables to match your domain configuration

    Password_Expiring_Email.ps1
    ==============================

    #Set Variables

    $smtpServer="yoursmtp.yourdomain.com"

    $helpdeskaccount = "helpdesk@yourdomain.com"

    $Scriptaccount = "passwordexpire@yourdomain.com"

    $expireindays = 7

    $OutputFileName = "C:\Password_Expiring_List.txt"

    $ITNotifyThreashold = 3

    $TotalITNotifyUsers = 0

    #Clear last nights IT notification list file and start a new one

    Write-Output "The following are user with expiring passwords:" | Out-file $OutputFileName

    Write-Output "-----------------------------------------------" | Out-file -Append $OutputFileName

    #Look for only Enabled users

    Import-Module ActiveDirectory

    $users = get-aduser -filter * -properties * |where {$_.Enabled -eq "True"} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false } | where { $_.EmailAddress -like "*"}

    #Loop through and only send emails to users under the expireindays varialbe
    foreach ($user in $users)
    {
    $FirstName = (Get-ADUser $user | foreach { $_.givenName})

    $LastName = (Get-ADUser $user | foreach { $_.sn})

    $FullName = (Get-ADUser $user | foreach { $_.Name})

    $emailaddress = (get-aduser $user -properties * | foreach { $_.mail })

    $passwordSetDate = (get-aduser $user -properties * | foreach { $_.PasswordLastSet })

    $maxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge

    $expireson = $passwordsetdate + $maxPasswordAge

    $today = (get-date)

    $daystoexpire = (New-TimeSpan -Start $today -End $Expireson).Days

    $subject="Your password will expire in $daystoExpire days"

    $body ="
    Dear $Firstname,
    <p> Your Password will expire in $daystoexpire days. To change your password on a PC press CTRL+ALT+DEL
    and chose Change Password. If you are currently working remotely please be sure you are connected
    to the VPN in order to change your password.

    <p> When changing your password please be sure to have all your mobile devices that receive email available
    to update your password on those devices as well. Also, be sure you are not logged onto multiple computers
    when you change your password. Having an old password in use on any device will cause your account to be
    locked out when that device or computer attempts to logon to your account using your old password.

    <p>
    <p> If you need any assistance please contact the Helpdesk at helpdesk@yourdomain.com
    or call 999 888-7777. <br>
    <p>Thank you, <br>
    IT Team<p><p>

    sent to: $FullName
    "

    #Check if this user is under the user notification threshold


    if ($daystoexpire -lt $expireindays)
    {
    Send-Mailmessage -smtpServer $smtpServer -from $helpdeskaccount -to $emailaddress -subject $subject -body $body -bodyasHTML -priority High

    }

    #Check if this user is under the Helpdesk notification threshold and add them to the IT notification file list

    if ($daystoexpire -lt $ITNotifyThreashold)
    {
    Write-Output "$FullName with email address: $emailaddress is expiring in $daystoExpire days`t" | Out-file -Append $OutputFileName

    $TotalITNotifyUsers = $TotalITNotifyUsers + 1

    }
    }

    #Complete the IT notification file with a sum of users with pending expirations

    Write-Output "Total User: $TotalITNotifyUsers" | Out-file -Append $OutputFileName

    #Send the list of users with imminent lockout possibilities to the helpdesk

    Send-MailMessage -SmtpServer $smtpServer -To $helpdeskaccount -From $Scriptaccount -Subject "User AD Password Expiring Report" -Body (Get-Content $OutputFileName | out-string) - JordanNolan 9 years ago
  • Very nice & detailed.

    however you may want to use this code below, as it converts it to show exactly how many days are left.

    $Output = @()
    $Days = 60

    Foreach ($ADUsers in Get-ADuser -filter *)
    {
    $ADUser = Get-ADUser -Identity $ADUsers.SAMAccountName -Properties *


    $DaysToExpire = ((get-date $aduser.passwordlastset).AddDays($days) - (get-date)).Days
    #Print out the days to expire and see if that is what you want.
    #$DaysToExpire


    $Output += New-Object PSObject -Property @{DisplayName=$ADUser.DisplayName; PasswordLastSet=$aduser.passwordlastset; DaysLeft=$DaysToExpire; EmailAddress=$ADUser.EmailAddress}
    }

    $Output | Out-GridView - 96Primera 9 years ago
This post is locked
 
This website uses cookies. By continuing to use this site and/or clicking the "Accept" button you are providing consent Quest Software and its affiliates do NOT sell the Personal Data you provide to us either when you register on our websites or when you do business with us. For more information about our Privacy Policy and our data protection efforts, please visit GDPR-HQ