2012/03/14

Powershell - Monitor Active Directory Groups membership change

UPDATE: The most recent update is available on Github


See also those related BlogPosts:




A couple of weeks back, my boss asked me to set a quick monitoring tool to check membership change made on Active Directory groups.
In my case here i'm talking about "Domain Admins" and "Enterprise Admins"

Unfortunately we currently don't have a tool in place to do this.

So why not taking advantage of Powershell ? :-)

Required
-A Script to monitor a list of Groups
-Create a Scheduled Task to run every minutes
(if you set the Scheduled task on a Windows Server 2008R2 or a Windows 7, you might want to take a look at my previous post: Run this task every minute !!!)

Description
This script will first check the members and export the result to a CSV file (if it does not exist yet) If a file already exist, it content will be compared with the result of $Members If different an email is sent to $EmailTo email with the member who has been added or removed.

Script
http://gallery.technet.microsoft.com/Monitor-Active-Directory-4c4e04c7

#requires -version 2.0 

# ############################################################################# 
# NAME: TOOL-Monitor-AD_DomainAdmins_EnterpriseAdmins.ps1 
#  
# AUTHOR:  Francois-Xavier CAT 
# DATE:  2012/02/01 
# EMAIL: info@lazywinadmin.com 
#  
# COMMENT:  This script is monitoring group(s) in Active Directory and send an email when  
#     someone is added or removed 
# 
# REQUIRES:  
#  -Quest AD Snapin 
#  -A Scheduled Task 
# 
# VERSION HISTORY 
# 1.0 2012.02.01 Initial Version. 
# 1.1 2012.03.13 CHANGE to monitor both Domain Admins and Enterprise Admins
# 1.2 2013.09.23 FIX issue when specifying group with domain 'DOMAIN\Group'
#                CHANGE Script Format (BEGIN, PROCESS, END)
#                ADD Minimal Error handling. (TRY CATCH)
# 
# ############################################################################# 
  

BEGIN {
    TRY{
        # Monitor the following groups 
        $Groups =  "Domain Admins","Enterprise Admins"

        # The report is saved locally 
        $ScriptPath = (Split-Path ((Get-Variable MyInvocation).Value).MyCommand.Path) 
        $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" 

        # Email information
        $Emailfrom   = "sender@company.local" 
        $Emailto   = "receive@company.local" 
        $EmailServer  = "emailserver.company.local" 
  
        # Quest Active Directory Snapin 
         if (!(Get-PSSnapin Quest.ActiveRoles.ADManagement -ErrorAction Silentlycontinue)) 
          {Add-PSSnapin Quest.ActiveRoles.ADManagement}
        }
    CATCH{Write-Warning "BEGIN BLOCK - Something went wrong"}
}

PROCESS{

    TRY{
        FOREACH ($item in $Groups){

            # Let's get the Current Membership
            $GroupName = Get-Qadgroup $item
            $Members = Get-QADGroupMember $item -Indirect | Select-Object Name, SamAccountName, DN 
            $EmailSubject = "PS MONITORING - $GroupName Membership Change" 
   
            # Store the group membership in this file 
            $StateFile = "$($GroupName.domain.name)_$($GroupName.name)-membership.csv" 
   
            # If the file doesn't exist, create one
            If (!(Test-Path $StateFile)){  
                $Members | Export-csv $StateFile -NoTypeInformation 
                }
   
            # Now get current membership and start comparing it to the last lot we recorded 
            # catching changes to membership (additions / removals) 
            $Changes =  Compare-Object $Members $(Import-Csv $StateFile) -Property Name, SamAccountName, DN | 
                Select-Object Name, SamAccountName, DN,
                    @{n='State';e={
                        If ($_.SideIndicator -eq "=>"){
                            "Removed" } Else { "Added" }
                        }
                    }
  
            # If we have some changes, mail them to $Email 
            If ($Changes) {  
                $body = $($Changes | Format-List | Out-String) 
                $smtp = new-object Net.Mail.SmtpClient($EmailServer) 
                $smtp.Send($emailFrom, $emailTo, $EmailSubject, $body) 
                } 
            #Save current state to the csv 
            $Members | Export-csv $StateFile -NoTypeInformation -Encoding Unicode
        }
    }
    CATCH{Write-Warning "PROCESS BLOCK - Something went wrong"}

}#PROCESS
END{"Script Completed"}



#end region script

5 comments:

  1. Hi
    Thanks for your great script but I facing challanges in this script.

    Every time I got the alert of added and removed user list. I need only the list of if someone add or removed the groups not all users.

    also If I can connect our child domain domain admins groups then it would be great for me..

    pls guide..

    ReplyDelete
    Replies
    1. Hey Gopi, thanks for the comment.

      Not sure I understand correctly.

      You want to get an alert only if a group object is added to the group you monitor, is that right ? You don't want to receive alert when user objects are added to the monitored group.

      Please send me the script and some more details here: fxcat@lazywinadmin.com

      Thanks

      Delete
  2. Thanks Xavier for your reply,
    I have sent you mail as well describing below:
    We have parent domain and child domain scenario, While running the above script on child domain, it providing all users list like below everytime.

    Name : gopi.chand
    SamAccountName : gchand-admin
    DN : CN=chand\, gopi,OU=IT,OU=Admins,OU=Management,DC=contoso,DC=com
    State : Removed

    Name : test
    SamAccountName : test
    DN : CN=test,gopi,OU=IT,OU=Admins,OU=Management,DC=contoso,DC=com
    State : Added

    Also, We need to enterprise admin, Schema Admins of parent domain monitoring in the same script if possible.

    ReplyDelete
  3. I have modified your original script to more suit my needs in that I use ActiveDirectory modules vice quest. Additionally I also needed to be able to support a Root and Child domain at the same time.

    # Original script from: http://www.lazywinadmin.com/2012/03/powershell-monitor-membership-change-to.html

    Clear-Host

    # Import Active Directory Modules, if needed
    if (!(Get-Module ActiveDirectory)) {Import-Module ActiveDirectory}

    # A little setup for dealing with Null Groups
    $GroupName = @()
    $NullGroups = New-Object PSObject -Property @{
    Name = "No Users or Groups"
    SamAccountName = "No Users or Groups"
    }

    # - Monitoring the following Groups -

    $ChildGroups = "Domain Admins"
    $RootGroups = "Enterprise Admins","Domain Admins","Schema Admins"

    # Loop through each child domain group to add "Domain"
    foreach ($ChildGroup in $ChildGroups)
    {
    $GroupName += New-Object PSObject -Property @{
    GPName = $ChildGroup;
    Domain = 0
    }
    }
    # Loop through each Root domain group to add "domain"
    foreach ($RootGroup in $RootGroups)
    {
    $GroupName += New-Object PSObject -Property @{
    GPName = $RootGroup;
    Domain = 1
    }
    }

    # Set up the logs path (I wanted to keep the logs separate from the actual script)
    $ScriptPath = (Split-Path ((Get-Variable MyInvocation).Value).MyCommand.Path) + "\Logs"
    if (!(Test-Path $ScriptPath))
    {
    New-Item -Path $ScriptPath -ItemType Directory | Out-Null
    }

    $emailFrom = "PSGroupMonitoring@yourEmail.com"
    $EmailTo = "YourEmail@yourEmail.com"
    $smtp = "YourSMTPServerName"

    foreach ($Group in $GroupName)
    {
    switch ($Group.Domain)
    {
    0 {$domain = (Get-ADDomain).DNSRoot}
    1 {$domain = (Get-ADDomain).ParentDomain}
    }
    [array]$members = Get-ADGroupMember $Group.GPName -server $domain | select Name, SamAccountName
    Write-Host "$($Group.GPName) Count = [$($members.count)]"
    $EmailSubject = "PS MONITORING - $($Group.GPName) Membership Change"

    $StateFile = "$($Group.GPName)_$($Group.Domain)_membership.csv"

    if (!($members))
    {
    $members = $NullGroups
    }

    if (!(Test-Path (Join-Path $ScriptPath $StateFile)))
    {
    # Create the file if one does not already exist
    $members | Export-Csv (Join-Path $ScriptPath $StateFile) -NoTypeInformation
    $subj = "PS Monitoring - $($Group.GPName)"
    $body = "$($Group.GPName), is now being monitored.`n`nNew baseline files have been created and stored at $(Join-Path $ScriptPath $StateFile)"
    Send-MailMessage -To $EmailTo -From $emailFrom -Subject $subj -body $body -SmtpServer $smtp
    }

    # Get the current membership and start comparing to the last one
    $Changes = Compare-Object $Members $(Import-Csv (Join-Path $ScriptPath $StateFile)) -Property Name, SamAccountName | Select-Object Name, SamAccountName,@{n='State';e={If ($_.SideIndicator -eq "=>") {"Removed"} Else {"Added"}}}

    if ($Changes)
    {
    $body = $changes | ConvertTo-Html | Out-String
    Send-MailMessage -To $EmailTo -From $emailFrom -Subject $EmailSubject -body $body -BodyAsHtml -SmtpServer $smtp

    # Update the CVS file with new Changes, only if there are changes
    $Members | Export-csv (Join-Path $ScriptPath $StateFile) -NoTypeInformation
    }
    }

    ReplyDelete