Active Directory Cleanser

Cull unused or deleted accounts using the LDIFDE tool and some scripting trickery.

Bill: I'm trying to clean up stale and unused AD user accounts. For accounts that logged in at least once, I can use the USRSTAT resource kit tool to show the last logon date and delete accounts unused for a certain period of time. For accounts that never logged in, I have no way of knowing if it's a new or old account. Is there a way or tool to get the creation date for an account so I can then determine whether or not this account can be cleaned?
—Zev

Zev: You can get the creation date for each account from Active Directory. Every AD object has a WhenCreated and WhenChanged attribute. You can dump these attributes into a flat file using the LDIFDE utility, or you can dump them into a comma-delimited file using CSVDE (both utilities come with Windows 2000).

Here's the syntax to dump the two attributes for the user objects in an OU called Phoenix in a domain called Company.com to the console for viewing (the entire entry should typed as a single line):

ldifde -d ou=phoenix,dc=company,dc=com -l whencreated, whenchanged -p onelevel -r "(ObjectCategory=user)"
-f con

Get Help from Bill

Got a Windows or Exchange question or need troubleshooting help? Or maybe you want a better explanation than provided in the manuals? Describe your dilemma in an e-mail to Bill at mailto:boswell@101com.com; the best questions get answered in this column.

When you send your questions, please include your full first and last name, location, certifications (if any) with your message. (If you prefer to remain anonymous, specify this in your message but submit the requested information for verification purposes.)

If you wanted to save the dump to a file, change the -f switch from con to a file name.

The last logon timestamp uses this format: YYYYMMDDHHMMSS, with the hour shown in Universal Coordinated Time. A time stamp of 20040115182937.0Z corresponds to Jan 15 2004 18:29:37 UCT.

USRSTAT is slow, and the report you get has to be merged with the LDIFDE dump. So, I put together a script that searches for user objects at each domain controller, then lists the local logon time and the creation time. The user logon timestamp requires conversion from a long integer. I borrowed the conversion code comes from Richard L. Mueller (www.rlmueller.net/Programs). Richard's full script also takes the local time zone from the Registry and converts the time from UCT to local time. Nifty.

'Establish ADO Constants
ADS_CHASE_REFERRALS_NEVER = &00
ADS_CHASE_REFERRALS_SUBORDINATE = &20
ADS_CHASE_REFERRALS_EXTERNAL = &40
ADS_CHASE_REFERRALS_ALWAYS = &60
ADS_SCOPE_BASE = 0
ADS_SCOPE_ONELEVEL = 1
ADS_SCOPE_SUBTREE = 2

'Get Distinguished Name for local domain
Set RootDSE = GetObject("LDAP://RootDSE")
domainDN = RootDSE.Get("DefaultNamingContext")

'Initialize ADO connection
Set connection = CreateObject("ADODB.Connection")
connection.Provider = "ADsDSOObject"
connection.open
Set command = CreateObject("ADODB.Command")
Set command.ActiveConnection = connection
Command.Properties("Page Size") = 1000
Command.Properties("Timeout") = 30
Command.Properties("searchscope") = ADS_SCOPE_SUBTREE
Command.Properties("Chase referrals") =   ADS_CHASE_REFERRALS_NEVER
Command.Properties("Cache Results") = False

'Get list of domain controllers for the domain
Set dcList = GetObject("LDAP://ou=domain controllers," &_   domainDN)

'Walk each domain controller for logons
For Each dc In dcList
WScript.Echo String(40,"=")
WScript.Echo "Logon dates at " & dc.DNSHostName

command.CommandText = "SELECT name,lastlogon,whencreated,whenchanged FROM " &_
  "'LDAP://" & dc.DNSHostName & "/" & domainDN &"' WHERE   objectcategory = 'user'"

Set rs = command.Execute
Do Until rs.EOF
  adoLastLogon = rs.fields("lastlogon")
  On Error Resume Next
  Err.Clear
  Set longDate = adoLastLogon
  If Err.Number <> 0 Then
    Err.Clear
    logonDate = "No Local Logon"
  Else
    longDateHigh = longDate.HighPart
    longDateLow = longDate.LowPart
    If (longDateLow = 0) And (longDateHigh = 0) Then
      logonDate = "No Local Logon"
    Else
      If longDateLow < 0="" then="" longdatehigh="longDateHigh" +="">
      logonDate = #1/1/1601# + (((longDateHigh * (2 ^ 32))
        + longDateLow)/600000000/1440)
    End If
  End If

  WScript.Echo "User Name: " & rs.fields("name")
  WScript.Echo " Last logon: " & logonDate
  WScript.Echo " Object Created: " & rs.fields("WhenCreated")
  WScript.Echo " Object Modified: " & rs.fields("WhenChanged")

  rs.MoveNext
Loop

WScript.Echo vbNL
Next

WScript.Quit()

As you mentioned, Zev, Windows Server 2003 has an additional attribute called LastLogonTimestamp that replicates to every domain controller once you shift to a Windows Server 2003 functional level. You can rewrite this script to search for the contents of LastLogonTimestamp on any domain controller.

Hope this helps.

About the Author

Contributing Editor Bill Boswell, MCSE, is the principal of Bill Boswell Consulting, Inc. He's the author of Inside Windows Server 2003 and Learning Exchange Server 2003 both from Addison Wesley. Bill is also Redmond magazine's "Windows Insider" columnist and a speaker at MCP Magazine's TechMentor Conferences.

comments powered by Disqus

Reader Comments:

Wed, Jan 20, 2010 John T. Ontario, Canada

You will need domain admin rights to read AD info...

Thu, Apr 5, 2007 t denver

doesn't run in windows 2003

Fri, Mar 9, 2007 kurt USA

Script doesn't run.

Fri, Oct 20, 2006 Anonymous Anonymous

Script doesn't run. Poor documentation

Tue, Dec 13, 2005 Britta Chadds Ford, PA

Thanks for sharing this info. I just saved several hours of going through papers and emails to create a list of all the accounts created in 2005 which is required for our SOX audit.

Tue, Oct 11, 2005 Eddy UK

This is a very nice and handy script.
I will need to output the result of this script to a file and I am not good with scripting. Is there anyone who can help?
thanks

Fri, Sep 23, 2005 Anonymous Anonymous

Nice & easy.

Thu, Sep 8, 2005 Pyr0TeK Anonymous

I have a need to search all 7 domain controllers in my environment for all user accounts and their creation date. This means the entire AD, all OU's. Any ideas on an easy way?

Mon, Feb 7, 2005 Neal Mitten Lancaster, Pa.

This is great. Do you have a script that will do the same thing for Computer objects. We are a large company and constantly replacing computers. I'd like to get rid of old extinct computer objects in AD.

Wed, Dec 29, 2004 Walid Beirut

Very Clear scrpit that really help administrators

Tue, Mar 30, 2004 Brian York

I'm new to this and could really use this new script. How can I compile it and run this on my DC?
Thanks, Brian

Tue, Feb 17, 2004 SteevW London

Great utility and especially good that it queries all DC's. Is there a way of amending it to traverse the directory for all OU's though. We have over 500 seperate OU's with user accounts and I would love a script that can search the whole directory for user (or computer accounts) and then perform an action.

Tue, Feb 3, 2004 Anonymous Anonymous

Very good and clear content

Add Your Comment Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Comment:
Please type the letters/numbers you see above

Redmond Tech Watch

Sign up for our newsletter.

I agree to this site's Privacy Policy.