For audit purposes I needed to list all active directory users and prove that ex-employees were either deleted of disabled. There are a couple of techniques to produce the required output. I think the easiest was to make use of the csvde.exe.
Arguements for csvde.exe
CSV Directory Exchange
General Parameters
==================
-i Turn on Import Mode (The default is Export)
-f filename Input or Output filename
-s servername The server to bind to (Default to DC of computer's domain)
-v Turn on Verbose Mode
-c FromDN ToDN Replace occurences of FromDN to ToDN
-j path Log File Location
-t port Port Number (default = 389)
-u Use Unicode format
-? Help
Export Specific
===============
-d RootDN The root of the LDAP search (Default to Naming Context)
-r Filter LDAP search filter (Default to "(objectClass=*)")
-p SearchScope Search Scope (Base/OneLevel/Subtree)
-l list List of attributes (comma separated) to look for in an
LDAP search
-o list List of attributes (comma separated) to omit from input.
-g Disable Paged Search.
-m Enable the SAM logic on export.
-n Do not export binary values
Import
======
-k The import will go on ignoring 'Constraint Violation' and
'Object Already Exists' errors
Credentials Establishment
=========================
Note that if no credentials is specified, CSVDE will bind as the currently
logged on user, using SSPI.
-a UserDN [Password | *] Simple authentication
-b UserName Domain [Password | *] SSPI bind method
Example: Simple import of current domain
csvde -i -f INPUT.CSV
Example: Simple export of current domain
csvde -f OUTPUT.CSV
Example: Export of specific domain with credentials
csvde -m -f OUTPUT.CSV
-b USERNAME DOMAINNAME *
-s SERVERNAME
-d "cn=users,DC=DOMAINNAME,DC=Microsoft,DC=Com"
-r "(objectClass=user)"
No log files were written. In order to generate a log file, please
specify the log file path via the -j option.So to display whether a user is disabled or not we need to extract the “userAccountControl” property.
C:\>Csvde -f UserList.csv -d "dc=domain,dc=com" -r "(ObjectCategory=Person)" -l "displayName, userAccountControl, uSNChanged,whenChanged,whenCreated" Connecting to "(null)" Logging in as current user using SSPI Exporting directory to file UserList.csv Searching for entries... Writing out entries ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ............................................ Export Completed. Post-processing in progress... 764 entries exported The command has completed successfully
You can translate the userAccountControl number to the account status using the table below.
| UserAccountControl_Num | Account_Status |
| 512 | Account: Enabled |
| 514 | Account: ACCOUNTDISABLE |
| 528 | Account: Enabled – LOCKOUT |
| 530 | Account: ACCOUNTDISABLE – LOCKOUT |
| 544 | Account: Enabled – PASSWD_NOTREQD |
| 546 | Account: ACCOUNTDISABLE – PASSWD_NOTREQD |
| 560 | Account: Enabled – PASSWD_NOTREQD – LOCKOUT |
| 640 | Account: Enabled – ENCRYPTED_TEXT_PWD_ALLOWED |
| 2048 | Account: INTERDOMAIN_TRUST_ACCOUNT |
| 2080 | Account: INTERDOMAIN_TRUST_ACCOUNT – PASSWD_NOTREQD |
| 4096 | Account: WORKSTATION_TRUST_ACCOUNT |
| 8192 | Account: SERVER_TRUST_ACCOUNT |
| 66048 | Account: Enabled – DONT_EXPIRE_PASSWORD |
| 66050 | Account: ACCOUNTDISABLE – DONT_EXPIRE_PASSWORD |
| 66064 | Account: Enabled – DONT_EXPIRE_PASSWORD – LOCKOUT |
| 66066 | Account: ACCOUNTDISABLE – DONT_EXPIRE_PASSWORD – LOCKOUT |
| 66080 | Account: Enabled – DONT_EXPIRE_PASSWORD – PASSWD_NOTREQD |
| 66082 | Account: ACCOUNTDISABLE – DONT_EXPIRE_PASSWORD – PASSWD_NOTREQD |
| 66176 | Account: Enabled – DONT_EXPIRE_PASSWORD – ENCRYPTED_TEXT_PWD_ALLOWED |
| 131584 | Account: Enabled – MNS_LOGON_ACCOUNT |
| 131586 | Account: ACCOUNTDISABLE – MNS_LOGON_ACCOUNT |
| 131600 | Account: Enabled – MNS_LOGON_ACCOUNT – LOCKOUT |
| 197120 | Account: Enabled – MNS_LOGON_ACCOUNT – DONT_EXPIRE_PASSWORD |
| 532480 | Account: SERVER_TRUST_ACCOUNT – TRUSTED_FOR_DELEGATION (Domain Controller) |
| 1049088 | Account: Enabled – NOT_DELEGATED |
| 1049090 | Account: ACCOUNTDISABLE – NOT_DELEGATED |
| 2097664 | Account: Enabled – USE_DES_KEY_ONLY |
| 2687488 | Account: Enabled – DONT_EXPIRE_PASSWORD – TRUSTED_FOR_DELEGATION - USE_DES_KEY_ONLY |
| 4194816 | Account: Enabled – DONT_REQ_PREAUTH |
Another technique is to use a VBScript to query active directory for the accounts. Below is one that I’ve used frequently.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | ' Assign Variables Dim DomainString, fso, DomianObj, UserDel, C, Code Dim ChkLast, UserObj, Flag, s ' Create Object for File System Access set fso = CreateObject ("Scripting.FileSystemObject") ' Set Domain Name DomainString = "domain.com" 'Modify this line or this script wont work ' Open ADSI and connect to Domain user data Set DomainObj = GetObject("WinNT://" & DomainString) DomainObj.Filter = Array("user") ' Insure that file does not already exist IF fso.FileExists ("show_user_account_info.txt") THEN set USERDel = fso.GetFile ("show_user_account_info.txt") USERDel.Delete End IF ' Create File in temp directory set C = fso.CreateTextFile ("show_user_account_info.txt", True) on error resume next ' Add collumn headings to new file C.WriteLine "Name" & vbTab & "FullName" & vbTab & "Description" & _ vbTab & "LastLogin" & vbTab & "PasswordExpirationDate" & _ vbTab & "IsAccountLocked" & vbTab & "Class" & vbTab & "UserFlags" ' List all users For Each UserObj In DomainObj ChkLast = UserObj.LastLogin ' ChkLast is used to determine the last logon time. IF UserObj.UserFlags <> 661103 Then s = UserObj.name s = s & vbTab s = s & UserObj.FullName s = s & vbTab s = s & UserObj.Description s = s & vbTab s = s & UserObj.LastLogin s = s & vbTab s = s & UserObj.PasswordExpirationDate s = s & vbTab s = s & UserObj.IsAccountLocked s = s & vbTab s = s & UserObj.Class s = s & vbTab ' Convert Flags to more undersandable terms. Flag = UserObj.UserFlags IF Flag = 66113 THEN Code = "Password never expires and user cannot change password" ELSEIF Flag = 8389123 THEN Code = "Disabled" ELSEIF Flag = 577 THEN Code = "Account is not setup right, login not possible" ELSEIF Flag = 515 THEN Code = "Account is disabled and user must change password at next logon" ELSEIF Flag = 513 THEN Code = "User is active" ELSEIF Flag = 66049 THEN Code = "Password Does Not expire" ELSE Code = "Unknown Code = " & UserObj.UserFlags END IF s = s & CHR (34) & Code & CHR (34) C.WriteLine s ' Write new line to file. End IF Next ' Repeat for all users Wscript.Quit |