Stop calls about locked accounts

From time to time in a corporate network a user may lock out their account accidentally. In the corporate network that I am involved in there are generic accounts (unfortunately with dead simple passwords) that occasionally get locked. Since this is a 24/7 operation, it can happen in the middle of the night (in fact, more common at night). So to ease this a bit, and be proactive, I decided to find a way to get an alert  every time an account gets locked. I of course, just used Windows easy to use built in feature. Oh wait, Windows doesn’t have that… Read on for how I did this…

I discovered this command called eventtriggers.exe. Apparently, its been in Windows since Windows XP (I couldn’t find it in Windows 7 though). Our domain controller is Windows 2003, but I am sure it is in Windows 2008 also. So I ran the following on the domain controller:

eventtriggers /create /tr EventID644 /l security /eid 644 /tk c:\tools\trigger.vbs

You will need to enter the password for the user that you are currently in. You could modify this command a bit and run as the system account I am sure, but I decided not to. This is create a Scheduled Task that runs every time the Event ID 644 is created. This event is the event that says an account has been locked. Another useful one is 675, which is when a user enters a wrong password. Unfortunately, I could not find an easy way to add arguments to the trigger.vbs file (in case I want to reuse the same command for other event IDs), so you can manually edit the task in Scheduled Tasks. I changed the command to the following:

c:\tools\trigger.vbs 644 "User Account Locked"

So now we need to create the trigger.vbs file. This file will read the data from the Event ID, and email it. Unfortunately, you can not pass the particular event that caused the issue to trigger.vbs via eventtriggers, so we will do some creative WMI reading. The code below searches for the passed event ID, but thankfully WMI starts with the newest first otherwise we’d get every event ID on the log. So we’ll just read the first one. Now of course, if two came in at the same time, and this script hadn’t started in the millisecond difference of the next event, you might read the wrong one. But seriously, how many accounts are being locked out during the day?

' Setup the variables
Dim Args, strMsg
Dim strEventID, strDesc
Dim objWMIService, colEvents, objItem
Dim Loops
Dim objMessage ' This one is very important!

' If there are no arguments, do nothing
Args = WScript.Arguments.Count
If Args < 1 Then
	WScript.Echo "Usage: trigger.vbs  """""
	WScript.Quit
End If

' Read in the arguments
strEventID = WScript.Arguments.Item(0)
strDesc = WScript.Arguments.Item(1)

' Connect to WMI and Query
Set objWMIService = GetObject("winmgmts:{(Security)}\\.\root\cimv2")
Set colEvents = objWMIService.ExecQuery("Select * From Win32_NTLogEvent Where " & _
	"Logfile = 'Security' AND EventCode= '" & strEventID & "'")

' We are going to load the events, but only read the latest one
Loops = 0
For Each objItem in colEvents
	If Loops = 1 Then
		Exit For
	End If
	' Create the body of the email
	strMsg = Now & vbCrLf & vbCrLf ' Add the time to the message
	strMsg = strMsg & "Time Generated: " & objItem.TimeGenerated & vbCrLf
	strMsg = strMsg & "Category: " & objItem.Category & vbCrLf
	strMsg = strMsg & "Event Code: " & objItem.EventCode & vbCrLf
	strMsg = strMsg & "Record Number: " & objItem.RecordNumber & vbCrLf
	strMsg = strMsg & "Source Name: " & objItem.SourceName & vbCrLf
	strMsg = strMsg & "Event Type: " & objItem.Type & vbCrLf
	strMsg = strMsg & "Message: " & objItem.Message
	Loops = 1 ' Set the variable so it doesn't run again
Next

' Load CDO and create the message, then send it
Set objMessage = CreateObject("CDO.Message")
objMessage.Subject = "AD Audit Event: " & strDesc ' Add the description to the subject
objMessage.From = "support@company.com"
objMessage.To = "support@company.com"
objMessage.TextBody = strMsg
' The commands below use SMTP to do the sending
objMessage.Configuration.Fields.Item( _
	"http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
objMessage.Configuration.Fields.Item( _
	"http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.company.com"
objMessage.Configuration.Fields.Item( _
	"http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
objMessage.Configuration.Fields.Update
' Send the message
objMessage.Send

' Done

Now this particular file just reads from the Security event log file, but you could adjust this and read any type of event. You would have to adjust both winmgmts and the SQL statement and then modify the what items it reads from the the particular event.

That should do it. You could modify the trigger file to add this information to a database. For example, I will be modifying it to add 675 event IDs (wrong password) to a database, since I don’t want an email every 10 minutes, but I can view history leading up to a locked account.

Now of course, you’ll still get calls in the middle of the night if you were asleep when the email came in. But at least you can say that you already knew and were working on it!

Leave a Reply

Your email address will not be published. Required fields are marked *