Hey, Scripting Guy! How Can I Query Event Logs to Discover Active Directory Information?

Hey, Scripting Guy! Question

Hey, Scripting Guy! We have this problem at work. I keep finding groups in Active Directory, and I do not know why they were created or by whom. I am wondering if there is some way to search Active Directory to see who created a group. Is there an owner attribute or something that would let me know who created it? If I can find that, I could at least go ask the turkey why he is going around creating all these strange groups. I mean, they do not even conform to our naming convention.

- MM

SpacerHey, Scripting Guy! Answer

Hi MM,

It has been kind of gloomy and rainy all day down here in Charlotte, North Carolina, in the United States. It is one of those days where you can move from morning to afternoon to evening to night and never tell any difference outside. Come to think of it, it is getting rather late at night. There wasn’t any sunshine at all—until your e-mail arrived. Querying Active Directory is a lot of fun. But in this case, it will not help you. We have never been those "if all you have is a hammer" people. The best course of action for us is to query the event log to find your information.

This week is Event Log Week. We have quite a few good scripts that work with event logs in the Script Center Script Repository. The Scripting Guide has some good information about querying event logs, managing event logs, and writing to event logs from a VBScript perspective. These same types of information are covered from a Windows PowerShell perspective in chapter 3 of the Windows PowerShell Scripting Guide. Over the years, there have been a few “Hey, Scripting Guy!” articles on topics such as finding the oldest event in an event log, the newest event in an event log, backing up the event log to a text file, retrieving audit failures from the event log, or retrieving all failures from the security event log. There is also the Log Parser 2.2, and we have a number of examples on the Script Center of how to use Log Parser. The scripts this week are written in Windows PowerShell. If you need help converting VBScript to Windows PowerShell, you can refer to this conversion guide.

What you need to do is to turn on auditing for account management. By default, account management is not audited, and on a large and busy network, it could add quite a bit of information to the security log. However, if you are having problems in this area, it may very well make sense. You can easily use the Group Policy Management Console to edit the appropriate domain policy as seen here:

Image of using the Group Policy Management Console to edit domain policy


After you have enabled auditing, you may want to run the GPUpdate.exe command from within the Windows PowerShell console to refresh the Group Policy settings on the machine. After you have done that, you may want to create a bogus user or group to make sure that the policy settings were applied correctly and that it is working as expected. Several event log entries will be found when the user is created. The first one will be the one where the user is actually created. The other entries will be related to the password reset and the status of the user account. The account-created entry is the one you are interested in and is shown here:

Image of the account-created entry


As you can see in that image, when the user account is created and auditing is enabled, a 4720 event is logged to the Security log. Using the technique we examined in yesterday’s “Hey, Scripting Guy!” article, we dutifully type in the following command:

Get-EventLog -LogName Security | Where-Object { $_.EventID -eq 4720 }

Rather than being greeted with the appropriate event log entry, we instead are confronted with some kind of bogus error related to the registry. This is seen here:

Image of an error message related to the registry


The reason for the error is that access to the Security event log requires administrator rights (more technically the security privilege). Windows PowerShell does not bypass security, but is not User Account Control (UAC) aware. To get around this problem, we will need to start the Windows PowerShell console as an administrator. Right-click the icon and then click Run As Administrator. Now a UAC prompt is generated. We will probably want to modify the query just a bit. The reason is the size of the Security event log. As seen here, the Security log on my server is set by default to a size of 130 megabytes, and it has more than 260,000 entries in it.

PS C:\> Get-EventLog -List

Max(K) Retain OverflowAction Entries Log
---------- --------- ------------------- ---------- ---------------------------
20,480 0 OverwriteAsNeeded 2,749 Application
15,168 0 OverwriteAsNeeded 979 DFS Replication
512 0 OverwriteAsNeeded 1,686 Directory Service
16,384 0 OverwriteAsNeeded 640 DNS Server
20,480 0 OverwriteAsNeeded 0 HardwareEvents
512 7 OverwriteOlder 0 Internet Explorer
20,480 0 OverwriteAsNeeded 0 Key Management Service
131,072 0 OverwriteAsNeeded 261,493 Security
20,480 0 OverwriteAsNeeded 14,207 System
15,360 0 OverwriteAsNeeded 1,027 Windows PowerShell

It can take a little bit of time to troll through 260,000 entries, and because we are just experimenting, there is no reason to wait that long. We can limit our search easily by using the –newest switch. I first tried limiting it to the first 10 entries, then the next 20 entries, and finally the newest 100 entries to find the entry for the user I just created. I then realized why the security event log is so big: It logs all kinds of stuff all the time—doubly so now that we enabled additional auditing. The revised query is shown here:

PS C:\> Get-EventLog -LogName Security -Newest 100 | Where-Object { $_.EventID -
eq 4720 }

Index Time EntryType Source InstanceID Message
----- ------------ ----------- ----------------- ---------- ----------
282271 Apr 02 21:43 SuccessA... Microsoft-Windows... 4720 The des...

The results of the previous command do not tell us a whole lot, but we have found the index number for the event log entry. We can revise the command, and force it to display all of the data from the entire event log entry. In the new command you will notice that I had to raise the –newest value due to additional logging that takes place behind the scenes in the event log. We change the Where-Object filter to look for an index value that is equal to 282271. This index number will be different on your computer because it is specific to my event log. The Format-List cmdlet chooses which properties to display. I selected all of them by using the asterisk character.

PS C:\> Get-EventLog -LogName Security -Newest 200 | Where-Object { $_.Index -eq '282271' } |
Format-List -Property *

EventID : 4720
MachineName : Berlin.nwtraders.com
Data : {}
Index : 282271
Category : (13824)
CategoryNumber : 13824
EntryType : SuccessAudit
Message : The description for Event ID '4720' in Source 'Microsoft-W
indows-Security-Auditing' cannot be found. The local comp
uter may not have the necessary registry information or me
ssage DLL files to display the message, or you may not hav
e permission to access them. The following information is
part of the event:'bogus1', 'NWTRADERS', 'S-1-5-21-540299
044-341859138-929407116-1154', 'S-1-5-21-540299044-3418591
38-929407116-500', 'administrator', 'NWTRADERS', '0x29e7f'
, '-', 'bogus1', 'bogus1', 'bogus1@nwtraders.com', '-', '-
', '-', '-', '-', '%%1794', '%%1794', '513', '-', '0x0', '
0x15', '
%%2084', '-', '-', '%%1793'
Source : Microsoft-Windows-Security-Auditing
ReplacementStrings : {bogus1, NWTRADERS, S-1-5-21-540299044-341859138-929407116
-1154, S-1-5-21-540299044-341859138-929407116-500...}
InstanceId : 4720
TimeGenerated : 4/2/2009 9:43:04 PM
TimeWritten : 4/2/2009 9:43:04 PM
UserName :
Site :
Container :

If we look at the message property, we can see the username as well as the person's name and domain that created the user. The username was bogus1, and the person who created that account was administrator in the NWTraders domain.

We can use this information to our advantage. We now want to search the event log for all entries that are related to this specific user. We got rid of the –newest parameter because we lost count of how many entries have been recently added. After you know all the entries related to your query have been retrieved, you can press CTRL+C if you wish to stop the query from running. You will notice that we switched from using the –eq (equality) operator to using the –match (regular expression match) to find our bogus1 entries. This is because there is a lot of information in the message field, and we simply want to see if our expression (bogus1) is contained within that big field. If it is, we return the default properties from the entries. This is seen here:

PS C:\> Get-EventLog -LogName Security | Where-Object { $_.message -match 'bogus
1' }

Index Time EntryType Source InstanceID Message
--------- ------------------- -------------- --------------------- --------- ----------
282274 Apr 02 21:43 SuccessA... Microsoft-Windows... 4738 The des..
282273 Apr 02 21:43 SuccessA... Microsoft-Windows... 4738 The des..
282272 Apr 02 21:43 SuccessA... Microsoft-Windows... 4724 The des..
282271 Apr 02 21:43 SuccessA... Microsoft-Windows... 4720 The des..

One more example before we go. Suppose I only wanted to return the account creation entry for the bogus1 user. This would entail creating a compound Where-Object filter. To do this, we use the up arrow to retrieve the previous command (avoids additional typing). We go to the end of the previous command, just inside the curly brackets, and add an additional operator—the AND operator. Now (the part that is confusing for some people) we need to repeat the $_ variable because we are still working with the same event record, and we specify the EventID property as we did before. The revised command is shown here along with the results from that command:

PS C:\> Get-EventLog -LogName Security | Where-Object { $_.message -match 'bogus
1' -AND $_.EventID -eq 4720 }

Index Time EntryType Source InstanceID Message
------- --------------- --------------- ---------------------- ------------ -------------
282271 Apr 02 21:43 SuccessA... Microsoft-Windows... 4720 The des...

Well, MM, that is all there is to querying the event log to find information related to user object creation. Remember, for this to be successful, you need to enable auditing for account management activities. Also as we have seen, this can generate a substantial amount of additional traffic to your security logs. Therefore, you will in all likelihood need to increase the size of your security event log, or you will need to back up your event log more frequently. We will look at these activities tomorrow as Event Log Week continues. Until then, take care.


Ed Wilson and Craig Liebendorfer, Scripting Guys

Comments (3)

  1. Alem says:

    Can you also show to script the audit filters for the application, system and security log settings please?

  2. Silver says:

    We have multiple DC's, And these changes are only stored in the local security log. So if anyone would make a change in activedirectory on another domain controller. I wouldnt see it. I therefore wrote a similar script. Except it gets servers from a text file. For each server it will get the security logs.

    However except for it taking quite awhile to get the logs, I have the problem that, when getting logs from a remote DC, I get SID's instead of usernames.

    I would love to know how to get around that.

  3. Paru says:

    in security event log many events with Event ID 4688 won't be applications started by the user. Most of these events are generated by background processes and services .how can we differ these events?

Skip to main content