Account Lockouts

Hello,

    I need help in creating a script that monitors a particular threshold of lockouts.  For example, let's say my threshold for lockouts presently is 2 lockouts in an eight hour period.  In an example scenerio, I have a user that has been locked out 4 times in one day (within 8 hours), the script would then catch that and then send out an email alert, noting this, and the report might look like this below:

Account    Account Name          DN      Description      PC Name        IP (if possible)

And in the Subject line of the email say:  "These accounts surpass the 2 lockout attempt
                                                                  threshold"

    I would assume that basic logic behind this is that it would run it's scan, create a text file and based off the scan bounce it's finding's off of the master text file each time the script is ran.  Or scan through the lockout's folder on the log server for that day.  

   Any help with this is GREATLY appreciated.

 
itsmevicAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

RobSampsonCommented:
Hi, this one is probably only possible if auditing is turned on, so that you can query the System event log using code similar to this:

How Can I Retrieve Just Audit Failures, Warnings, and Errors from My Event Logs?
http://www.microsoft.com/technet/scriptcenter/resources/qanda/sept05/hey0913.mspx

Where you can also specify a time range to query using the TimeWritten property.

You could just search for the relevant event ids, then read the other attributes to determine what happened.

If you post a sample event of an account lockout, we can modify that code to suit.

Regards,

Rob.
0
itsmevicAuthor Commented:
Hi Rob, good to hear from you again. Here is a sample of the lockout log format:

USER,04/30/2009,04:09:20,DC-1,PCNAME
USER,04/30/2009,04:09:20,DC-1,PCNAME
USER,04/30/2009,04:09:20,DC-2,PCNAME
USER,04/30/2009,04:09:20,DC-10,PCNAME
USER,04/30/2009,04:09:20,DC-1,PCNAME
0
RobSampsonCommented:
Hmmm, so that's a flat text file, not the events from the Event Viewer?  In that case, we won't need to query the Event Log, but we should be able to read that data into a disconnected recordset, then perform filters on it.....I'll see what I can create, and you can see if that suits....

Regards,

Rob.
0
Simplify Active Directory Administration

Administration of Active Directory does not have to be hard.  Too often what should be a simple task is made more difficult than it needs to be.The solution?  Hyena from SystemTools Software.  With ease-of-use as well as powerful importing and bulk updating capabilities.

itsmevicAuthor Commented:
I basically already know the triggers I'll need to use i.e. any account that has been locked out more than 2 times in an 8 hour period.  If the script detects this, then a formatted email is sent out providing the following information to the receipant:

acct, acct name, dn, description, pc name

then for the subject line of the alert email:  "these account(s) exceed the lockout threshold of 2"
0
itsmevicAuthor Commented:
Ok cool Rob.  It is exactly as you described above, all of my logs are all just flat text files.
0
RobSampsonCommented:
OK, so here's a start....it doesn't report any time frames, it just counts the total lockouts for each user from the log file.

See how that goes, and if would like extra information output, let me know.

Regards,

Rob.
If LCase(Right(Wscript.FullName, 11)) = "wscript.exe" Then
    strPath = Wscript.ScriptFullName
    strCommand = "%comspec% /k cscript  """ & strPath & """"
    Set objShell = CreateObject("Wscript.Shell")
    objShell.Run(strCommand), 1, True
    Wscript.Quit
End If
 
strLogFile = Replace(WScript.ScriptFullName, WScript.ScriptName, "") & "Account_Lockouts_Log.txt"
 
WScript.Echo "Parsing log file " & strLogFile & VbCrLf & VbCrLf
 
Const ForReading = 1
Const adVarChar = 200
Const MaxCharacters = 255
Const adDouble = 5
 
Set objData = CreateObject("ADOR.Recordset")
objData.Fields.Append "Username", adVarChar, MaxCharacters
objData.Fields.Append "DateTime", adVarChar, MaxCharacters
objData.Fields.Append "DC", adVarChar, MaxCharacters
objData.Fields.Append "PC", adVarChar, MaxCharacters
objData.Open
 
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(strLogFile, ForReading, False)
 
strUsers = ";"
 
While Not objFile.AtEndOfStream
    strLine = objFile.ReadLine
    If strLine <> "" Then
	    arrLine = Split(LCase(strLine), ",")
	    objData.AddNew
	    objData("Username") = Trim(arrLine(0))
	    objData("DateTime") = Trim(arrLine(1)) & " " & Trim(arrLine(2))
	    objData("DC") = Trim(arrLine(3))
	    objData("PC") = Trim(arrLine(4))
	    objData.Update
	    If strUsers = "" Then
	    	strUsers = arrLine(0) & ";"
	    Else
	    	If InStr(strUsers, ";" & arrLine(0) & ";") = 0 Then strUsers = strUsers & arrLine(0) & ";"
	    End If
	End If
Wend
 
objFile.Close
 
If Left(strUsers, 1) = ";" Then strUsers = Mid(strUsers, 2)
If Right(strUsers, 1) = ";" Then strUsers = Left(strUsers, Len(strUsers) - 1)
 
For Each strUser In Split(strUsers, ";")
	objData.Filter = "Username='" & strUser & "'"
	objData.MoveFirst
	intLockoutCount = 0
	While Not objData.EOF
		intLockoutCount = intLockoutCount + 1
		objData.MoveNext
	Wend
	WScript.Echo strUser & " has been locked out " & intLockoutCount & " times."
Next
 
WScript.Echo VbCrLf & VbCrLf & "Done"

Open in new window

0
itsmevicAuthor Commented:
Hi Rob,

    Unfortunately the script above appears to be erroring out on me for some reason.  One thing I noticed is that it's not like your other scripts where you open a Search dialog box, then select a single file and carry on from there.  In this script, the file is specified at:

strLogFile = Replace(WScript.ScriptFullName, WScript.ScriptName, "") & "Account_Lockouts_Log.txt"

    After realizing this, I changed it to reflect a log file name in my Lockout folder i.e. DCNAMEActLockout_11.ActLock, re-ran the script and got the error:

\\log-server\logs\ActLockout\logs\Lockout.vbs(26, 1) Microsoft VBScript runtime error:  File not found.

    Below is a visual hopefully this will shed some light on the whole thing.  
Lockout.bmp
0
RobSampsonCommented:
OK, so let's just try and point it to a single file, by changing this:
strLogFile = Replace(WScript.ScriptFullName, WScript.ScriptName, "") & "Account_Lockouts_Log.txt"

to this
strLogFile = "\\log-server\logs\actLockout\Logs\DC1-100ActLockout_27.ActLock"

The whole Replace(WScript.ScriptFullName, WScript.ScriptName, "") in the original script only refers to the parent folder that the script is running from. I use it for a relative paths.

Regards,

Rob.
0
itsmevicAuthor Commented:
Yep that worked great.  Command prompt appeared went through that DC's lockout reports with no problems.

Is it possible to do this with the script?

1.)  Instead having to manually enter in the file name into the script, can we set it to scan all lockout files FOR THAT DATE in that AcctLockout folder? (This would save a ton of time rather than having to manually input each individual log file you needed to check.)

2.)  Adding the logic within the script to set a threshold trigger.  That trigger being any account that has had more than 2 lockouts in an 8 hour period.

3.)  For it to produce an alert email if it finds accounts that that have gone over 2 lockouts in an 8 hour period.  If found, to fire out an email alert providing this in the email:

Acct Name, DN, Description, PC Name

Crossin' my fingers on this one ( - :
0
RobSampsonCommented:
Yeah, I'm pretty sure all of the above is possible....certainly the first two, although for point 1, does the file name include the full date, or should we check by created date or last modified date?  Created date should be good enough I would think.

For point 3, does the log file include the full DN path for each user, or is it samAccountName only?  I only ask so that I can find out how to find each user....

Regards,

Rob.
0
itsmevicAuthor Commented:
That's a great question.  

Presently the naming format for my lockedout files is:

"DomainControllerNameActLockout_27.Actlock

The 27 represents the day of the month.  However since this ActLockout folder contains all lockout logs from Jan 1, 2009 up until now, there would be other 27's listed in log file names as well, so I think the best bet would be to do it by create date since I feel that would be most accurate in this case.

For point 3, the present lockout format for the log file is this:

username,date,time,dc,computer name

it does not include full DN path.

Thanks again for your help Rob!  
0
RobSampsonCommented:
Hello again,

OK, so that required a bit of a rewrite...and I hope I've got your output correct....see how this script goes for you....There's no email yet, and no account threshold either, but both are easier to put in.  I may not have any more time today, so I might have to finish it tomorrow.

Regards,

Rob.
Set objShell = CreateObject("Wscript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
If LCase(Right(Wscript.FullName, 11)) = "wscript.exe" Then
    strPath = Wscript.ScriptFullName
    strCommand = "%comspec% /k cscript  """ & strPath & """"
    objShell.Run(strCommand), 1, True
    Wscript.Quit
End If
 
strLogFolder = "\\log-server\logs\actLockout\Logs\"
'strLogFolder = Replace(WScript.ScriptFullName, WScript.ScriptName, "")
 
Const ForReading = 1
Const adVarChar = 200
Const MaxCharacters = 255
Const adDouble = 5
 
strShortDate = objShell.RegRead("HKCU\Control Panel\International\sShortDate")
If InStr(LCase(strShortDate), "d") < InStr(LCase(strShortDate), "m") Then
	strDateFormat = "dd/mm/yyyy"
Else
	strDateFormat = "mm/dd/yyyy"
End If
 
strDate = InputBox("Please enter the date of the files that you want to search" & VbCrLf & _
	"in " & strDateFormat & " format:", "Date to Search", strDateFormat)
 
If strDate <> "" And strDate <> strDateFormat Then
	
	dteDateFrom = CDate(strDate & " 00:00:00 AM")
	dteDateTo = CDate(strDate & " 11:59:59 PM")
	
	WScript.Echo "Parsing log folder " & strLogFolder & " for files created on " & strDate & VbCrLf & VbCrLf
 
	' Create the recordset to hold the entire data from each file parsed
	Set objData = CreateObject("ADOR.Recordset")
	objData.Fields.Append "Username", adVarChar, MaxCharacters
	objData.Fields.Append "DateTime", adVarChar, MaxCharacters
	objData.Fields.Append "DC", adVarChar, MaxCharacters
	objData.Fields.Append "PC", adVarChar, MaxCharacters
	objData.Open
	
	' Create the recordset to hold the information for each user once all of the files have been parsed
	Set objUsers = CreateObject("ADOR.Recordset")
	objUsers.Fields.Append "Username", adVarChar, MaxCharacters
	objUsers.Fields.Append "DistinguishedName", adVarChar, MaxCharacters
	objUsers.Fields.Append "ComputerName", adVarChar, MaxCharacters
	objUsers.Fields.Append "LockoutCount", adDouble
	objUsers.Open
 
	For Each objFile In objFSO.GetFolder(strLogFolder).Files
		If CDate(objFile.DateCreated) > dteDateFrom And CDate(objFile.DateCreated) < dteDateTo Then
			WScript.Echo "Parsing file " & objFile.Name & VbCrLf & VbCrLf
			 
			Set objFile = objFSO.OpenTextFile(objFile.Path, ForReading, False)
 
			strUsers = ";"
			 
			While Not objFile.AtEndOfStream
			    strLine = objFile.ReadLine
			    If strLine <> "" Then
				    arrLine = Split(LCase(strLine), ",")
				    objData.AddNew
				    objData("Username") = Trim(arrLine(0))
				    objData("DateTime") = Trim(arrLine(1)) & " " & Trim(arrLine(2))
				    objData("DC") = Trim(arrLine(3))
				    objData("PC") = Trim(arrLine(4))
				    objData.Update
				    If strUsers = "" Then
				    	strUsers = arrLine(0) & ";"
				    Else
				    	If InStr(strUsers, ";" & arrLine(0) & ";") = 0 Then strUsers = strUsers & arrLine(0) & ";"
				    End If
				End If
			Wend
			 
			objFile.Close
			 
			If Left(strUsers, 1) = ";" Then strUsers = Mid(strUsers, 2)
			If Right(strUsers, 1) = ";" Then strUsers = Left(strUsers, Len(strUsers) - 1)
 
		End If
	Next
	For Each strUser In Split(strUsers, ";")
		strUserDN = Get_LDAP_User_Properties("user", "samAccountName", strUser, "distinguishedName")
		objData.Filter = "Username='" & strUser & "'"
		objData.MoveFirst
		While Not objData.EOF
			objUsers.Filter = ""
			If Not objUsers.EOF Then objUsers.MoveFirst
		    objUsers.Filter = "Username='" & strUser & "' AND DistinguishedName='" & strUserDN & "' AND ComputerName='" & objData("PC") & "'"
		    If objUsers.EOF Then
			    objUsers.AddNew
			    objUsers("Username") = strUser
			    objUsers("DistinguishedName") = strUserDN
			    objUsers("ComputerName") = objData("PC")
			    objUsers("LockoutCount") = 1
			    objUsers.Update
			Else
				objUsers("LockoutCount") = objUsers("LockoutCount") + 1
			End If
			objData.MoveNext
		Wend
	Next
	objUsers.Filter = ""
	objUsers.MoveFirst
	While Not objUsers.EOF
		WScript.Echo objUsers("Username") & " (" & objUsers("DistinguishedName") & ") has been locked out of " & objUsers("ComputerName") & " " & objUsers("LockoutCount") & " times."
		objUsers.MoveNext
	Wend
Else
	WScript.Echo VbCrLf & VbCrLf & "Invalid date entered. Exiting script."
End If
 
WScript.Echo VbCrLf & VbCrLf & "Done"
 
Function Get_LDAP_User_Properties(strObjectType, strSearchField, strObjectToGet, strCommaDelimProps)
      
      ' This is a custom function that connects to the Active Directory, and returns the specific
      ' Active Directory attribute value, of a specific Object.
      ' strObjectType: usually "User" or "Computer"
      ' strSearchField: the field by which to seach the AD by. This acts like an SQL Query's WHERE clause.
      '				It filters the results by the value of strObjectToGet
      ' strObjectToGet: the value by which the results are filtered by, according the strSearchField.
      '				For example, if you are searching based on the user account name, strSearchField
      '				would be "samAccountName", and strObjectToGet would be that speicific account name,
      '				such as "jsmith".  This equates to "WHERE 'samAccountName' = 'jsmith'"
      '	strCommaDelimProps: the field from the object to actually return.  For example, if you wanted
      '				the home folder path, as defined by the AD, for a specific user, this would be
      '				"homeDirectory".  If you want to return the ADsPath so that you can bind to that
      '				user and get your own parameters from them, then use "ADsPath" as a return string,
      '				then bind to the user: Set objUser = GetObject("LDAP://" & strReturnADsPath)
      
      ' Now we're checking if the user account passed may have a domain already specified,
      ' in which case we connect to that domain in AD, instead of the default one.
      If InStr(strObjectToGet, "\") > 0 Then
            arrGroupBits = Split(strObjectToGet, "\")
            strDC = arrGroupBits(0)
            strDNSDomain = strDC & "/" & "DC=" & Replace(Mid(strDC, InStr(strDC, ".") + 1), ".", ",DC=")
            strObjectToGet = arrGroupBits(1)
      Else
      ' Otherwise we just connect to the default domain
            Set objRootDSE = GetObject("LDAP://RootDSE")
            strDNSDomain = objRootDSE.Get("defaultNamingContext")
      End If
 
      strBase = "<LDAP://" & strDNSDomain & ">"
      ' Setup ADO objects.
      Set adoCommand = CreateObject("ADODB.Command")
      Set adoConnection = CreateObject("ADODB.Connection")
      adoConnection.Provider = "ADsDSOObject"
      adoConnection.Open "Active Directory Provider"
      adoCommand.ActiveConnection = adoConnection
 
 
      ' Filter on user objects.
      'strFilter = "(&(objectCategory=person)(objectClass=user))"
      strFilter = "(&(objectClass=" & strObjectType & ")(" & strSearchField & "=" & strObjectToGet & "))"
 
      ' Comma delimited list of attribute values to retrieve.
      strAttributes = strCommaDelimProps
      arrProperties = Split(strCommaDelimProps, ",")
 
      ' Construct the LDAP syntax query.
      strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
      adoCommand.CommandText = strQuery
      ' Define the maximum records to return
      adoCommand.Properties("Page Size") = 100
      adoCommand.Properties("Timeout") = 30
      adoCommand.Properties("Cache Results") = False
 
      ' Run the query.
      Set adoRecordset = adoCommand.Execute
      ' Enumerate the resulting recordset.
      strReturnVal = ""
      Do Until adoRecordset.EOF
          ' Retrieve values and display.    
          For intCount = LBound(arrProperties) To UBound(arrProperties)
                If strReturnVal = "" Then
                      strReturnVal = adoRecordset.Fields(intCount).Value
                Else
                      strReturnVal = strReturnVal & VbCrLf & adoRecordset.Fields(intCount).Value
                End If
          Next
          ' Move to the next record in the recordset.
          adoRecordset.MoveNext
      Loop
 
      ' Clean up.
      adoRecordset.Close
      adoConnection.Close
      Get_LDAP_User_Properties = strReturnVal
 
End Function

Open in new window

0
itsmevicAuthor Commented:
Wow Rob what a nifty little script.  Very cool the way it opens up and allows you to type in a date range.  VERY NICE.  I ran it against the ActLockout folder this morning for 5/28/2009 and appear to be getting this error:

"\\log-server\logs\scripts\lockout-threshold.vbs(106, 2) ADODB.Recordset: Either BOF or EOF is True, or the current record has been deleted.  Requested operation requires a current record."
0
RobSampsonCommented:
Oh, on line 106 above, change this line:
      objUsers.MoveFirst

to this
      If Not objUsers.EOF Then objUsers.MoveFirst

Regards,

Rob.
0
itsmevicAuthor Commented:
Hi Rob, yet that appeared to correct the error, it opens does it thing and then say's "done" when complete.  Don't see where it wrote to though but you maybe just approaching this in steps building up to all of that or maybe perhaps I'm not looking in the right places.  Looks GREAT so far!
0
RobSampsonCommented:
Hmmm, OK, for a bit more output, try this....we need to see if it's populating the recordsets for you.

Regards,

Rob.
Set objShell = CreateObject("Wscript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
If LCase(Right(Wscript.FullName, 11)) = "wscript.exe" Then
    strPath = Wscript.ScriptFullName
    strCommand = "%comspec% /k cscript  """ & strPath & """"
    objShell.Run(strCommand), 1, True
    Wscript.Quit
End If
 
strLogFolder = "\\log-server\logs\actLockout\Logs\"
'strLogFolder = Replace(WScript.ScriptFullName, WScript.ScriptName, "")
 
Const ForReading = 1
Const adVarChar = 200
Const MaxCharacters = 255
Const adDouble = 5
 
strShortDate = objShell.RegRead("HKCU\Control Panel\International\sShortDate")
If InStr(LCase(strShortDate), "d") < InStr(LCase(strShortDate), "m") Then
	strDateFormat = "dd/mm/yyyy"
Else
	strDateFormat = "mm/dd/yyyy"
End If
 
strDate = InputBox("Please enter the date of the files that you want to search" & VbCrLf & _
	"in " & strDateFormat & " format:", "Date to Search", strDateFormat)
 
If strDate <> "" And strDate <> strDateFormat Then
	
	dteDateFrom = CDate(strDate & " 00:00:00 AM")
	dteDateTo = CDate(strDate & " 11:59:59 PM")
	
	WScript.Echo "Parsing log folder " & strLogFolder & " for files created on " & strDate & VbCrLf & VbCrLf
 
	' Create the recordset to hold the entire data from each file parsed
	Set objData = CreateObject("ADOR.Recordset")
	objData.Fields.Append "Username", adVarChar, MaxCharacters
	objData.Fields.Append "DateTime", adVarChar, MaxCharacters
	objData.Fields.Append "DC", adVarChar, MaxCharacters
	objData.Fields.Append "PC", adVarChar, MaxCharacters
	objData.Open
	
	' Create the recordset to hold the information for each user once all of the files have been parsed
	Set objUsers = CreateObject("ADOR.Recordset")
	objUsers.Fields.Append "Username", adVarChar, MaxCharacters
	objUsers.Fields.Append "DistinguishedName", adVarChar, MaxCharacters
	objUsers.Fields.Append "ComputerName", adVarChar, MaxCharacters
	objUsers.Fields.Append "LockoutCount", adDouble
	objUsers.Open
 
	For Each objFile In objFSO.GetFolder(strLogFolder).Files
		If CDate(objFile.DateCreated) > dteDateFrom And CDate(objFile.DateCreated) < dteDateTo Then
			WScript.Echo "Parsing file " & objFile.Name & VbCrLf & VbCrLf
			 
			Set objFile = objFSO.OpenTextFile(objFile.Path, ForReading, False)
 
			strUsers = ";"
			 
			While Not objFile.AtEndOfStream
			    strLine = objFile.ReadLine
			    If strLine <> "" Then
				    arrLine = Split(LCase(strLine), ",")
				    objData.AddNew
				    WScript.Echo "Adding " & Trim(arrLine(0)) & " from file to recordset"
				    objData("Username") = Trim(arrLine(0))
				    objData("DateTime") = Trim(arrLine(1)) & " " & Trim(arrLine(2))
				    objData("DC") = Trim(arrLine(3))
				    objData("PC") = Trim(arrLine(4))
				    objData.Update
				    If strUsers = "" Then
				    	strUsers = arrLine(0) & ";"
				    Else
				    	If InStr(strUsers, ";" & arrLine(0) & ";") = 0 Then strUsers = strUsers & arrLine(0) & ";"
				    End If
				End If
			Wend
			 
			objFile.Close
			 
			If Left(strUsers, 1) = ";" Then strUsers = Mid(strUsers, 2)
			If Right(strUsers, 1) = ";" Then strUsers = Left(strUsers, Len(strUsers) - 1)
 
		End If
	Next
	
	WScript.Echo VbCrLf & VbCrLf & "Finished reading files."
	
	For Each strUser In Split(strUsers, ";")
		strUserDN = Get_LDAP_User_Properties("user", "samAccountName", strUser, "distinguishedName")
		objData.Filter = "Username='" & strUser & "'"
		objData.MoveFirst
		While Not objData.EOF
			objUsers.Filter = ""
			If Not objUsers.EOF Then objUsers.MoveFirst
		    objUsers.Filter = "Username='" & strUser & "' AND DistinguishedName='" & strUserDN & "' AND ComputerName='" & objData("PC") & "'"
		    If objUsers.EOF Then
			    objUsers.AddNew
			    WScript.Echo VbCrLf & VbCrLf & "Adding " & strUser & " to user table to count lockouts"
			    objUsers("Username") = strUser
			    objUsers("DistinguishedName") = strUserDN
			    objUsers("ComputerName") = objData("PC")
			    objUsers("LockoutCount") = 1
			    objUsers.Update
			Else
				objUsers("LockoutCount") = objUsers("LockoutCount") + 1
			End If
			objData.MoveNext
		Wend
	Next
	objUsers.Filter = ""
	If Not objUsers.EOF Then objUsers.MoveFirst
	While Not objUsers.EOF
		WScript.Echo objUsers("Username") & " (" & objUsers("DistinguishedName") & ") has been locked out of " & objUsers("ComputerName") & " " & objUsers("LockoutCount") & " times."
		objUsers.MoveNext
	Wend
Else
	WScript.Echo VbCrLf & VbCrLf & "Invalid date entered. Exiting script."
End If
 
WScript.Echo VbCrLf & VbCrLf & "Done"
 
Function Get_LDAP_User_Properties(strObjectType, strSearchField, strObjectToGet, strCommaDelimProps)
      
      ' This is a custom function that connects to the Active Directory, and returns the specific
      ' Active Directory attribute value, of a specific Object.
      ' strObjectType: usually "User" or "Computer"
      ' strSearchField: the field by which to seach the AD by. This acts like an SQL Query's WHERE clause.
      '				It filters the results by the value of strObjectToGet
      ' strObjectToGet: the value by which the results are filtered by, according the strSearchField.
      '				For example, if you are searching based on the user account name, strSearchField
      '				would be "samAccountName", and strObjectToGet would be that speicific account name,
      '				such as "jsmith".  This equates to "WHERE 'samAccountName' = 'jsmith'"
      '	strCommaDelimProps: the field from the object to actually return.  For example, if you wanted
      '				the home folder path, as defined by the AD, for a specific user, this would be
      '				"homeDirectory".  If you want to return the ADsPath so that you can bind to that
      '				user and get your own parameters from them, then use "ADsPath" as a return string,
      '				then bind to the user: Set objUser = GetObject("LDAP://" & strReturnADsPath)
      
      ' Now we're checking if the user account passed may have a domain already specified,
      ' in which case we connect to that domain in AD, instead of the default one.
      If InStr(strObjectToGet, "\") > 0 Then
            arrGroupBits = Split(strObjectToGet, "\")
            strDC = arrGroupBits(0)
            strDNSDomain = strDC & "/" & "DC=" & Replace(Mid(strDC, InStr(strDC, ".") + 1), ".", ",DC=")
            strObjectToGet = arrGroupBits(1)
      Else
      ' Otherwise we just connect to the default domain
            Set objRootDSE = GetObject("LDAP://RootDSE")
            strDNSDomain = objRootDSE.Get("defaultNamingContext")
      End If
 
      strBase = "<LDAP://" & strDNSDomain & ">"
      ' Setup ADO objects.
      Set adoCommand = CreateObject("ADODB.Command")
      Set adoConnection = CreateObject("ADODB.Connection")
      adoConnection.Provider = "ADsDSOObject"
      adoConnection.Open "Active Directory Provider"
      adoCommand.ActiveConnection = adoConnection
 
 
      ' Filter on user objects.
      'strFilter = "(&(objectCategory=person)(objectClass=user))"
      strFilter = "(&(objectClass=" & strObjectType & ")(" & strSearchField & "=" & strObjectToGet & "))"
 
      ' Comma delimited list of attribute values to retrieve.
      strAttributes = strCommaDelimProps
      arrProperties = Split(strCommaDelimProps, ",")
 
      ' Construct the LDAP syntax query.
      strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
      adoCommand.CommandText = strQuery
      ' Define the maximum records to return
      adoCommand.Properties("Page Size") = 100
      adoCommand.Properties("Timeout") = 30
      adoCommand.Properties("Cache Results") = False
 
      ' Run the query.
      Set adoRecordset = adoCommand.Execute
      ' Enumerate the resulting recordset.
      strReturnVal = ""
      Do Until adoRecordset.EOF
          ' Retrieve values and display.    
          For intCount = LBound(arrProperties) To UBound(arrProperties)
                If strReturnVal = "" Then
                      strReturnVal = adoRecordset.Fields(intCount).Value
                Else
                      strReturnVal = strReturnVal & VbCrLf & adoRecordset.Fields(intCount).Value
                End If
          Next
          ' Move to the next record in the recordset.
          adoRecordset.MoveNext
      Loop
 
      ' Clean up.
      adoRecordset.Close
      adoConnection.Close
      Get_LDAP_User_Properties = strReturnVal
 
End Function

Open in new window

0
itsmevicAuthor Commented:
Appears to run through script fine w/no errors.  When finished it will say:

Finished Reading Files.

Done

C:\WINDOWS

Doesn't appear to write anything but then again maybe I"m not looking in the correct location.
0
RobSampsonCommented:
Hmmm, do you see output from this line:
WScript.Echo "Parsing log folder " & strLogFolder & " for files created on " & strDate & VbCrLf & VbCrLf

That folder that it referenced needs to be the name of the folder that the files exist in....

Rob.
0
itsmevicAuthor Commented:
Yep I'll test this on Monday morning and let you know Rob.  Thanks.
0
itsmevicAuthor Commented:
I had to read what you said over a few times to make sure I was understanding it.  When you said
"That folder that it referenced needs to be the name of the folder that the files exist in"  I understand that statement meaning as such:

Where you reference the strLogFolder ( and in my case --->) "\\logs-server\logs\ActLockout\Logs"
                                                                                                                                         ^        
                                                                                                                        (Folder it's referencing)

It appears to be referencing correctly as far as the path and folder name that the file resides in.  I've double-checked it.  What do you think?
0
RobSampsonCommented:
Sorry for the confusion, I'll try again :-)

From what I understood with the image you posted, you have
\\log-server\logs\ActLockout    <------ FOLDER
\Logs   <-----------FOLDER
DC1-100ActLockout_27.ActLock     <-------- FILE
DC2-101ActLockout_27.ActLock     <-------- FILE

where the FILE's are in the LOGS folder.  So that's why I had
strLogFolder = "\\log-server\logs\actLockout\Logs\"

to reference the "parent" folder that the ActLock files are in.  Does that make more sense?

The strLogFile needs to point to the folder that the log files are immediately contained in.

The script then goes through *every* file in that folder, processing only those that fall within the date range.

Regards,

Rob.
0
itsmevicAuthor Commented:
Hmmm very odd.  It appears to be pointing to the correct path i.e. \\logs-server\logs\actLockout\Logs\ just don't see any output anywhere.  I've posted some screenshots to hopefully help.
1.JPG
2.JPG
0
RobSampsonCommented:
Hmmm, strange....One thing I've noticed though...the file that called
ActLockout_5.ActLock was last modified on the 4th of May....seems a bit odd, buy in any case, you should still catch one or two files.  Try specifying a date that falls in the middle of the files you have there....

Also, just out of curiosity, try the date in dd/mm/yyy order, instead of mm/dd/yyyy, just to see....

I have files that were last modified on 28th of May 2009, and this is what I get...

Regards,

Rob.
ParseFiles.jpg
0
itsmevicAuthor Commented:
    I'm a complete and utter dork...LOL.  It was right there the whole time.  In my Explorer window I have the column header set to "Date Modified" I added the column "Date Created" and got a whole shlew of different dates and when I re-ran the script usings "those" date's it populated record sets perfectly.  I apologize, blonde moment there..haha.

    One thing I did notice is that my "Date Created" dates are completely and totally different than my "Date Modified" dates.  For example, let's say I sort the "Date Modified"  column in ascending order Window (recent at top oldest at bottom)  I have 6/2/2009 showing naturally, however it's "Date Created" date will say something like 08/30/2008.  I'm curious, if these files just append over the top of each other?  If that is the case, perhaps searching by "Date Modified" would work out better?  What do you think?
0
itsmevicAuthor Commented:
I fixed it Rob ( :

Changed this to read:

 If CDate(objFile.DateLastModified) > dteDateFrom And CDate(objFile.DateLastModified) < dteDateTo Then
                  WScript.Echo "Parsing file " & objFile.Name & VbCrLf & VbCrL

it populates and goes through all record sets fine.   I suppose the last thing we can do now is add the email functionality upon detection of any anomalies then we are DONE.. wooohooo!
0
RobSampsonCommented:
Awesome! Good work!  OK, so I can add a threshold, given by this bit:

' Set this to the maximum lockouts at which to trigger, inclusive
Const ActLockoutThreshold = 3

However, as opposed to actually specifying an 8 hour period, it would be much easier to leave it at the 24 hour period that is basically specified by the day that you enter.....is that OK?

OK, so the emailing has been added, only when a user reaches the threshold. Try this.

Regards,

Rob.
Set objShell = CreateObject("Wscript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
If LCase(Right(Wscript.FullName, 11)) = "wscript.exe" Then
    strPath = Wscript.ScriptFullName
    strCommand = "%comspec% /k cscript  """ & strPath & """"
    objShell.Run(strCommand), 1, True
    Wscript.Quit
End If
 
'strLogFolder = "\\log-server\logs\actLockout\Logs\"
strLogFolder = Replace(WScript.ScriptFullName, WScript.ScriptName, "")
 
' Set this to the maximum lockouts at which to trigger, inclusive
Const ActLockoutThreshold = 3
 
' Email variables:
strServer = "mailhost.test.com"
strTo = "johndoe@abc.com"
strFrom = "johndoe@abc.com"
strSubject = "Account Lockout Status"
strBody = "Please see the account lockout status below:" & VbCrLf
 
Const ForReading = 1
Const adVarChar = 200
Const MaxCharacters = 255
Const adDouble = 5
 
boolThresholdReached = False
 
strShortDate = objShell.RegRead("HKCU\Control Panel\International\sShortDate")
If InStr(LCase(strShortDate), "d") < InStr(LCase(strShortDate), "m") Then
	strDateFormat = "dd/mm/yyyy"
Else
	strDateFormat = "mm/dd/yyyy"
End If
 
strDate = InputBox("Please enter the date of the files that you want to search" & VbCrLf & _
	"in " & strDateFormat & " format:", "Date to Search", strDateFormat)
 
If strDate <> "" And strDate <> strDateFormat Then
	
	dteDateFrom = CDate(strDate & " 00:00:00 AM")
	dteDateTo = CDate(strDate & " 11:59:59 PM")
	
	WScript.Echo "Parsing log folder " & strLogFolder & " for files created on " & strDate & VbCrLf & VbCrLf
 
	' Create the recordset to hold the entire data from each file parsed
	Set objData = CreateObject("ADOR.Recordset")
	objData.Fields.Append "Username", adVarChar, MaxCharacters
	objData.Fields.Append "DateTime", adVarChar, MaxCharacters
	objData.Fields.Append "DC", adVarChar, MaxCharacters
	objData.Fields.Append "PC", adVarChar, MaxCharacters
	objData.Open
	
	' Create the recordset to hold the information for each user once all of the files have been parsed
	Set objUsers = CreateObject("ADOR.Recordset")
	objUsers.Fields.Append "Username", adVarChar, MaxCharacters
	objUsers.Fields.Append "DistinguishedName", adVarChar, MaxCharacters
	objUsers.Fields.Append "ComputerName", adVarChar, MaxCharacters
	objUsers.Fields.Append "LockoutCount", adDouble
	objUsers.Open
 
	For Each objFile In objFSO.GetFolder(strLogFolder).Files
		If CDate(objFile.DateLastModified) > dteDateFrom And CDate(objFile.DateLastModified) < dteDateTo Then
			WScript.Echo "Parsing file " & objFile.Name & VbCrLf & VbCrLf
			 
			Set objFile = objFSO.OpenTextFile(objFile.Path, ForReading, False)
 
			strUsers = ";"
			 
			While Not objFile.AtEndOfStream
			    strLine = objFile.ReadLine
			    If strLine <> "" Then
				    arrLine = Split(LCase(strLine), ",")
				    objData.AddNew
				    objData("Username") = Trim(arrLine(0))
				    objData("DateTime") = Trim(arrLine(1)) & " " & Trim(arrLine(2))
				    objData("DC") = Trim(arrLine(3))
				    objData("PC") = Trim(arrLine(4))
				    objData.Update
				    If strUsers = "" Then
				    	strUsers = arrLine(0) & ";"
				    Else
				    	If InStr(strUsers, ";" & arrLine(0) & ";") = 0 Then strUsers = strUsers & arrLine(0) & ";"
				    End If
				End If
			Wend
			 
			objFile.Close
			 
			If Left(strUsers, 1) = ";" Then strUsers = Mid(strUsers, 2)
			If Right(strUsers, 1) = ";" Then strUsers = Left(strUsers, Len(strUsers) - 1)
 
		End If
	Next
	For Each strUser In Split(strUsers, ";")
		strUserDN = Get_LDAP_User_Properties("user", "samAccountName", strUser, "distinguishedName")
		objData.Filter = "Username='" & strUser & "'"
		objData.MoveFirst
		While Not objData.EOF
			objUsers.Filter = ""
			If Not objUsers.EOF Then objUsers.MoveFirst
		    objUsers.Filter = "Username='" & strUser & "' AND DistinguishedName='" & strUserDN & "' AND ComputerName='" & objData("PC") & "'"
		    If objUsers.EOF Then
			    objUsers.AddNew
			    objUsers("Username") = strUser
			    objUsers("DistinguishedName") = strUserDN
			    objUsers("ComputerName") = objData("PC")
			    objUsers("LockoutCount") = 1
			    objUsers.Update
			Else
				objUsers("LockoutCount") = objUsers("LockoutCount") + 1
			End If
			objData.MoveNext
		Wend
	Next
	objUsers.Filter = ""
	If Not objUsers.EOF Then objUsers.MoveFirst
	While Not objUsers.EOF
		If objUsers("LockoutCount") >= ActLockoutThreshold Then
			boolThresholdReached = True
			strBody = strBody & VbCrLf & "WARNING: " & objUsers("Username") & " (" & objUsers("DistinguishedName") & ") has been locked out of " & objUsers("ComputerName") & " " & objUsers("LockoutCount") & " times."
			WScript.Echo "WARNING: " & objUsers("Username") & " (" & objUsers("DistinguishedName") & ") has been locked out of " & objUsers("ComputerName") & " " & objUsers("LockoutCount") & " times."
		Else
			WScript.Echo objUsers("Username") & " (" & objUsers("DistinguishedName") & ") has been locked out of " & objUsers("ComputerName") & " " & objUsers("LockoutCount") & " times."
		End If
		objUsers.MoveNext
	Wend
Else
	WScript.Echo VbCrLf & VbCrLf & "Invalid date entered. Exiting script."
End If
 
' Now send the file
If boolThresholdReached = True Then
	SendEmail strServer, strTo, strFrom, strSubject, strBody
	WScript.Echo VbCrLf & VbCrLf & strBody & VbCrLf & VbCrLf
	WScript.Echo "Email has been sent."
Else
	WScript.Echo "No account changes have been made."
End If
 
WScript.Echo VbCrLf & VbCrLf & "Done"
 
Function Get_LDAP_User_Properties(strObjectType, strSearchField, strObjectToGet, strCommaDelimProps)
      
      ' This is a custom function that connects to the Active Directory, and returns the specific
      ' Active Directory attribute value, of a specific Object.
      ' strObjectType: usually "User" or "Computer"
      ' strSearchField: the field by which to seach the AD by. This acts like an SQL Query's WHERE clause.
      '				It filters the results by the value of strObjectToGet
      ' strObjectToGet: the value by which the results are filtered by, according the strSearchField.
      '				For example, if you are searching based on the user account name, strSearchField
      '				would be "samAccountName", and strObjectToGet would be that speicific account name,
      '				such as "jsmith".  This equates to "WHERE 'samAccountName' = 'jsmith'"
      '	strCommaDelimProps: the field from the object to actually return.  For example, if you wanted
      '				the home folder path, as defined by the AD, for a specific user, this would be
      '				"homeDirectory".  If you want to return the ADsPath so that you can bind to that
      '				user and get your own parameters from them, then use "ADsPath" as a return string,
      '				then bind to the user: Set objUser = GetObject("LDAP://" & strReturnADsPath)
      
      ' Now we're checking if the user account passed may have a domain already specified,
      ' in which case we connect to that domain in AD, instead of the default one.
      If InStr(strObjectToGet, "\") > 0 Then
            arrGroupBits = Split(strObjectToGet, "\")
            strDC = arrGroupBits(0)
            strDNSDomain = strDC & "/" & "DC=" & Replace(Mid(strDC, InStr(strDC, ".") + 1), ".", ",DC=")
            strObjectToGet = arrGroupBits(1)
      Else
      ' Otherwise we just connect to the default domain
            Set objRootDSE = GetObject("LDAP://RootDSE")
            strDNSDomain = objRootDSE.Get("defaultNamingContext")
      End If
 
      strBase = "<LDAP://" & strDNSDomain & ">"
      ' Setup ADO objects.
      Set adoCommand = CreateObject("ADODB.Command")
      Set adoConnection = CreateObject("ADODB.Connection")
      adoConnection.Provider = "ADsDSOObject"
      adoConnection.Open "Active Directory Provider"
      adoCommand.ActiveConnection = adoConnection
 
 
      ' Filter on user objects.
      'strFilter = "(&(objectCategory=person)(objectClass=user))"
      strFilter = "(&(objectClass=" & strObjectType & ")(" & strSearchField & "=" & strObjectToGet & "))"
 
      ' Comma delimited list of attribute values to retrieve.
      strAttributes = strCommaDelimProps
      arrProperties = Split(strCommaDelimProps, ",")
 
      ' Construct the LDAP syntax query.
      strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
      adoCommand.CommandText = strQuery
      ' Define the maximum records to return
      adoCommand.Properties("Page Size") = 100
      adoCommand.Properties("Timeout") = 30
      adoCommand.Properties("Cache Results") = False
 
      ' Run the query.
      Set adoRecordset = adoCommand.Execute
      ' Enumerate the resulting recordset.
      strReturnVal = ""
      Do Until adoRecordset.EOF
          ' Retrieve values and display.    
          For intCount = LBound(arrProperties) To UBound(arrProperties)
                If strReturnVal = "" Then
                      strReturnVal = adoRecordset.Fields(intCount).Value
                Else
                      strReturnVal = strReturnVal & VbCrLf & adoRecordset.Fields(intCount).Value
                End If
          Next
          ' Move to the next record in the recordset.
          adoRecordset.MoveNext
      Loop
 
      ' Clean up.
      adoRecordset.Close
      adoConnection.Close
      Get_LDAP_User_Properties = strReturnVal
 
End Function
 
Sub SendEmail(strServer, strTo, strFrom, strSubject, strBody)
	Dim objMessage
	
	Set objMessage = CreateObject("CDO.Message")
	objMessage.To = strTo
	objMessage.From = strFrom
	objMessage.Subject = strSubject
	objMessage.TextBody = strBody
 
	'==This section provides the configuration information for the remote SMTP server.
	objMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
	'Name or IP of Remote SMTP Server
	objMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = strServer
	'Server port (typically 25)
	objMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25	
	objMessage.Configuration.Fields.Update
	'==End remote SMTP server configuration section==
 
	objMessage.Send
	Set objMessage = Nothing
End Sub

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
RobSampsonCommented:
Oh, I left my test lines in again.  Please change this:
'strLogFolder = "\\log-server\logs\actLockout\Logs\"
strLogFolder = Replace(WScript.ScriptFullName, WScript.ScriptName, "")
 
to this
strLogFolder = "\\log-server\logs\actLockout\Logs\"

Regards,

Rob.
0
itsmevicAuthor Commented:
Rob I think this did it man, AWESOME JOB as usual man!  I really hope your being rewarded a little more besides points by EE.  Thanks a million man!
0
itsmevicAuthor Commented:
Everytime Rob enlists his skill's and knowledge into my projects, I get this warm and fuzzy feeling of comfort.  GREAT JOB!
0
RobSampsonCommented:
>> I really hope your being rewarded a little more besides points by EE
I wish!  Well I have had a few job offers, but I can't take them up just yet...things are too busy for me....but I'm learning all the time, so that's good enough for me! I still get to impress my boss with the things I can do with making our IT Administration easier!

Thanks for the grade....onto the next one! :-)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
VB Script

From novice to tech pro — start learning today.