Solved

Enumerating User Accounts in AD for Password Age

Posted on 2009-07-10
9
2,948 Views
Last Modified: 2012-06-27
My scripting skills are weak so I need some help on this one. I have a script that would search the Users container in AD and then create an output text file of the password age for each user. Last Name, First Name and password age. We have since divided our AD by Practice and within each Practice container there are sub containers which have user accounts. I am trying to adjust my existing script to enumerate all users from the root of the domain down but am having no success. I have found reference to 'subtree' but am unfamiliar  with its use. The script that I used in the past is pasted below.

Thanks
Option Explicit
 
Dim objDSE, strDefaultDN, strDN, objContainer, objChild, dtmValue, intAge, strFileName
dim objFSO, objFile, objTextFile, strReportName
Const ForAppending = 8
 
'**** OPEN OBJECTS
Set objDSE = GetObject("LDAP://rootDSE") 'open Active Directory
strDefaultDN = "OU=Users,OU=Insurance,OU=SABC," & objDSE.Get("defaultNamingContext") 'full path to users folder
'strDefaultDN = "OU=Users," & objDSE.Get("defaultNamingContext") 'full path to users folder
 
'**** ARE YOU SURE?
strDN = 	InputBox("Enter the distinguished name of a container" & _
	vbCrLf & "(e.g. " & strDefaultDN & ")", , strDefaultDN) 'allow user to change folder
 
If strDN = "" Then WScript.Quit(1) 'user clicked Cancel = exit script
 
'******** NAME REPORT
strReportName = InputBox("Enter Report File Name:", , "PasswordReport") 'allow user to name report
 
'***** CREATE AND OPEN TEXT FILE
Set objContainer = GetObject("LDAP://" & strDN) 'open users folder
objContainer.Filter = Array("user") 'only read users
strFileName = "C:\Documents and Settings\maunw\Desktop\Password Age Checker\Report\" & strReportName & ".txt"
Set objFSO = CreateObject("Scripting.FileSystemObject") 'create file system object
Set objFile = objFSO.CreateTextFile(strFileName) 'create report file
objFile.close 'close report file
Set objTextFile = objFSO.OpenTextFile(strFileName, ForAppending, True) 'open report file to edit
 
'**** PROCESS
on error resume next 'if there is a bad record, move to the next
For Each objChild In objContainer 'process each user
	dtmValue = objChild.PasswordLastChanged 'date password was changed
	intAge = Int(Now - dtmValue) 'Today - password last changed = age
	objTextFile.WriteLine(objChild.FullName & vbtab & intAge) 'write line to file
Next
objTextFile.Close 'close file
WScript.Echo "END" 'notify that processing is done

Open in new window

0
Comment
Question by:wtm
  • 5
  • 3
9 Comments
 
LVL 31

Expert Comment

by:Henrik Johansson
ID: 24828667
Open a recordset with objRS.Open as showed in the snippet below and loop through the recordset.
strQuery has three parts separated by ; (from, criteria and fields)

I see I missed to save output to file, but it can either be done by using scripting.filesystemobject as used in question or by executing the script with the following command

cscript //NoLogo script.vbs > filename.txt
Option Explicit
On Error Resume Next
 
Dim objDSE,strDomain,strDN,strFields,strFrom,strWhere,strQuery,objRS,objConn,fld,strSeparator,strLine,objUser
 
Set objDSE=GetObject("LDAP://rootDSE") 
strDomain=objDSE.Get("defaultNamingContext")
 
strDN=InputBox("Enter OU in current domain")
If strDN<>"" And Right(strDN,1)<>"," Then strDN=strDN & ","
strFields="samAccountName,sn,givenName,pwdLastSet,adsPath"
strFrom="<LDAP://" & strDN & objDSE.Get("defaultNamingContext") &">"
strWhere="(&(objectClass=user)(objectClass=Person)(samAccountName=*)(userprincipalname=*))"
strQuery= strFrom & ";" & strWhere & ";" & strFields
 
Set objRS=CreateObject("ADODB.RecordSet")
Set objConn=CreateObject("ADODB.Connection")
Call objConn.Open("Provider=ADSDSOObject")
Call objRS.Open(strQuery,objConn)
 
strLine=""
strSeparator=vbTab
For Each fld in objRS.Fields
	Select Case fld.Name
		Case "sn" strLine=strLine & "Last name" & strSeparator
		Case "givenName" strLine=strLine & "First name" & strSeparator
		Case "samAccountName" strLine=strLine & "Username" & strSeparator
		Case "pwdLastSet" strLine=strLine & "Password age" & strSeparator
	End Select
Next
wscript.echo strLine
 
Do While Not objRS.EOF
	strLine=""
	Set objUser=GetObject(objRS("adsPath"))
	For Each fld in objRS.Fields
		If fld.Name="pwdLastSet" Then
			If Not IsNull(objUser.PasswordLastChanged) Then 
				strLine=strLine & CStr(DateDiff("h",objUser.PasswordLastChanged,Now))
			End IF
			strLine=strLine & strSeparator
		ElseIf fld.Name<>"adsPath" Then
			If Not isNull(fld) Then strLine=strLine & CStr(fld) 
			strLine=strLIne & strSeparator
		End IF
	Next
	Set objUser=Nothing
	wscript.echo strLine
	objRS.moveNext
Loop
objRS.Close: Set objRS=Nothing
objConn.Close: Set objConn=Nothing

Open in new window

0
 
LVL 70

Expert Comment

by:Chris Dent
ID: 24829863
wtm,

If you have no existing experience with VbScript you might consider PowerShell for these kind of tasks. It needs these two installed:

http://www.microsoft.com/windowsserver2003/technologies/management/powershell/default.mspx
http://www.quest.com/activeroles-server/arms.aspx

But once you have those open up the version from the Quest folder in the start menu and all you need to do to generate this report is this:

Get-QADUser -IncludedProperties PwdLastSet | `
  Select-Object FirstName, LastName, sAMAccountName, PwdLastSet

If you want to pop that into a file it's:

Get-QADUser -IncludedProperties PwdLastSet | `
  Select-Object FirstName, LastName, sAMAccountName, PwdLastSet | `
  Export-CSV "SomeFile.csv"

The ` above just lets it carry the command onto the next line. Easier to read. It could just be written on a single line.

It can also sort, or easily include any other attributes. To see what could be included run:

Get-QADUser "You" | Format-List *

Sorting would be:

Get-QADUser -IncludedProperties PwdLastSet | `
  Select-Object FirstName, LastName, sAMAccountName, PwdLastSet | `
  Sort-Object PwdLastSet | `
  Export-CSV "SomeFile.csv"

Those tools can be installed wherever you like, it doesn't have to be on your Domain Controller or anywhere silly like that.

Chris
0
 

Author Comment

by:wtm
ID: 24841715
henjoh09:

Thank you for your help. Hoping you can point me in the right direction. I don't need the pop up for each user account info. I just need the information dumped into a text file. I am not able to follow what you did or how you did it. Why the case statement?
0
Are your AD admin tools letting you down?

Managing Active Directory can get complicated.  Often, the native tools for managing AD are just not up to the task.  The largest Active Directory installations in the world have relied on one tool to manage their day-to-day administration tasks: Hyena. Start your trial today.

 

Author Comment

by:wtm
ID: 24845183
What I am looking for is just the code to allow the present script to enumerate through my AD.

Thanks
0
 
LVL 31

Accepted Solution

by:
Henrik Johansson earned 300 total points
ID: 24847558
Looking on the local timestamp of my previous post, and realize I nead to learn myself to avoid coding at 3AM and be better in commenting... :)

The 'case' section is giving a more user friendly labels ('last name' instead of field name sn) when looping through the field names.

The following part executes the query.

Set objRS=CreateObject("ADODB.RecordSet")
Set objConn=CreateObject("ADODB.Connection")
Call objConn.Open("Provider=ADSDSOObject")
Call objRS.Open(strQuery,objConn)

The data is in the objRS recordset, and is accessed by looping through the recordset with moveNext

Do While Not objRS.EOF
  ... handle current record ...
  objRS.moveNext
Loop

I see that I used Datediff("h",...) instead of DateDiff("d",...) in previous sample making the list to be the difference of hours instead of days

You can as said use 'cscript //NoLogo scriptname.vbs > filename.txt' for letting previous sample write the output to file instead of getting the popups for each line. Rewrote the sample to also include the filesystemobject handling from original question, so script is saving the textfile.

Option Explicit
'On Error Resume Next
 
Dim objDSE,strDomain,strDN,strFields,strFrom,strWhere,strQuery,objRS,objConn,fld,strSeparator,strLine,objUser
 
Set objDSE=GetObject("LDAP://rootDSE") 
strDomain=objDSE.Get("defaultNamingContext")
 
strDN=InputBox("Enter OU in current domain")
If strDN<>"" And Right(strDN,1)<>"," Then strDN=strDN & ","
strFields="samAccountName,sn,givenName,pwdLastSet,adsPath"
strFrom="<LDAP://" & strDN & strDomain &">"
strWhere="(&(objectClass=user)(objectClass=Person)(samAccountName=*)(userprincipalname=*))"
strQuery= strFrom & ";" & strWhere & ";" & strFields
 
dim objFSO, objFile, objTextFile, strReportName, strFileName
Const ForAppending = 8
 
' Open connection and execute query 
Set objRS=CreateObject("ADODB.RecordSet")
Set objConn=CreateObject("ADODB.Connection")
Call objConn.Open("Provider=ADSDSOObject")
Call objRS.Open(strQuery,objConn)
 
strLine=""
strSeparator=vbTab
 
 
strReportName = InputBox("Enter Report File Name:", , "PasswordReport_" & Replace(Replace(strDN,"=","-"),",","_")) 'allow user to name report
strFileName = "C:\temp\" & strReportName & ".txt"
Set objFSO = CreateObject("Scripting.FileSystemObject") 'create file system object
Set objFile = objFSO.CreateTextFile(strFileName) 'create report file
objFile.close: Set objFile=Nothing 'close report file
Set objTextFile = objFSO.OpenTextFile(strFileName, ForAppending, True) 'open report file for append
 
 
' Column labels
For Each fld in objRS.Fields
	Select Case fld.Name
		Case "sn" strLine=strLine & "Last name" & strSeparator
		Case "givenName" strLine=strLine & "First name" & strSeparator
		Case "samAccountName" strLine=strLine & "Username" & strSeparator
		Case "pwdLastSet" strLine=strLine & "Password age" & strSeparator
	End Select
Next
'wscript.echo strLine
objTextFile.WriteLIne strLIne
 
' Loop through data 
Do While Not objRS.EOF
	strLine=""
	Set objUser=GetObject(objRS("adsPath"))
	For Each fld in objRS.Fields
		If fld.Name="pwdLastSet" Then
			If Not IsNull(objUser.PasswordLastChanged) Then 
				strLine=strLine & CStr(DateDiff("d",objUser.PasswordLastChanged,Now))
			End IF
			strLine=strLine & strSeparator
 
		ElseIf fld.Name<>"adsPath" Then ' exclude DN
			If Not isNull(fld) Then strLine=strLine & CStr(fld) 
			strLine=strLIne & strSeparator
		End IF
	Next
	Set objUser=Nothing
'	wscript.echo strLine
	objTextFile.WriteLIne strLIne
 
	objRS.moveNext
Loop
 
objRS.Close: Set objRS=Nothing
objConn.Close: Set objConn=Nothing
objTextFile.Close: Set objTextFile=Nothing
Set objFSO=Nothing

Open in new window

0
 

Author Closing Comment

by:wtm
ID: 31602250
Thank you henjoh09 for the redue. Works as expected.
0
 

Author Comment

by:wtm
ID: 24848177
henjoh09,

One last thing. After testing, the scrip works great with the exception of enumerating through one of my OU's. If I give the entire path to the OU i.e OU=Commercial A and A,OU=SFACTS, it works fine. But if I only give the root OU i.e. OU=SFACTS, I get a script failure on line 55 char 4 source (null). Under the SFACTS OU there are several sub OU's with OU's under that. Under my SABC OU I have only one sub layer of OU's. Enumerating through the SABC OU works Fine. Any Ideas?

Thanks Again
0
 
LVL 31

Expert Comment

by:Henrik Johansson
ID: 24848259
I commented out the 'On Error Resume Next' on line 2 for debugging and forgot to change it back when posting.
0
 

Author Comment

by:wtm
ID: 24848613
That did it. Thanks for all your help.
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Resolve DNS query failed errors for Exchange
With User Account Control (UAC) enabled in Windows 7, one needs to open an elevated Command Prompt in order to run scripts under administrative privileges. Although the elevated Command Prompt accomplishes the task, the question How to run as script…
This tutorial will walk an individual through the process of transferring the five major, necessary Active Directory Roles, commonly referred to as the FSMO roles from a Windows Server 2008 domain controller to a Windows Server 2012 domain controlle…
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …

808 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question