Printout Header
RSS Feed

How to search and find locked user accounts in Active Directory


For this search, we use the Active Directory attribute lockoutTime, which indicates the time when a user was locked out. This is a value expressing a time interval with the Microsoft Integer8 format. But the search for accounts whose lockoutTime value is greater than zero does not lead directly to the destination. If the account is unlocked automatically by the system after a while, the lockoutTime value is not immediately set to zero. Therefore we have to calculate the real lockout situation for each account by using it's lockout time and the domain account lockout policy.

 

Caution: Blocked users should not be confused with disabled users. You should also read the SelfADSI article 'Unlock: Unlock Active Directory User Accounts'.

For the general explanation of LDAP searches read the SelfADSI chapter 'Searching for LDAP Directory Objects'.


Finding all locked user accounts in the own domain


This script finds all locked users of the domain in which the current user is a member. If the lock is automatically removed from the system again, then the remaining lock time per user is displayed. This implies reading of the lockout period from the attribute lockoutDuration of the domain object at the beginning of the script . At this point you need a special conversion function of the value to an appropriate time period (in minutes) - later when calculating the lockout timing is a similar transformation necessary:

ldapFilter = "(&(sAMAccountType=805306368)(lockoutTime>=1))" Set rootDSE = GetObject("LDAP://rootDSE") domainDN = rootDSE.Get("defaultNamingContext") Set objDomain = GetObject("LDAP://" & domainDN) Set interval = objDomain.lockoutDuration domainLockout = LargeIntegerToMinutes(interval) If (domainLockout > 0) then WScript.Echo "Domain lockout duration is: " & domainLockout & " minutes" Else WScript.Echo "Domain lockout duration is permanent until the Admin unlocks the accounts" End If WScript.Echo "Locked accounts:" WScript.Echo Set ado = CreateObject("ADODB.Connection") ado.Provider = "ADSDSOObject" ado.Open "ADSearch" Set objectList = ado.Execute("<LDAP://" & domainDN & ">;" & ldapFilter & ";distinguishedName,lockoutTime;subtree") While Not objectList.EOF If (domainLockout > 0) then dateTime = objectList.Fields("lockoutTime") lockOutTime = LargeIntegerToDate(dateTime) userLockoutLeft = domainLockout - DateDiff("n", lockOutTime, Now) If (userLockoutLeft > 0) Then WScript.Echo objectList.Fields("distinguishedName") WScript.Echo "User is locked out (for " & userLockoutLeft & " more minutes)" WScript.Echo End If Else WScript.Echo objectList.Fields("distinguishedName") End If objectList.MoveNext Wend '_______________________________________________________________________________________ [ LargeIntegerToDate ] ' ' Function LargeIntegerToDate(value) 'takes Microsoft LargeInteger value (Integer8) and returns according the date and time 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 i8High = value.HighPart i8Low = value.LowPart If (i8Low < 0) Then i8High = i8High + 1 End If 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 '_______________________________________________________________________________________ [ LargeIntegerToMinutes ] ' ' Function LargeIntegerToMinutes(value) 'takes Microsoft LargeInteger value (Integer8) and returns according the time interval in minutes (-1 for 'Never') If (value.HighPart = -2147483648) And (value.LowPart = 0) then LargeIntegerToMinutes = -1 Else i8High = value.HighPart i8Low = value.LowPart If (i8Low < 0) Then i8High = i8High + 1 End If LargeIntegerToMinutes = -(((i8High * 2^32) + i8Low)/600000000) End If End Function

Finding all locked user accounts - Short version 1


The shorter version, which unfortunately includes an additional bind operation for each account to be checked - there is no display of the remaining lockout duration and therefore, be do not need the conversion functions for the Integer8 values here. However, we must connect here to each user with the ADSI WinNT provider to check the API Property IsAccountLocked. Therefor we need the NetBIOS name of the domain - it is detected by the use of the ADSystemInfo COM object at the beginning of the script:

Const ADS_UF_LOCKOUT = 16 ldapFilter = "(&(sAMAccountType=805306368)(lockoutTime>=1))" Set rootDSE = GetObject("LDAP://rootDSE") domainDN = rootDSE.Get("defaultNamingContext") Set aio = CreateObject("ADSystemInfo") domainNETBios = aio.DomainShortName WScript.Echo "Locked accounts:" WScript.Echo Set ado = CreateObject("ADODB.Connection") ado.Provider = "ADSDSOObject" ado.Open "ADSearch" Set objectList = ado.Execute("<LDAP://" & domainDN & ">;" & ldapFilter & ";distinguishedName,samAccountName;subtree") While Not objectList.EOF userLogonName = domainNETBios & "/" & objectList.Fields("samAccountName") Set user = GetObject("WinNT://" & userLogonName) 'Syntax=>   WinNT://domain/user  if (user.IsAccountLocked) then WScript.Echo objectList.Fields("distinguishedName") End if objectList.MoveNext Wend

Finding all locked user accounts - Short version 2 (>Windows 2003)


Another variant of the short script version, which works only in environments with Windows 2003: You can read here the attribute msDS-User-Account-Control-Computed (it is an constructed attribute and can not be used directly in the search) and check if the bit field is set in the flag UF_LOCKOUT (16).

Const ADS_UF_LOCKOUT = 16 ldapFilter = "(&(sAMAccountType=805306368)(lockoutTime>=1))" Set rootDSE = GetObject("LDAP://rootDSE") domainDN = rootDSE.Get("defaultNamingContext") WScript.Echo "Locked accounts:" WScript.Echo Set ado = CreateObject("ADODB.Connection") ado.Provider = "ADSDSOObject" ado.Open "ADSearch" Set objectList = ado.Execute("<LDAP://" & domainDN & ">;" & ldapFilter & ";ADSPath,distinguishedName;subtree") While Not objectList.EOF Set user = GetObject(objectList.Fields("ADSPath")) user.GetInfoEx Array("msDS-User-Account-Control-Computed"), 0 flags = user.Get("msDS-User-Account-Control-Computed") if (flags and ADS_UF_LOCKOUT) then WScript.Echo objectList.Fields("distinguishedName") End if objectList.MoveNext Wend