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"
}