This post will show you how to generate a list of all the users’ Distinguished Name, then filter it, then do something useful with it.

Scenario: saturday morning (after having crashed into bed at 4:00 a.m., btw), Customer calls. A virus hit the Company and one of the most annoying consequences of the outburst, is that every domain user account gets locked due to brute-force login attempts (as per the “Account Lockout Threshold” policy). While they run around cleaning PCs and fixing A/V installations1, I’m asked for a method to quickly unlock the accounts.

I tend to carry out these kind of tasks “the Unix way”, using the available DOS prompt commands and a bit of VBScript.

  • Start off by calling LDIFDE:
    ldifde -r "(objectclass=user)" -l sAMAccountName -m -f users.ldf

    LDIFDE exports/imports Active Directory data to/from properly formatted (LDIF) text files. I use it a lot. Ran as shown above, LDIFDE exports the objects of class “user” into a file named users.ldf . Of the many attributes an LDAP object bears, I tell LDIFDE to output just the “sAMAccountName” one. If I hadn’t specified any attribute, in the resulting file I’d have found duplicate DNs for the same user. That’s because of how the resulting LDIF file is described. Some A/D data is “incrementally” added to existing objects given their DN. I just picked sAMAccountName because every user has one and, also, to keep the file small.

  • Then:
    findstr /I /b dn.*ou=service.users users.ldf > service_users.txt
    findstr /I /b dn.*cn=users users.ldf > normal_users.txt

    findstr is Microsoft’s “poor man version” of grep, supporting a subset of the regular expression everyone has or should’ve come to love. Here I’m using it to extract Distinguished Names from the LDIF (only the ones that lie in a given Organizational Unit), and saving them to the *_users.txt files. They will look like:

    dn: CN=squidauth,OU=Service Users,DC=contoso,DC=com
    dn: CN=exchangebackup,OU=Service Users,DC=contoso,DC=com
    dn: CN=ldap,OU=Service Users,DC=contoso,DC=com
    dn: CN=batchcopy,OU=Service Users,DC=contoso,DC=com
  • Here’s the VBScript function to unlock an account given its DN:
    Sub unlockuser(userDN)
      Set objUser = GetObject ("LDAP://" & userDN)
      objUser.IsAccountLocked = False
    End Sub

    We just need to transform findstr’s output, substituting the leading “dn: ” with “unlockuser” and enclosing in double quotes what follows. At the top of the new, transformed, file, we’ll copy/paste unlockuser subroutine definition. That’ll make our final script.

  • How to carry out the transform? Using this VBS snippet; it processes its Standard Input line by line, and outputs the modifications on Standard Output, just like any Unix file filtering command.
    Set StdIn = WScript.StdIn
    Do While Not StdIn.AtEndOfStream
        line = stdin.readline
        line = right(line,len(line)-4)
        wscript.echo "unlockuser """ & line & """"

    I saved it in a “dnfilter.vbs” file and used it this way:

    type service_users.txt | cscript /nologo dnfilter.vbs > unlock_service_users.vbs

    To obtain something like this:

    unlockuser "CN=squidauth,OU=Service Users,DC=contoso,DC=com"
    unlockuser "CN=exchangebackup,OU=Service Users,DC=contoso,DC=com"
    unlockuser "CN=ldap,OU=Service Users,DC=contoso,DC=com"
    unlockuser "CN=batchcopy,OU=Service Users,DC=contoso,DC=com"

As I said, add the unlockuser function at the top of unlock_service_users.vbs and you’ll have your bulk unlocking script.

  1. A/V usefulness is often questionable. At least three times a year an unfortunate Customer gets infected by a 0-day threat… 🙁