The last few weeks, I've been spending time with Active Directory and automating management tasks with PowerShell. If you go back a page or two of posts, you'll see some of the scripts and functions I've shared. Today, I want to address something that has come up in recent comments related to tracking changes in Active Directory. A logical question is, "Who made the change?". I would also want to know "What did they change?" PowerShell alone can't get all of these answers. And if you run any decent size Active Directory infrastructure, I would hope you would invest in enterprise-grade management tools. The code I'm sharing is fine for small shops who have no choice but to do their own thing. Or for situations where you need to fill in a gap and address a short-term problem.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Event Log Digging
To get the information you want about who is making changes in Active Directory, you will have to dig into event logs. Specifically, you need to query the Security event log. And to be even more specific, you need to query the Security event log on a domain controller that can write to Active Directory. When an administrator enables a user account on DOM1, an entry is made in the Security event log. Obviously, you won't see the change in the Security event log on DOM2.
My premise, and the code I'll be showing you, assumes the default event log settings on Windows servers. If you are forwarding logs or managing them with a third-party solution, you'll need to take that into account when looking at my code. The information I'm going to show you is in the Security event log and it is server specific. How you manage these logs is up to you.
Before I dig deeper a quick caveat. Every organization has its own policies and procedures for event log management. You can only query for Active Directory related changes if the information hasn't been flushed from the Security event log. This means you might need to make sure your log files are large enough to meet your needs. In my test domain, I ran this command to increase the size to 1GB.
limit-eventlog -LogName security -ComputerName dom2,dom1 -MaximumSize 1024MB
User Management Events
I'm going to focus on user management events. Each type of event is connected to an event ID. Here's a cheater hashtable I use.
# a hashtable of user management event IDs for the Security event log
$ADEvent = @{
UserChanged = 4738
UserCreated = 4720
UserDeleted = 4726
UserEnabled = 4722
UserDisabled = 4725
}
When you manage an AD user account, you might get multiple log entries for the same activity. For example, if you create a user account and enable it, you'll most likely see a UserCreated and UserEnabled event. Probably a UserChanged as well. Keep that in mind.
To query the event log, I'm going to suggest you learn how to use Get-WinEvent. It offers more querying flexibility, is a little bit faster (I think) and when you get to PowerShell 7 is the only tool you'll have. I'll count on you to read help and examples. In the mean time, here's a sample.
Get-WinEvent -FilterHashtable @{Logname = 'Security';ID=4720;Starttime="2/1/2021"} -ComputerName dom1
This is querying for all user creation event on DOM1 that have been recorded since Feb. 1, 2021.
Let's dive into the message detail.
The event log entry will show the Subject, the account that is responsible for the activity, and the Target. In this case it looks like Company\Administrator created Company\L.Kuja. I have no idea where that account was created but I can always look that up with Get-ADUser.
While I have this up, I also want to point out the attribute list. When a user account is modified, these are the only attributes that are captured in the event log. If I change a user's Department or Title, that does not get captured by the Security event log. If this kind of audit trail is important to you, that's why you look to third-party management products.
Get-ADUserAudit
With these limitations in mind, I wrote a PowerShell function called Get-ADUserAudit. It has parameters to create a search on one or more domain controllers for different type of user management events that have been logged since a given time.
Get-ADUserAudit -event created,deleted -since "2/1/2021"
The default behavior is to search all replica domain controllers.
The function writes a custom object to the pipeline which has a default custom format view. Let me briefly explain the output. The DomainController, Since and EventType properties should be self-evident. The Targets property is a collection of all user accounts that were identified for a particular event. TargetCount is a quick way to see how many user accounts. In other words, since Feb 1, 5 user accounts were created (or at least logged) on DOM1. Administrators shows all of the admin accounts associated with the events. So in the same period, Administrator, AprilS and ArtD created the accounts. The result won't tell you which administrator created which user account. You'd have to dig back into the event log to get that information.
Of course, you can customize results however you'd like.
Get-ADUserAudit -event created,deleted,enabled,disabled -outvariable a | sort-object eventtype | format-table -GroupBy eventtype -Property since,domaincontroller,targetcount
Or here's a way to summarize events.
$a | group-object eventtype -ov b | select-object @{Name="Since";Expression={$a[0].Since}},Name,@{Name="Total";Expression = { ($_.group.targetcount | measure-object -sum).sum}}
$A is the saved result from my earlier command.
If this was something I wanted to run often, I would create a control script to save some typing. I could even take it further, because I can't help myself, and set it up as a scheduled job to create an HTML version and email it.
Where's the Code?
If you would like to see how I did all of this, or grab the code so you can try it out yourself, you can download the files from this Github gist. On a related note, you might also be interested in the Convert-EventLogRecord command which is part of the PSScriptTools module. This command makes it easier to parse out information from event log records.
Get-WinEvent -FilterHashtable @{Logname = 'Security';ID=4720;Starttime="2/1/2021"} -ComputerName dom2 |
Convert-EventLogRecord |
Format-table -GroupBy Computername -Property ID,TimeCreated,
@{Name="Admin";Expression={"$($_.SubjectDomainName)\$($_.SubjectUsername)"}},
@{Name="Account";Expression = { (Get-ADuser $_.samaccountname).DistinguishedName }}
Now I can clearly see which administrator created which account.
Let me know what you think or if any of this is helpful.
Wonderful and insightful article Jeff. I learned a lot from this article. Thanks!