diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..849db55 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +Output/ +ChangeHistory/ +HTML/ \ No newline at end of file diff --git a/Monitor-ADGroupMemberShip.ps1 b/Monitor-ADGroupMemberShip.ps1 index 60645fc..351c7c6 100644 --- a/Monitor-ADGroupMemberShip.ps1 +++ b/Monitor-ADGroupMemberShip.ps1 @@ -31,935 +31,954 @@ <# .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). + 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). .SYNOPSIS - This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership. + This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership. .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. + 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. + 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. + 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'. + 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'. + 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. + 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. + Specify the Email Server IPAddress/FQDN. .PARAMETER EmailTo - Specify the Email Address(es) of the Destination. Example: fxcat@fx.lab + Specify the Email Address(es) of the Destination. Example: fxcat@fx.lab .PARAMETER EmailFrom - Specify the Email Address of the Sender. Example: Reporting@fx.lab + Specify the Email Address of the Sender. Example: Reporting@fx.lab .PARAMETER EmailEncoding - Specify the Body and Subject Encoding to use in the Email. - Default is ASCII. + Specify the Body and Subject Encoding to use in the Email. + Default is ASCII. .PARAMETER Server - Specify the Domain Controller to use. - Aliases: DomainController, Service + Specify the Domain Controller to use. + Aliases: DomainController, Service .PARAMETER HTMLLog - Specify if you want to save a local copy of the Report. - It will be saved under the directory "HTML". + Specify if you want to save a local copy of the Report. + It will be saved under the directory "HTML". .PARAMETER IncludeMembers - Specify if you want to include all members in the Report. - + Specify if you want to include all members in the Report. + .PARAMETER AlwaysReport - Specify if you want to generate a Report each time. + Specify if you want to generate a Report each time. .PARAMETER OneReport - Specify if you want to only send one email with all group report as attachment + Specify if you want to only send one email with all group report as attachment .PARAMETER ExtendedProperty - Specify if you want to add Enabled and PasswordExpired Attribute on members in the report - + Specify if you want to add Enabled and PasswordExpired Attribute on members in the report + +.PARAMETER AllGroups + Search for all group types and scopes. + .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -EmailTo "To@Company.com" -EmailServer "mail.company.com" + .\AD-GROUP-Monitor_MemberShip.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. + 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 - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup","FXGroup2","FXGroup3" -EmailFrom "From@Company.com" -Emailto "To@Company.com" -EmailServer "mail.company.com" + .\AD-GROUP-Monitor_MemberShip.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. + 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 - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto "To@Company.com" -EmailServer "mail.company.com" -Verbose + .\AD-GROUP-Monitor_MemberShip.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. + 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 - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto "Auditor@Company.com","Auditor2@Company.com" -EmailServer "mail.company.com" -Verbose + .\AD-GROUP-Monitor_MemberShip.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 Auditor@Company.com and Auditor2@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. + This will run the script against the group FXGROUP and send an email to Auditor@Company.com and Auditor2@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 - .\AD-GROUP-Monitor_MemberShip.ps1 -SearchRoot 'FX.LAB/TEST/Groups' -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -SearchRoot 'FX.LAB/TEST/Groups' -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -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 catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against all the groups present in the CanonicalName 'FX.LAB/TEST/Groups' and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -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 catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose - This will run the script against the Domain Controller "DC01.fx.lab" on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against the Domain Controller "DC01.fx.lab" on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab:389 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab:389 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose - This will run the script against the Domain Controller "DC01.fx.lab" (on port 389) on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against the Domain Controller "DC01.fx.lab" (on port 389) on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -group "Domain Admins" -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -EmailEncoding 'ASCII' -HTMLlog + .\AD-GROUP-Monitor_MemberShip.ps1 -group "Domain Admins" -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -EmailEncoding 'ASCII' -HTMLlog - This will run the script against the group "Domain Admins" and send an email (using the encoding ASCII) to catfx@fx.lab using the address Reporting@fx.lab and the SMTP server 192.168.1.10. It will also save a local HTML report under the HTML Directory. + This will run the script against the group "Domain Admins" and send an email (using the encoding ASCII) to catfx@fx.lab using the address Reporting@fx.lab and the SMTP server 192.168.1.10. It will also save a local HTML report under the HTML Directory. .INPUTS - System.String + System.String .OUTPUTS - Email Report + Email Report .NOTES - NAME: AD-GROUP-Monitor_MemberShip.ps1 - AUTHOR: Francois-Xavier Cat - EMAIL: info@lazywinadmin.com - WWW: www.lazywinadmin - Twitter:@lazywinadm - - 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 - - 1.7 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 - - 1.8 2013.11.27 - Minor syntax changes - UPDATE Help - - 1.8.1 2013.12.29 - Rename to AD-GROUP-Monitor_MemberShip - - 1.8.2 2014.02.17 - Update Notes - - 2.0 2014.05.04 - ADD Support for ActiveDirectory module (AD module is use by default) - ADD failover to Quest AD Cmdlet if AD module is available - RENAME GetQADGroupParams variable to ADGroupParams - - 2.0.1 2015.01.05 - REMOVE the DisplayName property from the email - ADD more clear details/Comments - RENAME a couple of Verbose and Warning Messages - FIX the DN of the group in the Summary - FIX SearchBase/SearchRoot Parameter which was not working with AD Module - FIX Some other minor issues - ADD Check to validate data added to $Group is valid - ADD Server Parameter to be able to specify a domain controller - - 2.0.2 2015.01.14 - FIX an small issue with the $StateFile which did not contains the domain - ADD the property Name into the final output. - ADD Support to export the report to a HTML file (-HTMLLog) It will save - the report under the folder HTML - ADD Support for alternative Email Encoding: Body and Subject. Default is ASCII. - - 2.0.3 2017.06.30 - ADD 'IncludeMembers' Switch to list all members in the report. - ADD 'AlwaysReport' switch to send report each run. - ADD 'OneReport' Switch to have it only send one email, with each group - report in the mail as attachment. - ADD 'ExtendedProperty' switch to add Enabled and PasswordExpired to the member list. - - 2.0.4 2017.06.30 - FIX Minor typos - Update CATCH Blow to throw the error - Update verbose and messages to include the script name - - 2.0.5 2017.07.04 - FIX member liste showing in report were the history, not current. - - TODO: - -Add Switch to make the Group summary Optional (info: Description,DN,CanonicalName,SID, Scope, Type) - -Current Member Count, Added Member count, Removed Member Count - -Switch to Show all the Current Members (Name, Department, Role, EMail) - -Possibility to Ignore some groups - -Email Credential - -Recursive Membership search - -Switch to save a local copy of the HTML report (maybe put this by default) + NAME: AD-GROUP-Monitor_MemberShip.ps1 + AUTHOR: Francois-Xavier Cat + EMAIL: info@lazywinadmin.com + WWW: www.lazywinadmin + Twitter:@lazywinadm + + 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 + + 1.7 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 + + 1.8 2013.11.27 + Minor syntax changes + UPDATE Help + + 1.8.1 2013.12.29 + Rename to AD-GROUP-Monitor_MemberShip + + 1.8.2 2014.02.17 + Update Notes + + 2.0 2014.05.04 + ADD Support for ActiveDirectory module (AD module is use by default) + ADD failover to Quest AD Cmdlet if AD module is available + RENAME GetQADGroupParams variable to ADGroupParams + + 2.0.1 2015.01.05 + REMOVE the DisplayName property from the email + ADD more clear details/Comments + RENAME a couple of Verbose and Warning Messages + FIX the DN of the group in the Summary + FIX SearchBase/SearchRoot Parameter which was not working with AD Module + FIX Some other minor issues + ADD Check to validate data added to $Group is valid + ADD Server Parameter to be able to specify a domain controller + + 2.0.2 2015.01.14 + FIX an small issue with the $StateFile which did not contains the domain + ADD the property Name into the final output. + ADD Support to export the report to a HTML file (-HTMLLog) It will save + the report under the folder HTML + ADD Support for alternative Email Encoding: Body and Subject. Default is ASCII. + + 2.0.3 2017.06.30 + ADD 'IncludeMembers' Switch to list all members in the report. + ADD 'AlwaysReport' switch to send report each run. + ADD 'OneReport' Switch to have it only send one email, with each group + report in the mail as attachment. + ADD 'ExtendedProperty' switch to add Enabled and PasswordExpired to the member list. + + 2.0.4 2017.06.30 + FIX Minor typos + Update CATCH Blow to throw the error + Update verbose and messages to include the script name + + 2.0.5 2017.07.04 + FIX member liste showing in report were the history, not current. + + TODO: + -Add Switch to make the Group summary Optional (info: Description,DN,CanonicalName,SID, Scope, Type) + -Current Member Count, Added Member count, Removed Member Count + -Switch to Show all the Current Members (Name, Department, Role, EMail) + -Possibility to Ignore some groups + -Email Credential + -Recursive Membership search + -Switch to save a local copy of the HTML report (maybe put this by default) #> #requires -version 3.0 [CmdletBinding(DefaultParameterSetName = "Group")] PARAM ( - [Parameter(ParameterSetName = "Group", Mandatory = $true, HelpMessage = "You must specify at least one Active Directory group")] - [ValidateNotNull()] - [Alias('DN', 'DistinguishedName', 'GUID', 'SID', 'Name')] - [string[]]$Group, - - [Parameter(ParameterSetName = "OU", Mandatory = $true)] - [Alias('SearchBase')] - [String[]]$SearchRoot, - - [Parameter(ParameterSetName = "OU")] - [ValidateSet("Base", "OneLevel", "Subtree")] - [String]$SearchScope, - - [Parameter(ParameterSetName = "OU")] - [ValidateSet("Global", "Universal", "DomainLocal")] - [String]$GroupScope, - - [Parameter(ParameterSetName = "OU")] - [ValidateSet("Security", "Distribution")] - [String]$GroupType, - - [Parameter(ParameterSetName = "File", Mandatory = $true)] - [ValidateScript({ Test-Path -Path $_ })] - [String[]]$File, - - [Parameter()] - [Alias('DomainController', 'Service')] - $Server, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Sender Email Address")] - [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] - [String]$Emailfrom, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Destination Email Address")] - [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] - [String[]]$Emailto, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Email Server to use (IPAddress or FQDN)")] - [String]$EmailServer, - - [Parameter()] - [ValidateSet("ASCII", "UTF8", "UTF7", "UTF32", "Unicode", "BigEndianUnicode", "Default")] - [String]$EmailEncoding="ASCII", - - [Parameter()] - [Switch]$HTMLLog, + [Parameter(ParameterSetName = "Group", Mandatory = $true, HelpMessage = "You must specify at least one Active Directory group")] + [ValidateNotNull()] + [Alias('DN', 'DistinguishedName', 'GUID', 'SID', 'Name')] + [string[]]$Group, + + [Parameter(ParameterSetName = "OU", Mandatory = $true)] + [Parameter(ParameterSetName = "OUAllGroups", Mandatory = $true)] + [Alias('SearchBase')] + [String[]]$SearchRoot, + + [Parameter(ParameterSetName = "OU")] + [Parameter(ParameterSetName = "OUAllGroups", Mandatory = $true)] + [ValidateSet("Base", "OneLevel", "Subtree")] + [String]$SearchScope, + + [Parameter(ParameterSetName = "OU")] + [ValidateSet("Global", "Universal", "DomainLocal")] + [String]$GroupScope, + + [Parameter(ParameterSetName = "OU")] + [ValidateSet("Security", "Distribution")] + [String]$GroupType, + + [Parameter(ParameterSetName = "OUAllGroups", Mandatory = $true)] + [switch]$AllGroups, + + [Parameter(ParameterSetName = "File", Mandatory = $true)] + [ValidateScript({ Test-Path -Path $_ })] + [String[]]$File, + + [Parameter()] + [Alias('DomainController', 'Service')] + $Server, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Sender Email Address")] + [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] + [String]$Emailfrom, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Destination Email Address")] + [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] + [String[]]$Emailto, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Email Server to use (IPAddress or FQDN)")] + [String]$EmailServer, [Parameter()] - [Switch]$IncludeMembers, + [ValidateSet("ASCII", "UTF8", "UTF7", "UTF32", "Unicode", "BigEndianUnicode", "Default")] + [String]$EmailEncoding="ASCII", [Parameter()] - [Switch]$AlwaysReport, + [Switch]$HTMLLog, [Parameter()] - [Switch]$OneReport, + [Switch]$IncludeMembers, + + [Parameter()] + [Switch]$AlwaysReport, [Parameter()] - [Switch]$ExtendedProperty + [Switch]$OneReport, + + [Parameter()] + [Switch]$ExtendedProperty ) BEGIN { - TRY - { - # Retrieve the Script name - $ScriptName = $MyInvocation.MyCommand - - # Set the Paths Variables and create the folders if not present - $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) - $ScriptPathOutput = $ScriptPath + "\Output" - IF (-not(Test-Path -Path $ScriptPathOutput)) - { - Write-Verbose -Message "[$ScriptName][BEGIN] Creating the Output Folder : $ScriptPathOutput" - New-Item -Path $ScriptPathOutput -ItemType Directory | Out-Null - } - $ScriptPathChangeHistory = $ScriptPath + "\ChangeHistory" - IF (-not(Test-Path -Path $ScriptPathChangeHistory)) - { - Write-Verbose -Message "[$ScriptName][BEGIN] Creating the ChangeHistory Folder : $ScriptPathChangeHistory" - New-Item -Path $ScriptPathChangeHistory -ItemType Directory | Out-Null - } - - # Set the Date and Time variables format - $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" - #$ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" - - - # Active Directory Module - IF (Get-Module -Name ActiveDirectory -ListAvailable) #verify ad module is installed - { - Write-Verbose -Message "[$ScriptName][BEGIN] Active Directory Module" - # Verify Ad module is loaded - IF (-not (Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINGetADModule)) - { - Write-Verbose -Message "[$ScriptName][BEGIN] Active Directory Module - Loading" - Import-Module -Name ActiveDirectory -ErrorAction Stop -ErrorVariable ErrorBEGINAddADModule - Write-Verbose -Message "[$ScriptName][BEGIN] Active Directory Module - Loaded" - $script:ADModule = $true - } - ELSE - { - Write-Verbose -Message "[$ScriptName][BEGIN] Active Directory module seems loaded" - $script:ADModule = $true - } - } - ELSE # Else we try to load Quest Ad Cmdlets - { - Write-Verbose -Message "[$ScriptName][BEGIN] Quest AD Snapin" - # Verify Quest Active Directory Snapin is loaded - IF (-not (Get-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINGetQuestAD)) - { - Write-Verbose -Message "[$ScriptName][BEGIN] Quest Active Directory - Loading" - Add-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINAddQuestAd - Write-Verbose -Message "[$ScriptName][BEGIN] Quest Active Directory - Loaded" - $script:QuestADSnappin = $true - } - ELSE - { - Write-Verbose -Message "[$ScriptName][BEGIN] Quest AD Snapin seems loaded" - $script:QuestADSnappin = $true - } - } - - Write-Verbose -Message "[$ScriptName][BEGIN] Setting HTML Variables" - # HTML Report settings - $Report = "

" + - "Report Time: $DateFormat
" + - "Account: $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper())" + - "

" - - $Head = "" - $Head2 = "" - - - }#TRY - CATCH - { - Write-Warning -Message "[$ScriptName][BEGIN] Something went wrong" - - # Quest AD Cmdlets Errors - if ($ErrorBEGINGetQuestAD) { Write-Warning -Message "[$ScriptName][BEGIN] Can't Find the Quest Active Directory Snappin" } - if ($ErrorBEGINAddQuestAD) { Write-Warning -Message "[$ScriptName][BEGIN] Can't Load the Quest Active Directory Snappin" } - - # AD module Errors - if ($ErrorBEGINGetADmodule) { Write-Warning -Message "[$ScriptName][BEGIN] Can't find the Active Directory module" } - if ($ErrorBEGINAddADmodule) { Write-Warning -Message "[$ScriptName][BEGIN] Can't load the Active Directory module" } - - Throw $_ - } #CATCH + TRY + { + # Retrieve the Script name + $ScriptName = $MyInvocation.MyCommand + + # Set the Paths Variables and create the folders if not present + $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) + $ScriptPathOutput = $ScriptPath + "\Output" + IF (-not(Test-Path -Path $ScriptPathOutput)) + { + Write-Verbose -Message "[$ScriptName][BEGIN] Creating the Output Folder : $ScriptPathOutput" + New-Item -Path $ScriptPathOutput -ItemType Directory | Out-Null + } + $ScriptPathChangeHistory = $ScriptPath + "\ChangeHistory" + IF (-not(Test-Path -Path $ScriptPathChangeHistory)) + { + Write-Verbose -Message "[$ScriptName][BEGIN] Creating the ChangeHistory Folder : $ScriptPathChangeHistory" + New-Item -Path $ScriptPathChangeHistory -ItemType Directory | Out-Null + } + + # Set the Date and Time variables format + $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" + #$ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" + + + # Active Directory Module + IF (Get-Module -Name ActiveDirectory -ListAvailable) #verify ad module is installed + { + Write-Verbose -Message "[$ScriptName][BEGIN] Active Directory Module" + # Verify Ad module is loaded + IF (-not (Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINGetADModule)) + { + Write-Verbose -Message "[$ScriptName][BEGIN] Active Directory Module - Loading" + Import-Module -Name ActiveDirectory -ErrorAction Stop -ErrorVariable ErrorBEGINAddADModule + Write-Verbose -Message "[$ScriptName][BEGIN] Active Directory Module - Loaded" + $script:ADModule = $true + } + ELSE + { + Write-Verbose -Message "[$ScriptName][BEGIN] Active Directory module seems loaded" + $script:ADModule = $true + } + } + ELSE # Else we try to load Quest Ad Cmdlets + { + Write-Verbose -Message "[$ScriptName][BEGIN] Quest AD Snapin" + # Verify Quest Active Directory Snapin is loaded + IF (-not (Get-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINGetQuestAD)) + { + Write-Verbose -Message "[$ScriptName][BEGIN] Quest Active Directory - Loading" + Add-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINAddQuestAd + Write-Verbose -Message "[$ScriptName][BEGIN] Quest Active Directory - Loaded" + $script:QuestADSnappin = $true + } + ELSE + { + Write-Verbose -Message "[$ScriptName][BEGIN] Quest AD Snapin seems loaded" + $script:QuestADSnappin = $true + } + } + + Write-Verbose -Message "[$ScriptName][BEGIN] Setting HTML Variables" + # HTML Report settings + $Report = "

" + + "Report Time: $DateFormat
" + + "Account: $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper())" + + "

" + + $Head = "" + $Head2 = "" + + + }#TRY + CATCH + { + Write-Warning -Message "[$ScriptName][BEGIN] Something went wrong" + + # Quest AD Cmdlets Errors + if ($ErrorBEGINGetQuestAD) { Write-Warning -Message "[$ScriptName][BEGIN] Can't Find the Quest Active Directory Snappin" } + if ($ErrorBEGINAddQuestAD) { Write-Warning -Message "[$ScriptName][BEGIN] Can't Load the Quest Active Directory Snappin" } + + # AD module Errors + if ($ErrorBEGINGetADmodule) { Write-Warning -Message "[$ScriptName][BEGIN] Can't find the Active Directory module" } + if ($ErrorBEGINAddADmodule) { Write-Warning -Message "[$ScriptName][BEGIN] Can't load the Active Directory module" } + + Throw $_ + } #CATCH }#BEGIN PROCESS { - TRY - { - - # # # # # # # # # # # # # # # # # - # SEARCHROOT parameter specified# - # # # # # # # # # # # # # # # # # - - IF ($PSBoundParameters['SearchRoot']) - { - Write-Verbose -Message "[$ScriptName][PROCESS] SearchRoot specified" - FOREACH ($item in $SearchRoot) - { - # ADGroup Splatting - $ADGroupParams = @{ } - - - # ActiveDirectory Module - IF ($ADModule) - { - $ADGroupParams.SearchBase = $item - - # Server Specified - IF ($PSBoundParameters['Server']) { $ADGroupParams.Server = $Server} - } - IF ($QuestADSnappin) - { - $ADGroupParams.SearchRoot = $item - - # Server Specified - IF ($PSBoundParameters['Server']) { $ADGroupParams.Service = $Server } - } - - - # # # # # # # # # # # # # # # # # # - # SEARCHSCOPE Parameter specified # - # # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['SearchScope']) - { - Write-Verbose -Message "[$ScriptName][PROCESS] SearchScope specified" - $ADGroupParams.SearchScope = $SearchScope - } - - - # # # # # # # # # # # # # # # # # - # GROUPSCOPE Parameter specified# - # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['GroupScope']) - { - Write-Verbose -Message "[$ScriptName][PROCESS] GroupScope specified" - # ActiveDirectory Module Parameter - IF ($ADModule) { $ADGroupParams.Filter = "GroupScope -eq `'$GroupScope`'" } - # Quest ActiveDirectory Snapin Parameter - ELSE { $ADGroupParams.GroupScope = $GroupScope } - } - - - # # # # # # # # # # # # # # # # # - # GROUPTYPE Parameter specified # - # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['GroupType']) - { - Write-Verbose -Message "[$ScriptName][PROCESS] GroupType specified" - # ActiveDirectory Module - IF ($ADModule) - { - # ActiveDirectory Module Parameter - IF ($ADGroupParams.Filter) - { - $ADGroupParams.Filter = "$($ADGroupParams.Filter) -and GroupCategory -eq `'$GroupType`'" - } - ELSE - { - $ADGroupParams.Filter = "GroupCategory -eq '$GroupType'" - } - } - # Quest ActiveDirectory Snapin - ELSE - { - $ADGroupParams.GroupType = $GroupType - } - }#IF ($PSBoundParameters['GroupType']) - - - - IF ($ADModule) - { + TRY + { + + # # # # # # # # # # # # # # # # # + # SEARCHROOT parameter specified# + # # # # # # # # # # # # # # # # # + + IF ($PSBoundParameters['SearchRoot']) + { + Write-Verbose -Message "[$ScriptName][PROCESS] SearchRoot specified" + FOREACH ($item in $SearchRoot) + { + # ADGroup Splatting + $ADGroupParams = @{ } + + + # ActiveDirectory Module + IF ($ADModule) + { + $ADGroupParams.SearchBase = $item + + # Server Specified + IF ($PSBoundParameters['Server']) { $ADGroupParams.Server = $Server} + } + IF ($QuestADSnappin) + { + $ADGroupParams.SearchRoot = $item + + # Server Specified + IF ($PSBoundParameters['Server']) { $ADGroupParams.Service = $Server } + } + + + # # # # # # # # # # # # # # # # # # + # SEARCHSCOPE Parameter specified # + # # # # # # # # # # # # # # # # # # + IF ($PSBoundParameters['SearchScope']) + { + Write-Verbose -Message "[$ScriptName][PROCESS] SearchScope specified" + $ADGroupParams.SearchScope = $SearchScope + } + + + # # # # # # # # # # # # # # # # # + # GROUPSCOPE Parameter specified# + # # # # # # # # # # # # # # # # # + IF ($PSBoundParameters['GroupScope']) + { + Write-Verbose -Message "[$ScriptName][PROCESS] GroupScope specified" + # ActiveDirectory Module Parameter + IF ($ADModule) { $ADGroupParams.Filter = "GroupScope -eq `'$GroupScope`'" } + # Quest ActiveDirectory Snapin Parameter + ELSE { $ADGroupParams.GroupScope = $GroupScope } + } + + + # # # # # # # # # # # # # # # # # + # GROUPTYPE Parameter specified # + # # # # # # # # # # # # # # # # # + IF ($PSBoundParameters['GroupType']) + { + Write-Verbose -Message "[$ScriptName][PROCESS] GroupType specified" + # ActiveDirectory Module + IF ($ADModule) + { + # ActiveDirectory Module Parameter + IF ($ADGroupParams.Filter) + { + $ADGroupParams.Filter = "$($ADGroupParams.Filter) -and GroupCategory -eq `'$GroupType`'" + } + ELSE + { + $ADGroupParams.Filter = "GroupCategory -eq '$GroupType'" + } + } + # Quest ActiveDirectory Snapin + ELSE + { + $ADGroupParams.GroupType = $GroupType + } + }#IF ($PSBoundParameters['GroupType']) + + + # # # # # # # # # # # # # # # # # + # AllGroups Parameter specified # + # # # # # # # # # # # # # # # # # + IF ($PSBoundParameters['AllGroups']) + { + Write-Verbose -Message "[$ScriptName][PROCESS] AllGroups specified" + IF ($ADModule) + { + $ADGroupParams.Filter = "Name -like '*'" + } + } + + IF ($ADModule) + { IF (-not($ADGroupParams.filter)){$ADGroupParams.Filter = "*"} - - Write-Verbose -Message "[$ScriptName][PROCESS] AD Module - Querying..." - - # Add the groups to the variable $Group + + Write-Verbose -Message "[$ScriptName][PROCESS] AD Module - Querying..." + + # Add the groups to the variable $Group $GroupSearch = Get-ADGroup @ADGroupParams if ($GroupSearch){ - $group += $GroupSearch.Distinguishedname + $group += $GroupSearch.Distinguishedname Write-Verbose -Message "[$ScriptName][PROCESS] OU: $item" } - } - - IF ($QuestADSnappin) - { - Write-Verbose -Message "[$ScriptName][PROCESS] Quest AD Snapin - Querying..." - # Add the groups to the variable $Group + } + + IF ($QuestADSnappin) + { + Write-Verbose -Message "[$ScriptName][PROCESS] Quest AD Snapin - Querying..." + # Add the groups to the variable $Group $GroupSearchQuest = Get-QADGroup @ADGroupParams if ($GroupSearchQuest){ - $group += $GroupSearchQuest.DN - Write-Verbose -Message "[$ScriptName][PROCESS] OU: $item" + $group += $GroupSearchQuest.DN + Write-Verbose -Message "[$ScriptName][PROCESS] OU: $item" } - } - - }#FOREACH ($item in $OU) - }#IF ($PSBoundParameters['SearchRoot']) - - - - - # # # # # # # # # # # # # # # - # FILE parameter specified # - # # # # # # # # # # # # # # # - - IF ($PSBoundParameters['File']) - { - Write-Verbose -Message "[$ScriptName][PROCESS] File" - FOREACH ($item in $File) - { - Write-Verbose -Message "[$ScriptName][PROCESS] Loading File: $item" - + } + + }#FOREACH ($item in $OU) + }#IF ($PSBoundParameters['SearchRoot']) + + + + + # # # # # # # # # # # # # # # + # FILE parameter specified # + # # # # # # # # # # # # # # # + + IF ($PSBoundParameters['File']) + { + Write-Verbose -Message "[$ScriptName][PROCESS] File" + FOREACH ($item in $File) + { + Write-Verbose -Message "[$ScriptName][PROCESS] Loading File: $item" + $FileContent = Get-Content -Path $File - + if ($FileContent) { # Add the groups to the variable $Group - $Group += Get-Content -Path $File + $Group += Get-Content -Path $File } - - - - }#FOREACH ($item in $File) - }#IF ($PSBoundParameters['File']) - - - - # # # # # # # # # # # # # # # # # # # # # # # # # # # - # GROUP or SEARCHROOT or FILE parameters specified # - # # # # # # # # # # # # # # # # # # # # # # # # # # # - - # This will run for any parameter set name ParameterSetName = OU, Group or File - FOREACH ($item in $Group) - { - TRY - { - - Write-Verbose -Message "[$ScriptName][PROCESS] GROUP: $item... " - - # Splatting for the AD Group Request - $GroupSplatting = @{ } - $GroupSplatting.Identity = $item - - # Group Information - if ($ADModule) - { - Write-Verbose -Message "[$ScriptName][PROCESS] ActiveDirectory module" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupSplatting.Server = $Server } - - # Look for Group - $GroupName = Get-ADGroup @GroupSplatting -Properties * -ErrorAction Continue -ErrorVariable ErrorProcessGetADGroup - $DomainName = ($GroupName.canonicalname -split '/')[0] - $RealGroupName = $GroupName.name - } - if ($QuestADSnappin) - { - Write-Verbose -Message "[$ScriptName][PROCESS] Quest ActiveDirectory Snapin" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupSplatting.Service = $Server } - - # Look for Group - $GroupName = Get-QADgroup @GroupSplatting -ErrorAction Continue -ErrorVariable ErrorProcessGetQADGroup - $DomainName = $($GroupName.domain.name) - $RealGroupName = $GroupName.name - } - - # GroupName Found - IF ($GroupName) - { - - # Splatting for the AD Group Members Request - $GroupMemberSplatting = @{ } - $GroupMemberSplatting.Identity = $GroupName - - - # Get GroupName Membership - if ($ADModule) - { - Write-Verbose -Message "[$ScriptName][PROCESS] GROUP: $item - Querying Membership (AD Module)" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Server = $Server } - - # Look for Members - $Members = Get-ADGroupMember @GroupMemberSplatting -Recursive -ErrorAction Stop -ErrorVariable ErrorProcessGetADGroupMember - $Members = $members | get-aduser -Properties PasswordExpired | Select-Object -Property *,@{ Name = 'DN'; Expression = { $_.DistinguishedName } } + + + + }#FOREACH ($item in $File) + }#IF ($PSBoundParameters['File']) + + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # + # GROUP or SEARCHROOT or FILE parameters specified # + # # # # # # # # # # # # # # # # # # # # # # # # # # # + + # This will run for any parameter set name ParameterSetName = OU, Group or File + FOREACH ($item in $Group) + { + TRY + { + + Write-Verbose -Message "[$ScriptName][PROCESS] GROUP: $item... " + + # Splatting for the AD Group Request + $GroupSplatting = @{ } + $GroupSplatting.Identity = $item + + # Group Information + if ($ADModule) + { + Write-Verbose -Message "[$ScriptName][PROCESS] ActiveDirectory module" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupSplatting.Server = $Server } + + # Look for Group + $GroupName = Get-ADGroup @GroupSplatting -Properties * -ErrorAction Continue -ErrorVariable ErrorProcessGetADGroup + $DomainName = ($GroupName.canonicalname -split '/')[0] + $RealGroupName = $GroupName.name + } + if ($QuestADSnappin) + { + Write-Verbose -Message "[$ScriptName][PROCESS] Quest ActiveDirectory Snapin" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupSplatting.Service = $Server } + + # Look for Group + $GroupName = Get-QADgroup @GroupSplatting -ErrorAction Continue -ErrorVariable ErrorProcessGetQADGroup + $DomainName = $($GroupName.domain.name) + $RealGroupName = $GroupName.name + } + + # GroupName Found + IF ($GroupName) + { + + # Splatting for the AD Group Members Request + $GroupMemberSplatting = @{ } + $GroupMemberSplatting.Identity = $GroupName + + + # Get GroupName Membership + if ($ADModule) + { + Write-Verbose -Message "[$ScriptName][PROCESS] GROUP: $item - Querying Membership (AD Module)" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Server = $Server } + + # Look for Members + $Members = Get-ADGroupMember @GroupMemberSplatting -Recursive -ErrorAction Stop -ErrorVariable ErrorProcessGetADGroupMember + $Members = $members | get-aduser -Properties PasswordExpired | Select-Object -Property *,@{ Name = 'DN'; Expression = { $_.DistinguishedName } } + + } + if ($QuestADSnappin) + { + Write-Verbose -Message "[$ScriptName][PROCESS] GROUP: $item - Querying Membership (Quest AD Snapin)" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Service = $Server } + + $Members = Get-QADGroupMember @GroupMemberSplatting -Indirect -ErrorAction Stop -ErrorVariable ErrorProcessGetQADGroupMember #| Select-Object -Property *,@{ Name = 'DistinguishedName'; Expression = { $_.dn } } + } + # NO MEMBERS, Add some info in $members to avoid the $null + # If the value is $null the compare-object won't work + IF (-not ($Members)) + { + Write-Verbose -Message "[$ScriptName][PROCESS] GROUP: $item is empty" + $Members = New-Object -TypeName PSObject -Property @{ + Name = "No User or Group" + SamAccountName = "No User or Group" + } + } + + + # GroupName Membership File + # If the file doesn't exist, assume we don't have a record to refer to + $StateFile = "$($DomainName)_$($RealGroupName)-membership.csv" + IF (-not(Test-Path -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile))) + { + Write-Verbose -Message "[$ScriptName][PROCESS] $item - The following file did not exist: $StateFile" + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Exporting the current membership information into the file: $StateFile" + $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation -Encoding Unicode + } + ELSE + { + Write-Verbose -Message "[$ScriptName][PROCESS] $item - The following file Exists: $StateFile" + } + + + # GroupName Membership File is compared with the current GroupName Membership + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Comparing Current and Before" + $ImportCSV = Import-Csv -Path (Join-Path -path $ScriptPathOutput -childpath $StateFile) -ErrorAction Stop -ErrorVariable ErrorProcessImportCSV + $Changes = Compare-Object -DifferenceObject $ImportCSV -ReferenceObject $Members -ErrorAction stop -ErrorVariable ErrorProcessCompareObject -Property Name, SamAccountName, DN | + Select-Object @{ Name = "DateTime"; Expression = { Get-Date -Format "yyyyMMdd-hh:mm:ss" } }, @{ + n = 'State'; e = { + IF ($_.SideIndicator -eq "=>") { "Removed" } + ELSE { "Added" } + } + }, DisplayName, Name, SamAccountName, DN | Where-Object { $_.name -notlike "*no user or group*" } + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Compare Block Done !" + + <# Troubleshooting + Write-Verbose -Message "[$ScriptName]IMPORTCSV var" + $ImportCSV | fl -Property Name, SamAccountName, DN + + Write-Verbose -Message "[$ScriptName]MEMBER" + $Members | fl -Property Name, SamAccountName, DN + Write-Verbose -Message "[$ScriptName]CHANGE" + $Changes + #> + + # CHANGES FOUND ! + If ($Changes -or $AlwaysReport) + { + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Some changes found" + $changes | Select-Object -Property DateTime, State, Name, SamAccountName, DN - } - if ($QuestADSnappin) - { - Write-Verbose -Message "[$ScriptName][PROCESS] GROUP: $item - Querying Membership (Quest AD Snapin)" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Service = $Server } - - $Members = Get-QADGroupMember @GroupMemberSplatting -Indirect -ErrorAction Stop -ErrorVariable ErrorProcessGetQADGroupMember #| Select-Object -Property *,@{ Name = 'DistinguishedName'; Expression = { $_.dn } } - } - # NO MEMBERS, Add some info in $members to avoid the $null - # If the value is $null the compare-object won't work - IF (-not ($Members)) - { - Write-Verbose -Message "[$ScriptName][PROCESS] GROUP: $item is empty" - $Members = New-Object -TypeName PSObject -Property @{ - Name = "No User or Group" - SamAccountName = "No User or Group" - } - } - - - # GroupName Membership File - # If the file doesn't exist, assume we don't have a record to refer to - $StateFile = "$($DomainName)_$($RealGroupName)-membership.csv" - IF (-not(Test-Path -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile))) - { - Write-Verbose -Message "[$ScriptName][PROCESS] $item - The following file did not exist: $StateFile" - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Exporting the current membership information into the file: $StateFile" - $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation -Encoding Unicode - } - ELSE - { - Write-Verbose -Message "[$ScriptName][PROCESS] $item - The following file Exists: $StateFile" - } - - - # GroupName Membership File is compared with the current GroupName Membership - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Comparing Current and Before" - $ImportCSV = Import-Csv -Path (Join-Path -path $ScriptPathOutput -childpath $StateFile) -ErrorAction Stop -ErrorVariable ErrorProcessImportCSV - $Changes = Compare-Object -DifferenceObject $ImportCSV -ReferenceObject $Members -ErrorAction stop -ErrorVariable ErrorProcessCompareObject -Property Name, SamAccountName, DN | - Select-Object @{ Name = "DateTime"; Expression = { Get-Date -Format "yyyyMMdd-hh:mm:ss" } }, @{ - n = 'State'; e = { - IF ($_.SideIndicator -eq "=>") { "Removed" } - ELSE { "Added" } - } - }, DisplayName, Name, SamAccountName, DN | Where-Object { $_.name -notlike "*no user or group*" } - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Compare Block Done !" - - <# Troubleshooting - Write-Verbose -Message "[$ScriptName]IMPORTCSV var" - $ImportCSV | fl -Property Name, SamAccountName, DN - - Write-Verbose -Message "[$ScriptName]MEMBER" - $Members | fl -Property Name, SamAccountName, DN - Write-Verbose -Message "[$ScriptName]CHANGE" - $Changes - #> - - # CHANGES FOUND ! - If ($Changes -or $AlwaysReport) - { - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Some changes found" - $changes | Select-Object -Property DateTime, State, Name, SamAccountName, DN - - # CHANGE HISTORY - # Get the Past Changes History - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Get the change history for this group" - $ChangesHistoryFiles = Get-ChildItem -Path $ScriptPathChangeHistory\$($DomainName)_$($RealGroupName)-ChangeHistory.csv -ErrorAction 'SilentlyContinue' - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Change history files: $(($ChangesHistoryFiles|Measure-Object).Count)" - - # Process each history changes - IF ($ChangesHistoryFiles) - { - $infoChangeHistory = @() - FOREACH ($file in $ChangesHistoryFiles.FullName) - { - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Change history files - Loading $file" - # Import the file and show the $file creation time and its content - $ImportedFile = Import-Csv -Path $file -ErrorAction Stop -ErrorVariable ErrorProcessImportCSVChangeHistory - FOREACH ($obj in $ImportedFile) - { - $Output = "" | Select-Object -Property DateTime, State, DisplayName,Name, SamAccountName, DN - #$Output.DateTime = $file.CreationTime.GetDateTimeFormats("u") | Out-String - $Output.DateTime = $obj.DateTime - $Output.State = $obj.State - $Output.DisplayName = $obj.DisplayName - $Output.Name = $obj.Name - $Output.SamAccountName = $obj.SamAccountName - $Output.DN = $obj.DN - $infoChangeHistory = $infoChangeHistory + $Output - }#FOREACH $obj in Import-csv $file - }#FOREACH $file in $ChangeHistoryFiles - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Change history process completed" - }#IF($ChangeHistoryFiles) - - if($IncludeMembers) { - $infoMembers = @() - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Full member list - Loading" - FOREACH ($obj in $Members) - { - $Output = "" | Select-Object -Property Name, SamAccountName, DN,Enabled,PasswordExpired - #$Output.DateTime = $file.CreationTime.GetDateTimeFormats("u") | Out-String - $Output.Name = $obj.Name - $Output.SamAccountName = $obj.SamAccountName - $Output.DN = $obj.distinguishedName - if($ExtendedProperty) { - $Output.Enabled = $obj.Enabled - $Output.PasswordExpired = $obj.PasswordExpired - } - $infoMembers = $infoMembers + $Output - }#FOREACH $obj in Import-csv $file - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Full member list process completed" - } #IF($IncludeMembers) - - - # CHANGE(S) EXPORT TO CSV - IF ($Changes) - { - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Save changes to a ChangesHistory file" - - IF (-not (Test-Path -path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv"))) - { - $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation -Encoding Unicode - } - ELSE - { - #$Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$DomainName_$RealGroupName-ChangeHistory-$DateFormat.csv") -NoTypeInformation - $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation -Append -Encoding Unicode - } - } - - # EMAIL - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Preparing the notification email..." - - $EmailSubject = "PS MONITORING - $($GroupName.SamAccountName) Membership Change" - - # Preparing the body of the Email - $body = "

Group: $($GroupName.SamAccountName)

" - $body += "

" - $body += "Group Description: $($GroupName.Description)
" - $body += "Group DistinguishedName: $($GroupName.DistinguishedName)
" - $body += "Group CanonicalName: $($GroupName.CanonicalName)
" - $body += "Group SID: $($GroupName.Sid.value)
" - $body += "Group Scope/Type: $($GroupName.GroupScope) / $($GroupName.GroupType)
" - $body += "

" - - if($Changes) { - $body += "

Membership Change" - $body += "

" - $body += "The membership of this group changed. See the following Added or Removed members." - - # Removing the old DisplayName Property - $Changes = $changes | Select-Object -Property DateTime, State,Name, SamAccountName, DN - - $body += $changes | ConvertTo-Html -head $head | Out-String - $body += "


" - } - else { - $body += "

Membership Change" - $body += "

" - $body += "No changes." - } - - IF ($infoChangeHistory) - { - # Removing the old DisplayName Property - $infoChangeHistory = $infoChangeHistory | Select-Object -Property DateTime, State, Name, SamAccountName, DN - - $body += "

Change History

" - $body += "List of the previous changes on this group observed by the script" - $body += $infoChangeHistory | Sort-Object -Property DateTime -Descending | ConvertTo-Html -Fragment -PreContent $Head2 | Out-String - } - if($infoMembers) { - $body += "

Members

" - $body += "List of all members" - $body += $infoMembers | Sort-Object -Property SamAccountName -Descending | ConvertTo-Html -Fragment -PreContent $Head2 | Out-String - } - - $body = $body -replace "Added", "Added" - $body = $body -replace "Removed", "Removed" - $body += $Report - if(-not($OneReport)) { - # Preparing the Email properties - $SmtpClient = New-Object -TypeName system.net.mail.smtpClient - $SmtpClient.host = $EmailServer - $MailMessage = New-Object -TypeName system.net.mail.mailmessage - #$MailMessage.from = $EmailFrom.Address - $MailMessage.from = $EmailFrom - #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} - FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } - $MailMessage.IsBodyHtml = 1 - $MailMessage.Subject = $EmailSubject - $MailMessage.Body = $Body - - # Encoding - $MailMessage.BodyEncoding = [System.Text.Encoding]::$EmailEncoding - $MailMessage.SubjectEncoding = [System.Text.Encoding]::$EmailEncoding - - - # Sending the Email - $SmtpClient.Send($MailMessage) - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Email Sent." - - } - # GroupName Membership export to CSV - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Exporting the current membership to $StateFile" - $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation -Encoding Unicode - - # Export HTML File - IF ($PSBoundParameters['HTMLLog']) - { - # Create HTML Directory if it does not exist - $ScriptPathHTML = $ScriptPath + "\HTML" - IF (-not(Test-Path -Path $ScriptPathHTML)) - { - Write-Verbose -Message "[$ScriptName][PROCESS] Creating the HTML Folder : $ScriptPathHTML" - New-Item -Path $ScriptPathHTML -ItemType Directory | Out-Null - } - - # Define HTML File Name - $HTMLFileName = "$($DomainName)_$($RealGroupName)-$(Get-Date -Format 'yyyyMMdd_HHmmss').html" - - # Save HTML File - $Body | Out-File -FilePath (Join-Path -Path $ScriptPathHTML -ChildPath $HTMLFileName) - } - # One Report - if ($OneReport) { - # Create HTML Directory if it does not exist - $ScriptPathOneReport= $ScriptPath + "\OneReport" - IF (-not(Test-Path -Path $ScriptPathOneReport)) - { - Write-Verbose -Message "[$ScriptName][PROCESS] Creating the OneReport Folder : $ScriptPathOneReport" - New-Item -Path $ScriptPathOneReport -ItemType Directory | Out-Null - } - # Define HTML File Name - $HTMLFileName = "$($DomainName)_$($RealGroupName)-$(Get-Date -Format 'yyyyMMdd_HHmmss').html" - - # Save HTML File - $Body | Out-File -FilePath (Join-Path -Path $ScriptPathOneReport -ChildPath $HTMLFileName) - } # if $OneReport - - - }#IF $Change - ELSE { Write-Verbose -Message "[$ScriptName][PROCESS] $item - No Change" } - - }#IF ($GroupName) - ELSE - { - Write-Verbose -Message "[$ScriptName][PROCESS] $item - Group can't be found" - #IF (Get-ChildItem (Join-Path $ScriptPathOutput "*$item*-membership.csv" -ErrorAction Continue) -or (Get-ChildItem (Join-Path $ScriptPathChangeHistory "*$item*.csv" -ErrorAction Continue))) - #{ - # Write-Warning "$item - Looks like a file contains the name of this group, this group was possibly deleted from Active Directory" - #} - - }#ELSE $GroupName - }#TRY - CATCH - { - Write-Warning -Message "[$ScriptName][PROCESS] Something went wrong" - #Quest Snappin Errors - if ($ErrorProcessGetQADGroup) { Write-Warning -Message "[$ScriptName][PROCESS] QUEST AD - Error When querying the group $item in Active Directory" } - if ($ErrorProcessGetQADGroupMember) { Write-Warning -Message "[$ScriptName][PROCESS] QUEST AD - Error When querying the group $item members in Active Directory" } - - #ActiveDirectory Module Errors - if ($ErrorProcessGetADGroup) { Write-Warning -Message "[$ScriptName][PROCESS] AD MODULE - Error When querying the group $item in Active Directory" } - if ($ErrorProcessGetADGroupMember) { Write-Warning -Message "[$ScriptName][PROCESS] AD MODULE - Error When querying the group $item members in Active Directory" } - - # Import CSV Errors - if ($ErrorProcessImportCSV) { Write-Warning -Message "[$ScriptName][PROCESS] Error Importing $StateFile" } - if ($ErrorProcessCompareObject) { Write-Warning -Message "[$ScriptName][PROCESS] Error when comparing" } - if ($ErrorProcessImportCSVChangeHistory) { Write-Warning -Message "[$ScriptName][PROCESS] Error Importing $file" } - - Write-Warning -Message $_.Exception.Message - - }#CATCH - }#FOREACH - if($OneReport) { - $EmailSubject = "PS MONITORING - Membership Report" - # Preparing the body of the Email - $body = "

See Report in Attachment

" - # Preparing the Email properties - $SmtpClient = New-Object -TypeName system.net.mail.smtpClient - $SmtpClient.host = $EmailServer - $MailMessage = New-Object -TypeName system.net.mail.mailmessage - #$MailMessage.from = $EmailFrom.Address - $MailMessage.from = $EmailFrom - #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} - FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } - $MailMessage.IsBodyHtml = 1 - $MailMessage.Subject = $EmailSubject - $MailMessage.Body = $Body - $ScriptPathOneReport= $ScriptPath + "\OneReport" - foreach ($attachment in (Get-ChildItem $ScriptPathOneReport) ){ - $MailMessage.Attachments.Add($attachment.fullname) - } - - # Encoding - $MailMessage.BodyEncoding = [System.Text.Encoding]::$EmailEncoding - $MailMessage.SubjectEncoding = [System.Text.Encoding]::$EmailEncoding - - # Sending the Email - $SmtpClient.Send($MailMessage) - $SmtpClient.Dispose() - foreach($attach in $MailMessage.Attachments) {$attach.Dispose()} - $SmtpClient.Dispose() - Get-ChildItem $ScriptPathOneReport | remove-item -Force -Confirm:$false - Write-Verbose -Message "[$ScriptName][PROCESS] OneReport - Email Sent." - - } # if $onereport - }#TRY - CATCH - { - Write-Warning -Message "[$ScriptName][PROCESS] Something wrong happened" + # CHANGE HISTORY + # Get the Past Changes History + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Get the change history for this group" + $ChangesHistoryFiles = Get-ChildItem -Path $ScriptPathChangeHistory\$($DomainName)_$($RealGroupName)-ChangeHistory.csv -ErrorAction 'SilentlyContinue' + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Change history files: $(($ChangesHistoryFiles|Measure-Object).Count)" + + # Process each history changes + IF ($ChangesHistoryFiles) + { + $infoChangeHistory = @() + FOREACH ($file in $ChangesHistoryFiles.FullName) + { + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Change history files - Loading $file" + # Import the file and show the $file creation time and its content + $ImportedFile = Import-Csv -Path $file -ErrorAction Stop -ErrorVariable ErrorProcessImportCSVChangeHistory + FOREACH ($obj in $ImportedFile) + { + $Output = "" | Select-Object -Property DateTime, State, DisplayName,Name, SamAccountName, DN + #$Output.DateTime = $file.CreationTime.GetDateTimeFormats("u") | Out-String + $Output.DateTime = $obj.DateTime + $Output.State = $obj.State + $Output.DisplayName = $obj.DisplayName + $Output.Name = $obj.Name + $Output.SamAccountName = $obj.SamAccountName + $Output.DN = $obj.DN + $infoChangeHistory = $infoChangeHistory + $Output + }#FOREACH $obj in Import-csv $file + }#FOREACH $file in $ChangeHistoryFiles + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Change history process completed" + }#IF($ChangeHistoryFiles) + + if($IncludeMembers) { + $infoMembers = @() + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Full member list - Loading" + FOREACH ($obj in $Members) + { + $Output = "" | Select-Object -Property Name, SamAccountName, DN,Enabled,PasswordExpired + #$Output.DateTime = $file.CreationTime.GetDateTimeFormats("u") | Out-String + $Output.Name = $obj.Name + $Output.SamAccountName = $obj.SamAccountName + $Output.DN = $obj.distinguishedName + if($ExtendedProperty) { + $Output.Enabled = $obj.Enabled + $Output.PasswordExpired = $obj.PasswordExpired + } + $infoMembers = $infoMembers + $Output + }#FOREACH $obj in Import-csv $file + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Full member list process completed" + } #IF($IncludeMembers) + + + # CHANGE(S) EXPORT TO CSV + IF ($Changes) + { + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Save changes to a ChangesHistory file" + + IF (-not (Test-Path -path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv"))) + { + $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation -Encoding Unicode + } + ELSE + { + #$Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$DomainName_$RealGroupName-ChangeHistory-$DateFormat.csv") -NoTypeInformation + $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation -Append -Encoding Unicode + } + } + + # EMAIL + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Preparing the notification email..." + + $EmailSubject = "PS MONITORING - $($GroupName.SamAccountName) Membership Change" + + # Preparing the body of the Email + $body = "

Group: $($GroupName.SamAccountName)

" + $body += "

" + $body += "Group Description: $($GroupName.Description)
" + $body += "Group DistinguishedName: $($GroupName.DistinguishedName)
" + $body += "Group CanonicalName: $($GroupName.CanonicalName)
" + $body += "Group SID: $($GroupName.Sid.value)
" + $body += "Group Scope/Type: $($GroupName.GroupScope) / $($GroupName.GroupType)
" + $body += "

" + + if($Changes) { + $body += "

Membership Change" + $body += "

" + $body += "The membership of this group changed. See the following Added or Removed members." + + # Removing the old DisplayName Property + $Changes = $changes | Select-Object -Property DateTime, State,Name, SamAccountName, DN + + $body += $changes | ConvertTo-Html -head $head | Out-String + $body += "


" + } + else { + $body += "

Membership Change" + $body += "

" + $body += "No changes." + } + + IF ($infoChangeHistory) + { + # Removing the old DisplayName Property + $infoChangeHistory = $infoChangeHistory | Select-Object -Property DateTime, State, Name, SamAccountName, DN + + $body += "

Change History

" + $body += "List of the previous changes on this group observed by the script" + $body += $infoChangeHistory | Sort-Object -Property DateTime -Descending | ConvertTo-Html -Fragment -PreContent $Head2 | Out-String + } + if($infoMembers) { + $body += "

Members

" + $body += "List of all members" + $body += $infoMembers | Sort-Object -Property SamAccountName -Descending | ConvertTo-Html -Fragment -PreContent $Head2 | Out-String + } + + $body = $body -replace "Added", "Added" + $body = $body -replace "Removed", "Removed" + $body += $Report + if(-not($OneReport)) { + # Preparing the Email properties + $SmtpClient = New-Object -TypeName system.net.mail.smtpClient + $SmtpClient.host = $EmailServer + $MailMessage = New-Object -TypeName system.net.mail.mailmessage + #$MailMessage.from = $EmailFrom.Address + $MailMessage.from = $EmailFrom + #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} + FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } + $MailMessage.IsBodyHtml = 1 + $MailMessage.Subject = $EmailSubject + $MailMessage.Body = $Body + + # Encoding + $MailMessage.BodyEncoding = [System.Text.Encoding]::$EmailEncoding + $MailMessage.SubjectEncoding = [System.Text.Encoding]::$EmailEncoding + + + # Sending the Email + $SmtpClient.Send($MailMessage) + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Email Sent." + + } + # GroupName Membership export to CSV + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Exporting the current membership to $StateFile" + $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation -Encoding Unicode + + # Export HTML File + IF ($PSBoundParameters['HTMLLog']) + { + # Create HTML Directory if it does not exist + $ScriptPathHTML = $ScriptPath + "\HTML" + IF (-not(Test-Path -Path $ScriptPathHTML)) + { + Write-Verbose -Message "[$ScriptName][PROCESS] Creating the HTML Folder : $ScriptPathHTML" + New-Item -Path $ScriptPathHTML -ItemType Directory | Out-Null + } + + # Define HTML File Name + $HTMLFileName = "$($DomainName)_$($RealGroupName)-$(Get-Date -Format 'yyyyMMdd_HHmmss').html" + + # Save HTML File + $Body | Out-File -FilePath (Join-Path -Path $ScriptPathHTML -ChildPath $HTMLFileName) + } + # One Report + if ($OneReport) { + # Create HTML Directory if it does not exist + $ScriptPathOneReport= $ScriptPath + "\OneReport" + IF (-not(Test-Path -Path $ScriptPathOneReport)) + { + Write-Verbose -Message "[$ScriptName][PROCESS] Creating the OneReport Folder : $ScriptPathOneReport" + New-Item -Path $ScriptPathOneReport -ItemType Directory | Out-Null + } + # Define HTML File Name + $HTMLFileName = "$($DomainName)_$($RealGroupName)-$(Get-Date -Format 'yyyyMMdd_HHmmss').html" + + # Save HTML File + $Body | Out-File -FilePath (Join-Path -Path $ScriptPathOneReport -ChildPath $HTMLFileName) + } # if $OneReport + + + }#IF $Change + ELSE { Write-Verbose -Message "[$ScriptName][PROCESS] $item - No Change" } + + }#IF ($GroupName) + ELSE + { + Write-Verbose -Message "[$ScriptName][PROCESS] $item - Group can't be found" + #IF (Get-ChildItem (Join-Path $ScriptPathOutput "*$item*-membership.csv" -ErrorAction Continue) -or (Get-ChildItem (Join-Path $ScriptPathChangeHistory "*$item*.csv" -ErrorAction Continue))) + #{ + # Write-Warning "$item - Looks like a file contains the name of this group, this group was possibly deleted from Active Directory" + #} + + }#ELSE $GroupName + }#TRY + CATCH + { + Write-Warning -Message "[$ScriptName][PROCESS] Something went wrong" + #Quest Snappin Errors + if ($ErrorProcessGetQADGroup) { Write-Warning -Message "[$ScriptName][PROCESS] QUEST AD - Error When querying the group $item in Active Directory" } + if ($ErrorProcessGetQADGroupMember) { Write-Warning -Message "[$ScriptName][PROCESS] QUEST AD - Error When querying the group $item members in Active Directory" } + + #ActiveDirectory Module Errors + if ($ErrorProcessGetADGroup) { Write-Warning -Message "[$ScriptName][PROCESS] AD MODULE - Error When querying the group $item in Active Directory" } + if ($ErrorProcessGetADGroupMember) { Write-Warning -Message "[$ScriptName][PROCESS] AD MODULE - Error When querying the group $item members in Active Directory" } + + # Import CSV Errors + if ($ErrorProcessImportCSV) { Write-Warning -Message "[$ScriptName][PROCESS] Error Importing $StateFile" } + if ($ErrorProcessCompareObject) { Write-Warning -Message "[$ScriptName][PROCESS] Error when comparing" } + if ($ErrorProcessImportCSVChangeHistory) { Write-Warning -Message "[$ScriptName][PROCESS] Error Importing $file" } + + Write-Warning -Message $_.Exception.Message + + }#CATCH + }#FOREACH + if($OneReport) { + $EmailSubject = "PS MONITORING - Membership Report" + # Preparing the body of the Email + $body = "

See Report in Attachment

" + # Preparing the Email properties + $SmtpClient = New-Object -TypeName system.net.mail.smtpClient + $SmtpClient.host = $EmailServer + $MailMessage = New-Object -TypeName system.net.mail.mailmessage + #$MailMessage.from = $EmailFrom.Address + $MailMessage.from = $EmailFrom + #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} + FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } + $MailMessage.IsBodyHtml = 1 + $MailMessage.Subject = $EmailSubject + $MailMessage.Body = $Body + $ScriptPathOneReport= $ScriptPath + "\OneReport" + foreach ($attachment in (Get-ChildItem $ScriptPathOneReport) ){ + $MailMessage.Attachments.Add($attachment.fullname) + } + + # Encoding + $MailMessage.BodyEncoding = [System.Text.Encoding]::$EmailEncoding + $MailMessage.SubjectEncoding = [System.Text.Encoding]::$EmailEncoding + + # Sending the Email + $SmtpClient.Send($MailMessage) + $SmtpClient.Dispose() + foreach($attach in $MailMessage.Attachments) {$attach.Dispose()} + $SmtpClient.Dispose() + Get-ChildItem $ScriptPathOneReport | remove-item -Force -Confirm:$false + Write-Verbose -Message "[$ScriptName][PROCESS] OneReport - Email Sent." + + } # if $onereport + }#TRY + CATCH + { + Write-Warning -Message "[$ScriptName][PROCESS] Something wrong happened" throw $_ - } - + } + }#PROCESS END { - Write-Verbose -Message "[$ScriptName][END] Script Completed" + Write-Verbose -Message "[$ScriptName][END] Script Completed" }