Ever wanted to monitor group changes in AD? This is a script I came up with to do just that. It will collect the security log events from the last hour on all your domain controllers.
In order to use this script, just create a new scheduled task on a machine with the Active Directory module for Windows PowerShell installed and run this script every hour (or whatever you changed $time to). !! Note that the scheduled task needs to be run with an account which has domain admin privileges to be able to read from the security logs of all your domain controllers !!>
The script needs some customization to your environment:
- variable $Time, as mentioned before, needs to be changed to the interval you want the scheduled task to run
- variable $ListOfMonitoredOUs needs to be changed to contain all OU’s which contain the groups you want to monitor
- variables $FromAddress, $ToAddress and $SMTPServer need to be updated to reflect the appropriate email addresses and SMTP server
!! Note that the scheduled task needs to be run with an account which has domain admin privileges to be able to read from the security logs of all your domain controllers.
I would advice you to create and use a dedicated service account for running this scheduled task, and to limit the Logon To of this account to just the server running the scheduled task !!
#############################################################################################################################
# // Start of section Script initialization. Load all required modules and declare base variables \\
If (-not (Get-Module -Name 'ActiveDirectory' -ErrorAction SilentlyContinue)) {
Import-Module ActiveDirectory
}
# Define variables
$Domain = '<FQDN of AD domain. Eg. contoso.com>' ### Change according to your environment
$SMTPServer = '<FQDN or IP address of your SMTP server>' ### Change according to your environment
$Time = (Get-Date).AddHours(-1) ### Change according to your requirement
$ListOfMonitoredGroups = $null
$ListOfMonitoredOUs = (
"OU=OU1,$DomainDistinguishedName",
"OU=OU2,$DomainDistinguishedName",
"OU=OU3,$DomainDistinguishedName"
) ### Change according to your environment
$DomainDistinguishedName = (Get-ADDomain -Server $Domain).DistinguishedName
$ListOfDCs = Get-ADDomainController -Server $Domain -Filter { Name -like '*' } | Sort-Object
# https://www.morgantechspace.com/2013/08/active-directory-change-audit-events.html
# The following table document lists the event IDs of the Security Group Management category.
$EventIDHashTable = [ordered]@{
'4727' = 'A security-enabled global group was created'
'4728' = 'A member was added to a security-enabled global group'
'4729' = 'A member was removed from a security-enabled global group'
'4730' = 'A security-enabled global group was deleted'
'4731' = 'A security-enabled local group was created'
'4732' = 'A member was added to a security-enabled local group'
'4733' = 'A member was removed from a security-enabled local group'
'4734' = 'A security-enabled local group was deleted'
'4735' = 'A security-enabled local group was changed'
'4737' = 'A security-enabled global group was changed'
'4754' = 'A security-enabled universal group was created'
'4755' = 'A security-enabled universal group was changed'
'4756' = 'A member was added to a security-enabled universal group'
'4757' = 'A member was removed from a security-enabled universal group'
'4758' = 'A security-enabled universal group was deleted'
'4764' = 'A groups type was changed'
}
# \\ End of section Script initialization //
#############################################################################################################################
# // Start of main code section \\
## Part 1. First get the security logs from all domain controllers
$LogEntries = @()
$LogEntries = ForEach ($DC in $ListofDCs) {
Write-Host "Processing domain controller $DC"
Get-WinEvent -ComputerName $DC.Hostname -FilterHashtable @{
Logname = 'Security'
StartTime = $Time
ID = 4727, 4728, 4729, 4730, 4731, 4732, 4733, 4734, 4735, 4754, 4755, 4756, 4757, 4758, 4764
} -ErrorAction Ignore # In case you want to filter the results, pipe the results to a Where-Object clause like | Where-Object { $_.Message -notmatch "text" }
}
$LogEntries = $LogEntries | Sort-Object TimeCreated
## Part 2. Get a list of all groups located within the monitored OUs
$ListOfMonitoredGroups = ForEach ($MonitoredOU in $ListOfMonitoredOUs) {
Get-ADGroup -Server $Domain -SearchBase $MonitoredOU -Filter '*'
}
$ListOfMonitoredGroups = $ListOfMonitoredGroups | Sort-Object -Property Name
## Part 3. Filter variable $LogEntries to match only the groups located within the monitored OUs
$FilteredLog = $null
$FilteredLog = ForEach ($LogEntry in $LogEntries) {
$ListOfMonitoredGroups | ForEach-Object $_.Name {
If ($LogEntry.Message -match $_.Name) {
$LogEntry
}
}
}
$LogEntries = $FilteredLog | Sort-Object TimeCreated
##Part 4. Mail each entry in variable $LogEntries
If ($LogEntries -ne "") {
ForEach ($LogEntry in $LogEntries) {
#$Subject = "[$Domain] - Eventid " + $LogEntry.ID + " - " + $EventIDHashTable.Item($LogEntry.Id.ToString())
# Mail report
Write-Host "Emailing Report"
$SmtpClient = New-Object Net.Mail.SmtpClient($smtpServer);
$MailMessage = New-Object system.net.mail.mailmessage
$MailMessage.From = '<from address>'
$MailMessage.To.add('<mail recipient 1')
$MailMessage.To.add('<mail recipient 2')
$MailMessage.To.add('<mail recipient 3')
$MailMessage.Subject = "[$Domain] - Eventid " + $LogEntry.ID + " - " + $EventIDHashTable.Item($LogEntry.Id.ToString())
$MailMessage.IsBodyHtml = $false
$MailMessage.Body = ($LogEntry | Format-List | Out-String)
$smtpclient.Send($MailMessage)
Start-Sleep 1
}
}
# \\ End of main code section //
#############################################################################################################################
As always, please keep in mind this script is tailored to my environment, but can be used as a template for your environment. I do not pretend to be a PowerShell guru and as such my script may not be perfect. I am open to suggestions 🙂 . If you found this script useful, I’d appreciate it if you leave a comment.
Hello,
Thanks for your script.
However when I run it, I got some emails, and for some events I don’t have the Group Name. I have the variable. when I search this variable in my ActiveDirectory I get the real group.
How to get the real group name for all events ?
Examples:
OK:
TimeCreated : 3/15/2021 10:32:08 AM
ProviderName : Microsoft-Windows-Security-Auditing
Id : 4728
Message : A member was added to a security-enabled global group.
Subject:
Security ID: S-1-5-21-2356255079-306637177-891954067-11111
Account Name: User1
Account Domain: Domain1
Logon ID: 0xB9A7C48
Member:
Security ID: S-1-5-21-2356255079-306637177-891954067-22222
Account Name: CN=Firstname Lastname,OU=xxxx,OU=Users,OU=xx-xxxx,DC=vxxxxxDC=xxxxx,DC=axxxx
Group:
Security ID: S-1-5-21-2356255079-306637177-891954067-4444
Group Name: GroupName1
Group Domain: Domain1
Additional Information:
Privileges: –
NOT OK :
TimeCreated : 3/15/2021 9:56:20 AM
ProviderName : Microsoft-Windows-Security-Auditing
Id : 4728
Message : A member was added to a security-enabled global group.
Subject:
Security ID: S-1-5-21-2356255079-306637177-891954067-1111
Account Name: User2
Account Domain: Domain1
Logon ID: 0xB61DE89
Member:
Security ID: S-1-5-21-2356255079-306637177-891954067-1111
Account Name: CN=Firstname Lastname,OU=xxxx,OU=Users,OU=xxxxxx,DC=xxxxx,DC=xxxxx,DC=xxx
Group:
Security ID: S-1-5-21-2356255079-306637177-891954067-2222
Group Name: $4O4100-6RB7AH7DLQB8
Group Domain: Domain1
Additional Information:
Privileges: –
The problem is this Group Name: $4O4100-6RB7AH7DLQB8
How to get the real name of this group instead of this variable ??
Many thanks for your help and regards,
Johnny