Link to home
Start Free TrialLog in
Avatar of Jerry Seinfield
Jerry SeinfieldFlag for United States of America

asked on

powershell script to monitor any changes to AD groups

Hello Experts,

Can you help me to write a report that would be emailed to an AD user weekly to monitor these AD groups?

SQLPRODAdmins
SQLReadOnly

The idea to know any changes to membership, updates, removes, any task performed over those groups and generate a report weekly that is sent via email to a user
Avatar of sirbounty
sirbounty
Flag of United States of America image

Something like this?
$groups = @('SQLPRODAdmins','SQLReadOnly')
foreach ($group in $groups) {
  $members += @(get-adgroupmember $group | select-object Name))
}

send-mailmessage -smtp mailserver.domain.com -from Admin@domain.com -to User@domain.com -subject 'SQL Group report' -body $members

Open in new window

Avatar of Jerry Seinfield

ASKER

Thanks Sir,

Will the script above monitor for every single change to each of the groups defined? i.e, membership, additions, removals, and so on? What line is supposed to do that?

Thanks in advance
No, it would produce an output of members only.
If you want to produce a report on changes only, you could alter it slightly:

First, create a baseline for both:
get-adgroupmember SQLPRODAdmins | sort-object Name | select-object Name | out-file c:\folder\ProdAdmins.txt
get-adgroupmember SQLReadOnly | sort-object Name | select-object Name | out-file c:\folder\SQLRO.txt

Then you could perform a comparison.  Do you want to show only those added/removed?

$SQLAdmins = (get-adgroupmember SQLPRODAdmins | sort-object Name | select-object Name )
$SQLRO = (get-adgroupmember SQLReadOnly | sort-object Name | select-object Name )

$AdminDiff = compare-object $SQLAdmins c:\folder\SQLAdmins.txt
$RODiff = compare-object $SQLRO c:\folder\SQLRO.txt


$body = $AdminDiff | select @{label='Admin differences';expression={$_.inputobject.anem + ' ' + $_.sideindicator}}
$body += $RODiff | select @{label='Read only differences';expression={$_.inputobject.anem + ' ' + $_.sideindicator}}

send-mailmessage -smtp mailserver.domain.com -from Admin@domain.com -to User@domain.com -subject 'SQL Group report' -body $body

Open in new window

I want to show everything  added, removed, and any other change that you might think of
The above should indicate members in the original that are not in the new query, with a <= symbol, and those members in the new query that were not in the original with a => symbol.  You can include those that are in both, but I think that's not going to help based on what you've described.
The only thing left would be to refresh the file each time it launches, otherwise all updates you get will be based off of that original output.
Let me know if that sorts it for you and we can easily make that adjustment...
That works for me.

how would the final script look?
Again, remember - this needs to be run first to identify the current state of both groups: (changing the folder/file location as desired)
get-adgroupmember SQLPRODAdmins | sort-object Name | select-object Name | out-file c:\folder\ProdAdmins.txt
get-adgroupmember SQLReadOnly | sort-object Name | select-object Name | out-file c:\folder\SQLRO.txt

Open in new window


Then you simply target that same file to be overwritten each time the script executes.  With the count property, you will only get notified if there was a change in membership - otherwise, no news is good news...
$SQLAdmins = (get-adgroupmember SQLPRODAdmins | sort-object Name | select-object Name )
$SQLRO = (get-adgroupmember SQLReadOnly | sort-object Name | select-object Name )

$AdminDiff = compare-object $SQLAdmins c:\folder\SQLAdmins.txt
$RODiff = compare-object $SQLRO c:\folder\SQLRO.txt

if ($AdminDiff.count -gt 0) {$body = $AdminDiff | select @{label='Admin differences';expression={$_.inputobject.anem + ' ' + $_.sideindicator}}}
if ($RODiff.count -gt 0) {$body += $RODiff | select @{label='Read only differences';expression={$_.inputobject.anem + ' ' + $_.sideindicator}}}

send-mailmessage -smtp mailserver.domain.com -from Admin@domain.com -to User@domain.com -subject 'SQL Group report' -body $body

$SQLAdmins | out-file c:\folder\ProdAdmins.txt
$SQLRO | out-file c:\folder\SQLRO.txt

Open in new window

Thank you Sir,

Did you have a chance to test the script?
So far as I can, sure.  But I don't have those specific groups in my environment. :^)
the first part of the script runs, the second part did not

see error on the attached screenshot

and the final code with my variables, patch and groups names in AD

$SQLAdmins = (get-adgroupmember 'SQL Admins' | sort-object Name | select-object Name )
$SQLRO = (get-adgroupmember SQLReadOnly | sort-object Name | select-object Name )
$NonSQLAdmins = (get-adgroupmember 'SQL Admins NonProd' | sort-object Name | select-object Name )

$AdminDiff = compare-object $SQLAdmins c:\temp\ProdAdmins.txt
$RODiff = compare-object $SQLRO c:\temp\SQLRO.txt
$NonAdminDiff = compare-object $NonSQLAdmins c:\temp\NonProdAdmins.txt

if ($AdminDiff.count -gt 0) {$body = $AdminDiff | select @{label='Admin differences';expression={$_.inputobject.anem + ' ' + $_.sideindicator}}}
if ($RODiff.count -gt 0) {$body += $RODiff | select @{label='Read only differences';expression={$_.inputobject.anem + ' ' + $_.sideindicator}}}
if ($NonAdminDiff.count -gt 0) {$body = $NonAdminDiff | select @{label='Non Admin differences';expression={$_.inputobject.anem + ' ' + $_.sideindicator}}}

send-mailmessage -smtp relayprod.DOMAIN.com -from donetreply@DOMAIN.com -to XXXX@DOMAIN.com -subject 'SQL Group report' -body $body

$SQLAdmins | out-file c:\temp\ProdAdmins.txt
$SQLRO | out-file c:\temp\SQLRO.txt
$NonSQLAdmins | out-file c:\temp\NonProdAdmins.txt
TrackADGroup.jpg
Ah - need to read in the file, not reference - my mistake:
$AdminDiff = compare-object $SQLAdmins (get-content c:\temp\ProdAdmins.txt)
$RODiff = compare-object $SQLRO (get-content c:\temp\SQLRO.txt)
$NonAdminDiff = compare-object (get-content $NonSQLAdmins c:\temp\NonProdAdmins.txt)

Open in new window

how would look the final second script? can you please paste the final code here? see my  updates above,
Use the code block - it helps with copy/pasting the script sections:


$SQLAdmins = (get-adgroupmember 'SQL Admins' | sort-object Name | select-object Name )
$SQLRO = (get-adgroupmember SQLReadOnly | sort-object Name | select-object Name )
$NonSQLAdmins = (get-adgroupmember 'SQL Admins NonProd' | sort-object Name | select-object Name )

$AdminDiff = compare-object $SQLAdmins (get-content c:\temp\ProdAdmins.txt)
$RODiff = compare-object $SQLRO (get-content c:\temp\SQLRO.txt)
$NonAdminDiff = compare-object $NonSQLAdmins (get-content c:\temp\NonProdAdmins.txt)

if ($AdminDiff.count -gt 0) {$body = $AdminDiff | select @{label='Admin differences';expression={$_.inputobject.anem + ' ' + $_.sideindicator}}}
if ($RODiff.count -gt 0) {$body += $RODiff | select @{label='Read only differences';expression={$_.inputobject.anem + ' ' + $_.sideindicator}}}
if ($NonAdminDiff.count -gt 0) {$body = $NonAdminDiff | select @{label='Non Admin differences';expression={$_.inputobject.anem + ' ' + $_.sideindicator}}}

send-mailmessage -smtp relayprod.DOMAIN.com -from donetreply@DOMAIN.com -to XXXX@DOMAIN.com -subject 'SQL Group report' -body $body

$SQLAdmins | out-file c:\temp\ProdAdmins.txt
$SQLRO | out-file c:\temp\SQLRO.txt
$NonSQLAdmins | out-file c:\temp\NonProdAdmins.txt

Open in new window

no luck,

see attached error
no attachment?
Try this slight adjustment - condenses some of the code since we have a 3rd group...you'll need to re-run the initial file creation piece, here, to have the files appropriately named:
foreach ($group in @('SQL Admins', 'SQLReadOnly', 'SQL Admins NonProd') {get-adgroupmember $group | sort-object name | select-object -expand name | out-file "c:\temp\$group.txt")

Open in new window


Then launch this code - of course, you would need to manually manipulate the source file(s) to get an indication of the differences:
$SQLAdmins = (get-adgroupmember 'SQL Admins' | sort-object Name | select-object -expand Name )
$SQLRO = (get-adgroupmember SQLReadOnly | sort-object Name | select-object -expand Name )
$NonSQLAdmins = (get-adgroupmember 'SQL Admins NonProd' | sort-object Name | select-object -expand Name )


foreach ($group in @('SQL Admins', 'SQLReadOnly', 'SQL Admins NonProd') {
  $groupMembers =  get-adgroupmember $group | sort-object name | select-object -expand name 
  $difference = compare-object $groupMembers (get-content "c:\temp\$group.txt")
  if ($difference.count -gt 0) {
    $notify=$true
    $body += ($difference | select-object @{label="$group differences";expression={$_.inputobject.anem + ' ' + $_.sideindicator}}})
    $groupMembers | out-file "c:\temp\$group.txt"
  }

if ($notify) {
  send-mailmessage -smtp relayprod.DOMAIN.com -from donetreply@DOMAIN.com -to XXXX@DOMAIN.com -subject 'SQL Group report' -body $body
}

Open in new window

still no luck

attached screenshot with the error found
grouperror2.jpg
Try this version:
$SQLAdmins = (get-adgroupmember 'SQL Admins' | sort-object Name | select-object -expand Name )
$SQLRO = (get-adgroupmember SQLReadOnly | sort-object Name | select-object -expand Name )
$NonSQLAdmins = (get-adgroupmember 'SQL Admins NonProd' | sort-object Name | select-object -expand Name )

foreach ($group in @('SQL Admins', 'SQLReadOnly', 'SQL Admins NonProd') {
  $groupMembers =  get-adgroupmember $group | sort-object name | select-object -expand name 
  $difference = compare-object $groupMembers (get-content "c:\temp\$group.txt")
  if ($difference.count -gt 0) {
    $notify=$true
    $body += ($difference | select-object @{label="$group differences";expression={$_.inputobject.name + ' ' + $_.sideindicator}})
    $groupMembers | out-file "c:\temp\$group.txt"
  }
}

if ($notify) {send-mailmessage -smtp relayprod.DOMAIN.com -from donetreply@DOMAIN.com -to XXXX@DOMAIN.com -subject 'SQL Group report' -body $body}

Open in new window

no luck, but I believe we almost there, see error attached
GROUpError3.jpg
Stupid type-o's - I swear I dropped this into the ISE and no errors were raised...ugh. o.O
Try this...

$SQLAdmins = (get-adgroupmember 'SQL Admins' | sort-object Name | select-object -expand Name )
$SQLRO = (get-adgroupmember SQLReadOnly | sort-object Name | select-object -expand Name )
$NonSQLAdmins = (get-adgroupmember 'SQL Admins NonProd' | sort-object Name | select-object -expand Name )

foreach ($group in @('SQL Admins', 'SQLReadOnly', 'SQL Admins NonProd')) {
  $groupMembers =  get-adgroupmember $group | sort-object name | select-object -expand name 
  $difference = compare-object $groupMembers (get-content "c:\temp\$group.txt")
  if ($difference.count -gt 0) {
    $notify=$true
    $body += ($difference | select-object @{label="$group differences";expression={$_.inputobject.name + ' ' + $_.sideindicator}})
    $groupMembers | out-file "c:\temp\$group.txt"
  }
}

if ($notify) {send-mailmessage -smtp relayprod.DOMAIN.com -from donetreply@DOMAIN.com -to XXXX@DOMAIN.com -subject 'SQL Group report' -body $body}

Open in new window

no luck Sir

see attached, different error. it seems like is unable to find a file in a path
groupError5.jpg
Yes, did you re-run the initial creation process?  Do you see those files present in c:\temp?
Yep, I ran the following and I can see the files on same path

get-adgroupmember 'SQL Admins' | sort-object Name | select-object Name | out-file c:\temp\ProdAdmins.txt
get-adgroupmember SQLReadOnly | sort-object Name | select-object Name | out-file c:\temp\SQLRO.txt
get-adgroupmember 'SQL Admins NonProd' | sort-object Name | select-object Name | out-file c:\temp\NonProdAdmins.txt

then I ran the other script

foreach ($group in @('SQL Admins', 'SQLReadOnly', 'SQL Admins NonProd') {get-adgroupmember $group | sort-object name | select-object -expand name | out-file "c:\temp\$group.txt")

and finally the problematic one

$SQLAdmins = (get-adgroupmember 'SQL Admins' | sort-object Name | select-object -expand Name )
$SQLRO = (get-adgroupmember SQLReadOnly | sort-object Name | select-object -expand Name )
$NonSQLAdmins = (get-adgroupmember 'SQL Admins NonProd' | sort-object Name | select-object -expand Name )

foreach ($group in @('SQL Admins', 'SQLReadOnly', 'SQL Admins NonProd')) {
  $groupMembers =  get-adgroupmember $group | sort-object name | select-object -expand name
  $difference = compare-object $groupMembers (get-content "c:\temp\$group.txt")
  if ($difference.count -gt 0) {
    $notify=$true
    $body += ($difference | select-object @{label="$group differences";expression={$_.inputobject.name + ' ' + $_.sideindicator}})
    $groupMembers | out-file "c:\temp\$group.txt"
  }
}

if ($notify) {send-mailmessage -smtp relayprod.domain.com -from donetreply@domain.com -to xxx@domain.com -subject 'SQL Group report' -body $body}


is that the logical sequence of running the scripts?
This is no longer needed...
get-adgroupmember 'SQL Admins' | sort-object Name | select-object Name | out-file c:\temp\ProdAdmins.txt
get-adgroupmember SQLReadOnly | sort-object Name | select-object Name | out-file c:\temp\SQLRO.txt
get-adgroupmember 'SQL Admins NonProd' | sort-object Name | select-object Name | out-file c:\temp\NonProdAdmins.txt

Open in new window

We replaced it with this loop:
foreach ($group in @('SQL Admins', 'SQLReadOnly', 'SQL Admins NonProd') {get-adgroupmember $group | sort-object name | select-object -expand name | out-file "c:\temp\$group.txt")

Open in new window

If those files are being created, then the last code block, which is the script you'd need to schedule, is all that's needed.
What's in C:\temp?
C:\Temp is a folder where i am placing all files
ASKER CERTIFIED SOLUTION
Avatar of sirbounty
sirbounty
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
While most of the solutions here are good solutions they have one flaw. They don't tell you when the change happend and who did that change. If you want that information (and you need to be precise) you need to dive into Event  logs. I've written such script. It's still work in progress but it does send email daily with summary of group membership changes https://evotec.xyz/monitoring-active-directory-changes-on-users-and-groups-with-powershell/  hope it helps.