Table of Content > Attributes for Active Directory Users > pwdLastSet
  
    
    Attributes for AD Users : pwdLastSet
    
    The Active Directory attribute lastLogon shows the exact timestamp of the last password change for the regarding account. Here it doesn't matter if the user changed it's password himself or if the password was reset by an administrator.
    
    
      
      pwdLastSet
    
    
      
        | LDAP Name | pwdLastSet | 
      
        | Data type | Integer8 (64 bit signed numeric) | 
      
        | Multivalue (Array) | No | 
      
        | System Flags | 0x10 | 
      
        | Search Flags | 0x0 | 
      
        | In Global Catalog? | No | 
      
        | Attribute ID | 1.2.840.113556.1.4.96 | 
      
        | AD DB attribute name | Pwd-Last-Set | 
      
        | ADSI
          datatype | 10 - LargeInteger | 
      
        | LDAP syntax | 1.2.840.113556.1.4.906 - Microsoft Large Integer | 
      
        | Used in ... | > W2K | 
      
        | Schema Doku | Microsoft
          - MSDN | 
    
    
      If the pwdLastSet value is null, thaht means that the user has to change his password at the next logon:
      
	  
      
      The lastLogon value is a Microsoft Large Integer, these are signed numeric values of 8 Byte (64 bit) - those  are often called Integer8 values for this reason:
    
    
    
      
        | Minimum value: -9223372036854775808 (-2^63)  or
 hex 0x8000000000000000
 | Maximum value:9223372036854775807 (2^63 - 1) or hex 0x7FFFFFFFFFFFFFFF
 | 
    
     
      There is another article in the SelfADSI Tutorial about the Microsoft Integer8 values which represent date and time or time intervals.
      
      The value stored in the lastLogon attribute represents the date and time of the account logon, expressed in 100-nanosecond steps since 12:00 AM, January 1, 1601.
      
      By the way, this is a specification which is also used in the Microsoft FileTime structure. Additionally, it is important to know that an Active Directory domain controller stores the date and time  always  in the UTC time format  (Universal Coordinated Time)  - this is (almost) the former Greenwich Meantime (GMT). So if your systems are for example in  Pacific Standard Time (PST, which is GMT-8), so you have to recalculate the Integer8 attribute values if you want to know the date and times in your local time.
      
      If you want to read the pwdLastSet  attribute of a certain user, you first have to handle the returned Large Integer which is divided into two 32bit parts: The HighPart and the LowPart. These parts are accessible in the ADSI interface for this datatype. But: You always have to use a leading 'Set' statement when reading a Large Integer/Integer8 attribute in an ADSI script. Otherwise you can't access the ADSI interface properties 'Highpart' and 'Lowpart'.
      
      
    Convert a pwdLastSet value to a readable date and time value
    So here is the script code to convert an Integer8 into a date and time, including the local time zone adjustment (we take the time abbreviation from UTC from the registry):
    'you have to use a distinguished name of an object from you own environment here!
Set obj = GetObject("LDAP://cn=Administrator,cn=Users,dc=cerrotorre,dc=de")
set llValue = obj.Get("pwdLastSet")
'remember: use 'SET' to create an object!
WScript.Echo LargeIntegerToDate(llValue)
Function LargeIntegerToDate(value)
'takes Microsoft LargeInteger value (Integer8) and returns according the date and time
    'first determine the local time from the timezone bias in the registry
    Set sho = CreateObject("Wscript.Shell")
    timeShiftValue = sho.RegRead("HKLM\System\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias")
    If IsArray(timeShiftValue) Then
        timeShift = 0
        For i = 0 To UBound(timeShiftValue)
            timeShift = timeShift + (timeShiftValue(i) * 256^i)
        Next
    Else
        timeShift = timeShiftValue
    End If
    'get the large integer into two long values (high part and low part)
    i8High = value.HighPart
    i8Low = value.LowPart
    If (i8Low < 0) Then
           i8High = i8High + 1 
    End If
    'calculate the date and time: 100-nanosecond-steps since 12:00 AM, 1/1/1601
    If (i8High = 0) And (i8Low = 0) Then 
        LargeIntegerToDate = #1/1/1601#
    Else 
        LargeIntegerToDate = #1/1/1601# + (((i8High * 2^32) + i8Low)/600000000 - timeShift)/1440 
    End If
End Function
	
    Search for all users whose password wasn't changed for six months
    Here comes another script where you need to convert a date and time value to the according Integer8 - we want to find all users whose password was not changed for the last six months. To build a correct LDAP filter, we need the Large Integer value for the date and time six months ago.... If you don't know exactly how the script searches for the objects - there is a detailed article here in the SelfADSI Tutorial which explains the LDAP search with ADO techniques. 
    'you have to use  names and credentials of your own environment here!
serverName = "nadrash.cerrotorre.de"
baseStr = "dc=cerrotorre,dc=de"
userName = "philipp@cerrotorre.de"
userPass = "secret"
'get the date and time of one week ago, DateAdd is a standard vbscript function, we could also calculate last month, year etc.
sixMonthsBefore = DateAdd("m", -6, Now)
'get the Integer8 value for the regarding date an time...
sixMonthsBeforeValue = DateToLargeIntegerString(sixMonthsBefore)
'show the both values: 
WScript.Echo "Timestamp of six months ago: " & sixMonthsBefore & "  => " & sixMonthsBeforeValue
'now get a filter of all user accounts whose password wasn't changed in the last six months
filterStr = "(&(objectclass=user)(pwdLastSet<=" & sixMonthsBeforeValue & "))"
'do the search! 
Set ado = CreateObject("ADODB.Connection")         'create new ADO connection
ado.Provider = "ADSDSOObject"                      'use the ADSI interface
ado.Properties("User ID") = userName               'pass credentials - omit these 2 lines to use your current credentials!
ado.Properties("Password") = userPass
ado.Properties("Encrypt Password") = True
ado.Open "ADS-Search"                              'use any name for the connection 
Set adoCmd = CreateObject("ADODB.Command")         'create new ADO command 
adoCmd.ActiveConnection = ado                      'assignment to an existing ADO connection 
adoCmd.Properties("Page Size") = 1000              'set the Paged Results value to 1000 (AD standard)
adoCmd.Properties("Cache Results") = True
adoCmd.CommandText =  "<LDAP://"& serverName & "/" & baseStr &">;" & filterStr & ";distinguishedName;subtree" 
Set objectList = adoCmd.Execute                    'perform search 
While Not objectList.EOF
    WScript.Echo objectList.Fields("distinguishedName") 'output: distinguishedName of these objects 
    objectList.MoveNext                            'jump to the next search result entry 
Wend
Function DateToLargeIntegerString(value)
'takes a date/time and returns the according Microsoft LargeInteger value (Intger8)
    'first determine the local time from the timezone bias in the registry
    Set sho = CreateObject("Wscript.Shell")
    timeShiftValue = sho.RegRead("HKLM\System\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias")
    If IsArray(timeShiftValue) Then
        timeShift = 0
        For i = 0 To UBound(timeShiftValue)
            timeShift = timeShift + (timeShiftValue(i) * 256^i)
        Next
    Else
        timeShift = timeShiftValue
    End If
    'adjust the local time to UTC
    value = DateAdd("n", timeShift, value) 
    'how much seconds since 1601 are in the time?
    secs = DateDiff("s", #1/1/1601#, value) 
    'convert it to 100-nanosecond steps
    DateToLargeIntegerString = CStr(secs) & "0000000" 
End Function