Table of Content > Extended Handling of AD Objects > LDAP Search Factory > How to find locked user accounts in Active Directory
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