2013/10/13

PowerShell - Monitor and Report Active Directory Group Membership Change


UPDATE 2016/05/03: The most recent update is available on Github

See also the related blogpost: http://www.lazywinadmin.com/2013/11/update-powershell-monitor-and-report.html

Today I will update a post that I published at the beginning of last year : Monitor Active Directory Membership changes. I updated the script to add some of the things I learned during the Scripting Games 2013 back in April/May. The script will also create a nice html report and send it via Email.

Basically, the script will monitor the Active Directory groups that you specify and notify you if a change occurred since the last time it checked.




Overview

  • How does the script works ?
    • Main Process
    • Comparing
    • Change History
    • Reporting
    • What does this script does not cover ?
  • Workflow
  • Requirements
    • PowerShell 3.0
    • Quest Active Directory Snapin
    • A Schedule task (Automate it!)
    • Permissions
  • Running the Script the first time
  • Running the Script after the first change
  • Running the Script after the second change
  • Comments Based Help
  • Download




How does the script works ?


Main Process

This script checks an Active Directory Group membership that you specify and notify you if a change occurred since the last time it checked.

In order to find the right group to monitor, you can specify the group Name, SID(Security Identifier), GUID(Globally Unique IDentifier) or DN (Distinguished Name).
Group name like 'DOMAIN\GROUPNAME' will also work.

Comparing
The membership of each group is saved in a CSV file "DOMAIN_GROUPNAME-membership.csv"
If the file does not exist, the script will create one, so the next time it will be able to compare the  membership with this file.

Change History
Each time a change is detected (Add or Remove an Account (Nested or Not)) a CSV file will be generated with the following name: "DOMAIN_GROUPNAME-ChangesHistory-yyyyMMdd-hhmmss.csv"

When generating the HTML Report, the script will add this Change History to the Email (if there is one to add)

Reporting

Here is an example of report generated when a change is detected.
You can see the user 'catfx' was removed from the group FX\FXGROUP
Also, If the script find some Change History files for this group, it will be added to the report.
Finally at the end of the report, information on when, where and who ran the script.

Example of Email Report generated by the script


What does this script does not cover ?


This script won't:

  • Use the Active Directory module (Next update)
  • Tell you who changed the Group Membership in Active Directory (You would need to active Auditing in Active Directory)
  • Create the Schedule Task for you
  • Generate a HTML file (Next update)



Workflow


The script is a bit complex to understand, so I created a small workflow that explain how each parts interact with each other:








Requirements

PowerShell v3.0

 You will need PowerShell version 3.0 to take advantage [mailaddress] which make a validation on the Source and Destination Email addresses.

 Quest Active Directory PowerShell Snappin

I used the Quest Active Directory Snapin to do my queries, get it here
In the next update I plan to add support for the Active Directory module (maybe ADSI too).

A Scheduled task (Automate it!!)


You will need to configure a Scheduled task that will run your task for you every minute
  • Make sure the account that will run the task is an Administrator OR is member of the Local Security Policy "Log on as a batch job".
  • Create your task and make sure it runs every minutes!
    • I recommend to assign a dedicated AD account to this task
Local Security Policy console


Permissions

This PowerShell script will need some rights, it will:
  • Create CSV files inside of the script parent directory
    • Make sure the account you use for this job has the rights to write files inside the parent directory of this script (this does not apply if the account is part of the Local Administrators group)
  • Access the Active Directory (read)
    • Make sure the account you use has the rights to read the membership of the group(s) you want to monitor.



Running the Script the first time

In this example I run the script against 2 Active Directory Groups with the verbose parameter so it will let me know what it's doing.
.\TOOL-MONITOR-AD_Groups.ps1 -verbose -Group "FXGROUP", "FXGROUP2" -EmailServer "mail.fx.local" -EmailTo "catfx@fx.local" -EmailFrom "PowerShellMonitoring@fx.local"
VERBOSE: Creating the Output Folder : C:\LazyWinAdmin\Output
VERBOSE: Creating the ChangeHistory Folder : C:\LazyWinAdmin\ChangeHistory
VERBOSE: GROUP: FXGROUP
VERBOSE: FXGROUP - The following file did not exist: FX_FXGROUP-membership.csv
VERBOSE: FXGROUP - Exporting the current membership information into the file: FX_FXGROUP-membership.csv
VERBOSE: FXGROUP - Comparing Current and Before
VERBOSE: FXGROUP - Compare Block Done !
VERBOSE: FXGROUP - No Change
VERBOSE: GROUP: FXGROUP2
VERBOSE: FXGROUP2 - The following file did not exist: FX_FXGROUP2-membership.csv
VERBOSE: FXGROUP2 - Exporting the current membership information into the file: FX_FXGROUP2-membership.csv
VERBOSE: FXGROUP2 - Comparing Current and Before
VERBOSE: FXGROUP2 - Compare Block Done !
VERBOSE: FXGROUP2 - No Change
VERBOSE: Script Completed

As you can see, the first time you run the script it will create the two directories 'Output' and 'ChangeHistory'

Output: contains the membership files
ChangeHistory: contains all the change that occurred since the script is operational.


In the Output folder, the script save the current membership of each groups


At this point the ChangeHistory is empty.



Running the script after making a first change on a group

I changed the membership of the group FX\FXGROUP


A change is detected by the script, so it creates a file for this group.


Then the script send the following Email Report:

The script send an email with the above report. Notice there is no 'Change History'
since no previous changes were detected




Running the script after making a second change on a group

Again, I changed the membership of the group FX\FXGROUP

A second Change History file is created by the script


You will notice that the email you receive is a bit different. It will contains the Change History of the first file (the previous change made at first) "FX_FXGROUP-ChangeHistory-20131013_185831.csv"



This time the Email Report is a bit different, the script did detect some previous change
and include a 'Change History' table in the Email Report.

Comments Based Help

Let's test the help now...

PS C:\LazyWinAdmin> Get-Help .\TOOL-MONITOR-AD_Groups.ps1 -full
NAME
    C:\LazyWinAdmin\TOOL-MONITOR-AD_Group.ps1

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

SYNTAX
    C:\LazyWinAdmin\TOOL-MONITOR-AD_Group.ps1 [-Group] <string> [-Emailfrom] <mailaddress>
    [-Emailto] <mailaddress> [-EmailServer] <string> [<commonparameters>]


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).


PARAMETERS
    -Group 
        Specifies 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.

        Required?                    true
        Position?                    1
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -Emailfrom 
        Specifies the Email Address of the Sender

        Required?                    true
        Position?                    2
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -Emailto 
        Specifies the Email Address of the Destination

        Required?                    true
        Position?                    3
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -EmailServer 
        Specifies the Email Server IPAddress/FQDN

        Required?                    true
        Position?                    4
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see
        about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216).

INPUTS
    System.String




OUTPUTS
    Email Report




NOTES


        NAME:    TOOL-Monitor-AD_Group.ps1
        AUTHOR:    Francois-Xavier CAT
        DATE:    2012/02/01
        EMAIL:    info@lazywinadmin.com

        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

    -------------------------- EXAMPLE 1 --------------------------

    C:\PS>.\TOOL-Monitor-AD_Group.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -EmailTo
    "To@Company.com" -EmailServer "mail.company.com"


    This will run the script against the group FXGROUP and send an email to To@Company.com using
    the address From@Company.com and the server mail.company.com.





    -------------------------- EXAMPLE 2 --------------------------

    C:\PS>.\TOOL-Monitor-AD_Group.ps1 -Group "FXGroup","FXGroup2","FXGroup3" -EmailFrom
    "From@Company.com" -Emailto "To@Company.com" -EmailServer "mail.company.com"


    This will run the script against the groups FXGROUP,FXGROUP2 and FXGROUP3  and send an email
    to To@Company.com using the address From@Company.com and the Server mail.company.com.





    -------------------------- EXAMPLE 3 --------------------------

    C:\PS>.\TOOL-Monitor-AD_Group.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto
    "To@Company.com" -EmailServer "mail.company.com" -Verbose


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





    -------------------------- EXAMPLE 4 --------------------------

    C:\PS>.\TOOL-Monitor-AD_Group.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto
    "Auditor@Company.com","Auditor2@Company.com" -EmailServer "mail.company.com" -Verbose


    This will run the script against the group FXGROUP and send an email to 2 persons using the
    address From@Company.com and the server mail.company.com. Additionally the switch Verbose is
    activated to show the activities of the script.






RELATED LINKS



Download

Github Repo
Technet Script Repository


Thanks for Reading! If you have any questions, leave a comment or send me an email at fxcat@lazywinadmin.com. I invite you to follow me on Twitter @lazywinadm

27 comments:

  1. Great Work !
    Thanks for sharing :)

    But I was just wondering if when a Group membership changes, does that create some sort of event ?
    Can we create a event subscription and perform the operation then? I guess that event will be fired on a DC (if it does) and our script will have to run there unlike this one.

    ReplyDelete
    Replies
    1. Hey DexterPosh!
      Thanks for your comment! Appreciated!
      BTW, Good job on your work during the last Scripting Games ;-)

      Good point,
      Unless things change recently in Windows Server, I believe you would have to activate the Auditing on the Active Directory, the problem with this is... It will be activated for all the groups and generate a lot of events.

      Once this is activated I guess you could use it instead of the Schedule Tasks.

      If an event subscription is only possible for this group it would be really awesome! Without activating the whole auditing...

      Delete
    2. On a change of group membership event logs are created you can find if auditing is enable but the major problem is to consolidate all created logs according to their type so here comes the necessity of the commercial product quest do have AD Auditor but apart from that Lepide AD Auditor and Manage Engine performing well in the market

      Delete
  2. Hi Francois-Xavier Cat,
    Great Script, I'm receiving an error:

    Unable to find type [mailaddress]: make sure that the assembly containing this type is loaded.
    At C:\Scripts\ADGr​oupMonitoring\T​OOL-MONITOR-AD_​Group.ps1:93 char:22
    + [mailaddress] <<<< $Emailfrom,
    + CategoryInfo : InvalidOperatio​n: (mailaddress:St​ring) [], RuntimeExceptio​n
    + FullyQualifiedE​rrorId : TypeNotFound


    Am I doing something wrong?

    I'm using following syntax:
    PowerShell.exe -Command "C:\Scripts\TOOL-MONITOR-AD_Group.ps1 -verbose -Group "Domain Admins", -EmailServer "mail.mycompany.com" -EmailTo "me@mycompany.com" -EmailFrom "powershell@mycompany.com""

    Thanks!
    Sahilexe

    ReplyDelete
    Replies
    1. Hey Sahilexe,

      Two things:
      -You have a comma after the "Domain Admin"
      -You must add an escape charactere like \ to ignore the quotes inside your line

      The correct line would give this:

      PowerShell.exe -Command "C:\Scripts\TOOL-MONITOR-AD_Group.ps1 -verbose -Group \"Domain Admins\" -EmailServer \"mail.mycompany.com\" -EmailTo \"me@mycompany.com\" -EmailFrom \"powershell@mycompany.com\""

      I guess this is the command you put inside your Task Scheduler.

      Delete
    2. What is your OS ?

      [mailaddress] seems to only work from Windows7
      http://msdn.microsoft.com/en-us/library/system.net.mail.mailaddress.aspx

      to make it work on your computer, you can remove this validation by changing some lines:

      FIND:
      [Parameter(Mandatory=$true,HelpMessage="You must specify the Sender Email Address")]
      [mailaddress]$Emailfrom,
      [Parameter(Mandatory=$true,HelpMessage="You must specify the Destination Email Address")]
      [mailaddress[]]$Emailto,

      REPLACE BY

      [Parameter(Mandatory=$true,HelpMessage="You must specify the Sender Email Address")]
      [STRING]$Emailfrom,
      [Parameter(Mandatory=$true,HelpMessage="You must specify the Destination Email Address")]
      [STRING[]]$Emailto,


      ####

      FIND:

      $MailMessage.from = $EmailFrom.Address
      FOREACH ($To in $Emailto){
      $MailMessage.To.add($($To.Address))
      }

      REPLACE BY:

      $MailMessage.from = $EmailFrom
      FOREACH ($To in $Emailto){
      $MailMessage.To.add($($To))
      }

      Delete
    3. Hi Francois-Xavier Cat,

      The script now works and change history and output folder were created, also the csv files get generated with correct info. but the only issue is that I don't get any email. I'm sure that I'm using correct correct syntax around the "/", correct email address and mail server. I also modified other lines as per your recommendation. The OS on which I'm running this script is Windows Server 2008 R2. Can you try your script on that OS and let me know if still anything needs to be changed to make the email part working? Appreciate your help.

      PowerShell.exe -Command "C:\Scripts\ADGroupMonitoring\TOOL-MONITOR-AD_Group.ps1 -verbose -Group \"Domain Admins\" -EmailServer /"mailserver.mycompany.com/" -EmailTo /"me@mycompany.com/" -Emailfrom /"Powershell@mycompany.com/""

      C:\Scripts\ADGroupMonitoring>script.bat
      VERBOSE: Creating the Output Folder : C:\Scripts\ADGroupMonitoring\Output
      VERBOSE: Creating the ChangeHistory Folder :
      C:\Scripts\ADGroupMonitoring\ChangeHistory
      VERBOSE: Quest Active Directory - Loading
      VERBOSE: Quest Active Directory - Loaded
      VERBOSE: GROUP: Domain Admins
      VERBOSE: Domain Admins - The following file did not exist: mycompany_Domain
      Admins-membership.csv
      VERBOSE: Domain Admins - Exporting the current membership information into the
      file: mycompany_Domain Admins-membership.csv
      VERBOSE: Domain Admins - Comparing Current and Before
      VERBOSE: Domain Admins - Compare Block Done !
      VERBOSE: Domain Admins - No Change
      VERBOSE: Script Completed

      Delete
    4. this is what I get now...
      VERBOSE: groupname - Preparing the notification email...
      WARNING: PROCESS BLOCK - Something went wrong

      Delete
    5. Hi,

      For your first comment, It's normal you don't get an email, once you do a change to the group and run the script again, you will get notified.

      For your error on "VERBOSE: groupname - Preparing the notification email..." Just want to make sure this is not related to the SLASH you used instead of BACKSLASH

      REPLACE:
      PowerShell.exe -Command "C:\Scripts\ADGroupMonitoring\TOOL-MONITOR-AD_Group.ps1 -verbose -Group \"Domain Admins\" -EmailServer /"mailserver.mycompany.com/" -EmailTo /"me@mycompany.com/" -Emailfrom /"Powershell@mycompany.com/""

      BY
      PowerShell.exe -Command "C:\Scripts\ADGroupMonitoring\TOOL-MONITOR-AD_Group.ps1 -verbose -Group \"Domain Admins\" -EmailServer \"mailserver.mycompany.com\" -EmailTo \"me@mycompany.com\" -Emailfrom \"Powershell@mycompany.com\""

      Will try to test on W2008R2 at some point during the day

      Delete
    6. Hi again,

      I tried on a Windows Server 2008R2, and like you did I used a script.bat (you could put this line straight inside the Task scheduler btw)

      CONTENT OF MY SCRIPT.BAT
      PowerShell.exe -Command "D:\test\TOOL-MONITOR-AD_Group.ps1 -verbose -Group \"FXGROUP\" -EmailServer \"mail.fx.lab\" -EmailTo \"fxcat@fx.lab\" -Emailfrom \"Powershell@fx.lab\""

      And it did work! Just make sure you use \ and not /

      The first time i ran the script, it does create a CSV
      After a change on the group, I do receive an Email.

      Let me know if I can do anything else.



      NOTE:
      After a few check, the [mailaddress] you got is because you need the version 3.0 of PowerShell.
      But you can work around with the lines modification I said in a previous reply.

      Delete
  3. After correcting "/" to "\" and installing PowerShell3 and DotNetFramework4.5 on my WinSvr2008R2, your script worked out of box for me. You-da-man!! :)

    ReplyDelete
  4. Is it possible to input the list of Active Directory groups from a text file or provide AD location in which it checks all the groups inside. I have around 30 AD groups right now that I want to monitor using this script, but if I create more groups inside an OU, the script would pick them automatically. That would be a nice monitoring system for AD.

    ReplyDelete
    Replies
    1. Hi!

      Great ideas! I can definitely add this to the next version.
      With the current version, you can do the following (Quest Active Directory is loaded need to be loaded first.)


      # USING A SEARCH ROOT (AD LOCATION)
      .\TOOL-MONITOR-AD_Group.ps1 -Group (get-qadgroup -searchroot 'OU=Test,DC=FX,DC=lab').NTAccountName -Emailfrom PowerShell@fx.lab -Emailto fxcat@fx.lab -EmailServer mail.fx.lab -Verbose

      You specify the Distinguished Name of the OU with the parameter -SEARCHROOT

      Additionally you can specify if the cmdlet should scan recursively with the parameter -SEARCHSCOPE
      It would looks like that:

      .\TOOL-MONITOR-AD_Group.ps1 -Group (get-qadgroup -searchroot 'OU=Test,DC=FX,DC=lab' -SearchScope Subtree).NTAccountName -Emailfrom PowerShell@fx.lab -Emailto fxcat@fx.lab -EmailServer mail.fx.lab -Verbose

      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.

      More information on Get-QADGroup here: http://wiki.powergui.org/index.php/Get-QADGroup

      # USING A TEXT FILE
      .\TOOL-MONITOR-AD_Group.ps1 -Group (Get-Content c:\myfile.txt) -Emailfrom PowerShell@fx.lab -Emailto fxcat@fx.lab -EmailServer mail.fx.lab -Verbose

      Hope this help

      Delete
  5. Script runs fine if I do it manually from powershell, but when I try to create the scheduled task I get the error:

    A positional parameter cannot be found that accepts argument 'System.Object[]'.

    Ideas?

    ReplyDelete
    Replies
    1. Hi,

      Which command line do you use in your scheduled task ?

      Delete
    2. Here is the command I am running.

      "C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe" -command C:\AD-Monitor\TOOL-Monitor-AD_Group.ps1 -Group "Domain Admins","Enterprise Admins","VMware Admins","Web Filter Admin","SophosAdministrator" -EmailFrom "DomainChange@company.com" -Emailto "italerts@company.com" -EmailServer "smtp.company.com"

      Delete
    3. Try the following:

      "C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe" -command "C:\AD-Monitor\TOOL-Monitor-AD_Group.ps1 -Group \"Domain Admins\",\"Enterprise Admins\",\"VMware Admins\",\"Web Filter Admin\",\"SophosAdministrator\" -EmailFrom \"DomainChange@company.com\" -Emailto \"italerts@company.com\" -EmailServer \"smtp.company.com\""

      Let me know.

      Delete
    4. That did the trick. I appreciate it. Another question. I have an older second domain that I would like to use this in as well, but it's Server 2008 Standard 32bit. Is that going to be an issue?

      Delete
    5. Great! happy to hear it is working for you

      2008 R1 32 bits, I believe PowerShell 3.0 can be install there
      http://blogs.technet.com/b/heyscriptingguy/archive/2012/09/06/powershell-3-0-is-now-available-for-download.aspx

      If you run the script from a DC in another domain, make sure you have a trust set properly, then specify the group with the domain name to the script, example:
      DOMAINA\GroupA
      DOMAINA\GroupB
      DOMAINB\GroupC
      DOMAINB\GroupD

      Hope this help!

      Delete
  6. Works like a champ. I appreciate it.

    ReplyDelete
  7. Hello Francois-Xavier,
    could it be that there is an error in line 323 in your Script ?
    At the end you filter for $_.name but I think you should filter for $_.samaccountname.
    I think this is causing the appearance in the Report of "No User or Group" being removed from the Group........
    Except this behavior is intentional.........

    Great Script !!

    ReplyDelete
    Replies
    1. Hi

      Thanks for your comment.
      Yes this behavior is intentional.

      I'm open to suggestion, Would you prefer this to stay in the report ?

      Thanks

      Delete
  8. Hi,
    This is great script but I am having problems running it. I am trying to monitor a specific OU on our domain to make sure no new clients are added or removed from it. Can this script do that?
    When I try to run the following command I get an error.
    .\Tool-Monitor-AD_Group_20131127.ps1 -Verbose -Group "Administrative Groups" -EmailServer "**.**.**.**" -EmailTo "user@domain.com" -EmailFrom "OUCheckTest@domain.com"

    Error:
    VERBOSE: GROUP: Administrative Groups
    VERBOSE: Administrative Groups - Group can't be found
    VERBOSE: Script Completed

    Apologies if this is a stupid question, I'm new to powershell.

    Thank you,
    Fionnan

    ReplyDelete
    Replies
    1. Hi Fionnan,

      Yes you can monitor an OU using the last version of the script.
      Please check my last post update on this script: http://www.lazywinadmin.com/2013/11/update-powershell-monitor-and-report.html

      You would run it this way
      .\TOOL-MONITOR-AD_Group.ps1 -SearchRoot 'FX.LAB/TEST/Groups' -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose

      Delete
  9. Looks well designed, but I don't see why it's necessary to have some thrid-party snap-in. This could be done natively.
    Anyway, it looks like you put a lot of effort into it, and you have a lot people that like it; so what do I know.

    ReplyDelete
    Replies
    1. Hi, thanks for your message.
      I totally agree, and this point is already in my list, but did not put time on this yet.

      Feel free to contribute to the script on github here : https://github.com/lazywinadmin/PowerShell/tree/master/AD-GROUP-Monitor_MemberShip

      Delete