Will you be notified when there are changes to group memberships? No? Memberships in groups are particularly interesting. Especially if it is the group of the domain administrators. The following article shows how to recognize changes and then check them at regular intervals. The administrator should be notified of any changes. This can be done by message or e-mail. Instead of configuring Audit Policies we do everything in PowerShell and then we put our script into a scheduled task.
The Goal
We want to achieve the following.
If there are membership changes in the Domain Admins Group, then notifiy me by E-Mail or Message or whatever. It could be look like this:
Or this:
Introduction
First we have to retrieve all Domain Admin group members.
Get-ADGroupMember -Identity "Domain Admins").Name
Alternatively, we could save it in a file Admins.txt:
(Get-ADGroupMember -Identity "Domain Admins").Name | Out-File C:\Temp\Admins.txt
I now add a new user to the group. His name is Arnold Schwarzenberg. Similarities to famous persons are purely coincidental. 😉
Then I save the result in another file Admins2.txt
(Get-ADGroupMember -Identity "Domain Admins").Name | Out-File C:\Temp\Admins2.txt
Now let’s have a look at both files.
Our next step is to compare the content of both files. In order to do that I use Compare-Object.
$a=Get-Content C:\Temp\Admins.txt $b=Get-Content C:\Temp\Admins2.txt $differ=Compare-Object -ReferenceObject $a -DifferenceObject $b | Select-Object -ExpandProperty InputObject
Fine. We’ve got him!
Another method is to save the group members in a variable. This is my preferred way. I don’t like those text files 😉
$ref=(Get-ADGroupMember -Identity "Domain Admins").Name $diff=(Get-ADGroupMember -Identity "Domain Admins").Name
Create a Script to compare membership on a regular basis once per day
Open PowerShell ISE. Let’s put it all together in a script. The first script initiates a Message on the Administrator’s desktop. It checks the members and then waits for about an hour. Then the script checks again and compares the result. Now we use the SideIndicator. This indicator could be helpful if you only want to query changes of values in one direction. The valid values are => and <=
$ref=(Get-ADGroupMember -Identity "Domain Admins").Name Start-Sleep -Seconds 86398 $diff=(Get-ADGroupMember -Identity "Domain Admins").Name $result=(Compare-Object -ReferenceObject $ref -DifferenceObject $diff | Where-Object {$_.SideIndicator -eq "=>"} | Select-Object -ExpandProperty InputObject) -join ", " If ($result) {msg * "The following user was added to the Domain Admins Group: $result"}
The second one sends an alert E-Mail.
$ref=(Get-ADGroupMember -Identity "Domain Admins").Name Start-Sleep -Seconds 86398 $diff=(Get-ADGroupMember -Identity "Domain Admins").Name $date=Get-Date -Format F $result=(Compare-Object -ReferenceObject $ref -DifferenceObject $diff | Where-Object {$_.SideIndicator -eq "=>"} | Select-Object -ExpandProperty InputObject) -join ", " If ($result) {Send-MailMessage -From SecurityAlert@domain.com -To p.gruenauer@domain.com -SmtpServer EX01 -Subject "Domain Admin Membership Changes | $result was added to the Group" -Body "This alert was generated at $date" -Priority High}
Note the last line. You must fill in these values. Make sure your Mailserver accepts E-Mails from your computer.
The Test
For testing, I’ve simplified the script and sent PowerShell to sleep for only 20 seconds. In this period I quickly add a user to the domain admins group. That was stressful 😉
We’ve got him again!
Now save your script as PowerShell file. (ps1)
Put it into a Scheduled Task
No matter which notification you prefer, you should put it all into a scheduled task that will run every 60 minutes, for example. Watch the Argument in the first line (location of your script), the RepititionInterval in the second line and the UserId in the 4th line. You have to modify this values, especially the UserId.
$Action=New-ScheduledTaskAction -Execute "powershell" -Argument "C:\Alerts\domain_admins.ps1" $Trigger=New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Seconds 86400) -RepetitionDuration ([timespan]::MaxValue) $Set=New-ScheduledTaskSettingsSet $Principal=New-ScheduledTaskPrincipal -UserId "sid-500\administrator" -LogonType S4U $Task=New-ScheduledTask -Action $Action -Trigger $Trigger -Settings $Set -Principal $Principal Register-ScheduledTask -TaskName "Domain Admins Check" -InputObject $Task -Force
Create a script based on a baseline
The disadvantage of the method above is that we have a time gap of 2 seconds per day.
If the administrator group membership changes very rarely, I recommend creating a baseline. First save your baseline to a file.
(Get-ADGroupMember -Identity "Domain Admins").Name | Out-File C:\Temp\Admins.txt
Then create a script which compares the group membership against your baseline.
$base=Get-Content C:\Temp\Admins.txt $diff=(Get-ADGroupMember -Identity "Domain Admins").Name $result=(Compare-Object -ReferenceObject $base -DifferenceObject $diff | Where-Object {$_.SideIndicator -eq "=>"} | Select-Object -ExpandProperty InputObject) -join ", " If ($result) {msg * "The following user was added to the Domain Admins Group: $result"}
Now put this script into a Scheduled Task and run it as often as you like.
Adaption
A further idea would be creating two Scheduled Tasks. (One for the reference, the second for the differences). Another possibility would be to configure audit logging in combination with event log subscriptions.
Anyway: Take the opportunity to use the ideas shown above and adapt it.
See also
See also my other article in which I created a small script which informs you if a Domain Controller is down: PowerShell: Alert me, if a Domain-Controller is down (Try + Catch)
Categories: Cyber Security, PowerShell, Windows Server
One significant oversight in that process is handling nested groups. We’ve typically nested groups in the Domain Admins group to define roles and service accounts to control access needs.
If an account was added to a nested group, this script would not catch it.
LikeLike
Hallo Marc, thx your the advice. Yes, in nested groups this script will in it’s original state not work. As mentioned, if you want to do real time professionale monitoring the script has to be more than 10 lines of code for sure.
LikeLike
Thanks for this one!
How do I list members of a security group and send it with e-mail with powershell? 🙂
LikeLike
Hi, replace Domain Admins with the name of your security group. Regards, P
LikeLike
I run this every 10 minutes from the monitoring software, PRTG. It alerts if the value changes:
$users = Get-ADGroupMember -Identity ‘Domain Admins’
$admins = $users.count
write-host $admins,”:OK”
LikeLiked by 1 person
Hello,
I am also using PRTG but this script doesn’t work with me.It all shows green when I added users to Domain Admins group:) If possible could you give me further details.
Thanks in advance
LikeLike
Hi
You need to change the “If Value Changes” on the settings page – this way everything will bumble along as normal until PRTG detects there’s been a change in the number of AD admins 🙂
LikeLike
thanks, but the pictures do not show up. Can u check it?
Good work
LikeLike
Hi Onur! You can’t see the screenshots?
LikeLike
Hi,
I tried chrome, explorer and edge.
Best Regards,
LikeLike
I can see all screens on every browser !?
LikeLike
This isn’t perfect but something to play with. Avoid the scheduled job.
function watch-group {
param ($groupname)
$membercount = (get-adgroupmember $groupname).count
start-job -name watchgroup -scriptblock {
param ($groupname,
$membercount)
while ($true) {
$searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]”)
$searcher.Filter = “(&(objectClass=group)(samAccountName=$groupname))”
$result = $searcher.FindOne()
$group = [ADSI]$result.path
if ($membercount -ne $group.member.count) {
$host.ui.rawui.windowtitle = “Group membership changed.”
}
start-sleep -seconds 30
}
} -argumentlist $groupname, $membercount | out-null
if (!($timer)) {
$timer = new-object timers.timer
$action = {
$Global:windowtitle = receive-job -name watchgroup -keep
}
$timer.Interval = 10000
$Timer.Enabled = $True
Register-ObjectEvent -InputObject $timer -EventName elapsed –SourceIdentifier groupTimer -Action $action | Out-Null
$Timer.Start()
}
}
watch-group ‘domain admins’
LikeLike
Thx for your suggestion
LikeLiked by 1 person
Note that if you do not save it as a scheduled task, the script will not run when the server is restarted.
LikeLike
True. Just an alternative if you wanted to watch it while you work. I primarily use this method to play my itunes 🙂
LikeLiked by 1 person
Perfekt, vielen Dank. Funktioniert wie gewünscht.
LikeLike
Danke fürs Testen auf einem anderen System. Bei so langen Codes und Blog Posts ist man ohne Reviewer ziemlich alleine 😉 Lg, P
LikeLike