[UPDATE] PowerShell - Monitor and Report Active Directory Group Membership Change

2013/11/27 | 11 minute read |

Update The most recent update is available on Github

image-left I found some time to update the script Monitor Active Directory Membership changes. This is now the version 1.6.

As a reminder, this script allows you to monitor Active Directory groups membership changes. It will send you a report via email only when a change occur. I explained in details in my last post how the script work.

See also my previous blogposts:

1.6 Changes

  • Support for Organization Unit path(s)
    • SearchRoot parameter You can know specify a Distinguished Name (DN) or a Canonical Name where all the groups you want to monitor are located.
    • SearchScope parameter
      • Base : Limits the search to the base (SearchRoot) object.The result contains a maximum of one object.
      • OneLevel : Searches the immediate child objects of the base (SearchRoot) object, excluding the base object.
      • Subtree : Searches the whole sub-tree, including the base (SearchRoot) object and all its child objects.
    • GroupScope parameter Specify the group scope of groups you want to find. Acceptable values are :
      • Global
      • Universal
      • DomainLocal
    • GroupType parameter Specify the group type of groups you want to find. Acceptable values are:
      • Security
      • Distribution
  • Support for File(s)
    • Input Specify the File where the Group are listed. DN, SID, GUID, or Domain\Name of the group are accepted
  • Update of the ParameterSetNames
    • Group
    • Organization Unit
    • List
  • Change History files changes
    • In the previous version, a file was generate for each change of each group, I decided to use only one file per group for the change history. The script will just append the new information to it.
    • Membership and Changes History files changes
    • ADD Properties DisplayName and the DateTime
  • Reporting changes
    • ADD Additional information on the Group
    • ADD Title include the DOMAIN\GroupName
    • [MailAddress] type which is available in PowerShell v3.0 has been removed on the parameter $Emailto and $EmailFrom to allow support on PowerShell 2.0. This is actually replaced by aRegular Expression validation
  • … and some more minors other changes…

Note: Unfortunately, if you were already using the script, this update won’t work with the old ChangeHistory.csv files (So you will need to archives the old ones in another directory). I added some properties and decided to keep only one file per group to track the change history, these changes are not compatible with the old files (previous to version 1.6)

Requirements

Report Example

Here is a quick example of report generated by the script.

Changes:

  • Email Subject contains the DOMAIN\GROUPNAME
  • Add some Information about the group
  • Columns in Membership Change and Change History now matche
  • New Columns DisplayName and DateTime

image-center

Using the script

In the following example I’m playing with two test groups: FXGROUP01 and FXGROUP02

image-center

For my test, I placed the script in the directory C:\LazyWinAdmin\Tool-Monitor-AD_Groups

image-center

Running the script the first time

You will notice that the script is creating folders and files. At this point you won’t get any email report.

image-center

PS C:\LazyWinAdmin\TOOL-MONITOR-AD_Group> .\TOOL-MONITOR-AD_Group.ps1 -group "FXGroup01","FXGroup02" -Emailfrom [email protected] -Emailto "[email protected]" -EmailServer 192.168.1.10 -Verbose
VERBOSE: Creating the Output Folder : C:\LazyWinAdmin\TOOL-MONITOR-AD_Group\Output
VERBOSE: Creating the ChangeHistory Folder : C:\LazyWinAdmin\TOOL-MONITOR-AD_Group\ChangeHistory
VERBOSE: GROUP: FXGroup01
VERBOSE: FXGroup01 - The following file did not exist: FX_FXGROUP01-membership.csv
VERBOSE: FXGroup01 - Exporting the current membership information into the file:
FX_FXGROUP01-membership.csv
VERBOSE: FXGroup01 - Comparing Current and Before
VERBOSE: FXGroup01 - Compare Block Done !
VERBOSE: FXGroup01 - No Change
VERBOSE: GROUP: FXGroup02
VERBOSE: FXGroup02 - The following file did not exist: FX_FXGROUP02-membership.csv
VERBOSE: FXGroup02 - Exporting the current membership information into the file:
FX_FXGROUP02-membership.csv
VERBOSE: FXGroup02 - Comparing Current and Before
VERBOSE: FXGroup02 - Compare Block Done !
VERBOSE: FXGroup02 - No Change
VERBOSE: Script Completed

Two directories and two files are created:

  • 2 FilesFor each of the groupwe just queried FXGROUP01 and FXGROUP02. Since these groups are currently empty, the script will add the value “No User or Group” in both files.
  • *OUTPUT Directory *Each time the script run, It query the group membership in the Active Directory and save the current membership in the files (It won’t touch the file if it’s the same membership at each check).
  • CHANGEHISTORY Directory contains the list of changes observed by the script. One file per Group per domain, if multiple changes occur, the script will append the change in the same file. Output Directorycontains the 2 files for each monitored groups

image-center

Each file contains the current membership of each groups. Since these are empty the script just create the following file with two properties* SamAccountName* and Name with the value “No User or Group”

image-center

The ChangeHistory Directory is empty at this point since no change was observed by the script.

image-center

Running the script a second time (without change on the groups)

If I re-run the script we will get the following output. The script does not see any change in the membership by comparing the content of the file FX_FXGROUP01-membership.csv and the current membership in Active Directory for this group.

image-center

Running the script after a change

Ok now let’s make one change and add one account in FXGROUP01 and run the script again.

PS C:\LazyWinAdmin\TOOL-MONITOR-AD_Group> .\TOOL-MONITOR-AD_Group.ps1 -group "FXGroup01","FXGroup02" -Emailfrom [email protected] -Emailto "[email protected]" -EmailServer 192.168.1.10 -Verbose
VERBOSE: GROUP: FXGroup01
VERBOSE: FXGroup01 - The following file Exists: FX_FXGROUP01-membership.csv
VERBOSE: FXGroup01 - Comparing Current and Before
VERBOSE: FXGroup01 - Compare Block Done !
VERBOSE: FXGroup01 - Some changes found

DateTime       : 20131118-08:51:10
State          : Removed
DisplayName    :
SamAccountName : No User or Group
DN             :

DateTime       : 20131118-08:51:10
State          : Added
DisplayName    :
SamAccountName : fxtest
DN             : CN=fxtest,CN=Users,DC=FX,DC=LAB

VERBOSE: FXGroup01 - Get the change history for this group
VERBOSE: FXGroup01 - Change history files: 0
VERBOSE: FXGroup01 - Save changes to a ChangesHistory file
VERBOSE: FXGroup01 - Preparing the notification email...
VERBOSE: FXGroup01 - Email Sent.
VERBOSE: FXGroup01 - Exporting the current membership to FX_FXGROUP01-membership.csv
VERBOSE: GROUP: FXGroup02
VERBOSE: FXGroup02 - The following file Exists: FX_FXGROUP02-membership.csv
VERBOSE: FXGroup02 - Comparing Current and Before
VERBOSE: FXGroup02 - Compare Block Done !
VERBOSE: FXGroup02 - No Change
VERBOSE: Script Completed

As you can see One account was added “fxtest” and the default “No User or Group” was removed by the script

image-center

Let’s make another change to this group, add another user.

PS C:\LazyWinAdmin\TOOL-MONITOR-AD_Group> .\TOOL-MONITOR-AD_Group.ps1 -group "FXGroup01","FXGroup02" -Emailfrom [email protected] -Emailto "[email protected]" -EmailServer 192.168.1.10 -Verbose
VERBOSE: GROUP: FXGroup01
VERBOSE: FXGroup01 - The following file Exists: FX_FXGROUP01-membership.csv
VERBOSE: FXGroup01 - Comparing Current and Before
VERBOSE: FXGroup01 - Compare Block Done !
VERBOSE: FXGroup01 - Some changes found

DateTime       : 20131118-09:02:49
State          : Added
DisplayName    :
SamAccountName : AnneD
DN             : CN=AnneD,CN=Users,DC=FX,DC=LAB

VERBOSE: FXGroup01 - Get the change history for this group
VERBOSE: FXGroup01 - Change history files: 1
VERBOSE: FXGroup01 - Change history files - Loading
C:\LazyWinAdmin\TOOL-MONITOR-AD_Group\ChangeHistory\FX_FXGROUP01-ChangeHistory.csv
VERBOSE: FXGroup01 - Change history process completed
VERBOSE: FXGroup01 - Save changes to a ChangesHistory file
VERBOSE: FXGroup01 - Preparing the notification email...
VERBOSE: FXGroup01 - Email Sent.
VERBOSE: FXGroup01 - Exporting the current membership to FX_FXGROUP01-membership.csv
VERBOSE: GROUP: FXGroup02
VERBOSE: FXGroup02 - The following file Exists: FX_FXGROUP02-membership.csv
VERBOSE: FXGroup02 - Comparing Current and Before
VERBOSE: FXGroup02 - Compare Block Done !
VERBOSE: FXGroup02 - No Change
VERBOSE: Script Completed

Here is the report generated

image-center

Notice this time, the report contains a section called “Change History” so you’ll be able to know which previous changes were made.

Using the SearchRoot parameter (Organization Unit path)

PS C:\LazyWinAdmin\TOOL-MONITOR-AD_Group> .\TOOL-MONITOR-AD_Group.ps1 -SearchRoot 'FX.LAB/TEST/Groups' -Emailfrom [email protected] -Emailto "[email protected]" -EmailServer 192.168.1.10 -Verbose
VERBOSE: OU: FX.LAB/TEST/Groups
VERBOSE: GROUP: CN=FXGROUP01,OU=Groups,OU=TEST,DC=FX,DC=LAB
VERBOSE: CN=FXGROUP01,OU=Groups,OU=TEST,DC=FX,DC=LAB - The following file Exists:
FX_FXGROUP01-membership.csv
VERBOSE: CN=FXGROUP01,OU=Groups,OU=TEST,DC=FX,DC=LAB - Comparing Current and Before
VERBOSE: CN=FXGROUP01,OU=Groups,OU=TEST,DC=FX,DC=LAB - Compare Block Done !
VERBOSE: CN=FXGROUP01,OU=Groups,OU=TEST,DC=FX,DC=LAB - No Change
VERBOSE: GROUP: CN=FXGROUP02,OU=Groups,OU=TEST,DC=FX,DC=LAB
VERBOSE: CN=FXGROUP02,OU=Groups,OU=TEST,DC=FX,DC=LAB - The following file Exists:
FX_FXGROUP02-membership.csv
VERBOSE: CN=FXGROUP02,OU=Groups,OU=TEST,DC=FX,DC=LAB - Comparing Current and Before
VERBOSE: CN=FXGROUP02,OU=Groups,OU=TEST,DC=FX,DC=LAB - Compare Block Done !
VERBOSE: CN=FXGROUP02,OU=Groups,OU=TEST,DC=FX,DC=LAB - No Change
VERBOSE: Script Completed

Using the File parameter

image-center

PS C:\LazyWinAdmin\TOOL-MONITOR-AD_Group> .\TOOL-MONITOR-AD_Group.ps1 -file .\groupslist.txt -Emailfrom [email protected] -Emailto "[email protected]" -EmailServer 192.168.1.10 -Verbose
VERBOSE: Loading File: .\groupslist.txt
VERBOSE: GROUP: CN=FXGROUP01,OU=Groups,OU=TEST,DC=FX,DC=LAB
VERBOSE: CN=FXGROUP01,OU=Groups,OU=TEST,DC=FX,DC=LAB - The following file Exists:
FX_FXGROUP01-membership.csv
VERBOSE: CN=FXGROUP01,OU=Groups,OU=TEST,DC=FX,DC=LAB - Comparing Current and Before
VERBOSE: CN=FXGROUP01,OU=Groups,OU=TEST,DC=FX,DC=LAB - Compare Block Done !
VERBOSE: CN=FXGROUP01,OU=Groups,OU=TEST,DC=FX,DC=LAB - No Change
VERBOSE: GROUP: FXGROUP02
VERBOSE: FXGROUP02 - The following file Exists: FX_FXGROUP02-membership.csv
VERBOSE: FXGROUP02 - Comparing Current and Before
VERBOSE: FXGROUP02 - Compare Block Done !
VERBOSE: FXGROUP02 - No Change
VERBOSE: Script Completed

Syntax

There are three different ParameterSetNames, so you will see three different SYNTAX.

  • Group
  • SearchRoot (Organization Unit path)
  • File
SYNTAX
    C:\LazyWinAdmin\TOOL-MONITOR-AD_Group\TOOL-MONITOR-AD_Group.ps1 -Group <String[]> -Emailfrom
    <String> -Emailto <String[]> -EmailServer <String> [<CommonParameters>]

    C:\LazyWinAdmin\TOOL-MONITOR-AD_Group\TOOL-MONITOR-AD_Group.ps1 -SearchRoot <String[]>
    [-SearchScope <String>] [-GroupScope <String>] [-GroupType <String>] -Emailfrom <String>
    -Emailto <String[]> -EmailServer <String> [<CommonParameters>]

    C:\LazyWinAdmin\TOOL-MONITOR-AD_Group\TOOL-MONITOR-AD_Group.ps1 -File <String[]> -Emailfrom
    <String> -Emailto <String[]> -EmailServer <String> [<CommonParameters>]

Help

<#
.SYNOPSIS
    This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership.

.DESCRIPTION
    This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership.
    It will also report the Change History made for this/those group(s).

.PARAMETER Group
    Specify the group(s) to query in Active Directory.
    You can also specify the 'DN','GUID','SID' or the 'Name' of your group(s).
    Using 'Domain\Name' will also work.

.PARAMETER Group
    Specify the group(s) to query in Active Directory.
    You can also specify the 'DN','GUID','SID' or the 'Name' of your group(s).
    Using 'Domain\Name' will also work.

.PARAMETER SearchRoot
    Specify the DN, GUID or canonical name of the domain or container to search. By default, the script searches the entire sub-tree of which SearchRoot is the topmost object (sub-tree search). This default behavior can be altered by using the SearchScope parameter.

.PARAMETER SearchScope
    Specify one of these parameter values
        'Base'      Limits the search to the base (SearchRoot) object.
                    The result contains a maximum of one object.
        'OneLevel'  Searches the immediate child objects of the base (SearchRoot)
                    object, excluding the base object.
        'Subtree'   Searches the whole sub-tree, including the base (SearchRoot)
                    object and all its child objects.

.PARAMETER GroupScope
    Specify the group scope of groups you want to find. Acceptable values are:
        'Global'; 
        'Universal'; 
        'DomainLocal'.

.PARAMETER GroupType
    Specify the group type of groups you want to find. Acceptable values are:
        'Security';
        'Distribution'.

.PARAMETER File
    Specify the File where the Group are listed. DN, SID, GUID, or Domain\Name of the group are accepted.

.PARAMETER EmailServer
    Specify the Email Server IPAddress/FQDN.

.PARAMETER EmailTo
    Specify the Email Address(es) of the Destination. Example: [email protected]

.PARAMETER EmailFrom
    Specify the Email Address of the Sender. Example: [email protected]

.EXAMPLE
    .\TOOL-Monitor-AD_Group.ps1 -Group "FXGroup" -EmailFrom "[email protected]" -EmailTo "[email protected]" -EmailServer "mail.company.com"

    This will run the script against the group FXGROUP and send an email to [email protected] using the address [email protected] and the server mail.company.com.

.EXAMPLE
    .\TOOL-Monitor-AD_Group.ps1 -Group "FXGroup","FXGroup2","FXGroup3" -EmailFrom "[email protected]" -Emailto "[email protected]" -EmailServer "mail.company.com"

    This will run the script against the groups FXGROUP,FXGROUP2 and FXGROUP3  and send an email to [email protected] using the address [email protected] and the Server mail.company.com.

.EXAMPLE
    .\TOOL-Monitor-AD_Group.ps1 -Group "FXGroup" -EmailFrom "[email protected]" -Emailto "[email protected]" -EmailServer "mail.company.com" -Verbose

    This will run the script against the group FXGROUP and send an email to [email protected] using the address [email protected] and the server mail.company.com. Additionally the switch Verbose is activated to show the activities of the script.

.EXAMPLE
    .\TOOL-Monitor-AD_Group.ps1 -Group "FXGroup" -EmailFrom "[email protected]" -Emailto "[email protected]","[email protected]" -EmailServer "mail.company.com" -Verbose

    This will run the script against the group FXGROUP and send an email to [email protected] and [email protected] using the address [email protected] and the server mail.company.com. Additionally the switch Verbose is activated to show the activities of the script.

.EXAMPLE
    .\TOOL-Monitor-AD_Group.ps1 -SearchRoot 'FX.LAB/TEST/Groups' -Emailfrom [email protected] -Emailto "[email protected]" -EmailServer 192.168.1.10 -Verbose

    This will run the script against all the groups present in the CanonicalName 'FX.LAB/TEST/Groups' and send an email to [email protected] using the address [email protected] and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script.

.EXAMPLE
    .\TOOL-Monitor-AD_Group.ps1 -file .\groupslist.txt -Emailfrom [email protected] -Emailto "[email protected]" -EmailServer 192.168.1.10 -Verbose

    This will run the script against all the groups present in the file groupslists.txt and send an email to [email protected] using the address [email protected] and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script.

.INPUTS
    System.String

.OUTPUTS
    Email Report
.NOTES
    NAME:    TOOL-Monitor-AD_Group.ps1
    AUTHOR:    Francois-Xavier CAT
    DATE:    2012/02/01
    EMAIL:    [email protected]

    REQUIREMENTS:
        -Read Permission in Active Directory on the monitored groups
        -Quest Active Directory PowerShell Snapin
        -A Scheduled Task (in order to check every X seconds/minutes/hours)

    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)

    1.3 2013.10.05
        CHANGE in the PROCESS BLOCK, the TRY CATCH blocks and placed
         them inside the FOREACH instead of inside the TRY block
        ADD support for Verbose
        CHANGE the output file name "DOMAIN_GROUPNAME-membership.csv"
        ADD a Change History File for each group(s)
         example: "GROUPNAME-ChangesHistory-yyyyMMdd-hhmmss.csv"
        ADD more Error Handling
        ADD a HTML Report instead of plain text
        ADD HTML header
        ADD HTML header for change history

    1.4 2013.10.11
        CHANGE the 'Change History' filename to
         "DOMAIN_GROUPNAME-ChangesHistory-yyyyMMdd-hhmmss.csv"
        UPDATE Comments Based Help
        ADD Some Variable Parameters
    1.5 2013.10.13
        ADD the full Parameter Names for each Cmdlets used in this script
        ADD Alias to the Group ParameterName
    1.6 2013.11.21
        ADD Support for Organizational Unit (SearchRoot parameter)
        ADD Support for file input (File Parameter)
        ADD ParamaterSetNames and parameters GroupType/GroupScope/SearchScope
        REMOVE [mailaddress] type on $Emailfrom and $EmailTo to make the script available to PowerShell 2.0
        ADD Regular expression validation on $Emailfrom and $EmailTo

        2013.11.23
        ADD ValidateScript on File Parameter
        ADD Additional information about the Group in the Report
        CHANGE the format of the $changes output, it will now include the DateTime Property
        UPDATE Help
        ADD DisplayName Property in the report

        2013.11.27
        Minor syntax changes
        UPDATE Help
#>

Download

Leave a comment