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!