Boswell's Q&A
Active Directory Cleanser
Cull unused or deleted accounts using the LDIFDE tool and some scripting trickery.
- By Bill Boswell
- 02/01/2004
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:[email protected];
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.