It can be difficult to track exactly who made certain changes when the Active Directory domain infrastructure is managed by multiple administrators (added or removed a user from a security group). This article shows how to use an audit policy and a PowerShell script to automatically alert administrators (via e-mail or Messenger) when a new user is added to one of the privileged (administrative) AD groups (such as Domain Admins
, Enterprise Admins
, Schema Admins
, etc.).
Enable Audit Policy for AD Group Membership Changes
Enable Group Policy to audit AD security group policy changes on the domain controllers.
- Open the domain Group Policy Management snap-in (
gpmc.msc
); - Edit the Default Domain Controller Policy;
- Navigate to Computer Configuration -> Policies -> Windows Settings -> Security Settings -> Advanced Audit Configuration -> Account Management. Enable the policy Audit Security Group Management with Success events logging;
- Once the new GPO settings have been applied, any changes to AD groups (creation, deletion, adding/removing users to/from groups) will result in an event being logged in the security log on the domain controller.
When a user is added to a group, an event with EventID 4732 will appear:
A member was added to a security-enabled global group.
Or the EventID 4728:
A member was added to a security-enabled local group.
The event description contains the following info:
- Which security group has been changed: Group Name:
DnsAdmins
- The user who has been added to the group: Member:
Security ID:
- Who has made a change to the group membership:
Subject: Account Name:
Report on Changed Privileged AD Groups with PowerShell
You can use the Get-WinEvent PowerShell cmdlet to get information about all the recent 4732 and 4728 events from the Event Viewer on a domain controller in the last 24 hours:
$time = (get-date) - (new-timespan -hour 24) $result = Get-WinEvent -FilterHashtable @{logname='Security';id=4732,4728;StartTime=$Time} -ErrorAction SilentlyContinue| ForEach-Object { $eventXml = ([xml]$_.ToXml()).Event [PSCustomObject]@{ TimeCreated = $eventXml.System.TimeCreated.SystemTime -replace '\.\d+.*$' NewUser = $eventXml.EventData.Data[0]."#text" Group = $eventXml.EventData.Data[2]."#text" Admin = $eventXml.EventData.Data[6]."#text" Computer = $eventXml.System.Computer } } $result | Format-Table -AutoSize
The script results will include the name of the changed AD group, which user account was added, and which administrator made the change (script based on the example in the post How to detect who created a user account in Active Directory).
Create a variable to filter changes to privileged user groups only:
$AdminGroups = @('Enterprise Admins', 'Domain Admins', 'DNSAdmins', 'Microsoft Exchange Servers')
Now you only need to filter the events where privileged groups have been changed:
$filteredevents=$result | Where-Object { $_.Group -in $AdminGroups }
Alert Admin When a New User is Added to the AD Group
Now you need to notify administrators of changes to privileged groups in AD. You can use the Send-MailMessage cmdlet to send an e-mail notification:
If ($filteredevents) { $mailbody= $filteredevents|ConvertTo-Html {Send-MailMessage -SmtpServer war-msg01 -From [email protected] -To [email protected] -Subject "AD administrative groups have changed!" -Body $mailbody -Priority High} }
Or send an alert to your messenger:
- How to send notifications to Telegram from a PowerShell script
- Sending a message to an MS Teams channel using PowerShell
Monitor Active Directory Group Membership Changes Across All DCs
If you have more than one domain controller, you will need to run this script on each of them.
Group membership change events are logged on the domain controller that your ADUC snap-in or PowerShell console (with the Add-ADGroupMember
command) is connected to (this is usually the current Logon Server). As a result, you will need to check each DC to see if the specified events are present. You can use the script to loop through all the domain controllers:
$DCs = Get-ADDomainController -Filter * foreach ($DC in $DCs){ Get-WinEvent -ComputerName $DC -FilterHashtable … … }
But it’s more convenient to create a scheduler task on each of the DCs that will run your PowerShell script on a scheduled basis.
- Copy the PS1 file that contains the PowerShell script to the NETLOGON directory (
\\woshub.com\NETLOGON
); - Assign a new GPO that creates a new scheduler task to the Domain Controllers OU (Computer Configuration -> Preferences -> Control Panel Settings -> Scheduled Tasks). This is the task that will run your PowerShell script:
On a schedule ->Daily
Action:Start a program
Program/Script:C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe
Add Arguments (optional):-ExecutionPolicy Bypass -command "& \\woshub.loc\NETLOGON\CheckADAdminsGroups.ps1"
- This means that the membership of the admin groups will be checked once a day and if new users are added to them, the admin will be notified. The notification will include the privileged group name, the name of the new user, and information about who added the user to the AD group.
In domain infrastructures with a large number of Domain Controllers and administrators, you can use a centralized system to collect the logs from all of the DCs (ELK, Graylog, Splunk, etc.).
3 comments
I tried the ‘Comparing the Current Members of the Domain Group with the Saved Template’ part but it doesn’t work. It writes the files with the correct content but something in diff goes wrong.
(Get-ADGroupMember -Identity “Domain Admins” -recursive).Name | Out-File C:\PS\DomainAdmins.txt
(Get-ADGroupMember -Identity “Domain Admins” -recursive).Name | Out-File C:\PS\DomainAdminsActual.txt
$old_adgroup_members=GC C:\PS\DomainAdmins.txt
$new_adgroup_members=GC C:\PS\DomainAdminsActual.txt
$diff=Compare-Object -ReferenceObject $old_adgroup_members -DifferenceObject $new_adgroup_members | Select-Object -ExpandProperty InputObject
write-host $diff
$result=(Compare-Object -ReferenceObject $old_adgroup_members -DifferenceObject $diff | Where-Object {$_.SideIndicator -eq “=>”} | Select-Object -ExpandProperty InputObject) -join “, ”
If ($result)
{msg * “A user $result has been added to Domain the Admins group”}
Compare-Object : Cannot bind argument to parameter ‘DifferenceObject’ because it is null.
At C:\ps\da2.ps1:7 char:81
+ … -ReferenceObject $old_adgroup_members -DifferenceObject $diff | Where …
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Compare-Object], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.CompareObje
ctCommand
Please, make sure that your DomainAdmins.txt and DomainAdminsActual.txt files are not empty.
This only seems to work if you add users to security groups on the domain controller itself, not if someone adds a user on their workstation — it won’t generate an event on the DC. I’m only getting event ID 4728 when the user is added locally on the DC. If I add a user to a security group on my workstation via AD, I generate event ID 4732 on my local workstation, but nothing on the DC.