Two VBScript program to output all users in the domain with the date and time each last logged onto the domain. These program can be useful to identify old unused accounts that can be disabled and eventually deleted. Also a program to document the last logon dates for all users specified in a text file.

Because the lastLogon attribute is not replicated in Active Directory, a different value can be stored in the copy of Active Directory on each Domain Controller. The programs first use ADO to search Active Directory for all Domain Controllers. The AdsPath of each is saved in an array. Next, ADO is used to search the copy of Active Directory on each Domain Controller for all users and retrieve the lastLogon attribute. The latest date found for each user is saved in a dictionary object. Once all Domain Controllers have been queried, the program outputs each user's Distinguished Name and the latest lastLogon date. The Distinguished Name and lastLogon date for each user is output on a separate line delimited by a semicolon. If the output is redirected to a text file, it can be easily read by a spreadsheet program like Microsoft Excel.

The lastLogon attribute is stored in Active Directory as Integer8 (8 bytes). This means it is a 64-bit number, which cannot be handled directly by VBScript. Instead, the LDAP IADsLargeInteger interface provides HighPart and LowPart methods that break the number into two 32-bit components. The resulting value represents the number of 100 nanosecond intervals since 12:00 AM January 1, 1601. The date represented by this number is in Coordinated Universal Time (UTC). It must be adjusted by the time zone bias in the local machine registry to convert to local time.

The techniques in this program can be used to handle any attribute that is not replicated. Also, the conversion formulas used apply to all Integer8 attributes that represent dates, such as "pwdLastSet", "accountExpires", "badPasswordTime", and "lockoutTime".

The program should be run at a command prompt with the cscript host. The output can be redirected to a text file. For example, you can run the program with the following command:

cscript //nologo LastLogon.vbs > output.txt

LastLogon.txt <<-- Click here to view or download the program

If your domain is at Windows Server 2003 functional level, there is a new attribute called lastLogonTimeStamp you can use. Like lastLogon, this attribute is Integer8 and represents the time when the user last logged onto the domain. Unlike lastLogon, this new attribute is replicated. However, it is only updated when the user logs on if the old value is more than 14 days in the past. That means the value can only be trusted if it is more than 14 days in the past, which is fine for finding old unused accounts. This behavior reduces the synchronization load while still giving administrators the information they need.

Actually, the 14 day value can be modified by assigning a new value to the new msDS-LogonTimeSyncInterval attribute (in days). When a user logs on, if the current value of lastLogonTimeStamp is older than the current time less msDS-LogonTimeSyncInterval, then the value of lastLogonTimeStamp is updated (and this updated value replicates).

When the domain is first raised to Windows Server 2003 functional level, the value of lastLogonTimeStamp is initially updated after 14 days minus a random percentage of 5 days. This spreads out the initial replication of lastLogonTimeStamp for all users over a 5 day period.

A VBScript program to retrieve the value of lastLogonTimeStamp for all users only needs to query one Domain Controller. The example program is linked below:

LastLogonTimeStamp.txt <<-- Click here to view or download the program

Finally, a program similar to the first one linked above is provided, but instead of documenting all users in the domain, this program only documents the users listed in a text file. The name and path of the file are hardcoded in the program. The file should have one user name per line. The names should be the "pre-Windows 2000 logon" names of the users (the value of the sAMAccountName attribute).

LimitedLastLogon.txt <<-- Click here to view or download the program

A PowerShell script has also been developed to query all Domain Controllers in the domain for the largest (latest) value of the lastLogon attribute for each user in the domain. This program uses the DirectoryServices.DirectorySearcher object. The last logon dates for each user are converted into local time. The times are adjusted for daylight savings time, so they may differ from the values reported by the LastLogon.vbs program by one hour. This program works in PowerShell V1 and V2.

PSLastLogon.txt <<-- Click here to view or download the program

A similar program follows using the Get-ADDomainController and Get-ADUser cmdlets in PowerShell V2. This program will only work if all Domain Controllers have the Active Directory Web Services running. This requires Windows Server 2008 R2 on all Domain Controllers in the domain. This program is also considerably slower.

PSLastLogon2.txt <<-- Click here to view or download the program

Finally, a PowerShell script is linked below that is very similar to the program LimitedLastLogon.vbs linked above, except it retrieves the lastLogonTimeStamp attribute for all users specified in a text file. Again, the "pre-Windows 2000 logon" names are read from the text file. Because the program retrieves lastLogonTimeStamp, only one query is required. This program works in PowerShell V1 and V2. If you modify this program to retrieve the lastLogon attribute instead, you will need to add the code to enumerate all Domain Controllers, repeat the query on each, and track the largest values for each user in a hash table. This code can be copied from PSLastLogon.ps1 linked above.

PSLimitedLastLogon.txt <<-- Click here to view or download the program