Q: How can I find out the date and time a user's Active
Directory (AD) domain password expires?
A: You might think this excellent question has a simple answer. However, several
factors add to the question's complexity. To get the answer, you need to ask
several questions:
- Does the user's password expire? If not, you don't need to calculate an
expiration date.
- What is the domain's maximum password age? That is, how long can a password
be used before it expires? If the domain doesn't specify a maximum password
age, you don't need to calculate an expiration date.
- At what date and time was the user's password last set? If the password
hasn't been set, you can't calculate an expiration date.
I'll present each of these questions as a subtask, then I'll give you a script
that lets you calculate and display a user's password expiration date. These
subtasks are presented here in VBScript, but I also wrote JScript versions that
you can download from the Windows Scripting Solutions Web site at http://www.windowsitpro.com/
windowsscripting, InstantDoc ID 94256. I wrote the final script in JScript for
reasons I'll explain shortly.
Subtask 1: Determine Whether the User's Password Expires
To perform the first subtask, we need to retrieve the userAccountControl attribute
from the user account and determine whether the password doesn't expire bit
is set. The userAccountControl attribute is a 32-bit number, but it's not used
as a traditional number. It's a bit map; that is, if you convert the number
to binary, each binary digit position in the number represents a discrete on/off
value. You can then use the logical AND operator to determine whether a particular
bit is set. In this case, we're looking for the bit that's represented by the
value 65536 (10000 in hexadecimal). For this value, AD uses a named constant:
ADS_UF_DONT _EXPIRE_PASSWD. Listing 1, Does
PwdExpire.vbs, shows a sample script that names a user account and tests to
determine whether the ADS _UF_DONT_EXPIRE_PASSWD bit is set in the user's userAccountControl
bit map. (Web Listing 1 is the JScript
version of the code.)
Subtask 2: Determine the Domain's Maximum Password Age
The next subtask is to retrieve the domain's maximum password age, a value set
in the Default Domain Policy Group Policy Object (GPO Computer Configuration\Windows
Settings\ Security Settings\Account Policies\ Password Policy). The Group Policy
console expresses this value in days; for example, setting the value to 60 means
that passwords expire after 60 days. The corresponding AD attribute is maxPwdAge,
which is stored as an IADsLargeInteger object (a 64-bit value) that contains
the number of 100-nanosecond intervals that a password is valid. An IADsLargeInteger
object provides two properties, HighPart and LowPart, which represent the 64-bit
integer s high-order and low-order 32-bit values, respectively. To convert the
maxPwdAge 64-bit integer to a single number that you can use in a script, use
this formula:
(HighPart * 2 32 ) + LowPart
The absolute value of the result of this conversion is a number we can work with in a script. However, as I mentioned, this value tells you only the number of 100-nanosecond intervals. To get the number of days, we can divide the number of nanoseconds by 10,000,000 (giving the number of seconds), and divide the result by 86,400 (the number of seconds in a day). In other words:
days = (nanoseconds / 10000000) / 86400
If the LowPart property of the IAD-sLargeInteger object contains zero, then
the entire value is zero, and we don't need to perform the conversion. Listing
2, MaxPwdAge.vbs, shows a short script that returns the domain's maximum
password age as a number of days. (Web Listing
2 shows a JScript version of this code.)
Subtask 3: Determine When a User's Password Was Last
Set
The pwdLastSet attribute of an AD account contains a 64-bit integer that corresponds
to the number of 100-nanosecond intervals since January 1, 1601. We can't convert
this to a single numeric value (as we can with the domain's maxPwdAge attribute)
without losing precision in the calculation, because JScript (and VBScript)
don't support 64-bit values containing dates. (The calculation works with the
domain's maxPwdAge attribute because maxPwdAge is an interval rather than a
date.)
Fortunately, AD performs the calculation for us and stores it in the user account's PasswordLastChanged attribute, which is returned as a date value. The Pass-wordLastChanged
attribute won't exist if the password has never been set. In this case, attempting
to read the Pass-wordLastChanged attribute will raise error 8000500D (property
not found). Listing 3, PwdLastSet.vbs,
shows a script that reads a user account's PasswordLast-Changed attribute. Web
Listing 3 shows the same code in JScript. Note that the script uses the
On Error Resume Next statement in case the attribute doesn't exist.