diff --git a/ADHTMLReports.ps1 b/ADHTMLReports.ps1
new file mode 100644
index 0000000..e377565
--- /dev/null
+++ b/ADHTMLReports.ps1
@@ -0,0 +1,1732 @@
+<#
+.SYNOPSIS
+ Generate graphed report for all Active Directory objects.
+
+.DESCRIPTION
+ Generate graphed report for all Active Directory objects.
+
+.PARAMETER CompanyLogo
+ Enter URL or UNC path to your desired Company Logo for generated report.
+
+ -CompanyLogo "\\Server01\Admin\Files\CompanyLogo.png"
+
+.PARAMETER RightLogo
+ Enter URL or UNC path to your desired right-side logo for generated report.
+
+ -RightLogo "https://www.psmpartners.com/wp-content/uploads/2017/10/porcaro-stolarek-mete.png"
+
+.PARAMETER ReportTitle
+ Enter desired title for generated report.
+
+ -ReportTitle "Active Directory Report"
+
+.PARAMETER Days
+ Users that have not logged in [X] amount of days or more.
+
+ -Days "30"
+
+.PARAMETER UserCreatedDays
+ Users that have been created within [X] amount of days.
+
+ -UserCreatedDays "7"
+
+.PARAMETER DaysUntilPWExpireINT
+ Users password expires within [X] amount of days
+
+ -DaysUntilPWExpireINT "7"
+
+.PARAMETER ADModNumber
+ Active Directory Objects that have been modified within [X] amount of days.
+
+ -ADModNumber "3"
+
+.NOTES
+ Version: 1.0.3
+ Author: Bradley Wyatt
+ Date: 12/4/2018
+ Modified: JBear 12/5/2018
+ Bradley Wyatt 12/8/2018
+ jporgand 12/6/2018
+#>
+
+param (
+
+ #Company logo that will be displayed on the left, can be URL or UNC
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Enter URL or UNC path to Company Logo")]
+ [String]$CompanyLogo = "http://thelazyadministrator.com/wp-content/uploads/2018/06/logo-2-e1529684959389.png",
+ #Logo that will be on the right side, UNC or URL
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Enter URL or UNC path for Side Logo")]
+ [String]$RightLogo = "http://thelazyadministrator.com/wp-content/uploads/2018/06/amd.png",
+ #Title of generated report
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Enter desired title for report")]
+ [String]$ReportTitle = "Active Directory Report",
+ #Location the report will be saved to
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Enter desired directory path to save; Default: C:\Automation\")]
+ [String]$ReportSavePath = "C:\Lab\",
+ #Find users that have not logged in X Amount of days, this sets the days
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Users that have not logged on in more than [X] days. amount of days; Default: 30")]
+ $Days = 30,
+ #Get users who have been created in X amount of days and less
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Users that have been created within [X] amount of days; Default: 7")]
+ $UserCreatedDays = 7,
+ #Get users whos passwords expire in less than X amount of days
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Users password expires within [X] amount of days; Default: 7")]
+ $DaysUntilPWExpireINT = 7,
+ #Get AD Objects that have been modified in X days and newer
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "AD Objects that have been modified within [X] amount of days; Default: 3")]
+ $ADModNumber = 3
+
+ #CSS template located C:\Program Files\WindowsPowerShell\Modules\ReportHTML\1.4.1.1\
+ #Default template is orange and named "Sample"
+)
+
+$DomainAdmin = "Admins du domaine"
+$EntrepriseAdmin = "Administrateurs de l`'entreprise"
+
+Write-Host "Gathering Report Customization..." -ForegroundColor White
+Write-Host "__________________________________" -ForegroundColor White
+(Write-Host -NoNewline "Company Logo (left): " -ForegroundColor Yellow), (Write-Host $CompanyLogo -ForegroundColor White)
+(Write-Host -NoNewline "Company Logo (right): " -ForegroundColor Yellow), (Write-Host $RightLogo -ForegroundColor White)
+(Write-Host -NoNewline "Report Title: " -ForegroundColor Yellow), (Write-Host $ReportTitle -ForegroundColor White)
+(Write-Host -NoNewline "Report Save Path: " -ForegroundColor Yellow), (Write-Host $ReportSavePath -ForegroundColor White)
+(Write-Host -NoNewline "Amount of Days from Last User Logon Report: " -ForegroundColor Yellow), (Write-Host $Days -ForegroundColor White)
+(Write-Host -NoNewline "Amount of Days for New User Creation Report: " -ForegroundColor Yellow), (Write-Host $UserCreatedDays -ForegroundColor White)
+(Write-Host -NoNewline "Amount of Days for User Password Expiration Report: " -ForegroundColor Yellow), (Write-Host $DaysUntilPWExpireINT -ForegroundColor White)
+(Write-Host -NoNewline "Amount of Days for Newly Modified AD Objects Report: " -ForegroundColor Yellow), (Write-Host $ADModNumber -ForegroundColor White)
+Write-Host "__________________________________" -ForegroundColor White
+
+function LastLogonConvert ($ftDate) {
+
+ $Date = [DateTime]::FromFileTime($ftDate)
+
+ if ($Date -lt (Get-Date '1/1/1900') -or $date -eq 0 -or $date -eq $null) {
+
+ "Never"
+ }
+
+ else {
+
+ $Date
+ }
+
+} #End function LastLogonConvert
+
+#Check for ReportHTML Module
+$Mod = Get-Module -ListAvailable -Name "ReportHTML"
+
+If ($null -eq $Mod) {
+
+ Write-Host "ReportHTML Module is not present, attempting to install it"
+
+ Install-Module -Name ReportHTML -Force
+ Import-Module ReportHTML -ErrorAction SilentlyContinue
+}
+
+#Array of default Security Groups
+$DefaultSGs = @(
+
+ "Access Control Assistance Operators"
+ "Account Operators"
+ "Administrators"
+ "Allowed RODC Password Replication Group"
+ "Backup Operators"
+ "Certificate Service DCOM Access"
+ "Cert Publishers"
+ "Cloneable Domain Controllers"
+ "Cryptographic Operators"
+ "Denied RODC Password Replication Group"
+ "Distributed COM Users"
+ "DnsUpdateProxy"
+ "DnsAdmins"
+ "Domain Admins"
+ "Domain Computers"
+ "Domain Controllers"
+ "Domain Guests"
+ "Domain Users"
+ "Enterprise Admins"
+ "Enterprise Key Admins"
+ "Enterprise Read-only Domain Controllers"
+ "Event Log Readers"
+ "Group Policy Creator Owners"
+ "Guests"
+ "Hyper-V Administrators"
+ "IIS_IUSRS"
+ "Incoming Forest Trust Builders"
+ "Key Admins"
+ "Network Configuration Operators"
+ "Performance Log Users"
+ "Performance Monitor Users"
+ "Print Operators"
+ "Pre-Windows 2000 Compatible Access"
+ "Protected Users"
+ "RAS and IAS Servers"
+ "RDS Endpoint Servers"
+ "RDS Management Servers"
+ "RDS Remote Access Servers"
+ "Read-only Domain Controllers"
+ "Remote Desktop Users"
+ "Remote Management Users"
+ "Replicator"
+ "Schema Admins"
+ "Server Operators"
+ "Storage Replica Administrators"
+ "System Managed Accounts Group"
+ "Terminal Server License Servers"
+ "Users"
+ "Windows Authorization Access Group"
+ "WinRMRemoteWMIUsers"
+)
+
+$Table = New-Object 'System.Collections.Generic.List[System.Object]'
+$OUTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$UserTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$GroupTypetable = New-Object 'System.Collections.Generic.List[System.Object]'
+$DefaultGrouptable = New-Object 'System.Collections.Generic.List[System.Object]'
+$EnabledDisabledUsersTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$DomainAdminTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ExpiringAccountsTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$CompanyInfoTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$securityeventtable = New-Object 'System.Collections.Generic.List[System.Object]'
+$DomainTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$OUGPOTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$GroupMembershipTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$PasswordExpirationTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$PasswordExpireSoonTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$userphaventloggedonrecentlytable = New-Object 'System.Collections.Generic.List[System.Object]'
+$EnterpriseAdminTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$NewCreatedUsersTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$GroupProtectionTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$OUProtectionTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$GPOTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ADObjectTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ProtectedUsersTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ComputersTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ComputerProtectedTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ComputersEnabledTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$DefaultComputersinDefaultOUTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$DefaultUsersinDefaultOUTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$TOPUserTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$TOPGroupsTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$TOPComputersTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$GraphComputerOS = New-Object 'System.Collections.Generic.List[System.Object]'
+
+#Get all users right away. Instead of doing several lookups, we will use this object to look up all the information needed.
+$AllUsers = Get-ADUser -Filter * -Properties *
+
+$GPOs = Get-GPO -All | Select-Object DisplayName, GPOStatus, ModificationTime, @{ Label = "ComputerVersion"; Expression = { $_.computer.dsversion } }, @{ Label = "UserVersion"; Expression = { $_.user.dsversion } }
+
+<###########################
+ Dashboard
+############################>
+
+Write-Host "Working on Dashboard Report..." -ForegroundColor Green
+
+$dte = (Get-Date).AddDays(- $ADModNumber)
+
+$ADObjs = Get-ADObject -Filter { whenchanged -gt $dte -and ObjectClass -ne "domainDNS" -and ObjectClass -ne "rIDManager" -and ObjectClass -ne "rIDSet" } -Properties *
+
+foreach ($ADObj in $ADObjs) {
+
+ if ($ADObj.ObjectClass -eq "GroupPolicyContainer") {
+
+ $Name = $ADObj.DisplayName
+ }
+
+ else {
+
+ $Name = $ADObj.Name
+ }
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $Name
+ 'Object Type' = $ADObj.ObjectClass
+ 'When Changed' = $ADObj.WhenChanged
+ }
+
+ $ADObjectTable.Add($obj)
+}
+if (($ADObjectTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No AD Objects have been modified recently'
+ }
+
+ $ADObjectTable.Add($obj)
+}
+
+
+$ADRecycleBinStatus = (Get-ADOptionalFeature -Filter 'name -like "Recycle Bin Feature"').EnabledScopes
+
+if ($ADRecycleBinStatus.Count -lt 1) {
+
+ $ADRecycleBin = "Disabled"
+}
+
+else {
+
+ $ADRecycleBin = "Enabled"
+}
+
+#Company Information
+$ADInfo = Get-ADDomain
+$ForestObj = Get-ADForest
+$DomainControllerobj = Get-ADDomain
+$Forest = $ADInfo.Forest
+$InfrastructureMaster = $DomainControllerobj.InfrastructureMaster
+$RIDMaster = $DomainControllerobj.RIDMaster
+$PDCEmulator = $DomainControllerobj.PDCEmulator
+$DomainNamingMaster = $ForestObj.DomainNamingMaster
+$SchemaMaster = $ForestObj.SchemaMaster
+
+$obj = [PSCustomObject]@{
+
+ 'Domain' = $Forest
+ 'AD Recycle Bin' = $ADRecycleBin
+ 'Infrastructure Master' = $InfrastructureMaster
+ 'RID Master' = $RIDMaster
+ 'PDC Emulator' = $PDCEmulator
+ 'Domain Naming Master' = $DomainNamingMaster
+ 'Schema Master' = $SchemaMaster
+}
+
+$CompanyInfoTable.Add($obj)
+
+if (($CompanyInfoTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: Could not get items for table'
+ }
+ $CompanyInfoTable.Add($obj)
+}
+
+#Get newly created users
+$When = ((Get-Date).AddDays(- $UserCreatedDays)).Date
+$NewUsers = $AllUsers | Where-Object { $_.whenCreated -ge $When }
+
+foreach ($Newuser in $Newusers) {
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $Newuser.Name
+ 'Enabled' = $Newuser.Enabled
+ 'Creation Date' = $Newuser.whenCreated
+ }
+
+ $NewCreatedUsersTable.Add($obj)
+}
+if (($NewCreatedUsersTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No new users have been recently created'
+ }
+ $NewCreatedUsersTable.Add($obj)
+}
+
+
+
+#Get Domain Admins
+$DomainAdminMembers = Get-ADGroupMember $DomainAdmin
+
+foreach ($DomainAdminMember in $DomainAdminMembers) {
+
+ $Name = $DomainAdminMember.Name
+ $Type = $DomainAdminMember.ObjectClass
+ $Enabled = ($AllUsers | Where-Object { $_.Name -eq $Name }).Enabled
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $Name
+ 'Enabled' = $Enabled
+ 'Type' = $Type
+ }
+
+ $DomainAdminTable.Add($obj)
+}
+
+if (($DomainAdminTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No Domain Admin Members were found'
+ }
+ $DomainAdminTable.Add($obj)
+}
+
+
+#Get Enterprise Admins
+$EnterpriseAdminsMembers = Get-ADGroupMember $EntrepriseAdmin -Server $SchemaMaster
+
+foreach ($EnterpriseAdminsMember in $EnterpriseAdminsMembers) {
+
+ $Name = $EnterpriseAdminsMember.Name
+ $Type = $EnterpriseAdminsMember.ObjectClass
+ $Enabled = ($AllUsers | Where-Object { $_.Name -eq $Name }).Enabled
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $Name
+ 'Enabled' = $Enabled
+ 'Type' = $Type
+ }
+
+ $EnterpriseAdminTable.Add($obj)
+}
+
+if (($EnterpriseAdminTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: Enterprise Admin members were found'
+ }
+ $EnterpriseAdminTable.Add($obj)
+}
+
+$DefaultComputersOU = (Get-ADDomain).computerscontainer
+$DefaultComputers = Get-ADComputer -Filter * -Properties * -SearchBase "$DefaultComputersOU"
+
+foreach ($DefaultComputer in $DefaultComputers) {
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $DefaultComputer.Name
+ 'Enabled' = $DefaultComputer.Enabled
+ 'Operating System' = $DefaultComputer.OperatingSystem
+ 'Modified Date' = $DefaultComputer.Modified
+ 'Password Last Set' = $DefaultComputer.PasswordLastSet
+ 'Protect from Deletion' = $DefaultComputer.ProtectedFromAccidentalDeletion
+ }
+
+ $DefaultComputersinDefaultOUTable.Add($obj)
+}
+
+if (($DefaultComputersinDefaultOUTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No computers were found in the Default OU'
+ }
+ $DefaultComputersinDefaultOUTable.Add($obj)
+}
+
+$DefaultUsersOU = (Get-ADDomain).UsersContainer
+$DefaultUsers = $Allusers | Where-Object { $_.DistinguishedName -like "*$($DefaultUsersOU)" } | Select-Object Name, UserPrincipalName, Enabled, ProtectedFromAccidentalDeletion, EmailAddress, @{ Name = 'lastlogon'; Expression = { LastLogonConvert $_.lastlogon } }, DistinguishedName
+
+foreach ($DefaultUser in $DefaultUsers) {
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $DefaultUser.Name
+ 'UserPrincipalName' = $DefaultUser.UserPrincipalName
+ 'Enabled' = $DefaultUser.Enabled
+ 'Protected from Deletion' = $DefaultUser.ProtectedFromAccidentalDeletion
+ 'Last Logon' = $DefaultUser.LastLogon
+ 'Email Address' = $DefaultUser.EmailAddress
+ }
+
+ $DefaultUsersinDefaultOUTable.Add($obj)
+}
+if (($DefaultUsersinDefaultOUTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No Users were found in the default OU'
+ }
+ $DefaultUsersinDefaultOUTable.Add($obj)
+}
+
+
+#Expiring Accounts
+$LooseUsers = Search-ADAccount -AccountExpiring -UsersOnly
+
+foreach ($LooseUser in $LooseUsers) {
+
+ $NameLoose = $LooseUser.Name
+ $UPNLoose = $LooseUser.UserPrincipalName
+ $ExpirationDate = $LooseUser.AccountExpirationDate
+ $enabled = $LooseUser.Enabled
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $NameLoose
+ 'UserPrincipalName' = $UPNLoose
+ 'Expiration Date' = $ExpirationDate
+ 'Enabled' = $enabled
+ }
+
+ $ExpiringAccountsTable.Add($obj)
+}
+
+if (($ExpiringAccountsTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No Users were found to expire soon'
+ }
+ $ExpiringAccountsTable.Add($obj)
+}
+
+#Security Logs
+$SecurityLogs = Get-EventLog -Newest 7 -LogName "Security" | Where-Object { $_.Message -like "*An account*" }
+
+foreach ($SecurityLog in $SecurityLogs) {
+
+ $TimeGenerated = $SecurityLog.TimeGenerated
+ $EntryType = $SecurityLog.EntryType
+ $Recipient = $SecurityLog.Message
+
+ $obj = [PSCustomObject]@{
+
+ 'Time' = $TimeGenerated
+ 'Type' = $EntryType
+ 'Message' = $Recipient
+ }
+
+ $SecurityEventTable.Add($obj)
+}
+
+if (($SecurityEventTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No logon security events were found'
+ }
+ $SecurityEventTable.Add($obj)
+}
+
+#Tenant Domain
+$Domains = Get-ADForest | Select-Object -ExpandProperty upnsuffixes | ForEach-Object {
+
+ $obj = [PSCustomObject]@{
+
+ 'UPN Suffixes' = $_
+ Valid = "True"
+ }
+
+ $DomainTable.Add($obj)
+}
+if (($DomainTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No UPN Suffixes were found'
+ }
+ $DomainTable.Add($obj)
+}
+
+Write-Host "Done!" -ForegroundColor White
+
+<###########################
+
+ Groups
+
+############################>
+
+Write-Host "Working on Groups Report..." -ForegroundColor Green
+
+#Get groups and sort in alphabetical order
+$Groups = Get-ADGroup -Filter * -Properties *
+$SecurityCount = 0
+$MailSecurityCount = 0
+$CustomGroup = 0
+$DefaultGroup = 0
+$Groupswithmemebrship = 0
+$Groupswithnomembership = 0
+$GroupsProtected = 0
+$GroupsNotProtected = 0
+
+foreach ($Group in $Groups) {
+
+ $DefaultADGroup = 'False'
+ $Type = New-Object 'System.Collections.Generic.List[System.Object]'
+ $Gemail = (Get-ADGroup $Group -Properties mail).mail
+
+ if (($group.GroupCategory -eq "Security") -and ($Gemail -ne $Null)) {
+
+ $MailSecurityCount++
+ }
+
+ if (($group.GroupCategory -eq "Security") -and (($Gemail) -eq $Null)) {
+
+ $SecurityCount++
+ }
+
+ if ($Group.ProtectedFromAccidentalDeletion -eq $True) {
+
+ $GroupsProtected++
+ }
+
+ else {
+
+ $GroupsNotProtected++
+ }
+
+ if ($DefaultSGs -contains $Group.Name) {
+
+ $DefaultADGroup = "True"
+ $DefaultGroup++
+ }
+
+ else {
+
+ $CustomGroup++
+ }
+
+ if ($group.GroupCategory -eq "Distribution") {
+
+ $Type = "Distribution Group"
+ }
+
+ if (($group.GroupCategory -eq "Security") -and (($Gemail) -eq $Null)) {
+
+ $Type = "Security Group"
+ }
+
+ if (($group.GroupCategory -eq "Security") -and (($Gemail) -ne $Null)) {
+
+ $Type = "Mail-Enabled Security Group"
+ }
+
+ if ($Group.Name -ne "Domain Users") {
+
+ $Users = (Get-ADGroupMember -Identity $Group | Sort-Object DisplayName | Select-Object -ExpandProperty Name) -join ", "
+
+ if (!($Users)) {
+
+ $Groupswithnomembership++
+ }
+
+ else {
+
+ $Groupswithmemebrship++
+
+ }
+ }
+
+ else {
+
+ $Users = "Skipped Domain Users Membership"
+ }
+
+ $OwnerDN = Get-ADGroup -Filter { name -eq $Group.Name } -Properties managedBy | Select-Object -ExpandProperty ManagedBy
+ Try {
+ $Manager = Get-ADUser -Filter { distinguishedname -like $OwnerDN } | Select-Object -ExpandProperty Name
+ }
+ Catch {
+ write-host -ForegroundColor Yellow "Cannot resolve the manager, " $Manager " on the group " $group.name
+ }
+
+ #$Manager = $AllUsers | Where-Object { $_.distinguishedname -eq $OwnerDN } | Select-Object -ExpandProperty Name
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $Group.name
+ 'Type' = $Type
+ 'Members' = $users
+ 'Managed By' = $Manager
+ 'E-mail Address' = $GEmail
+ 'Protected from Deletion' = $Group.ProtectedFromAccidentalDeletion
+ 'Default AD Group' = $DefaultADGroup
+ }
+
+ $table.Add($obj)
+}
+
+if (($table).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No Groups were found'
+ }
+ $table.Add($obj)
+}
+#TOP groups table
+$obj1 = [PSCustomObject]@{
+
+ 'Total Groups' = $Groups.Count
+ 'Mail-Enabled Security Groups' = $MailSecurityCount
+ 'Security Groups' = $SecurityCount
+ 'Distribution Groups' = $DistroCount
+}
+
+$TOPGroupsTable.Add($obj1)
+
+$obj1 = [PSCustomObject]@{
+
+ 'Name' = 'Mail-Enabled Security Groups'
+ 'Count' = $MailSecurityCount
+}
+
+$GroupTypetable.Add($obj1)
+
+$obj1 = [PSCustomObject]@{
+
+ 'Name' = 'Security Groups'
+ 'Count' = $SecurityCount
+}
+
+$GroupTypetable.Add($obj1)
+$DistroCount = ($Groups | Where-Object { $_.GroupCategory -eq "Distribution" }).Count
+
+$obj1 = [PSCustomObject]@{
+
+ 'Name' = 'Distribution Groups'
+ 'Count' = $DistroCount
+}
+
+$GroupTypetable.Add($obj1)
+
+#Default Group Pie Chart
+$obj1 = [PSCustomObject]@{
+
+ 'Name' = 'Default Groups'
+ 'Count' = $DefaultGroup
+}
+
+$DefaultGrouptable.Add($obj1)
+
+$obj1 = [PSCustomObject]@{
+
+ 'Name' = 'Custom Groups'
+ 'Count' = $CustomGroup
+}
+
+$DefaultGrouptable.Add($obj1)
+
+#Group Protection Pie Chart
+$obj1 = [PSCustomObject]@{
+
+ 'Name' = 'Protected'
+ 'Count' = $GroupsProtected
+}
+
+$GroupProtectionTable.Add($obj1)
+
+$obj1 = [PSCustomObject]@{
+
+ 'Name' = 'Not Protected'
+ 'Count' = $GroupsNotProtected
+}
+
+$GroupProtectionTable.Add($obj1)
+
+#Groups with membership vs no membership pie chart
+$objmem = [PSCustomObject]@{
+
+ 'Name' = 'With Members'
+ 'Count' = $Groupswithmemebrship
+}
+
+$GroupMembershipTable.Add($objmem)
+
+$objmem = [PSCustomObject]@{
+
+ 'Name' = 'No Members'
+ 'Count' = $Groupswithnomembership
+}
+
+$GroupMembershipTable.Add($objmem)
+
+Write-Host "Done!" -ForegroundColor White
+
+<###########################
+
+ Organizational Units
+
+############################>
+
+Write-Host "Working on Organizational Units Report..." -ForegroundColor Green
+
+#Get all OUs'
+$OUs = Get-ADOrganizationalUnit -Filter * -Properties *
+$OUwithLinked = 0
+$OUwithnoLink = 0
+$OUProtected = 0
+$OUNotProtected = 0
+
+foreach ($OU in $OUs) {
+
+ $LinkedGPOs = New-Object 'System.Collections.Generic.List[System.Object]'
+
+ if (($OU.linkedgrouppolicyobjects).length -lt 1) {
+
+ $LinkedGPOs = "None"
+ $OUwithnoLink++
+ }
+
+ else {
+
+ $OUwithLinked++
+ $GPOslinks = $OU.linkedgrouppolicyobjects
+
+ foreach ($GPOlink in $GPOslinks) {
+
+ $Split1 = $GPOlink -split "{" | Select-Object -Last 1
+ $Split2 = $Split1 -split "}" | Select-Object -First 1
+ $LinkedGPOs.Add((Get-GPO -Guid $Split2 -ErrorAction SilentlyContinue).DisplayName)
+ }
+ }
+
+ if ($OU.ProtectedFromAccidentalDeletion -eq $True) {
+
+ $OUProtected++
+ }
+
+ else {
+
+ $OUNotProtected++
+ }
+
+ $LinkedGPOs = $LinkedGPOs -join ", "
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $OU.Name
+ 'Linked GPOs' = $LinkedGPOs
+ 'Modified Date' = $OU.WhenChanged
+ 'Protected from Deletion' = $OU.ProtectedFromAccidentalDeletion
+ }
+
+ $OUTable.Add($obj)
+}
+
+if (($OUTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No OUs were found'
+ }
+ $OUTable.Add($obj)
+}
+
+#OUs with no GPO Linked
+$obj1 = [PSCustomObject]@{
+
+ 'Name' = "OUs with no GPO's linked"
+ 'Count' = $OUwithnoLink
+}
+
+$OUGPOTable.Add($obj1)
+
+$obj2 = [PSCustomObject]@{
+
+ 'Name' = "OUs with GPO's linked"
+ 'Count' = $OUwithLinked
+}
+
+$OUGPOTable.Add($obj2)
+
+#OUs Protected Pie Chart
+$obj1 = [PSCustomObject]@{
+
+ 'Name' = "Protected"
+ 'Count' = $OUProtected
+}
+
+$OUProtectionTable.Add($obj1)
+
+$obj2 = [PSCustomObject]@{
+
+ 'Name' = "Not Protected"
+ 'Count' = $OUNotProtected
+}
+
+$OUProtectionTable.Add($obj2)
+
+Write-Host "Done!" -ForegroundColor White
+
+<###########################
+
+ USERS
+
+############################>
+
+Write-Host "Working on Users Report..." -ForegroundColor Green
+
+$UserEnabled = 0
+$UserDisabled = 0
+$UserPasswordExpires = 0
+$UserPasswordNeverExpires = 0
+$ProtectedUsers = 0
+$NonProtectedUsers = 0
+
+$UsersWIthPasswordsExpiringInUnderAWeek = 0
+$UsersNotLoggedInOver30Days = 0
+$AccountsExpiringSoon = 0
+
+
+#Get users that haven't logged on in X amount of days, var is set at start of script
+$userphaventloggedonrecentlytable = New-Object 'System.Collections.Generic.List[System.Object]'
+foreach ($User in $AllUsers) {
+
+ $AttVar = $User | Select-Object Enabled, PasswordExpired, PasswordLastSet, PasswordNeverExpires, PasswordNotRequired, Name, SamAccountName, EmailAddress, AccountExpirationDate, @{ Name = 'lastlogon'; Expression = { LastLogonConvert $_.lastlogon } }, DistinguishedName
+ $maxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
+
+ if ((($AttVar.PasswordNeverExpires) -eq $False) -and (($AttVar.Enabled) -ne $false)) {
+
+ #Get Password last set date
+ $passwordSetDate = ($User | ForEach-Object { $_.PasswordLastSet })
+
+ if ($null -eq $passwordSetDate) {
+
+ $daystoexpire = "User has never logged on"
+ }
+
+ else {
+
+ #Check for Fine Grained Passwords
+ $PasswordPol = (Get-ADUserResultantPasswordPolicy $user)
+
+ if (($PasswordPol) -ne $null) {
+
+ $maxPasswordAge = ($PasswordPol).MaxPasswordAge
+ }
+
+ $expireson = $passwordsetdate.AddDays($maxPasswordAge)
+ $today = (Get-Date)
+
+ #Gets the count on how many days until the password expires and stores it in the $daystoexpire var
+ $daystoexpire = (New-TimeSpan -Start $today -End $Expireson).Days
+ }
+ }
+
+ else {
+
+ $daystoexpire = "N/A"
+ }
+
+ if (($User.Enabled -eq $True) -and ($AttVar.LastLogon -lt ((Get-Date).AddDays(- $Days))) -and ($User.LastLogon -ne $NULL)) {
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $User.Name
+ 'UserPrincipalName' = $User.UserPrincipalName
+ 'Enabled' = $AttVar.Enabled
+ 'Protected from Deletion' = $User.ProtectedFromAccidentalDeletion
+ 'Last Logon' = $AttVar.lastlogon
+ 'Password Never Expires' = $AttVar.PasswordNeverExpires
+ 'Days Until Password Expires' = $daystoexpire
+ }
+
+ $userphaventloggedonrecentlytable.Add($obj)
+ }
+
+ #Items for protected vs non protected users
+ if ($User.ProtectedFromAccidentalDeletion -eq $False) {
+
+ $NonProtectedUsers++
+ }
+
+ else {
+
+ $ProtectedUsers++
+ }
+
+ #Items for the enabled vs disabled users pie chart
+ if (($AttVar.PasswordNeverExpires) -ne $false) {
+
+ $UserPasswordNeverExpires++
+ }
+
+ else {
+
+ $UserPasswordExpires++
+ }
+
+ #Items for password expiration pie chart
+ if (($AttVar.Enabled) -ne $false) {
+
+ $UserEnabled++
+ }
+
+ else {
+
+ $UserDisabled++
+ }
+
+ $Name = $User.Name
+ $UPN = $User.UserPrincipalName
+ $Enabled = $AttVar.Enabled
+ $EmailAddress = $AttVar.EmailAddress
+ $AccountExpiration = $AttVar.AccountExpirationDate
+ $PasswordExpired = $AttVar.PasswordExpired
+ $PasswordLastSet = $AttVar.PasswordLastSet
+ $PasswordNeverExpires = $AttVar.PasswordNeverExpires
+ $daysUntilPWExpire = $daystoexpire
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $Name
+ 'UserPrincipalName' = $UPN
+ 'Enabled' = $Enabled
+ 'Protected from Deletion' = $User.ProtectedFromAccidentalDeletion
+ 'Last Logon' = $LastLogon
+ 'Email Address' = $EmailAddress
+ 'Account Expiration' = $AccountExpiration
+ 'Change Password Next Logon' = $PasswordExpired
+ 'Password Last Set' = $PasswordLastSet
+ 'Password Never Expires' = $PasswordNeverExpires
+ 'Days Until Password Expires' = $daystoexpire
+ }
+
+ $usertable.Add($obj)
+
+ if ($daystoexpire -lt $DaysUntilPWExpireINT) {
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $Name
+ 'Days Until Password Expires' = $daystoexpire
+ }
+
+ $PasswordExpireSoonTable.Add($obj)
+ }
+}
+if (($userphaventloggedonrecentlytable).Count -eq 0) {
+ $userphaventloggedonrecentlytable = [PSCustomObject]@{
+
+ Information = "Information: No Users were found to have not logged on in $Days days or more"
+ }
+}
+if (($PasswordExpireSoonTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No users were found to have passwords expiring soon'
+ }
+ $PasswordExpireSoonTable.Add($obj)
+}
+
+
+if (($usertable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No users were found'
+ }
+ $usertable.Add($obj)
+}
+
+#Data for users enabled vs disabled pie graph
+$objULic = [PSCustomObject]@{
+
+ 'Name' = 'Enabled'
+ 'Count' = $UserEnabled
+}
+
+$EnabledDisabledUsersTable.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+
+ 'Name' = 'Disabled'
+ 'Count' = $UserDisabled
+}
+
+$EnabledDisabledUsersTable.Add($objULic)
+
+#Data for users password expires pie graph
+$objULic = [PSCustomObject]@{
+
+ 'Name' = 'Password Expires'
+ 'Count' = $UserPasswordExpires
+}
+
+$PasswordExpirationTable.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+
+ 'Name' = 'Password Never Expires'
+ 'Count' = $UserPasswordNeverExpires
+}
+
+$PasswordExpirationTable.Add($objULic)
+
+#Data for protected users pie graph
+$objULic = [PSCustomObject]@{
+
+ 'Name' = 'Protected'
+ 'Count' = $ProtectedUsers
+}
+
+$ProtectedUsersTable.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+
+ 'Name' = 'Not Protected'
+ 'Count' = $NonProtectedUsers
+}
+
+$ProtectedUsersTable.Add($objULic)
+if ($null -ne (($userphaventloggedonrecentlytable).Information)) {
+ $UHLONXD = "0"
+
+}
+Else {
+ $UHLONXD = $userphaventloggedonrecentlytable.Count
+
+}
+#TOP User table
+If ($null -eq (($ExpiringAccountsTable).Information)) {
+
+ $objULic = [PSCustomObject]@{
+ 'Total Users' = $AllUsers.Count
+ "Users with Passwords Expiring in less than $DaysUntilPWExpireINT days" = $PasswordExpireSoonTable.Count
+ 'Expiring Accounts' = $ExpiringAccountsTable.Count
+ "Users Haven't Logged on in $Days Days or more" = $UHLONXD
+ }
+
+ $TOPUserTable.Add($objULic)
+
+
+}
+Else {
+
+ $objULic = [PSCustomObject]@{
+ 'Total Users' = $AllUsers.Count
+ "Users with Passwords Expiring in less than $DaysUntilPWExpireINT days" = $PasswordExpireSoonTable.Count
+ 'Expiring Accounts' = "0"
+ "Users Haven't Logged on in $Days Days or more" = $UHLONXD
+ }
+ $TOPUserTable.Add($objULic)
+}
+
+Write-Host "Done!" -ForegroundColor White
+<###########################
+
+ Group Policy
+
+############################>
+Write-Host "Working on Group Policy Report..." -ForegroundColor Green
+
+$GPOTable = New-Object 'System.Collections.Generic.List[System.Object]'
+
+foreach ($GPO in $GPOs) {
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $GPO.DisplayName
+ 'Status' = $GPO.GpoStatus
+ 'Modified Date' = $GPO.ModificationTime
+ 'User Version' = $GPO.UserVersion
+ 'Computer Version' = $GPO.ComputerVersion
+ }
+
+ $GPOTable.Add($obj)
+}
+if (($GPOTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No Group Policy Obejects were found'
+ }
+ $GPOTable.Add($obj)
+}
+Write-Host "Done!" -ForegroundColor White
+<###########################
+
+ Computers
+
+############################>
+Write-Host "Working on Computers Report..." -ForegroundColor Green
+
+$Computers = Get-ADComputer -Filter * -Properties *
+$ComputersProtected = 0
+$ComputersNotProtected = 0
+$ComputerEnabled = 0
+$ComputerDisabled = 0
+#Only search for versions of windows that exist in the Environment
+$WindowsRegex = "(Windows (Server )?(\d+|XP)?( R2)?).*"
+$OsVersions = $Computers | Select-Object OperatingSystem -unique | ForEach-Object {
+ if ($_.OperatingSystem -match $WindowsRegex ) {
+ return $matches[1]
+ }
+ elseif ($_.OperatingSystem -ne $null) {
+ return $_.OperatingSystem
+ }
+} | Select-Object -unique | Sort-Object
+
+$OsObj = [PSCustomObject]@{}
+
+$OsVersions | ForEach-Object {
+
+ $OsObj | Add-Member -Name $_ -Value 0 -Type NoteProperty
+
+}
+
+foreach ($Computer in $Computers) {
+
+ if ($Computer.ProtectedFromAccidentalDeletion -eq $True) {
+
+ $ComputersProtected++
+ }
+
+ else {
+
+ $ComputersNotProtected++
+ }
+
+ if ($Computer.Enabled -eq $True) {
+
+ $ComputerEnabled++
+ }
+
+ else {
+
+ $ComputerDisabled++
+ }
+
+ $obj = [PSCustomObject]@{
+
+ 'Name' = $Computer.Name
+ 'Enabled' = $Computer.Enabled
+ 'Operating System' = $Computer.OperatingSystem
+ 'Modified Date' = $Computer.Modified
+ 'Password Last Set' = $Computer.PasswordLastSet
+ 'Protect from Deletion' = $Computer.ProtectedFromAccidentalDeletion
+ }
+
+ $ComputersTable.Add($obj)
+
+ if ($Computer.OperatingSystem -match $WindowsRegex) {
+ $OsObj."$($matches[1])"++
+ }
+
+}
+
+if (($ComputersTable).Count -eq 0) {
+
+ $Obj = [PSCustomObject]@{
+
+ Information = 'Information: No computers were found'
+ }
+ $ComputersTable.Add($obj)
+}
+
+#Pie chart breaking down OS for computer obj
+$OsObj.PSObject.Properties | ForEach-Object {
+ $GraphComputerOS.Add([PSCustomObject]@{'Name' = $_.Name; "Count" = $_.Value })
+}
+
+#Data for TOP Computers data table
+$OsObj | Add-Member -Name 'Total Computers' -Value $Computers.Count -Type NoteProperty
+
+$TOPComputersTable.Add($OsObj)
+
+
+#Data for protected Computers pie graph
+$objULic = [PSCustomObject]@{
+
+ 'Name' = 'Protected'
+ 'Count' = $ComputerProtected
+}
+
+$ComputerProtectedTable.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+
+ 'Name' = 'Not Protected'
+ 'Count' = $ComputersNotProtected
+}
+
+$ComputerProtectedTable.Add($objULic)
+
+#Data for enabled/vs Computers pie graph
+$objULic = [PSCustomObject]@{
+
+ 'Name' = 'Enabled'
+ 'Count' = $ComputerEnabled
+}
+
+$ComputersEnabledTable.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+
+ 'Name' = 'Disabled'
+ 'Count' = $ComputerDisabled
+}
+
+$ComputersEnabledTable.Add($objULic)
+
+Write-Host "Done!" -ForegroundColor White
+
+$tabarray = @('Dashboard', 'Groups', 'Organizational Units', 'Users', 'Group Policy', 'Computers')
+
+Write-Host "Compiling Report..." -ForegroundColor Green
+
+##--OU Protection PIE CHART--##
+#Basic Properties
+$PO12 = Get-HTMLPieChartObject
+$PO12.Title = "Organizational Units Protected from Deletion"
+$PO12.Size.Height = 250
+$PO12.Size.width = 250
+$PO12.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PO12.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PO12.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PO12.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PO12.DataDefinition.DataNameColumnName = 'Name'
+$PO12.DataDefinition.DataValueColumnName = 'Count'
+
+##--Computer OS Breakdown PIE CHART--##
+$PieObjectComputerObjOS = Get-HTMLPieChartObject
+$PieObjectComputerObjOS.Title = "Computer Operating Systems"
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectComputerObjOS.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectComputerObjOS.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectComputerObjOS.ChartStyle.ColorSchemeName = 'Random'
+
+##--Computers Protection PIE CHART--##
+#Basic Properties
+$PieObjectComputersProtected = Get-HTMLPieChartObject
+$PieObjectComputersProtected.Title = "Computers Protected from Deletion"
+$PieObjectComputersProtected.Size.Height = 250
+$PieObjectComputersProtected.Size.width = 250
+$PieObjectComputersProtected.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectComputersProtected.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectComputersProtected.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectComputersProtected.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectComputersProtected.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectComputersProtected.DataDefinition.DataValueColumnName = 'Count'
+
+##--Computers Enabled PIE CHART--##
+#Basic Properties
+$PieObjectComputersEnabled = Get-HTMLPieChartObject
+$PieObjectComputersEnabled.Title = "Computers Enabled vs Disabled"
+$PieObjectComputersEnabled.Size.Height = 250
+$PieObjectComputersEnabled.Size.width = 250
+$PieObjectComputersEnabled.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectComputersEnabled.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectComputersEnabled.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectComputersEnabled.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectComputersEnabled.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectComputersEnabled.DataDefinition.DataValueColumnName = 'Count'
+
+##--USERS Protection PIE CHART--##
+#Basic Properties
+$PieObjectProtectedUsers = Get-HTMLPieChartObject
+$PieObjectProtectedUsers.Title = "Users Protected from Deletion"
+$PieObjectProtectedUsers.Size.Height = 250
+$PieObjectProtectedUsers.Size.width = 250
+$PieObjectProtectedUsers.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectProtectedUsers.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectProtectedUsers.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectProtectedUsers.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectProtectedUsers.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectProtectedUsers.DataDefinition.DataValueColumnName = 'Count'
+
+#Basic Properties
+$PieObjectOUGPOLinks = Get-HTMLPieChartObject
+$PieObjectOUGPOLinks.Title = "OU GPO Links"
+$PieObjectOUGPOLinks.Size.Height = 250
+$PieObjectOUGPOLinks.Size.width = 250
+$PieObjectOUGPOLinks.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectOUGPOLinks.ChartStyle.ColorSchemeName = "ColorScheme4"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectOUGPOLinks.ChartStyle.ColorSchemeName = "Generated5"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectOUGPOLinks.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectOUGPOLinks.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectOUGPOLinks.DataDefinition.DataValueColumnName = 'Count'
+
+#Basic Properties
+$PieObject4 = Get-HTMLPieChartObject
+$PieObject4.Title = "Office 365 Unassigned Licenses"
+$PieObject4.Size.Height = 250
+$PieObject4.Size.width = 250
+$PieObject4.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObject4.ChartStyle.ColorSchemeName = "ColorScheme4"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObject4.ChartStyle.ColorSchemeName = "Generated4"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObject4.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObject4.DataDefinition.DataNameColumnName = 'Name'
+$PieObject4.DataDefinition.DataValueColumnName = 'Unassigned Licenses'
+
+#Basic Properties
+$PieObjectGroupType = Get-HTMLPieChartObject
+$PieObjectGroupType.Title = "Group Types"
+$PieObjectGroupType.Size.Height = 250
+$PieObjectGroupType.Size.width = 250
+$PieObjectGroupType.ChartStyle.ChartType = 'doughnut'
+
+#Pie Chart Groups with members vs no members
+$PieObjectGroupMembersType = Get-HTMLPieChartObject
+$PieObjectGroupMembersType.Title = "Group Membership"
+$PieObjectGroupMembersType.Size.Height = 250
+$PieObjectGroupMembersType.Size.width = 250
+$PieObjectGroupMembersType.ChartStyle.ChartType = 'doughnut'
+$PieObjectGroupMembersType.ChartStyle.ColorSchemeName = "ColorScheme4"
+$PieObjectGroupMembersType.ChartStyle.ColorSchemeName = "Generated8"
+$PieObjectGroupMembersType.ChartStyle.ColorSchemeName = 'Random'
+$PieObjectGroupMembersType.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectGroupMembersType.DataDefinition.DataValueColumnName = 'Count'
+
+#Basic Properties
+$PieObjectGroupType2 = Get-HTMLPieChartObject
+$PieObjectGroupType2.Title = "Custom vs Default Groups"
+$PieObjectGroupType2.Size.Height = 250
+$PieObjectGroupType2.Size.width = 250
+$PieObjectGroupType2.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectGroupType.ChartStyle.ColorSchemeName = "ColorScheme4"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectGroupType.ChartStyle.ColorSchemeName = "Generated8"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectGroupType.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectGroupType.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectGroupType.DataDefinition.DataValueColumnName = 'Count'
+
+##--Enabled users vs Disabled Users PIE CHART--##
+#Basic Properties
+$EnabledDisabledUsersPieObject = Get-HTMLPieChartObject
+$EnabledDisabledUsersPieObject.Title = "Enabled vs Disabled Users"
+$EnabledDisabledUsersPieObject.Size.Height = 250
+$EnabledDisabledUsersPieObject.Size.width = 250
+$EnabledDisabledUsersPieObject.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$EnabledDisabledUsersPieObject.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$EnabledDisabledUsersPieObject.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$EnabledDisabledUsersPieObject.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$EnabledDisabledUsersPieObject.DataDefinition.DataNameColumnName = 'Name'
+$EnabledDisabledUsersPieObject.DataDefinition.DataValueColumnName = 'Count'
+
+##--PasswordNeverExpires PIE CHART--##
+#Basic Properties
+$PWExpiresUsersTable = Get-HTMLPieChartObject
+$PWExpiresUsersTable.Title = "Password Expiration"
+$PWExpiresUsersTable.Size.Height = 250
+$PWExpiresUsersTable.Size.Width = 250
+$PWExpiresUsersTable.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PWExpiresUsersTable.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PWExpiresUsersTable.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PWExpiresUsersTable.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PWExpiresUsersTable.DataDefinition.DataNameColumnName = 'Name'
+$PWExpiresUsersTable.DataDefinition.DataValueColumnName = 'Count'
+
+##--Group Protection PIE CHART--##
+#Basic Properties
+$PieObjectGroupProtection = Get-HTMLPieChartObject
+$PieObjectGroupProtection.Title = "Groups Protected from Deletion"
+$PieObjectGroupProtection.Size.Height = 250
+$PieObjectGroupProtection.Size.width = 250
+$PieObjectGroupProtection.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectGroupProtection.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectGroupProtection.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectGroupProtection.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectGroupProtection.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectGroupProtection.DataDefinition.DataValueColumnName = 'Count'
+
+#Dashboard Report
+$FinalReport = New-Object 'System.Collections.Generic.List[System.Object]'
+$FinalReport.Add($(Get-HTMLOpenPage -TitleText $ReportTitle -LeftLogoString $CompanyLogo -RightLogoString $RightLogo))
+$FinalReport.Add($(Get-HTMLTabHeader -TabNames $tabarray))
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[0] -TabHeading ("Report: " + (Get-Date -Format MM-dd-yyyy))))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Company Information"))
+$FinalReport.Add($(Get-HTMLContentTable $CompanyInfoTable))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Groups"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText 'Domain Administrators'))
+$FinalReport.Add($(Get-HTMLContentDataTable $DomainAdminTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText 'Enterprise Administrators'))
+$FinalReport.Add($(Get-HTMLContentDataTable $EnterpriseAdminTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Objects in Default OUs"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText 'Computers'))
+$FinalReport.Add($(Get-HTMLContentDataTable $DefaultComputersinDefaultOUTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText 'Users'))
+$FinalReport.Add($(Get-HTMLContentDataTable $DefaultUsersinDefaultOUTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "AD Objects Modified in Last $ADModNumber Days"))
+$FinalReport.Add($(Get-HTMLContentDataTable $ADObjectTable))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Expiring Items"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Users with Passwords Expiring in less than $DaysUntilPWExpireINT days"))
+$FinalReport.Add($(Get-HTMLContentDataTable $PasswordExpireSoonTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText 'Accounts Expiring Soon'))
+$FinalReport.Add($(Get-HTMLContentDataTable $ExpiringAccountsTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Accounts"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Users Haven't Logged on in $Days Days or more"))
+$FinalReport.Add($(Get-HTMLContentDataTable $userphaventloggedonrecentlytable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Accounts Created in $UserCreatedDays Days or Less"))
+$FinalReport.Add($(Get-HTMLContentDataTable $NewCreatedUsersTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Security Logs"))
+$FinalReport.Add($(Get-HTMLContentDataTable $securityeventtable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "UPN Suffixes"))
+$FinalReport.Add($(Get-HTMLContentTable $DomainTable))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLTabContentClose))
+
+#Groups Report
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[1] -TabHeading ("Report: " + (Get-Date -Format MM-dd-yyyy))))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Groups Overivew"))
+$FinalReport.Add($(Get-HTMLContentTable $TOPGroupsTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Active Directory Groups"))
+$FinalReport.Add($(Get-HTMLContentDataTable $Table -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText 'Domain Administrators'))
+$FinalReport.Add($(Get-HTMLContentDataTable $DomainAdminTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText 'Enterprise Administrators'))
+$FinalReport.Add($(Get-HTMLContentDataTable $EnterpriseAdminTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Active Directory Groups Chart"))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 1 -ColumnCount 4))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectGroupType -DataSet $GroupTypetable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 2 -ColumnCount 4))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectGroupType2 -DataSet $DefaultGrouptable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 3 -ColumnCount 4))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectGroupMembersType -DataSet $GroupMembershipTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 4 -ColumnCount 4))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectGroupProtection -DataSet $GroupProtectionTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLTabContentClose))
+
+#Organizational Unit Report
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[2] -TabHeading ("Report: " + (Get-Date -Format MM-dd-yyyy))))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Organizational Units"))
+$FinalReport.Add($(Get-HTMLContentDataTable $OUTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Organizational Units Charts"))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 1 -ColumnCount 2))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectOUGPOLinks -DataSet $OUGPOTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 2 -ColumnCount 2))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PO12 -DataSet $OUProtectionTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentclose))
+$FinalReport.Add($(Get-HTMLTabContentClose))
+
+#Users Report
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[3] -TabHeading ("Report: " + (Get-Date -Format MM-dd-yyyy))))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Users Overivew"))
+$FinalReport.Add($(Get-HTMLContentTable $TOPUserTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Active Directory Users"))
+$FinalReport.Add($(Get-HTMLContentDataTable $UserTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Expiring Items"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Users with Passwords Expiring in less than $DaysUntilPWExpireINT days"))
+$FinalReport.Add($(Get-HTMLContentDataTable $PasswordExpireSoonTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText 'Accounts Expiring Soon'))
+$FinalReport.Add($(Get-HTMLContentDataTable $ExpiringAccountsTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Accounts"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Users Haven't Logged on in $Days Days or more"))
+$FinalReport.Add($(Get-HTMLContentDataTable $userphaventloggedonrecentlytable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Accounts Created in $UserCreatedDays Days or Less"))
+$FinalReport.Add($(Get-HTMLContentDataTable $NewCreatedUsersTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Users Charts"))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 1 -ColumnCount 3))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $EnabledDisabledUsersPieObject -DataSet $EnabledDisabledUsersTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 2 -ColumnCount 3))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PWExpiresUsersTable -DataSet $PasswordExpirationTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 3 -ColumnCount 3))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectProtectedUsers -DataSet $ProtectedUsersTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLTabContentClose))
+
+#GPO Report
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[4] -TabHeading ("Report: " + (Get-Date -Format MM-dd-yyyy))))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Group Policies"))
+$FinalReport.Add($(Get-HTMLContentDataTable $GPOTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLTabContentClose))
+
+#Computers Report
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[5] -TabHeading ("Report: " + (Get-Date -Format MM-dd-yyyy))))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Computers Overivew"))
+$FinalReport.Add($(Get-HTMLContentTable $TOPComputersTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Computers"))
+$FinalReport.Add($(Get-HTMLContentDataTable $ComputersTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Computers Charts"))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 1 -ColumnCount 2))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectComputersProtected -DataSet $ComputerProtectedTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 2 -ColumnCount 2))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectComputersEnabled -DataSet $ComputersEnabledTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentclose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Computers Operating System Breakdown"))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectComputerObjOS -DataSet $GraphComputerOS))
+$FinalReport.Add($(Get-HTMLContentclose))
+
+$FinalReport.Add($(Get-HTMLTabContentClose))
+$FinalReport.Add($(Get-HTMLClosePage))
+
+$Day = (Get-Date).Day
+$Month = (Get-Date).Month
+$Year = (Get-Date).Year
+$ReportName = ("$Day - $Month - $Year - AD Report")
+
+Save-HTMLReport -ReportContent $FinalReport -ShowReport -ReportName $ReportName -ReportPath $ReportSavePath
diff --git a/Audit-v3.ps1 b/Audit-v3.ps1
new file mode 100644
index 0000000..0408449
--- /dev/null
+++ b/Audit-v3.ps1
@@ -0,0 +1,1372 @@
+<#
+ .NOTES
+ Author : phillips321.co.uk
+ Creation Date: 16/08/2018
+ Script Name : ADAudit.ps1
+ .SYNOPSIS
+ PowerShell Script to perform a quick AD audit
+ .DESCRIPTION
+ o Compatibility :
+ * PowerShell v2.0 (PowerShell 5.0 needed if you intend to use DSInternals PowerShell module)
+ * Tested on Windows Server 2008R2/2012/2012R2/2016/2019/2022
+ * All languages (you may need to adjust $AdministratorTranslation variable)
+ o Changelog :
+ [x] Version 5.4 - 16/08/2022
+ * Added nessus output tags for LAPS
+ * Added nessus output for GPO issues
+ [ ] Version 5.3 - 07/03/2022
+ * Added SamAccountName to Get-PrivilegedGroupMembership output
+ * Swapped some write-host to write-both so it's captured in the consolelog.txt
+ [ ] Version 5.2 - 28/01/2022
+ * Enhanced Get-LAPSStatus
+ * Added news checks (AD services + Windows Update + NTP source + Computer/User container + RODC + Locked accounts + Password Quality + SYSVOL & NETLOGON share presence)
+ * Added support for WS 2022
+ * Fix OS version difference check for WS 2008
+ * Fix Write-Progress not disappearing when done
+ [ ] Version 5.1
+ * Added check for newly created users and groups
+ * Added check for replication mechanism
+ * Added check for Recycle Bin
+ * Fix ProtectedUsers for WS 2008
+ [ ] Version 5.0
+ * Make the script compatible with other language than English
+ * Fix the cpassword search in GPO
+ * Fix Get-ACL bad syntax error
+ * Fix Get-DNSZoneInsecure for WS 2008
+ [ ] Version 4.9
+ * Bug fix in checking password comlexity
+ [ ] Version 4.8
+ * Added checks for vista, win7 and 2008 old operating systems
+ * Added insecure DNS zone checks
+ [ ] Version 4.7
+ * Added powershel-v2 suport and fixed array issue
+ [ ] Version 4.6
+ * Fixed potential division by zero
+ [ ] Version 4.5
+ * PR to resolve count issue when count = 1
+ [ ] Version 4.4
+ * Reinstated nessus fix and put output in a list for findings
+ * Changed Get-AdminSDHolders with Get-PrivilegedGroupAccounts
+ [ ] Version 4.3
+ * Temp fix with nessus output
+ [ ] Version 4.2
+ * Bug fix on cpassword count
+ [ ] Version 4.1
+ * Loads of fixes
+ * Works with Powershellv2 again now
+ * Filtered out disabled accounts
+ * Improved domain trusts checking
+ * OUperms improvements and filtering
+ * Check for w2k
+ * Fixed typos/spelling and various other fixes
+ [ ] Version 4.0
+ * Added XML output for import to CheckSecCanopy
+ [ ] Version 3.5
+ * Added KB more references for internal use
+ [ ] Version 3.4
+ * Added KB references for internal use
+ [ ] Version 3.3
+ * Added a greater level of accuracy to Inactive Accounts (thanks exceedio)
+ [ ] Version 3.2
+ * Added search for DCs not owned by Domain Admins group
+ [ ] Version 3.1
+ * Added progress to functions that have count
+ * Added check for transitive trusts
+ [ ] Version 3.0
+ * Added ability to choose functions before runtime
+ * Cleaned up get-ouperms output
+ [ ] Version 2.5
+ * Bug fixes to version check for 2012R2 or greater specific checks
+ [ ] Version 2.4
+ * Forked project
+ * Added Get-OUPerms, Get-LAPSStatus, Get-AdminSDHolders, Get-ProtectedUsers and Get-AuthenticationPoliciesAndSilos functions
+ * Also added FineGrainedPasswordPolicies to Get-PasswordPolicy and changed order slightly
+ [ ] Version 2.3
+ * Added more useful user output to .txt files (Cheers DK)
+ [ ] Version 2.2
+ * Minor typo fix
+ [ ] Version 2.1
+ * Added check for null sessions
+ [ ] Version 2.0
+ * Multiple Additions and knocked off lots of the todo list
+ [ ] Version 1.9
+ * Fixed bug, that used Administrator account name instead of UID 500 and a bug with inactive accounts timespan
+ [ ] Version 1.8
+ * Added check for last time 'Administrator' account logged on
+ [ ] Version 1.6
+ * Added Get-FunctionalLevel and krbtgt password last changed check
+ [ ] Version 1.5
+ * Added Get-HostDetails to output simple info like username, hostname, etc...
+ [ ] Version 1.4
+ * Added Get-WinVersion version to assist with some checks (SMBv1 currently)
+ [ ] Version 1.3
+ * Added XML output for GPO (for offline processing using grouper https://github.com/l0ss/Grouper/blob/master/grouper.psm1)
+ [ ] Version 1.2
+ * Added check for modules
+ [ ] Version 1.1
+ * Fixed bug where SYSVOL research returns empty
+ [ ] Version 1.0
+ * First release
+ .EXAMPLE
+ PS> ADAudit.ps1 -installdeps -all
+ Install external features and launch all checks
+ .EXAMPLE
+ PS> ADAudit.ps1 -all
+ Launch all checks (but do not install external modules)
+ .EXAMPLE
+ PS> ADAudit.ps1 -installdeps
+ Installs optionnal features (DSInternals)
+ .EXAMPLE
+ PS> ADAudit.ps1 -hostdetails -domainaudit
+ Retrieves hostname and other useful audit info
+ Retrieves information about the AD such as functional level
+#>
+[CmdletBinding()]
+Param (
+ [switch]$installdeps = $false,
+ [switch]$hostdetails = $false,
+ [switch]$domainaudit = $false,
+ [switch]$trusts = $false,
+ [switch]$accounts = $false,
+ [switch]$passwordpolicy = $false,
+ [switch]$ntds = $false,
+ [switch]$oldboxes = $false,
+ [switch]$gpo = $false,
+ [switch]$ouperms = $false,
+ [switch]$laps = $false,
+ [switch]$authpolsilos = $false,
+ [switch]$insecurednszone = $false,
+ [switch]$recentchanges = $false,
+ [switch]$all = $false
+)
+$versionnum = "v5.4"
+$AdministratorTranslation = @("Administrator","Administrateur","Administrador")#If missing put the default Administrator name for your own language here
+
+Function Get-Variables(){#Retrieve group names and OS version
+ $script:OSVersion = (Get-Itemproperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName
+ $script:Administrators = (Get-ADGroup -Identity S-1-5-32-544).SamAccountName
+ $script:Users = (Get-ADGroup -Identity S-1-5-32-545).SamAccountName
+ $script:DomainAdminsSID = ((Get-ADDomain -Current LoggedOnUser).domainsid.value)+"-512"
+ $script:DomainUsersSID = ((Get-ADDomain -Current LoggedOnUser).domainsid.value)+"-513"
+ $script:DomainControllersSID = ((Get-ADDomain -Current LoggedOnUser).domainsid.value)+"-516"
+ $script:SchemaAdminsSID = ((Get-ADDomain -Current LoggedOnUser).domainsid.value)+"-518"
+ $script:EnterpriseAdminsSID = ((Get-ADDomain -Current LoggedOnUser).domainsid.value)+"-519"
+ $script:EveryOneSID = New-Object System.Security.Principal.SecurityIdentifier "S-1-1-0"
+ $script:EntrepriseDomainControllersSID = New-Object System.Security.Principal.SecurityIdentifier "S-1-5-9"
+ $script:AuthenticatedUsersSID = New-Object System.Security.Principal.SecurityIdentifier "S-1-5-11"
+ $script:SystemSID = New-Object System.Security.Principal.SecurityIdentifier "S-1-5-18"
+ $script:LocalServiceSID = New-Object System.Security.Principal.SecurityIdentifier "S-1-5-19"
+ $script:DomainAdmins = (Get-ADGroup -Identity $DomainAdminsSID).SamAccountName
+ $script:DomainUsers = (Get-ADGroup -Identity $DomainUsersSID).SamAccountName
+ $script:DomainControllers = (Get-ADGroup -Identity $DomainControllersSID).SamAccountName
+ $script:SchemaAdmins = (Get-ADGroup -Identity $SchemaAdminsSID).SamAccountName
+ $script:EnterpriseAdmins = (Get-ADGroup -Identity $EnterpriseAdminsSID).SamAccountName
+ $script:EveryOne = $EveryOneSID.Translate([System.Security.Principal.NTAccount]).Value
+ $script:EntrepriseDomainControllers = $EntrepriseDomainControllersSID.Translate([System.Security.Principal.NTAccount]).Value
+ $script:AuthenticatedUsers = $AuthenticatedUsersSID.Translate([System.Security.Principal.NTAccount]).Value
+ $script:System = $SystemSID.Translate([System.Security.Principal.NTAccount]).Value
+ $script:LocalService = $LocalServiceSID.Translate([System.Security.Principal.NTAccount]).Value
+ Write-Both " [+] Administrators : $Administrators"
+ Write-Both " [+] Users : $Users"
+ Write-Both " [+] Domain Admins : $DomainAdmins"
+ Write-Both " [+] Domain Users : $DomainUsers"
+ Write-Both " [+] Domain Controllers : $DomainControllers"
+ Write-Both " [+] Schema Admins : $SchemaAdmins"
+ Write-Both " [+] Enterprise Admins : $EnterpriseAdmins"
+ Write-Both " [+] Every One : $EveryOne"
+ Write-Both " [+] Entreprise Domain Controllers: $EntrepriseDomainControllers"
+ Write-Both " [+] Authenticated Users : $AuthenticatedUsers"
+ Write-Both " [+] System : $System"
+ Write-Both " [+] Local Service : $LocalService"
+}
+Function Write-Both(){#Writes to console screen and output file
+ Write-Host "$args"
+ Add-Content -Path "$outputdir\consolelog.txt" -Value "$args"
+}
+Function Write-Nessus-Header(){#Creates nessus XML file header
+ Add-Content -Path "$outputdir\adaudit.nessus" -Value ""
+ Add-Content -Path "$outputdir\adaudit.nessus" -Value ""
+ Add-Content -Path "$outputdir\adaudit.nessus" -Value ""
+}
+Function Write-Nessus-Finding( [string]$pluginname, [string]$pluginid, [string]$pluginexample){
+ Add-Content -Path "$outputdir\adaudit.nessus" -Value ""
+ Add-Content -Path "$outputdir\adaudit.nessus" -Value "There's an issue with $pluginname"
+ Add-Content -Path "$outputdir\adaudit.nessus" -Value "remoteLow"
+ Add-Content -Path "$outputdir\adaudit.nessus" -Value "CCS Recommends fixing the issues with $pluginname on the host"
+ Add-Content -Path "$outputdir\adaudit.nessus" -Value "There's an issue with the $pluginname settings on the host"
+ Add-Content -Path "$outputdir\adaudit.nessus" -Value "$pluginexample"
+}
+Function Write-Nessus-Footer(){
+ Add-Content -Path "$outputdir\adaudit.nessus" -Value ""
+}
+Function Get-DNSZoneInsecure{#Check DNS zones allowing insecure updates
+ if($OSVersion -notlike "Windows Server 2008*"){
+ $count = 0
+ $progresscount = 0
+ $insecurezones = Get-DnsServerZone | Where-Object {$_.DynamicUpdate -like '*nonsecure*'}
+ $totalcount = ($insecurezones | Measure-Object | Select-Object Count).count
+ if($totalcount -gt 0){
+ foreach($insecurezone in $insecurezones ){
+ Add-Content -Path "$outputdir\insecure_dns_zones.txt" -Value "The DNS Zone $($insecurezone.ZoneName) allows insecure updates ($($insecurezone.DynamicUpdate))"
+ }
+ Write-Both " [!] There were $totalcount DNS zones configured to allow insecure updates (KB842)"
+ Write-Nessus-Finding "InsecureDNSZone" "KB842" ([System.IO.File]::ReadAllText("$outputdir\insecure_dns_zones.txt"))
+ }
+ }else{
+ Write-Both " [-] Not Windows 2012 or above, skipping Get-DNSZoneInsecure check."
+ }
+}
+Function Get-OUPerms{#Check for non-standard perms for authenticated users, domain users, users and everyone groups
+ $count = 0
+ $progresscount = 0
+ $objects = (Get-ADObject -Filter *)
+ $totalcount = ($objects | Measure-Object | Select-Object Count).count
+ foreach($object in $objects){
+ if($totalcount -eq 0){ break }
+ $progresscount++
+ Write-Progress -Activity "Searching for non standard permissions for authenticated users..." -Status "Currently identifed $count" -PercentComplete ($progresscount / $totalcount*100)
+ if($OSVersion -like "Windows Server 2019*" -or $OSVersion -like "Windows Server 2022*"){
+ $output = (Get-Acl "Microsoft.ActiveDirectory.Management.dll\ActiveDirectory:://RootDSE/$object").Access | Where-Object {($_.IdentityReference -eq "$AuthenticatedUsers") -or ($_.IdentityReference -eq "$EveryOne") -or ($_.IdentityReference -like "*\$DomainUsers") -or ($_.IdentityReference -eq "BUILTIN\$Users")} | Where-Object {($_.ActiveDirectoryRights -ne 'GenericRead') -and ($_.ActiveDirectoryRights -ne 'GenericExecute') -and ($_.ActiveDirectoryRights -ne 'ExtendedRight') -and ($_.ActiveDirectoryRights -ne 'ReadControl') -and ($_.ActiveDirectoryRights -ne 'ReadProperty') -and ($_.ActiveDirectoryRights -ne 'ListObject') -and ($_.ActiveDirectoryRights -ne 'ListChildren') -and ($_.ActiveDirectoryRights -ne 'ListChildren, ReadProperty, ListObject') -and ($_.ActiveDirectoryRights -ne 'ReadProperty, GenericExecute') -and ($_.AccessControlType -ne 'Deny')}
+ }else{
+ $output = (Get-Acl AD:$object).Access | Where-Object {($_.IdentityReference -eq "$AuthenticatedUsers") -or ($_.IdentityReference -eq "$EveryOne") -or ($_.IdentityReference -like "*\$DomainUsers") -or ($_.IdentityReference -eq "BUILTIN\$Users")} | Where-Object {($_.ActiveDirectoryRights -ne 'GenericRead') -and ($_.ActiveDirectoryRights -ne 'GenericExecute') -and ($_.ActiveDirectoryRights -ne 'ExtendedRight') -and ($_.ActiveDirectoryRights -ne 'ReadControl') -and ($_.ActiveDirectoryRights -ne 'ReadProperty') -and ($_.ActiveDirectoryRights -ne 'ListObject') -and ($_.ActiveDirectoryRights -ne 'ListChildren') -and ($_.ActiveDirectoryRights -ne 'ListChildren, ReadProperty, ListObject') -and ($_.ActiveDirectoryRights -ne 'ReadProperty, GenericExecute') -and ($_.AccessControlType -ne 'Deny')}
+ }
+ if($output -ne $null){
+ $count++
+ Add-Content -Path "$outputdir\ou_permissions.txt" -Value "OU: $object"
+ Add-Content -Path "$outputdir\ou_permissions.txt" -Value "[!] Rights: $($output.IdentityReference) $($output.ActiveDirectoryRights) $($output.AccessControlType)"
+ }
+ }
+ Write-Progress -Activity "Searching for non standard permissions for authenticated users..." -Status "Ready" -Completed
+ if($count -gt 0){
+ Write-Both " [!] Issue identified, see $outputdir\ou_permissions.txt"
+ Write-Nessus-Finding "OUPermissions" "KB551" ([System.IO.File]::ReadAllText("$outputdir\ou_permissions.txt"))
+ }
+}
+Function Get-LAPSStatus{#Check for presence of LAPS in domain
+ try{
+ Get-ADObject "CN=ms-Mcs-AdmPwd,CN=Schema,CN=Configuration,$((Get-ADDomain).DistinguishedName)" -ErrorAction Stop | Out-Null
+ Write-Both " [+] LAPS Installed in domain"
+ }
+ catch{
+ Write-Both " [!] LAPS Not Installed in domain (KB258)"
+ Write-Nessus-Finding "LAPSMissing" "KB258" "LAPS Not Installed in domain"
+ }
+ if(Get-Module -ListAvailable -Name AdmPwd.PS){
+ Import-Module AdmPwd.PS
+ $count = 0
+ $missingComputers = (Get-ADComputer -Filter {ms-Mcs-AdmPwd -notlike "*"}).Name
+ $totalcount = ($missingComputers | Measure-Object | Select-Object Count).count
+ if($totalcount -gt 0){
+ $missingComputers | Add-Content -Path $outputdir\laps_missing-computers.txt
+ Write-Both " [!] Some computers/servers don't have LAPS password set, see $outputdir\laps_missing-computers.txt"
+ Write-Nessus-Finding "LAPSMissingorExpired" "KB258" ([System.IO.File]::ReadAllText("$outputdir\laps_missing-computers.txt"))
+ }
+ $count = 0
+ $computersList = (Get-ADComputer -Filter {ms-Mcs-AdmPwdExpirationTime -like "*"} -Properties ms-Mcs-AdmPwdExpirationTime | select Name,ms-Mcs-AdmPwdExpirationTime)
+ foreach($computer in $computersList ){
+ $expiration = [datetime]::FromFileTime($computer.'ms-Mcs-AdmPwdExpirationTime')
+ $today = Get-Date
+ if($expiration -lt $today){
+ $count++
+ "$($computer.Name) password is expired since $expiration" | Add-Content -Path $outputdir\laps_expired-passwords.txt
+ }
+ }
+ if($count -gt 0){
+ Write-Both " [!] Some computers/servers have LAPS password expired, see $outputdir\laps_expired-passwords.txt"
+ Write-Nessus-Finding "LAPSMissingorExpired" "KB258" ([System.IO.File]::ReadAllText("$outputdir\laps_expired-passwords.txt"))
+ }
+ Get-ADOrganizationalUnit -Filter * | Find-AdmPwdExtendedRights -PipelineVariable OU | foreach{
+ $_.ExtendedRightHolders | foreach{
+ if($_ -ne $System){
+ "$_ can read password attribute of $($Ou.ObjectDN)" | Add-Content -Path $outputdir\laps_read-extendedrights.txt
+ }
+ }
+ }
+ Write-Both " [!] LAPS extended rights exported, see $outputdir\laps_read-extendedrights.txt"
+ Write-Nessus-Finding "LAPSMissingorExpired" "KB258" ([System.IO.File]::ReadAllText("$outputdir\laps_read-extendedrights.txt"))
+
+ }else{
+ Write-Both " [!] LAPS PowerShell module is not installed, can't run LAPS checks on this DC"
+ }
+}
+Function Get-PrivilegedGroupAccounts{#Lists users in Admininstrators, DA and EA groups
+ [array]$privilegedusers = @()
+ $privilegedusers += Get-ADGroupMember $Administrators -Recursive
+ $privilegedusers += Get-ADGroupMember $DomainAdmins -Recursive
+ $privilegedusers += Get-ADGroupMember $EnterpriseAdmins -Recursive
+ $privusersunique = $privilegedusers | Sort-Object -Unique
+ $count = 0
+ $totalcount = ($privilegedusers | Measure-Object | Select-Object Count).count
+ foreach($account in $privusersunique){
+ if($totalcount -eq 0){ break }
+ Write-Progress -Activity "Searching for users who are in privileged groups..." -Status "Currently identifed $count" -PercentComplete ($count / $totalcount*100)
+ Add-Content -Path "$outputdir\accounts_userPrivileged.txt" -Value "$($account.SamAccountName) ($($account.Name))"
+ $count++
+ }
+ Write-Progress -Activity "Searching for users who are in privileged groups..." -Status "Ready" -Completed
+ if($count -gt 0){
+ Write-Both " [!] There are $count accounts in privileged groups, see accounts_userPrivileged.txt (KB426)"
+ Write-Nessus-Finding "AdminSDHolders" "KB426" ([System.IO.File]::ReadAllText("$outputdir\accounts_userPrivileged.txt"))
+ }
+}
+Function Get-ProtectedUsers{#Lists users in "Protected Users" group (2012R2 and above)
+ $DomainLevel = (Get-ADDomain).domainMode
+ if($DomainLevel -eq "Windows2012Domain" -or $DomainLevel -eq "Windows2012R2Domain" -or $DomainLevel -eq "Windows2016Domain"){#Checking for 2012 or above domain functional level
+ $ProtectedUsersSID = ((Get-ADDomain -Current LoggedOnUser).domainsid.value)+"-525"
+ $ProtectedUsers = (Get-ADGroup -Identity $ProtectedUsersSID).SamAccountName
+ $count = 0
+ $protectedaccounts = (Get-ADGroup $ProtectedUsers -Properties members).Members
+ $totalcount = ($protectedaccounts | Measure-Object | Select-Object Count).count
+ foreach($members in $protectedaccounts){
+ if($totalcount -eq 0){ break }
+ Write-Progress -Activity "Searching for protected users..." -Status "Currently identifed $count" -PercentComplete ($count / $totalcount*100)
+ $account = Get-ADObject $members -Properties SamAccountName
+ Add-Content -Path "$outputdir\accounts_protectedusers.txt" -Value "$($account.SamAccountName) ($($account.Name))"
+ $count++
+ }
+ Write-Progress -Activity "Searching for protected users..." -Status "Ready" -Completed
+ if($count -gt 0){
+ Write-Both " [!] There are $count accounts in the 'Protected Users' group, see accounts_protectedusers.txt"
+ Write-Nessus-Finding "ProtectedUsers" "KB549" ([System.IO.File]::ReadAllText("$outputdir\accounts_protectedusers.txt"))
+ }
+ }
+ else {Write-Both " [-] Not Windows 2012 Domain Functional level or above, skipping Get-ProtectedUsers check."}
+}
+Function Get-AuthenticationPoliciesAndSilos {#Lists any authentication policies and silos (2012R2 and above)
+ if([single](Get-WinVersion) -ge [single]6.3){#NT6.2 or greater detected so running this script
+ $count = 0
+ foreach($policy in Get-ADAuthenticationPolicy -Filter *){
+ Write-Both " [!] Found $policy Authentication Policy"
+ $count++
+ }
+ if($count -lt 1){
+ Write-Both " [!] There were no AD Authentication Policies found in the domain"
+ }
+ $count = 0
+ foreach($policysilo in Get-ADAuthenticationPolicySilo -Filter *){
+ Write-Both " [!] Found $policysilo Authentication Policy Silo"
+ $count++
+ }
+ if($count -lt 1){
+ Write-Both " [!] There were no AD Authentication Policy Silos found in the domain"
+ }
+ }
+}
+Function Get-MachineAccountQuota{#Get number of machines a user can add to a domain
+ $MachineAccountQuota = (Get-ADDomain | select -ExpandProperty DistinguishedName | Get-ADObject -Property 'ms-DS-MachineAccountQuota' | select -ExpandProperty ms-DS-MachineAccountQuota)
+ if($MachineAccountQuota -gt 0){
+ Write-Both " [!] Domain users can add $MachineAccountQuota devices to the domain! (KB251)"
+ Write-Nessus-Finding "DomainAccountQuota" "KB251" "Domain users can add $MachineAccountQuota devices to the domain"
+ }
+}
+Function Get-PasswordPolicy{
+ Write-Both " [+] Checking default password policy"
+ if(!(Get-ADDefaultDomainPasswordPolicy).ComplexityEnabled){
+ Write-Both " [!] Password Complexity not enabled (KB262)"
+ Write-Nessus-Finding "PasswordComplexity" "KB262" "Password Complexity not enabled"
+ }
+ if((Get-ADDefaultDomainPasswordPolicy).LockoutThreshold -lt 5){
+ Write-Both " [!] Lockout threshold is less than 5, currently set to $((Get-ADDefaultDomainPasswordPolicy).LockoutThreshold) (KB263)"
+ Write-Nessus-Finding "LockoutThreshold" "KB263" "Lockout threshold is less than 5, currently set to $((Get-ADDefaultDomainPasswordPolicy).LockoutThreshold)"
+ }
+ if((Get-ADDefaultDomainPasswordPolicy).MinPasswordLength -lt 14){
+ Write-Both " [!] Minimum password length is less than 14, currently set to $((Get-ADDefaultDomainPasswordPolicy).MinPasswordLength) (KB262)"
+ Write-Nessus-Finding "PasswordLength" "KB262" "Minimum password length is less than 14, currently set to $((Get-ADDefaultDomainPasswordPolicy).MinPasswordLength)"
+ }
+ if((Get-ADDefaultDomainPasswordPolicy).ReversibleEncryptionEnabled){
+ Write-Both " [!] Reversible encryption is enabled"
+ }
+ if((Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge -eq "00:00:00"){
+ Write-Both " [!] Passwords do not expire (KB254)"
+ Write-Nessus-Finding "PasswordsDoNotExpire" "KB254" "Passwords do not expire"
+ }
+ if((Get-ADDefaultDomainPasswordPolicy).PasswordHistoryCount -lt 12){
+ Write-Both " [!] Passwords history is less than 12, currently set to $((Get-ADDefaultDomainPasswordPolicy).PasswordHistoryCount) (KB262)"
+ Write-Nessus-Finding "PasswordHistory" "KB262" "Passwords history is less than 12, currently set to $((Get-ADDefaultDomainPasswordPolicy).PasswordHistoryCount)"
+ }
+ if((Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\Lsa).NoLmHash -eq 0){
+ Write-Both " [!] LM Hashes are stored! (KB510)"
+ Write-Nessus-Finding "LMHashesAreStored" "KB510" "LM Hashes are stored"
+ }
+ Write-Both " [-] Finished checking default password policy"
+ Write-Both " [+] Checking fine-grained password policies if they exist"
+ foreach($finegrainedpolicy in Get-ADFineGrainedPasswordPolicy -Filter *){
+ $finegrainedpolicyappliesto=$finegrainedpolicy.AppliesTo
+ Write-Both " [!] Policy: $finegrainedpolicy"
+ Write-Both " [!] AppliesTo: $($finegrainedpolicyappliesto)"
+ if(!($finegrainedpolicy).PasswordComplexity){
+ Write-Both " [!] Password Complexity not enabled (KB262)"
+ Write-Nessus-Finding "PasswordComplexity" "KB262" "Password Complexity not enabled for $finegrainedpolicy"
+ }
+ if(($finegrainedpolicy).LockoutThreshold -lt 5){
+ Write-Both " [!] Lockout threshold is less than 5, currently set to $($finegrainedpolicy).LockoutThreshold) (KB263)"
+ Write-Nessus-Finding "LockoutThreshold" "KB263" " Lockout threshold for $finegrainedpolicy is less than 5, currently set to $(($finegrainedpolicy).LockoutThreshold)"
+ }
+ if(($finegrainedpolicy).MinPasswordLength -lt 14){
+ Write-Both " [!] Minimum password length is less than 14, currently set to $(($finegrainedpolicy).MinPasswordLength) (KB262)"
+ Write-Nessus-Finding "PasswordLength" "KB262" "Minimum password length for $finegrainedpolicy is less than 14, currently set to $(($finegrainedpolicy).MinPasswordLength)"
+ }
+ if(($finegrainedpolicy).ReversibleEncryptionEnabled){
+ Write-Both " [!] Reversible encryption is enabled"
+ }
+ if(($finegrainedpolicy).MaxPasswordAge -eq "00:00:00"){
+ Write-Both " [!] Passwords do not expire (KB254)"
+ }
+ if(($finegrainedpolicy).PasswordHistoryCount -lt 12){
+ Write-Both " [!] Passwords history is less than 12, currently set to $(($finegrainedpolicy).PasswordHistoryCount) (KB262)"
+ Write-Nessus-Finding "PasswordHistory" "KB262" "Passwords history for $finegrainedpolicy is less than 12, currently set to $(($finegrainedpolicy).PasswordHistoryCount)"
+ }
+ }
+ Write-Both " [-] Finished checking fine-grained password policy"
+}
+Function Get-NULLSessions{
+ if((Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\Lsa).RestrictAnonymous -eq 0){
+ Write-Both " [!] RestrictAnonymous is set to 0! (KB81)"
+ Write-Nessus-Finding "NullSessions" "KB81" " RestrictAnonymous is set to 0"
+ }
+ if((Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\Lsa).RestrictAnonymousSam -eq 0){
+ Write-Both " [!] RestrictAnonymousSam is set to 0! (KB81)"
+ Write-Nessus-Finding "NullSessions" "KB81" " RestrictAnonymous is set to 0"
+ }
+ if((Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\Lsa).everyoneincludesanonymous -eq 1){
+ Write-Both " [!] EveryoneIncludesAnonymous is set to 1! (KB81)"
+ Write-Nessus-Finding "NullSessions" "KB81" "EveryoneIncludesAnonymous is set to 1"
+ }
+}
+Function Get-DomainTrusts{#Lists domain trusts if they are bad
+ foreach($trust in (Get-ADObject -Filter {objectClass -eq "trustedDomain"} -Properties TrustPartner,TrustDirection,trustType,trustAttributes)){
+ if($trust.TrustDirection -eq 2){
+ if($trust.TrustAttributes -eq 1 -or $trust.TrustAttributes -eq 4){#1 means trust is non-transitive, 4 is external so we check for anything but that
+ Write-Both " [!] The domain $($trust.Name) is trusted by $env:UserDomain! (KB250)"
+ Write-Nessus-Finding "DomainTrusts" "KB250" "The domain $($trust.Name) is trusted by $env:UserDomain."
+ }else{
+ Write-Both " [!] The domain $($trust.Name) is trusted by $env:UserDomain and it is Transitive! (KB250)"
+ Write-Nessus-Finding "DomainTrusts" "KB250" "The domain $($trust.Name) is trusted by $env:UserDomain and it is Transitive!"
+ }
+ }
+ if($trust.TrustDirection -eq 3){
+ if($trust.TrustAttributes -eq 1 -or $trust.TrustAttributes -eq 4){#1 means trust is non-transitive, 4 is external so we check for anything but that
+ Write-Both " [!] The domain $($trust.Name) is trusted by $env:UserDomain! (KB250)"
+ Write-Nessus-Finding "DomainTrusts" "KB250" "The domain $($trust.Name) is trusted by $env:UserDomain."
+ }else{
+ Write-Both " [!] The domain $($trust.Name) is trusted by $env:UserDomain and it is Transitive! (KB250)"
+ Write-Nessus-Finding "DomainTrusts" "KB250" "The domain $($trust.Name) is trusted by $env:UserDomain and it is Transitive!"
+ }
+ }
+ }
+}
+Function Get-WinVersion{
+ $WinVersion = [single]([string][environment]::OSVersion.Version.Major + "." + [string][environment]::OSVersion.Version.Minor)
+ return [single]$WinVersion
+}
+Function Get-SMB1Support{#Check if server supports SMBv1
+ if([single](Get-WinVersion) -le [single]6.1){#NT6.1 or less detected so checking reg key
+ if(!(Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters).SMB1 -eq 0){
+ Write-Both " [!] SMBv1 is not disabled (KB290)"
+ Write-Nessus-Finding "SMBv1Support" "KB290" "SMBv1 is enabled"
+ }
+ }elseif([single](Get-WinVersion) -ge [single]6.2){#NT6.2 or greater detected so using powershell function
+ if((Get-SmbServerConfiguration).EnableSMB1Protocol){
+ Write-Both " [!] SMBv1 is enabled! (KB290)"
+ Write-Nessus-Finding "SMBv1Support" "KB290" "SMBv1 is enabled"
+ }
+ }
+}
+Function Get-UserPasswordNotChangedRecently{#Reports users that haven't changed passwords in more than 90 days
+ $count = 0
+ $DaysAgo = (Get-Date).AddDays(-90)
+ $accountsoldpasswords = Get-ADUser -Filter {PwdLastSet -lt $DaysAgo -and Enabled -eq "true"} -Properties PasswordLastSet
+ $totalcount = ($accountsoldpasswords | Measure-Object | Select-Object Count).count
+ foreach($account in $accountsoldpasswords){
+ if($totalcount -eq 0){ break }
+ Write-Progress -Activity "Searching for passwords older than 90days..." -Status "Currently identifed $count" -PercentComplete ($count / $totalcount*100)
+ if($account.PasswordLastSet){
+ $datelastchanged = $account.PasswordLastSet
+ }else{
+ $datelastchanged = "Never"
+ }
+ Add-Content -Path "$outputdir\accounts_with_old_passwords.txt" -Value "User $($account.SamAccountName) ($($account.Name)) has not changed their password since $datelastchanged"
+ $count++
+ }
+ Write-Progress -Activity "Searching for passwords older than 90days..." -Status "Ready" -Completed
+ if($count -gt 0){
+ Write-Both " [!] $count accounts with passwords older than 90days, see accounts_with_old_passwords.txt (KB550)"
+ Write-Nessus-Finding "AccountsWithOldPasswords" "KB550" ([System.IO.File]::ReadAllText("$outputdir\accounts_with_old_passwords.txt"))
+ }
+ $krbtgtPasswordDate = (Get-ADUser -Filter {SamAccountName -eq "krbtgt"} -Properties PasswordLastSet).PasswordLastSet
+ if($krbtgtPasswordDate -lt (Get-Date).AddDays(-180)){
+ Write-Both " [!] krbtgt password not changed since $krbtgtPasswordDate! (KB253)"
+ Write-Nessus-Finding "krbtgtPasswordNotChanged" "KB253" "krbtgt password not changed since $krbtgtPasswordDate"
+ }
+}
+Function Get-GPOtoFile{#Outputs complete GPO report
+ if(Test-Path "$outputdir\GPOReport.html"){ Remove-Item "$outputdir\GPOReport.html" -Recurse }
+ Get-GPOReport -All -ReportType HTML -Path "$outputdir\GPOReport.html"
+ Write-Both " [+] GPO Report saved to GPOReport.html"
+ if(Test-Path "$outputdir\GPOReport.xml"){ Remove-Item "$outputdir\GPOReport.xml" -Recurse }
+ Get-GPOReport -All -ReportType XML -Path "$outputdir\GPOReport.xml"
+ Write-Both " [+] GPO Report saved to GPOReport.xml, now run Grouper offline using the following command (KB499)"
+ Write-Both " [+] PS>Import-Module Grouper.psm1 ; Invoke-AuditGPOReport -Path C:\GPOReport.xml -Level 3"
+}
+Function Get-GPOsPerOU{#Lists all OUs and which GPOs apply to them
+ $count = 0
+ $ousgpos = @(Get-ADOrganizationalUnit -Filter *)
+ $totalcount = ($ousgpos | Measure-Object | Select-Object Count).count
+ foreach($ouobject in $ousgpos){
+ if($totalcount -eq 0){ break }
+ Write-Progress -Activity "Identifying which GPOs apply to which OUs..." -Status "Currently identifed $count OUs" -PercentComplete ($count / $totalcount*100)
+ $combinedgpos = ($(((Get-GPInheritance -Target $ouobject).InheritedGpoLinks) | select DisplayName) | ForEach-Object { $_.DisplayName }) -join ','
+ Add-Content -Path "$outputdir\ous_inheritedGPOs.txt" -Value "$($ouobject.Name) Inherits these GPOs: $combinedgpos"
+ $count++
+ }
+ Write-Progress -Activity "Identifying which GPOs apply to which OUs..." -Status "Ready" -Completed
+ Write-Both " [+] Inherited GPOs saved to ous_inheritedGPOs.txt"
+}
+Function Get-NTDSdit{#Dumps NTDS.dit, SYSTEM and SAM for password cracking
+ if(Test-Path "$outputdir\ntds.dit"){ Remove-Item "$outputdir\ntds.dit" -Recurse }
+ $outputdirntds = '\"' + $outputdir + '\ntds.dit\"'
+ $command = "ntdsutil `"ac in ntds`" `"ifm`" `"cr fu $outputdirntds `" q q"
+ $hide = cmd.exe /c "$command" 2>&1
+ Write-Both " [+] NTDS.dit, SYSTEM & SAM saved to output folder"
+ Write-Both " [+] Use secretsdump.py -system registry/SYSTEM -ntds Active\ Directory/ntds.dit LOCAL -outputfile customer"
+}
+Function Get-SYSVOLXMLS{#Finds XML files in SYSVOL (thanks --> https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Get-GPPPassword.ps1)
+ $XMLFiles = Get-ChildItem -Path "\\$Env:USERDNSDOMAIN\SYSVOL" -Recurse -ErrorAction SilentlyContinue -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml'
+ $count = 0
+ if($XMLFiles){
+ $progresscount = 0
+ $totalcount = ($XMLFiles | Measure-Object | Select-Object Count).count
+ foreach($File in $XMLFiles){
+ if($totalcount -eq 0){ break }
+ $progresscount++
+ Write-Progress -Activity "Searching SYSVOL *.xmls for cpassword..." -Status "Currently searched through $count" -PercentComplete ($progresscount / $totalcount*100)
+ $Filename = Split-Path $File -Leaf
+ $Distinguishedname = (Split-Path (Split-Path (Split-Path( Split-Path (Split-Path $File -Parent) -Parent ) -Parent ) -Parent) -Leaf).Substring(1).TrimEnd('}')
+ [xml]$Xml = Get-Content ($File)
+ if($Xml.innerxml -like "*cpassword*" -and $Xml.innerxml -notlike '*cpassword=""*'){
+ if(!(Test-Path "$outputdir\sysvol")){ New-Item -ItemType Directory -Path "$outputdir\sysvol" | Out-Null }
+ Write-Both " [!] cpassword found in file, copying to output folder (KB329)"
+ Write-Both " $File"
+ Copy-Item -Path $File -Destination $outputdir\sysvol\$Distinguishedname.$Filename
+ $count++
+ }
+ }
+ Write-Progress -Activity "Searching SYSVOL *.xmls for cpassword..." -Status "Ready" -Completed
+ }
+ if($count -eq 0){
+ Write-Both " ...cpassword not found in the $($XMLFiles.count) XML files found."
+ }else{
+ $GPOxml = (Get-Content "$outputdir\sysvol\*.xml" -ErrorAction SilentlyContinue)
+ $GPOxml = $GPOxml -Replace "<", "<"
+ $GPOxml = $GPOxml -Replace ">", ">"
+ Write-Nessus-Finding "GPOPasswordStorage" "KB329" "$GPOxml"
+ }
+}
+Function Get-InactiveAccounts{#Lists accounts not used in past 180 days plus some checks for admin accounts
+ $count = 0
+ $progresscount = 0
+ $inactiveaccounts = Search-ADaccount -AccountInactive -Timespan (New-TimeSpan -Days 180) -UsersOnly | Where-Object {$_.Enabled -eq $true}
+ $totalcount = ($inactiveaccounts | Measure-Object | Select-Object Count).count
+ foreach($account in $inactiveaccounts){
+ if($totalcount -eq 0){ break }
+ $progresscount++
+ Write-Progress -Activity "Searching for inactive users..." -Status "Currently identifed $count" -PercentComplete ($progresscount / $totalcount*100)
+ if($account.Enabled){
+ if($account.LastLogonDate){
+ $userlastused = $account.LastLogonDate
+ }else{
+ $userlastused = "Never"
+ }
+ Add-Content -Path "$outputdir\accounts_inactive.txt" -Value "User $($account.SamAccountName) ($($account.Name)) has not logged on since $userlastused"
+ $count++
+ }
+ }
+ Write-Progress -Activity "Searching for inactive users..." -Status "Ready" -Completed
+ if($count -gt 0){
+ Write-Both " [!] $count inactive user accounts(180days), see accounts_inactive.txt (KB500)"
+ Write-Nessus-Finding "InactiveAccounts" "KB500" ([System.IO.File]::ReadAllText("$outputdir\accounts_inactive.txt"))
+ }
+}
+Function Get-AdminAccountChecks{#Checks if Administrator account has been renamed, replaced and is no longer used.
+ $AdministratorSID = ((Get-ADDomain -Current LoggedOnUser).domainsid.value)+"-500"
+ $AdministratorSAMAccountName = (Get-ADUser -Filter {SID -eq $AdministratorSID} -Properties SamAccountName).SamAccountName
+ $AdministratorName = (Get-ADUser -Filter {SID -eq $AdministratorSID} -Properties SamAccountName).Name
+ if($AdministratorTranslation -contains $AdministratorSAMAccountName){
+ Write-Both " [!] Local Administrator account (UID500) has not been renamed (KB309)"
+ Write-Nessus-Finding "AdminAccountRenamed" "KB309" "Local Administrator account (UID500) has not been renamed"
+ }else{
+ $count = 0
+ foreach($AdminName in $AdministratorTranslation){
+ if((Get-ADUser -Filter {SamAccountName -eq $AdminName})) { $count++ }
+ }
+ if($count -eq 0){
+ Write-Both " [!] Local Administrator account renamed to $AdministratorSAMAccountName ($($AdministratorName)), but a dummy account not made in it's place! (KB309)"
+ Write-Nessus-Finding "AdminAccountRenamed" "KB309" "Local Admin account renamed to $AdministratorSAMAccountName ($($AdministratorName)), but a dummy account not made in it's place"
+ }
+ }
+ $AdministratorLastLogonDate = (Get-ADUser -Filter {SID -eq $AdministratorSID} -Properties LastLogonDate).LastLogonDate
+ if($AdministratorLastLogonDate -gt (Get-Date).AddDays(-180)){
+ Write-Both " [!] UID500 (LocalAdministrator) account is still used, last used $AdministratorLastLogonDate! (KB309)"
+ Write-Nessus-Finding "AdminAccountRenamed" "KB309" "UID500 (LocalAdmini) account is still used, last used $AdministratorLastLogonDate"
+ }
+}
+Function Get-DisabledAccounts{#Lists disabled accounts
+ $disabledaccounts = Search-ADaccount -AccountDisabled -UsersOnly
+ $count = 0
+ $totalcount = ($disabledaccounts | Measure-Object | Select-Object Count).count
+ foreach($account in $disabledaccounts){
+ if($totalcount -eq 0){ break }
+ Write-Progress -Activity "Searching for disabled users..." -Status "Currently identifed $count" -PercentComplete ($count / $totalcount*100)
+ Add-Content -Path "$outputdir\accounts_disabled.txt" -Value "Account $($account.SamAccountName) ($($account.Name)) is disabled"
+ $count++
+ }
+ Write-Progress -Activity "Searching for disabled users..." -Status "Ready" -Completed
+ if($count -gt 0){
+ Write-Both " [!] $count disabled user accounts, see accounts_disabled.txt (KB501)"
+ Write-Nessus-Finding "DisabledAccounts" "KB501" ([System.IO.File]::ReadAllText("$outputdir\accounts_disabled.txt"))
+ }
+}
+Function Get-LockedAccounts{#Lists locked accounts
+ $lockedAccounts = Get-ADUser -Filter * -Properties LockedOut | Where-Object {$_.LockedOut -eq $true}
+ $count = 0
+ $totalcount = ($lockedAccounts | Measure-Object | Select-Object Count).Count
+ foreach($account in $lockedAccounts){
+ if($totalcount -eq 0){ break }
+ Write-Progress -Activity "Searching for locked users..." -Status "Currently identifed $count" -PercentComplete ($count / $totalcount*100)
+ Add-Content -Path "$outputdir\accounts_locked.txt" -Value "Account $($account.SamAccountName) ($($account.Name)) is locked"
+ $count++
+ }
+ Write-Progress -Activity "Searching for locked users..." -Status "Ready" -Completed
+ if($count -gt 0){
+ Write-Both " [!] $count locked user accounts, see accounts_locked.txt"
+ }
+}
+Function Get-AccountPassDontExpire{#Lists accounts who's passwords dont expire
+ $count = 0
+ $nonexpiringpasswords = Search-ADAccount -PasswordNeverExpires -UsersOnly | Where-Object {$_.Enabled -eq $true}
+ $totalcount = ($nonexpiringpasswords | Measure-Object | Select-Object Count).count
+ foreach($account in $nonexpiringpasswords){
+ if($totalcount -eq 0){ break }
+ Write-Progress -Activity "Searching for users with passwords that dont expire..." -Status "Currently identifed $count" -PercentComplete ($count / $totalcount*100)
+ Add-Content -Path "$outputdir\accounts_passdontexpire.txt" -Value "$($account.SamAccountName) ($($account.Name))"
+ $count++
+ }
+ Write-Progress -Activity "Searching for users with passwords that dont expire..." -Status "Ready" -Completed
+ if($count -gt 0){
+ Write-Both " [!] There are $count accounts that don't expire, see accounts_passdontexpire.txt (KB254)"
+ Write-Nessus-Finding "AccountsThatDontExpire" "KB254" ([System.IO.File]::ReadAllText("$outputdir\accounts_passdontexpire.txt"))
+ }
+}
+Function Get-OldBoxes{#Lists 2000/2003/XP/Vista/7/2008 machines
+ $count = 0
+ $oldboxes = Get-ADComputer -Filter {OperatingSystem -Like "*2003*" -and Enabled -eq "true" -or OperatingSystem -Like "*XP*" -and Enabled -eq "true" -or OperatingSystem -Like "*2000*" -and Enabled -eq "true" -or OperatingSystem -like '*Windows 7*' -and Enabled -eq "true" -or OperatingSystem -like '*vista*' -and Enabled -eq "true" -or OperatingSystem -like '*2008*' -and Enabled -eq "true"} -Property OperatingSystem
+ $totalcount = ($oldboxes | Measure-Object | Select-Object Count).count
+ foreach($machine in $oldboxes){
+ if($totalcount -eq 0){ break }
+ Write-Progress -Activity "Searching for 2000/2003/XP/Vista/7/2008 devices joined to the domain..." -Status "Currently identifed $count" -PercentComplete ($count / $totalcount*100)
+ Add-Content -Path "$outputdir\machines_old.txt" -Value "$($machine.Name), $($machine.OperatingSystem), $($machine.OperatingSystemServicePack), $($machine.OperatingSystemVersio), $($machine.IPv4Address)"
+ $count++
+ }
+ Write-Progress -Activity "Searching for 2000/2003/XP/Vista/7/2008 devices joined to the domain..." -Status "Ready" -Completed
+ if($count -gt 0){
+ Write-Both " [!] We found $count machines running 2000/2003/XP/Vista/7/2008! see machines_old.txt (KB3/37/38/KB259)"
+ Write-Nessus-Finding "OldBoxes" "KB259" ([System.IO.File]::ReadAllText("$outputdir\machines_old.txt"))
+ }
+}
+Function Get-DCsNotOwnedByDA {#Searches for DC objects not owned by the Domain Admins group
+ $count = 0
+ $progresscount = 0
+ $domaincontrollers = Get-ADComputer -Filter {PrimaryGroupID -eq 516 -or PrimaryGroupID -eq 521} -Property *
+ $totalcount = ($domaincontrollers | Measure-Object | Select-Object Count).count
+ if($totalcount -gt 0){
+ foreach($machine in $domaincontrollers){
+ $progresscount++
+ Write-Progress -Activity "Searching for DCs not owned by Domain Admins group..." -Status "Currently identifed $count" -PercentComplete ($progresscount / $totalcount*100)
+ if($machine.ntsecuritydescriptor.Owner -ne "$env:UserDomain\$DomainAdmins"){
+ Add-Content -Path "$outputdir\dcs_not_owned_by_da.txt" -Value "$($machine.Name), $($machine.OperatingSystem), $($machine.OperatingSystemServicePack), $($machine.OperatingSystemVersio), $($machine.IPv4Address), owned by $($machine.ntsecuritydescriptor.Owner)"
+ $count++
+ }
+ }
+ Write-Progress -Activity "Searching for DCs not owned by Domain Admins group..." -Status "Ready" -Completed
+ }
+ if($count -gt 0){
+ Write-Both " [!] We found $count DCs not owned by Domains Admins group! see dcs_not_owned_by_da.txt"
+ Write-Nessus-Finding "DCsNotByDA" "KB547" ([System.IO.File]::ReadAllText("$outputdir\dcs_not_owned_by_da.txt"))
+ }
+}
+Function Get-HostDetails{#Gets basic information about the host
+ Write-Both " [+] Device Name: $env:ComputerName"
+ Write-Both " [+] Domain Name: $env:UserDomain"
+ Write-Both " [+] User Name : $env:UserName"
+ Write-Both " [+] NT Version : $(Get-WinVersion)"
+ $IPAddresses = [net.dns]::GetHostAddresses("") | select -ExpandProperty IP*
+ foreach($ip in $IPAddresses){
+ if($ip -ne "::1"){
+ Write-Both " [+] IP Address : $ip"
+ }
+ }
+}
+Function Get-FunctionalLevel{#Gets the functional level for domain and forest
+ $DomainLevel = (Get-ADDomain).domainMode
+ if($DomainLevel -eq "Windows2000Domain" -and [single](Get-WinVersion) -gt 5.0) { Write-Both " [!] DomainLevel is reduced for backwards compatibility to $DomainLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "DomainLevel is reduced for backwards compatibility to $DomainLevel" }
+ if($DomainLevel -eq "Windows2003InterimDomain" -and [single](Get-WinVersion) -gt 5.1) { Write-Both " [!] DomainLevel is reduced for backwards compatibility to $DomainLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "DomainLevel is reduced for backwards compatibility to $DomainLevel" }
+ if($DomainLevel -eq "Windows2003Domain" -and [single](Get-WinVersion) -gt 5.2) { Write-Both " [!] DomainLevel is reduced for backwards compatibility to $DomainLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "DomainLevel is reduced for backwards compatibility to $DomainLevel" }
+ if($DomainLevel -eq "Windows2008Domain" -and [single](Get-WinVersion) -gt 6.0) { Write-Both " [!] DomainLevel is reduced for backwards compatibility to $DomainLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "DomainLevel is reduced for backwards compatibility to $DomainLevel" }
+ if($DomainLevel -eq "Windows2008R2Domain" -and [single](Get-WinVersion) -gt 6.1) { Write-Both " [!] DomainLevel is reduced for backwards compatibility to $DomainLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "DomainLevel is reduced for backwards compatibility to $DomainLevel" }
+ if($DomainLevel -eq "Windows2012Domain" -and [single](Get-WinVersion) -gt 6.2) { Write-Both " [!] DomainLevel is reduced for backwards compatibility to $DomainLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "DomainLevel is reduced for backwards compatibility to $DomainLevel" }
+ if($DomainLevel -eq "Windows2012R2Domain" -and [single](Get-WinVersion) -gt 6.3) { Write-Both " [!] DomainLevel is reduced for backwards compatibility to $DomainLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "DomainLevel is reduced for backwards compatibility to $DomainLevel" }
+ if($DomainLevel -eq "Windows2016Domain" -and [single](Get-WinVersion) -gt 10.0){ Write-Both " [!] DomainLevel is reduced for backwards compatibility to $DomainLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "DomainLevel is reduced for backwards compatibility to $DomainLevel" }
+ $ForestLevel = (Get-ADForest).ForestMode
+ if($ForestLevel -eq "Windows2000Forest" -and [single](Get-WinVersion) -gt 5.0) { Write-Both " [!] ForestLevel is reduced for backwards compatibility to $ForestLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "ForestLevel is reduced for backwards compatibility to $ForestLevel" }
+ if($ForestLevel -eq "Windows2003InterimForest" -and [single](Get-WinVersion) -gt 5.1) { Write-Both " [!] ForestLevel is reduced for backwards compatibility to $ForestLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "ForestLevel is reduced for backwards compatibility to $ForestLevel" }
+ if($ForestLevel -eq "Windows2003Forest" -and [single](Get-WinVersion) -gt 5.2) { Write-Both " [!] ForestLevel is reduced for backwards compatibility to $ForestLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "ForestLevel is reduced for backwards compatibility to $ForestLevel" }
+ if($ForestLevel -eq "Windows2008Forest" -and [single](Get-WinVersion) -gt 6.0) { Write-Both " [!] ForestLevel is reduced for backwards compatibility to $ForestLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "ForestLevel is reduced for backwards compatibility to $ForestLevel" }
+ if($ForestLevel -eq "Windows2008R2Forest" -and [single](Get-WinVersion) -gt 6.1) { Write-Both " [!] ForestLevel is reduced for backwards compatibility to $ForestLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "ForestLevel is reduced for backwards compatibility to $ForestLevel" }
+ if($ForestLevel -eq "Windows2012Forest" -and [single](Get-WinVersion) -gt 6.2) { Write-Both " [!] ForestLevel is reduced for backwards compatibility to $ForestLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "ForestLevel is reduced for backwards compatibility to $ForestLevel" }
+ if($ForestLevel -eq "Windows2012R2Forest" -and [single](Get-WinVersion) -gt 6.3) { Write-Both " [!] ForestLevel is reduced for backwards compatibility to $ForestLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "ForestLevel is reduced for backwards compatibility to $ForestLevel" }
+ if($ForestLevel -eq "Windows2016Forest" -and [single](Get-WinVersion) -gt 10.0){ Write-Both " [!] ForestLevel is reduced for backwards compatibility to $ForestLevel!" ; Write-Nessus-Finding "FunctionalLevel" "KB546" "ForestLevel is reduced for backwards compatibility to $ForestLevel" }
+}
+Function Get-GPOEnum{#Loops GPOs for some important domain-wide settings
+ $AllowedJoin = @()
+ $HardenNTLM = @()
+ $DenyNTLM = @()
+ $AuditNTLM = @()
+ $NTLMAuthExceptions = @()
+ $EncryptionTypesNotConfigured = $true
+ $AdminLocalLogonAllowed = $true
+ $AdminRPDLogonAllowed = $true
+ $AdminNetworkLogonAllowed = $true
+ $AllGPOs = Get-GPO -All | sort DisplayName
+ foreach($GPO in $AllGPOs){
+ $GPOreport = Get-GPOReport -Guid $GPO.Id -ReportType Xml
+ #Look for GPO that allows join PC to domain
+ $permissionindex = $GPOreport.IndexOf('SeMachineAccountPrivilege')
+ if($permissionindex -gt 0){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeMachineAccountPrivilege'}).Member) ){
+ $obj = New-Object -TypeName PSObject
+ $obj | Add-Member -MemberType NoteProperty -Name GPO -Value $GPO.DisplayName
+ $obj | Add-Member -MemberType NoteProperty -Name SID -Value $member.Sid.'#text'
+ $obj | Add-Member -MemberType NoteProperty -Name Name -Value $member.Name.'#text'
+ $AllowedJoin += $obj
+ }
+ }
+ #Look for GPO that hardens NTLM
+ $permissionindex = $GPOreport.IndexOf('NoLMHash')
+ if($permissionindex -gt 0){
+ $xmlreport = [xml]$GPOreport
+ $value = $xmlreport.GPO.Computer.ExtensionData.Extension.SecurityOptions | Where-Object {$_.KeyName -Match 'NoLMHash'}
+ $obj = New-Object -TypeName PSObject
+ $obj | Add-Member -MemberType NoteProperty -Name GPO -Value $GPO.DisplayName
+ $obj | Add-Member -MemberType NoteProperty -Name Value -Value "NoLMHash $($value.Display.DisplayBoolean)"
+ $HardenNTLM += $obj
+ }
+ $permissionindex = $GPOreport.IndexOf('LmCompatibilityLevel')
+ if($permissionindex -gt 0){
+ $xmlreport = [xml]$GPOreport
+ $value = $xmlreport.GPO.Computer.ExtensionData.Extension.SecurityOptions | Where-Object {$_.KeyName -Match 'LmCompatibilityLevel'}
+ $obj = New-Object -TypeName PSObject
+ $obj | Add-Member -MemberType NoteProperty -Name GPO -Value $GPO.DisplayName
+ $obj | Add-Member -MemberType NoteProperty -Name Value -Value "LmCompatibilityLevel $($value.Display.DisplayString)"
+ $HardenNTLM += $obj
+ }
+ #Look for GPO that denies NTLM
+ $permissionindex = $GPOreport.IndexOf('RestrictNTLMInDomain')
+ if($permissionindex -gt 0){
+ $xmlreport = [xml]$GPOreport
+ $value = $xmlreport.GPO.Computer.ExtensionData.Extension.SecurityOptions | Where-Object {$_.KeyName -Match 'RestrictNTLMInDomain'}
+ $obj = New-Object -TypeName PSObject
+ $obj | Add-Member -MemberType NoteProperty -Name GPO -Value $GPO.DisplayName
+ $obj | Add-Member -MemberType NoteProperty -Name Value -Value "RestrictNTLMInDomain $($value.Display.DisplayString)"
+ $DenyNTLM += $obj
+ }
+ #Look for GPO that audits NTLM
+ $permissionindex = $GPOreport.IndexOf('AuditNTLMInDomain')
+ if($permissionindex -gt 0){
+ $xmlreport = [xml]$GPOreport
+ $value = $xmlreport.GPO.Computer.ExtensionData.Extension.SecurityOptions | Where-Object {$_.KeyName -Match 'AuditNTLMInDomain'}
+ $obj = New-Object -TypeName PSObject
+ $obj | Add-Member -MemberType NoteProperty -Name GPO -Value $GPO.DisplayName
+ $obj | Add-Member -MemberType NoteProperty -Name Value -Value "AuditNTLMInDomain $($value.Display.DisplayString)"
+ $AuditNTLM += $obj
+ }
+ $permissionindex = $GPOreport.IndexOf('AuditReceivingNTLMTraffic')
+ if($permissionindex -gt 0){
+ $xmlreport = [xml]$GPOreport
+ $value = $xmlreport.GPO.Computer.ExtensionData.Extension.SecurityOptions | Where-Object {$_.KeyName -Match 'AuditReceivingNTLMTraffic'}
+ $obj = New-Object -TypeName PSObject
+ $obj | Add-Member -MemberType NoteProperty -Name GPO -Value $GPO.DisplayName
+ $obj | Add-Member -MemberType NoteProperty -Name Value -Value "AuditReceivingNTLMTraffic $($value.Display.DisplayString)"
+ $AuditNTLM += $obj
+ }
+ #Look for GPO that allows NTLM exclusions
+ $permissionindex = $GPOreport.IndexOf('DCAllowedNTLMServers')
+ if($permissionindex -gt 0){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.SecurityOptions | Where-Object {$_.KeyName -Match 'DCAllowedNTLMServers'}).SettingStrings.Value) ){
+ $NTLMAuthExceptions += $member
+ }
+ }
+ #Validate Kerberos Encryption algorythm
+ $permissionindex = $GPOreport.IndexOf('MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters\SupportedEncryptionTypes')
+ if($permissionindex -gt 0){
+ $EncryptionTypesNotConfigured = $false
+ $xmlreport = [xml]$GPOreport
+ $EncryptionTypes = $xmlreport.GPO.Computer.ExtensionData.Extension.SecurityOptions.Display.DisplayFields.Field
+ if(($EncryptionTypes | Where-Object {$_.Name -eq 'DES_CBC_CRC'} | select -ExpandProperty value) -eq 'true') { Write-Both " [!] GPO [$($GPO.DisplayName)] enabled DES_CBC_CRC for Kerberos!" }
+ elseif(($EncryptionTypes | Where-Object {$_.Name -eq 'DES_CBC_MD5'} | select -ExpandProperty value) -eq 'true') { Write-Both " [!] GPO [$($GPO.DisplayName)] enabled DES_CBC_MD5 for Kerberos!" }
+ elseif(($EncryptionTypes | Where-Object {$_.Name -eq 'RC4_HMAC_MD5'} | select -ExpandProperty value) -eq 'true') { Write-Both " [!] GPO [$($GPO.DisplayName)] enabled RC4_HMAC_MD5 for Kerberos!" }
+ elseif(($EncryptionTypes | Where-Object {$_.Name -eq 'AES128_HMAC_SHA1'} | select -ExpandProperty value) -eq 'false'){ Write-Both " [!] AES128_HMAC_SHA1 not enabled for Kerberos!" }
+ elseif(($EncryptionTypes | Where-Object {$_.Name -eq 'AES256_HMAC_SHA1'} | select -ExpandProperty value) -eq 'false'){ Write-Both " [!] AES256_HMAC_SHA1 not enabled for Kerberos!" }
+ elseif(($EncryptionTypes | Where-Object {$_.Name -eq 'Future encryption types'} | select -ExpandProperty value) -eq 'false'){ Write-Both " [!] Future encryption types not enabled for Kerberos!" }
+ }
+ #Validates Admins local logon restrictions
+ $permissionindex = $GPOreport.IndexOf('SeDenyInteractiveLogonRight')
+ if($permissionindex -gt 0){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeDenyInteractiveLogonRight'}).Member)){
+ if($member.Name.'#text' -match "$SchemaAdmins" -or $member.Name.'#text' -match "$DomainAdmins" -or $member.Name.'#text' -match "$EnterpriseAdmins"){
+ $AdminLocalLogonAllowed = $false
+ Add-Content -Path "$outputdir\admin_logon_restrictions.txt" -Value "$($GPO.DisplayName) SeDenyInteractiveLogonRight $($member.Name.'#text')"
+ }
+ }
+ }
+ #Validates Admins RDP logon restrictions
+ $permissionindex = $GPOreport.IndexOf('SeDenyRemoteInteractiveLogonRight')
+ if($permissionindex -gt 0){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeDenyRemoteInteractiveLogonRight'}).Member)){
+ if($member.Name.'#text' -match "$SchemaAdmins" -or $member.Name.'#text' -match "$DomainAdmins" -or $member.Name.'#text' -match "$EnterpriseAdmins"){
+ $AdminRPDLogonAllowed = $false
+ Add-Content -Path "$outputdir\admin_logon_restrictions.txt" -Value "$($GPO.DisplayName) SeDenyRemoteInteractiveLogonRight $($member.Name.'#text')"
+ }
+ }
+ }
+ #Validates Admins network logon restrictions
+ $permissionindex = $GPOreport.IndexOf('SeDenyNetworkLogonRight')
+ if($permissionindex -gt 0){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeDenyNetworkLogonRight'}).Member)){
+ if($member.Name.'#text' -match "$SchemaAdmins" -or $member.Name.'#text' -match "$DomainAdmins" -or $member.Name.'#text' -match "$EnterpriseAdmins"){
+ $AdminNetworkLogonAllowed = $false
+ Add-Content -Path "$outputdir\admin_logon_restrictions.txt" -Value "$($GPO.DisplayName) SeDenyNetworkLogonRight $($member.Name.'#text')"
+ }
+ }
+ }
+ }
+ #Output for join PC to domain
+ foreach($record in $AllowedJoin){
+ Write-Both " [+] GPO [$($record.GPO)] allows [$($record.Name)] to join computers to domain"
+ }
+ #Output for Admins local logon restrictions
+ if($AdminLocalLogonAllowed){
+ Write-Both " [!] No GPO restricts Domain, Schema and Enterprise local logon across domain!!!"
+ Write-Nessus-Finding "AdminLogon" "KB479" "No GPO restricts Domain, Schema and Enterprise local logon across domain!"
+ }
+ #Output for Admins RDP logon restrictions
+ if($AdminRPDLogonAllowed){
+ Write-Both " [!] No GPO restricts Domain, Schema and Enterprise RDP logon across domain!!!"
+ Write-Nessus-Finding "AdminLogon" "KB479" "No GPO restricts Domain, Schema and Enterprise RDP logon across domain!"
+ }
+ #Output for Admins network logon restrictions
+ if($AdminNetworkLogonAllowed){
+ Write-Both " [!] No GPO restricts Domain, Schema and Enterprise network logon across domain!!!"
+ Write-Nessus-Finding "AdminLogon" "KB479" "No GPO restricts Domain, Schema and Enterprise network logon across domain!"
+ }
+ #Output for Validate Kerberos Encryption algorythm
+ if($EncryptionTypesNotConfigured){
+ Write-Both " [!] RC4_HMAC_MD5 enabled for Kerberos across domain!!!"
+ Write-Nessus-Finding "WeakKerberosEncryption" "KB995" "RC4_HMAC_MD5 enabled for Kerberos across domain!"
+ }
+ #Output for deny NTLM
+ if($DenyNTLM.count -eq 0){
+ if($HardenNTLM.count -eq 0){
+ Write-Both " [!] No GPO denies NTLM authentication!"
+ Write-Both " [!] No GPO explicitely restricts LM or NTLMv1!"
+ }else{
+ Write-Both " [+] NTLM authentication hardening implemented, but NTLM not denied"
+ foreach($record in $HardenNTLM){
+ Write-Both " [-] $($record.value)"
+ Add-Content -Path "$outputdir\ntlm_restrictions.txt" -Value "NTLM restricted by GPO [$($record.gpo)] with value [$($record.value)]"
+ }
+ }
+ }else{
+ foreach($record in $DenyNTLM){
+ Add-Content -Path "$outputdir\ntlm_restrictions.txt" -Value "NTLM restricted by GPO [$($record.gpo)] with value [$($record.value)]"
+ }
+ }
+ #Output for NTLM exceptions
+ if($NTLMAuthExceptions.count -ne 0){
+ foreach($record in $NTLMAuthExceptions){
+ Add-Content -Path "$outputdir\ntlm_restrictions.txt" -Value "NTLM auth exceptions $($record)"
+ }
+ }
+ #Output for NTLM audit
+ if($AuditNTLM.count -eq 0){
+ Write-Both " [!] No GPO enables NTLM audit authentication!"
+ }else{
+ foreach($record in $DenyNTLM){
+ Add-Content -Path "$outputdir\ntlm_restrictions.txt" -Value "NTLM audit GPO [$($record.gpo)] with value [$($record.value)]"
+ }
+ }
+}
+Function Get-PrivilegedGroupMembership{#List Domain Admins, Enterprise Admins and Schema Admins members
+ $SchemaMembers = Get-ADGroup $SchemaAdmins | Get-ADGroupMember
+ $EnterpriseMembers = Get-ADGroup $EnterpriseAdmins | Get-ADGroupMember
+ $DomainAdminsMembers = Get-ADGroup $DomainAdmins | Get-ADGroupMember
+ if(($SchemaMembers | measure).count -ne 0){
+ Write-Both " [!] Schema Admins not empty!!!"
+ foreach($member in $SchemaMembers){
+ Add-Content -Path "$outputdir\schema_admins.txt" -Value "$($member.objectClass) $($member.SamAccountName) $($member.Name)"
+ }
+ }
+ if(($EnterpriseMembers | measure).count -ne 0){
+ Write-Both " [!] Enterprise Admins not empty!!!"
+ foreach($member in $EnterpriseMembers){
+ Add-Content -Path "$outputdir\enterprise_admins.txt" -Value "$($member.objectClass) $($member.SamAccountName) $($member.Name)"
+ }
+ }
+ foreach($member in $DomainAdminsMembers){
+ Add-Content -Path "$outputdir\domain_admins.txt" -Value "$($member.objectClass) $($member.SamAccountName) $($member.Name)"
+ }
+}
+Function Get-DCEval{#Basic validation of all DCs in forest
+ #Collect all DCs in forest
+ $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
+ $ADs = Get-ADDomainController -Filter { Site -like "*" }
+ #Validate OS version of DCs
+ $osList = @()
+ $ADs | ForEach-Object { $osList += $_.OperatingSystem }
+ if(($osList | sort -Unique | measure).Count -eq 1){
+ Write-Both " [+] All DCs are the same OS version of $($osList | sort -Unique)"
+ }else{
+ Write-Both " [!] Operating system differs across DCs!!!"
+ if(($ADs | Where-Object {$_.OperatingSystem -Match '2003'}) -ne $null){ Write-Both " [+] Domain controllers with WS 2003" ; $ADs | Where-Object {$_.OperatingSystem -Match '2003'} | ForEach-Object { Write-Both " [-] $($_.Name) has $($_.OperatingSystem)" }}
+ if(($ADs | Where-Object {$_.OperatingSystem -Match '2008 !(R2)'}) -ne $null){ Write-Both " [+] Domain controllers with WS 2008" ; $ADs | Where-Object {$_.OperatingSystem -Match '2008 !(R2)'} | ForEach-Object { Write-Both " [-] $($_.Name) has $($_.OperatingSystem)" }}
+ if(($ADs | Where-Object {$_.OperatingSystem -Match '2008 R2'}) -ne $null){ Write-Both " [+] Domain controllers with WS 2008 R2" ; $ADs | Where-Object {$_.OperatingSystem -Match '2008 R2'} | ForEach-Object { Write-Both " [-] $($_.Name) has $($_.OperatingSystem)" }}
+ if(($ADs | Where-Object {$_.OperatingSystem -Match '2012 !(R2)'}) -ne $null){ Write-Both " [+] Domain controllers with WS 2012" ; $ADs | Where-Object {$_.OperatingSystem -Match '2012 !(R2)'} | ForEach-Object { Write-Both " [-] $($_.Name) has $($_.OperatingSystem)" }}
+ if(($ADs | Where-Object {$_.OperatingSystem -Match '2012 R2'}) -ne $null){ Write-Both " [+] Domain controllers with WS 2012 R2" ; $ADs | Where-Object {$_.OperatingSystem -Match '2012 R2'} | ForEach-Object { Write-Both " [-] $($_.Name) has $($_.OperatingSystem)" }}
+ if(($ADs | Where-Object {$_.OperatingSystem -Match '2016'}) -ne $null){ Write-Both " [+] Domain controllers with WS 2016" ; $ADs | Where-Object {$_.OperatingSystem -Match '2016'} | ForEach-Object { Write-Both " [-] $($_.Name) has $($_.OperatingSystem)" }}
+ if(($ADs | Where-Object {$_.OperatingSystem -Match '2019'}) -ne $null){ Write-Both " [+] Domain controllers with WS 2019" ; $ADs | Where-Object {$_.OperatingSystem -Match '2019'} | ForEach-Object { Write-Both " [-] $($_.Name) has $($_.OperatingSystem)" }}
+ if(($ADs | Where-Object {$_.OperatingSystem -Match '2022'}) -ne $null){ Write-Both " [+] Domain controllers with WS 2022" ; $ADs | Where-Object {$_.OperatingSystem -Match '2022'} | ForEach-Object { Write-Both " [-] $($_.Name) has $($_.OperatingSystem)" }}
+ }
+ #Validate DCs hotfix level
+ if( (( $ADs | Select-Object OperatingSystemHotfix -Unique ) | measure).count -eq 1 -or ( $ADs | Select-Object OperatingSystemHotfix -Unique ) -eq $null ){
+ Write-Both " [+] All DCs have the same hotfix of [$($ADs | Select-Object OperatingSystemHotFix -Unique | ForEach-Object {$_.OperatingSystemHotfix})]"
+ }else{
+ Write-Both " [!] Hotfix level differs across DCs!!!"
+ $ADs | ForEach-Object {
+ Write-Both " [-] DC $($_.Name) hotfix [$($_.OperatingSystemHotfix)]"
+ }
+ }
+ #Validate DCs Service Pack level
+ if((($ADs | Select-Object OperatingSystemServicePack -Unique) | measure).count -eq 1 -or ($ADs | Select-Object OperatingSystemServicePack -Unique) -eq $null){
+ Write-Both " [+] All DCs have the same Service Pack of [$($ADs | Select-Object OperatingSystemServicePack -Unique | ForEach-Object {$_.OperatingSystemServicePack})]"
+ }else{
+ Write-Both " [!] Service Pack level differs across DCs!!!"
+ $ADs | ForEach-Object {
+ Write-Both " [-] DC $($_.Name) Service Pack [$($_.OperatingSystemServicePack)]"
+ }
+ }
+ #Validate DCs OS Version
+ if((($ADs | Select-Object OperatingSystemVersion -Unique ) | measure).count -eq 1 -or ($ADs | Select-Object OperatingSystemVersion -Unique) -eq $null){
+ Write-Both " [+] All DCs have the same OS Version of [$($ADs | Select-Object OperatingSystemVersion -Unique | ForEach-Object {$_.OperatingSystemVersion})]"
+ }else{
+ Write-Both " [!] OS Version differs across DCs!!!"
+ $ADs | ForEach-Object {
+ Write-Both " [-] DC $($_.Name) OS Version [$($_.OperatingSystemVersion)]"
+ }
+ }
+ #List sites without GC
+ $SitesWithNoGC = $false
+ foreach($Site in $Forest.Sites){
+ if(($ADs | Where-Object {$_.Site -eq $Site.Name} | Where-Object {$_.IsGlobalCatalog -eq $true}) -eq $null){
+ $SitesWithNoGC = $true
+ Add-Content -Path "$outputdir\sites_no_gc.txt" -Value "$($Site.Name)"
+ }
+ }
+ if($SitesWithNoGC -eq $true){
+ Write-Both " [!] You have sites with no Global Catalog!"
+ }
+ #Does one DC holds all FSMO
+ if(($ADs | Where-Object {$_.OperationMasterRoles -ne $null} | measure).count -eq 1){
+ Write-Both " [!] DC $($ADs | Where-Object {$_.OperationMasterRoles -ne $null} | select -ExpandProperty Hostname) holds all FSMO roles!"
+ }
+ #DCs with weak Kerberos algorhythm (*CH* Changed below to look for msDS-SupportedEncryptionTypes to work with 2008R2)
+ $ADcomputers = $ADs | ForEach-Object {Get-ADComputer $_.Name -Properties msDS-SupportedEncryptionTypes}
+ $WeakKerberos = $false
+ foreach($DC in $ADcomputers){#(*CH* Need to define all combinations here, only done 28 and 31 so far) (31 = "DES, RC4, AES128, AES256", 28 = "RC4, AES128, AES256")
+ if( $DC."msDS-SupportedEncryptionTypes" -eq 28 -or $DC."msDS-SupportedEncryptionTypes" -eq 31 ){
+ $WeakKerberos = $true
+ Add-Content -Path "$outputdir\dcs_weak_kerberos_ciphersuite.txt" -Value "$($DC.DNSHostName) $($dc."msDS-SupportedEncryptionTypes")"
+ }
+ }
+ Write-Both " [!] You have DCs with RC4 or DES allowed for Kerberos!!!"
+ #Check where newly joined computers go
+ $newComputers = (Get-ADDomain).ComputersContainer
+ $newUsers = (Get-ADDomain).UsersContainer
+ Write-Both " [+] New joined computers are stored in $newComputers"
+ Write-Both " [+] New users are stored in $newUsers"
+}
+Function Get-DefaultDomainControllersPolicy{#Enumerates Default Domain Controllers Policy for default unsecure and excessive options
+ $ExcessiveDCInteractiveLogon = $false
+ $ExcessiveDCBackupPermissions = $false
+ $ExcessiveDCRestorePermissions = $false
+ $ExcessiveDCDriverPermissions = $false
+ $ExcessiveDCLocalShutdownPermissions = $false
+ $ExcessiveDCRemoteShutdownPermissions = $false
+ $ExcessiveDCTimePermissions = $false
+ $ExcessiveDCBatchLogonPermissions = $false
+ $ExcessiveDCRDPLogonPermissions = $false
+ $GPO = Get-GPO 'Default Domain Controllers Policy'
+ $GPOreport = Get-GPOReport -Guid $GPO.Id -ReportType Xml
+ #Interactive local logon
+ $permissionindex = $GPOreport.IndexOf('SeInteractiveLogonRight')
+ if($permissionindex -gt 0 -and $GPO.DisplayName -eq 'Default Domain Controllers Policy'){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeInteractiveLogonRight'}).Member)){
+ if($member.Name.'#text' -ne 'BUILTIN\$Administrators' -and $member.Name.'#text' -ne "$EntrepriseDomainControllers"){
+ $ExcessiveDCInteractiveLogon = $true
+ Add-Content -Path "$outputdir\default_domain_controller_policy_audit.txt" -Value "SeInteractiveLogonRight $($member.Name.'#text')"
+ }
+ }
+ }
+ #Batch logon
+ $permissionindex = $GPOreport.IndexOf('SeBatchLogonRight')
+ if($permissionindex -gt 0 -and $GPO.DisplayName -eq 'Default Domain Controllers Policy'){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeBatchLogonRight'}).Member)){
+ if($member.Name.'#text' -ne 'BUILTIN\$Administrators'){
+ $ExcessiveDCBatchLogonPermissions = $true
+ Add-Content -Path "$outputdir\default_domain_controller_policy_audit.txt" -Value "SeBatchLogonRight $($member.Name.'#text')"
+ }
+ }
+ }
+ #RDP logon
+ $permissionindex = $GPOreport.IndexOf('SeInteractiveLogonRight')
+ if($permissionindex -gt 0 -and $GPO.DisplayName -eq 'Default Domain Controllers Policy'){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeInteractiveLogonRight'}).Member)){
+ if($member.Name.'#text' -ne 'BUILTIN\$Administrators' -and $member.Name.'#text' -ne "$EntrepriseDomainControllers"){
+ $ExcessiveDCRDPLogonPermissions = $true
+ Add-Content -Path "$outputdir\default_domain_controller_policy_audit.txt" -Value "SeInteractiveLogonRight $($member.Name.'#text')"
+ }
+ }
+ }
+ #Backup
+ $permissionindex = $GPOreport.IndexOf('SeBackupPrivilege')
+ if($permissionindex -gt 0 -and $GPO.DisplayName -eq 'Default Domain Controllers Policy'){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeBackupPrivilege'}).Member)){
+ if($member.Name.'#text' -ne 'BUILTIN\$Administrators'){
+ $ExcessiveDCBackupPermissions = $true
+ Add-Content -Path "$outputdir\default_domain_controller_policy_audit.txt" -Value "SeBackupPrivilege $($member.Name.'#text')"
+ }
+ }
+ }
+ #Restore
+ $permissionindex = $GPOreport.IndexOf('SeRestorePrivilege')
+ if($permissionindex -gt 0 -and $GPO.DisplayName -eq 'Default Domain Controllers Policy'){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeRestorePrivilege'}).Member)){
+ if($member.Name.'#text' -ne 'BUILTIN\$Administrators'){
+ $ExcessiveDCRestorePermissions = $true
+ Add-Content -Path "$outputdir\default_domain_controller_policy_audit.txt" -Value "SeRestorePrivilege $($member.Name.'#text')"
+ }
+ }
+ }
+ #Load driver
+ $permissionindex = $GPOreport.IndexOf('SeLoadDriverPrivilege')
+ if($permissionindex -gt 0 -and $GPO.DisplayName -eq 'Default Domain Controllers Policy'){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeLoadDriverPrivilege'}).Member)){
+ if($member.Name.'#text' -ne 'BUILTIN\$Administrators'){
+ $ExcessiveDCDriverPermissions = $true
+ Add-Content -Path "$outputdir\default_domain_controller_policy_audit.txt" -Value "SeLoadDriverPrivilege $($member.Name.'#text')"
+ }
+ }
+ }
+ #Local shutdown
+ $permissionindex = $GPOreport.IndexOf('SeShutdownPrivilege')
+ if($permissionindex -gt 0 -and $GPO.DisplayName -eq 'Default Domain Controllers Policy'){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeShutdownPrivilege'}).Member)){
+ if($member.Name.'#text' -ne 'BUILTIN\$Administrators'){
+ $ExcessiveDCLocalShutdownPermissions = $true
+ Add-Content -Path "$outputdir\default_domain_controller_policy_audit.txt" -Value "SeShutdownPrivilege $($member.Name.'#text')"
+ }
+ }
+ }
+ #Remote shutdown
+ $permissionindex = $GPOreport.IndexOf('SeRemoteShutdownPrivilege')
+ if($permissionindex -gt 0 -and $GPO.DisplayName -eq 'Default Domain Controllers Policy'){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeRemoteShutdownPrivilege'}).Member)){
+ if($member.Name.'#text' -ne 'BUILTIN\$Administrators'){
+ $ExcessiveDCRemoteShutdownPermissions = $true
+ Add-Content -Path "$outputdir\default_domain_controller_policy_audit.txt" -Value "SeRemoteShutdownPrivilege $($member.Name.'#text')"
+ }
+ }
+ }
+ #Change time
+ $permissionindex = $GPOreport.IndexOf('SeSystemTimePrivilege')
+ if($permissionindex -gt 0 -and $GPO.DisplayName -eq 'Default Domain Controllers Policy'){
+ $xmlreport = [xml]$GPOreport
+ foreach($member in (($xmlreport.GPO.Computer.ExtensionData.Extension.UserRightsAssignment | Where-Object {$_.Name -eq 'SeSystemTimePrivilege'}).Member)){
+ if($member.Name.'#text' -ne 'BUILTIN\$Administrators' -and $member.Name.'#text' -ne "$LocalService"){
+ $ExcessiveDCTimePermissions = $true
+ Add-Content -Path "$outputdir\default_domain_controller_policy_audit.txt" -Value "SeSystemTimePrivilege $($member.Name.'#text')"
+ }
+ }
+ }
+ #Output for Default Domain Controllers Policy
+ if($ExcessiveDCInteractiveLogon -or $ExcessiveDCBackupPermissions -or $ExcessiveDCRestorePermissions -or $ExcessiveDCDriverPermissions -or $ExcessiveDCLocalShutdownPermissions -or $ExcessiveDCRemoteShutdownPermissions -or $ExcessiveDCTimePermissions -or $ExcessiveDCBatchLogonPermissions -or $ExcessiveDCRDPLogonPermissions){
+ Write-Both " [!] Excessive permissions in Default Domain Controllers Policy detected!"
+ }
+}
+Function Get-RecentChanges(){#Retrieve users and groups that have been created during last 30 days
+ $DateCutOff = ((Get-Date).AddDays(-30)).Date
+ $newUsers = Get-ADUser -Filter {whenCreated -ge $DateCutOff} -Properties whenCreated | select whenCreated,SamAccountName
+ $newGroups = Get-ADGroup -Filter {whenCreated -ge $DateCutOff} -Properties whenCreated | select whenCreated,SamAccountName
+ $countUsers = 0
+ $countGroups = 0
+ $progresscountUsers = 0
+ $progresscountGroups = 0
+ $totalcountUsers = ($newUsers | Measure-Object | Select-Object Count).count
+ $totalcountGroups = ($newGroups | Measure-Object | Select-Object Count).count
+ if($totalcountUsers -gt 0){
+ foreach($newUser in $newUsers ){Add-Content -Path "$outputdir\new_users.txt" -Value "Account $($newUser.SamAccountName) was created $($newUser.whenCreated)"}
+ Write-Both " [!] $totalcountUsers new users were created last 30 days, see $outputdir\new_users.txt"
+ }
+ if($totalcountGroups -gt 0){
+ foreach($newGroup in $newGroups ){Add-Content -Path "$outputdir\new_groups.txt" -Value "Group $($newGroup.SamAccountName) was created $($newGroup.whenCreated)"}
+ Write-Both " [!] $totalcountGroups new groups were created last 30 days, see $outputdir\new_groups.txt"
+ }
+}
+Function Get-ReplicationType{#Retrieve replication mechanism (FRS or DFSR)
+ $objectName = "DFSR-GlobalSettings"
+ $searcher = [ADSISearcher] "(objectClass=msDFSR-GlobalSettings)"
+ $objectExists = $searcher.FindOne() -ne $null
+ if($objectExists){
+ $DFSRFlags=(Get-ADObject -Identity "CN=DFSR-GlobalSettings,$((Get-ADDomain).systemscontainer)" -Properties msDFSR-Flags).'msDFSR-Flags'
+ switch($DFSRFlags){
+ 0 { Write-Both " [!] Migration from FRS to DFSR is not finished. Current state: started!" }
+ 16 { Write-Both " [!] Migration from FRS to DFSR is not finished. Current state: prepared!" }
+ 32 { Write-Both " [!] Migration from FRS to DFSR is not finished. Current state: redirected!" }
+ 48 { Write-Both " [+] DFSR mechanism is used to replicate across domain controllers." }
+ }
+ }else{
+ Write-Both " [!] FRS mechanism is still used to replicate across domain controllers, you should migrate to DFSR!"
+ }
+}
+Function Get-RecycleBinState {#Check if recycle bin is enabled
+ if((Get-ADOptionalFeature -Filter 'Name -eq "Recycle Bin Feature"').EnabledScopes){
+ Write-Both " [+] Recycle Bin is enabled in the domain"
+ }else{
+ Write-Both " [!] Recycle Bin is disabled in the domain, you should consider enabling it!"
+ }
+}
+Function Get-CriticalServicesStatus{#Check AD services status
+ Write-Both " [+] Checking services on all DCs"
+ $dcList = @()
+ (Get-ADDomainController -Filter *) | ForEach-Object{$dcList += $_.Name}
+ $objectName = "DFSR-GlobalSettings"
+ $searcher = [ADSISearcher] "(objectClass=msDFSR-GlobalSettings)"
+ $objectExists = $searcher.FindOne() -ne $null
+ if($objectExists){
+ $services = @("dns","netlogon","kdc","w32time","ntds","dfsr")
+ }else{
+ $services = @("dns","netlogon","kdc","w32time","ntds","ntfrs")
+ }
+ foreach($DC in $dcList){
+ foreach($service in $services){
+ $checkService = Get-Service $service -ComputerName $DC -ErrorAction SilentlyContinue
+ $serviceName = $checkService.Name
+ $serviceStatus = $checkService.Status
+ if(!($serviceStatus)){
+ Write-Both " [!] Service $($service) cannot be checked on $DC!"
+ }
+ elseif($serviceStatus -ne "Running"){
+ Write-Both " [!] Service $($service) is not running on $DC!"
+ }
+ }
+ }
+}
+Function Get-LastWUDate{#Check Windows update status and last install date
+ $dcList = @()
+ (Get-ADDomainController -Filter *) | ForEach-Object{$dcList+=$_.Name}
+ $lastMonth = (Get-Date).AddDays(-30)
+ Write-Both " [+] Checking Windows Update"
+ foreach($DC in $dcList){
+
+ $startMode = (Get-WmiObject -ComputerName $DC -Class Win32_Service -Property StartMode -Filter "Name='wuauserv'" -ErrorAction SilentlyContinue).StartMode
+ if(!($startMode)){
+ Write-Both " [!] Windows Update service cannot be checked on $DC!"
+ }
+ elseif($startMode -eq "Disabled"){
+ Write-Both " [!] Windows Update service is disabled on $DC!"
+ }
+ }
+ $progresscount = 0
+ $totalcount = ($dcList | Measure-Object | Select-Object Count).count
+ foreach($DC in $dcList){
+ if($totalcount -eq 0){ break }
+ Write-Progress -Activity "Searching for last Windows Update installation on all DCs..." -Status "Currently searching on $DC" -PercentComplete ($progresscount / $totalcount*100)
+ try{
+ $lastHotfix = (Get-HotFix -ComputerName $DC | Where-Object {$_.InstalledOn -ne $null} | Sort-Object -Descending InstalledOn | Select-Object -First 1).InstalledOn
+ if($lastHotfix -lt $lastMonth){
+ Write-Both " [!] Windows is not up to date on $DC, last install: $($lastHotfix)"
+ }else{
+ Write-Both " [+] Windows is up to date on $DC, last install: $($lastHotfix)"
+ }
+ }
+ catch{
+ Write-Both " [!] Cannot check last update date on $DC"
+ }
+ $progresscount++
+ }
+ Write-Progress -Activity "Searching for last Windows Update installation on all DCs..." -Status "Ready" -Completed
+}
+Function Get-TimeSource {#Get NTP sync source
+ $dcList = @()
+ (Get-ADDomainController -Filter *) | ForEach-Object{$dcList += $_.Name}
+ Write-Both " [+] Checking NTP configuration"
+ foreach($DC in $dcList){
+ $ntpSource = w32tm /query /source /computer:$DC
+ if($ntpSource -like '*0x800706BA*'){
+ Write-Both " [+] Cannot get time source for $DC"
+ }else{
+ Write-Both " [+] $DC is syncing time from $ntpSource"
+ }
+ }
+}
+Function Get-RODC{#Check for RODC
+ Write-Both " [+] Checking for Read Only DCs"
+ $ADs = Get-ADDomainController -Filter { Site -like "*" }
+ $ADs | ForEach-Object{
+ if($_.IsReadOnly){
+ Write-Both " [+] DC $($_.Name) is a RODC server!"
+ }
+ }
+}
+Function Install-Dependencies{#Install DSInternals
+ if($PSVersionTable.PSVersion.Major -ge 5){
+ [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor
+ [Net.SecurityProtocolType]::Tls12
+ $count = 0
+ $totalcount = 3
+ Write-Progress -Activity "Installing dependencies..." -Status "Currently installing NuGet Package Provider" -PercentComplete ($count / $totalcount*100)
+ if(!(Get-PackageProvider -ListAvailable -Name Nuget -ErrorAction SilentlyContinue)){ Install-PackageProvider -Name NuGet -Force | Out-Null }
+ $count++
+ Write-Progress -Activity "Installing dependencies..." -Status "Currently adding PSGallery to trusted Repositories" -PercentComplete ($count / $totalcount*100)
+ if((Get-PSRepository -Name PSGallery).InstallationPolicy -eq "Untrusted"){ Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted }
+ $count++
+ Write-Progress -Activity "Installing dependencies..." -Status "Currently installing module DSInternals" -PercentComplete ($count / $totalcount*100)
+ if(!(Get-Module -ListAvailable -Name DSInternals)){ Install-Module -Name DSInternals -Force }
+ Write-Progress -Activity "Installing dependencies..." -Status "Ready" -Completed
+ Import-Module DSInternals
+ }else{
+ Write-Both " [!] PowerShell 5 or greater is needed, see https://www.microsoft.com/en-us/download/details.aspx?id=54616"
+ }
+}
+Function Remove-StringLatinCharacters{#Removes latin characters
+ PARAM ([string]$String)
+ [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($String))
+}
+Function Get-PasswordQuality{#Use DSInternals to evaluate password quality
+ if(Get-Module -ListAvailable -Name DSInternals){
+ $totalSite = (Get-ADObject -Filter {objectClass -like "site" } -SearchBase (Get-ADRootDSE).ConfigurationNamingContext | measure).Count
+ $count = 0
+ Get-ADObject -Filter {objectClass -like "site" } -SearchBase (Get-ADRootDSE).ConfigurationNamingContext | ForEach-Object{
+ if($_.Name -eq $(Remove-StringLatinCharacters $_.Name)){ $count++ }
+ }
+ if($count -ne $totalSite){
+ Write-Both " [!] One or more site have illegal characters in their name, can't get password quality!"
+ }else{
+ Get-ADReplAccount -All -Server $env:ComputerName -NamingContext $(Get-ADDomain | select -ExpandProperty DistinguishedName) | Test-PasswordQuality -IncludeDisabledAccounts | Out-File "$outputdir\password_quality.txt"
+ Write-Both " [!] Password quality test done, see $outputdir\password_quality.txt"
+ }
+ }
+}
+Function Check-Shares {#Check SYSVOL and NETLOGON share exists
+ $dcList = @()
+ (Get-ADDomainController -Filter *) | ForEach-Object{$dcList += $_.Name}
+ Write-Both " [+] Checking SYSVOL and NETLOGON shares on all DCs"
+ foreach($DC in $dcList){
+ $shareList = (Get-WmiObject -Class Win32_Share -ComputerName $DC -ErrorAction SilentlyContinue)
+ if(!($shareList)){
+ Write-Both " [!] Cannot test shares on $DC!"
+ }else{
+ $sysvolShare = ($shareList | ?{$_ -match 'SYSVOL'} | measure).Count
+ $netlogonShare = ($shareList | ?{$_ -match 'NETLOGON'} | measure).Count
+ if($sysvolShare -eq 0){ Write-Both " [!] SYSVOL share is missing on $DC!" }
+ if($netlogonShare -eq 0){ Write-Both " [!] NETLOGON share is missing on $DC!" }
+ }
+ }
+}
+
+$outputdir = (Get-Item -Path ".\").FullName + "\" + $env:computername
+$starttime = Get-Date
+$scriptname = $MyInvocation.MyCommand.Name
+if(!(Test-Path "$outputdir")){ New-Item -ItemType Directory -Path $outputdir | Out-Null }
+Write-Both " _____ ____ _____ _ _ _
+| _ | \ | _ |_ _ _| |_| |_
+| | | | | | | | . | | _|
+|__|__|____/ |__|__|___|___|_|_|
+$versionnum by phillips321
+"
+$running=$false
+Write-Both "[*] Script start time $starttime"
+if(Get-Module -ListAvailable -Name ActiveDirectory){ Import-Module ActiveDirectory }else{ Write-Both "[!] ActiveDirectory module not installed, exiting..." ; exit }
+if(Get-Module -ListAvailable -Name ServerManager) { Import-Module ServerManager }else{ Write-Both "[!] ServerManager module not installed, exiting..." ; exit }
+if(Get-Module -ListAvailable -Name GroupPolicy) { Import-Module GroupPolicy }else{ Write-Both "[!] GroupPolicy module not installed, exiting..." ; exit }
+if(Get-Module -ListAvailable -Name DSInternals) { Import-Module DSInternals }else{ Write-Both "[!] DSInternals module not installed, use -installdeps to force install" }
+if(Test-Path "$outputdir\adaudit.nessus"){ Remove-Item -recurse "$outputdir\adaudit.nessus" | Out-Null }
+Write-Nessus-Header
+Write-Host "[+] Outputting to $outputdir"
+Write-Both "[*] Lang specific variables"
+Get-Variables
+if($installdeps) { $running=$true ; Write-Both "[*] Installing optionnal features" ; Install-Dependencies }
+if($hostdetails -or $all) { $running=$true ; Write-Both "[*] Device Information" ; Get-HostDetails }
+if($domainaudit -or $all) { $running=$true ; Write-Both "[*] Domain Audit" ; Get-LastWUDate ; Get-DCEval ; Get-TimeSource ; Get-PrivilegedGroupMembership ; Get-MachineAccountQuota; Get-DefaultDomainControllersPolicy ; Get-SMB1Support ; Get-FunctionalLevel ; Get-DCsNotOwnedByDA ; Get-ReplicationType ; Check-Shares ; Get-RecycleBinState ; Get-CriticalServicesStatus ; Get-RODC }
+if($trusts -or $all) { $running=$true ; Write-Both "[*] Domain Trust Audit" ; Get-DomainTrusts }
+if($accounts -or $all) { $running=$true ; Write-Both "[*] Accounts Audit" ; Get-InactiveAccounts ; Get-DisabledAccounts ; Get-LockedAccounts ; Get-AdminAccountChecks ; Get-NULLSessions ; Get-PrivilegedGroupAccounts ; Get-ProtectedUsers }
+if($passwordpolicy -or $all) { $running=$true ; Write-Both "[*] Password Information Audit" ; Get-AccountPassDontExpire ; Get-UserPasswordNotChangedRecently ; Get-PasswordPolicy ; Get-PasswordQuality }
+if($ntds -or $all) { $running=$true ; Write-Both "[*] Trying to save NTDS.dit, please wait..." ; Get-NTDSdit }
+if($oldboxes -or $all) { $running=$true ; Write-Both "[*] Computer Objects Audit" ; Get-OldBoxes }
+if($gpo -or $all) { $running=$true ; Write-Both "[*] GPO audit (and checking SYSVOL for passwords)" ; Get-GPOtoFile ; Get-GPOsPerOU ; Get-SYSVOLXMLS; Get-GPOEnum }
+if($ouperms -or $all) { $running=$true ; Write-Both "[*] Check Generic Group AD Permissions" ; Get-OUPerms }
+if($laps -or $all) { $running=$true ; Write-Both "[*] Check For Existence of LAPS in domain" ; Get-LAPSStatus }
+if($authpolsilos -or $all) { $running=$true ; Write-Both "[*] Check For Existence of Authentication Polices and Silos" ; Get-AuthenticationPoliciesAndSilos }
+if($insecurednszone -or $all){ $running=$true ; Write-Both "[*] Check For Existence DNS Zones allowing insecure updates" ; Get-DNSZoneInsecure }
+if($recentchanges -or $all) { $running=$true ; Write-Both "[*] Check For newly created users and groups" ; Get-RecentChanges }
+if(!$running){ Write-Both "[!] No arguments selected"
+ Write-Both "[!] Other options are as follows, they can be used in combination"
+ Write-Both " -installdeps installs optionnal features (DSInternals)"
+ Write-Both " -hostdetails retrieves hostname and other useful audit info"
+ Write-Both " -domainaudit retrieves information about the AD such as functional level"
+ Write-Both " -trusts retrieves information about any doman trusts"
+ Write-Both " -accounts identifies account issues such as expired, disabled, etc..."
+ Write-Both " -passwordpolicy retrieves password policy information"
+ Write-Both " -ntds dumps the NTDS.dit file using ntdsutil"
+ Write-Both " -oldboxes identifies outdated OSs like 2000/2003/XP/Vista/7/2008 joined to the domain"
+ Write-Both " -gpo dumps the GPOs in XML and HTML for later analysis"
+ Write-Both " -ouperms checks generic OU permission issues"
+ Write-Both " -laps checks if LAPS is installed"
+ Write-Both " -authpolsilos checks for existence of authentication policies and silos"
+ Write-Both " -insecurednszone checks for insecure DNS zones"
+ Write-Both " -recentchanges checks for newly created users and groups (last 30 days)"
+ Write-Both " -all runs all checks, e.g. $scriptname -all"
+}
+Write-Nessus-Footer
+
+#Dirty fix for .nessus characters (will do this properly or as a function later. Will need more characters adding here...)
+$originalnessusoutput = Get-Content $outputdir\adaudit.nessus
+$nessusoutput = $originalnessusoutput -Replace "&", "&"
+$nessusoutput = $nessusoutput -Replace "`“", """
+$nessusoutput = $nessusoutput -Replace "`'", "'"
+$nessusoutput = $nessusoutput -Replace "ü", "u"
+$nessusoutput | Out-File $outputdir\adaudit-replaced.nessus
+
+$endtime = Get-Date
+Write-Both "[*] Script end time $endtime"
\ No newline at end of file
diff --git a/Audit.ps1 b/Audit.ps1
new file mode 100644
index 0000000..79fd9f3
--- /dev/null
+++ b/Audit.ps1
@@ -0,0 +1,1658 @@
+<#
+.SYNOPSIS
+
+
+.NOTES
+ Version : 1.0
+ Author : Hubert CORNET
+ Creation Date : 20/11/2022
+ Purpose/Change :
+
+.LINK
+ https://www.tips-of-mine.fr
+
+.EXEMPLE
+
+
+.DESCRIPTION
+
+
+.PARAMETER CompanyLogo
+ Enter URL or UNC path to your desired Company Logo for generated report.
+
+ -CompanyLogo "https://www.fichorga.fr/images/logo-logiciel-fichorga.png"
+
+.PARAMETER ReportTitle
+ Enter desired title for generated report.
+
+ -ReportTitle "Active Directory Report"
+
+.PARAMETER Days
+ Users that have not logged in within [X] amount of days.
+
+ -Days "90"
+
+.PARAMETER UserCreatedDays
+ Users that have been created within [X] amount of days.
+
+ -UserCreatedDays "15"
+
+.PARAMETER DaysUntilPWExpireINT
+ Users password expires within [X] amount of days
+
+ -DaysUntilPWExpireINT "15"
+
+.PARAMETER ADModNumber
+ Active Directory Objects that have been modified within [X] amount of days.
+
+ -ADModNumber "15"
+
+.INPUTS
+
+
+.OUTPUTS
+ .log>
+#>
+
+#---------------------------------------------------------[Initialisations]--------------------------------------------------------
+
+param (
+ #Company logo that will be displayed on the left, can be URL or UNC
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Entrez l'URL ou le chemin UNC vers le logo de l'entreprise")]
+ [String]$CompanyLogo = "https://www.fichorga.fr/images/logo-logiciel-fichorga.png",
+ #Logo that will be on the right side, UNC or URL
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Entrez le titre souhaité pour le rapport")]
+ [String]$ReportTitle = "Rapport Active Directory",
+ #Location the report will be saved to
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Entrez le chemin du répertoire souhaité pour enregistrer; Default: C:\Automation\")]
+ [String]$ReportSavePath = "C:\Automation\",
+ #Find users that have not logged in X Amount of days, this sets the days
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Les utilisateurs qui ne se sont pas connectés dans les [X] nombre de jours; Default: 90")]
+ $Days = 90,
+ #Get users who have been created in X amount of days and less
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Les utilisateurs qui ont été créés dans [X] nombre de jours; Default: 15")]
+ $UserCreatedDays = 15,
+ #Get users whos passwords expire in less than X amount of days
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Le mot de passe de l'utilisateur expire dans un délai de [X] nombre de jours; Default: 15")]
+ $DaysUntilPWExpireINT = 15,
+ #Get AD Objects that have been modified in X days and newer
+
+ [Parameter(ValueFromPipeline = $true, HelpMessage = "Les objets AD qui ont été modifiés dans [X] nombre de jours; Default: 15")]
+ $ADModNumber =15
+
+ #CSS template located C:\Program Files\WindowsPowerShell\Modules\ReportHTML\1.4.1.1\
+ #Default template is orange and named "Sample"
+)
+
+#---------------------------------------------------------[Initialisations]--------------------------------------------------------
+
+# Définir l'action d'erreur pour continuer silencieusement
+$ErrorActionPreference = "SilentlyContinue"
+
+$Minute = (Get-Date).Minute
+$Hour = (Get-Date).Hour
+$Day = (Get-Date).Day
+$Month = (Get-Date).Month
+$Year = (Get-Date).Year
+
+#-----------------------------------------------------------[Functions]------------------------------------------------------------
+
+Function LastLogonConvert ($ftDate) {
+ $Date = [DateTime]::FromFileTime($ftDate)
+ If ($Date -lt (Get-Date '1/1/1900') -or $date -eq 0 -or $date -eq $null) {
+ "Never"
+ }
+ Else {
+ $Date
+ }
+
+}
+
+Function Write-Color([String[]]$Text, [ConsoleColor[]]$Color = "White", [int]$StartTab = 0, [int] $LinesBefore = 0,[int] $LinesAfter = 0, [string] $LogFile = "", $TimeFormat = "yyyy-MM-dd HH:mm:ss") {
+
+ $DefaultColor = $Color[0]
+
+ If ($LinesBefore -ne 0) { for ($i = 0; $i -lt $LinesBefore; $i++) { Write-Host "`n" -NoNewline } } # Add empty line before
+
+ If ($StartTab -ne 0) { for ($i = 0; $i -lt $StartTab; $i++) { Write-Host "`t" -NoNewLine } } # Add TABS before text
+
+ If ($Color.Count -ge $Text.Count) {
+ For ($i = 0; $i -lt $Text.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -NoNewLine }
+ }
+ Else {
+ For ($i = 0; $i -lt $Color.Length ; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -NoNewLine }
+ For ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $DefaultColor -NoNewLine }
+ }
+
+ Write-Host
+ If ($LinesAfter -ne 0) { for ($i = 0; $i -lt $LinesAfter; $i++) { Write-Host "`n" } } # Add empty line after
+ If ($LogFile -ne "") {
+ $TextToFile = ""
+ For ($i = 0; $i -lt $Text.Length; $i++) {
+ $TextToFile += $Text[$i]
+ }
+ Write-Output "[$([datetime]::Now.ToString($TimeFormat))]$TextToFile" | Out-File $LogFile -Encoding unicode -Append
+ }
+}
+
+#--------------------------------------------------------[Debut Du Script]---------------------------------------------------------
+
+Clear-Host
+
+#Check for ReportHTML Module
+$Mod = Get-Module -ListAvailable -Name "ReportHTML"
+
+If ($null -eq $Mod) {
+ Write-Host "Le module ReportHTML n'est pas présent, tentative d'installation."
+
+ Install-Module -Name ReportHTML -Force
+ Import-Module ReportHTML -ErrorAction SilentlyContinue
+}
+
+Write-host "`n"
+Write-Color "Personnalisation des rapports de collecte ..." -Color White
+Write-host "`n"
+Write-Color "__________________________________________________________________________________" -Color White
+Write-Color "Logo de l'entreprise (gauche) : __________________________________________________ ", $CompanyLogo -Color White, Green
+Write-Color "Titre du rapport : _______________________________________________________________ ", $ReportTitle -Color White, Green
+Write-Color "Chemin de sauvegarde du rapport : ________________________________________________ ", $ReportSavePath -Color White, Green
+Write-Color "Rapport sur le nombre de jours depuis la dernière connexion de l'utilisateur : ___ ", $Days -Color White, Green
+Write-Color "Nombre de jours pour la création de nouveaux utilisateurs : ______________________ ", $UserCreatedDays -Color White, Green
+Write-Color "Nombre de jours pour l'expiration du mot de passe : ______________________________ ", $DaysUntilPWExpireINT -Color White, Green
+Write-Color "Nombre de jours pour les objets AD nouvellement modifiés : _______________________ ", $ADModNumber -Color White, Green
+Write-Color "__________________________________________________________________________________" -Color White
+
+#Array of default Security Groups
+$DefaultSGs = @(
+ "Opérateurs d’assistance Access Control"
+ "Opérateurs de compte"
+ "Administrateurs"
+ "Réplication de mot de passe RODC autorisée"
+ "Opérateurs de sauvegarde"
+ "Accès DCOM au service de certificats"
+ "Éditeurs de certificats"
+ "Contrôleurs de domaine clonables"
+ "Opérateurs de chiffrement"
+ "Réplication de mot de passe RODC refusée"
+ "Propriétaires d’appareils"
+ "Administrateurs DHCP"
+ "Utilisateurs DHCP"
+ "Utilisateurs du modèle COM distribué"
+ "DnsUpdateProxy"
+ "DnsAdmins"
+ "Administrateurs du domaine"
+ "Ordinateurs de domaine"
+ "Contrôleurs de domaine"
+ "Invités de domaine"
+ "Utilisateurs du domaine"
+ "Administrateurs de l’entreprise"
+ "Enterprise Key Admins"
+ "Contrôleurs de domaine d’entreprise en lecture seule"
+ "Lecteurs des journaux d’événements"
+ "Propriétaires créateurs de la stratégie de groupe"
+ "Invités"
+ "Administrateurs Hyper-V"
+ "IIS_IUSRS"
+ "Générateurs d’approbation de forêt entrante"
+ "Administrateurs de clés"
+ "Opérateurs de configuration réseau"
+ "Utilisateurs du journal des performances"
+ "Utilisateurs de l’Analyseur de performances"
+ "Accès pré-Windows 2000 compatible"
+ "Opérateurs d'impression"
+ "Utilisateurs protégés"
+ "Serveurs RAS et IAS"
+ "Serveurs de points de terminaison..."
+ "Serveurs d’administration RDS"
+ "Serveurs d’accès à distance RDS"
+ "Contrôleurs de domaine en lecture seule"
+ "Utilisateurs du Bureau à distance"
+ "Utilisateurs de gestion à distance"
+ "Duplicateur"
+ "Administrateurs du schéma"
+ "Opérateurs de serveur"
+ "Administrateurs de réplica de stockage"
+ "Comptes gérés par le système"
+ "Serveurs de licences Terminal Server"
+ "Utilisateurs"
+ "Accès à l’autorisation Windows"
+ "WinRMRemoteWMIUsers_"
+)
+
+$Table = New-Object 'System.Collections.Generic.List[System.Object]'
+$OUTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$UserTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$UserPasswordTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$GroupTypetable = New-Object 'System.Collections.Generic.List[System.Object]'
+$DefaultGrouptable = New-Object 'System.Collections.Generic.List[System.Object]'
+$EnabledDisabledUsersTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$DomainAdminTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ExpiringAccountsTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$CompanyInfoTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$securityeventtable = New-Object 'System.Collections.Generic.List[System.Object]'
+$DomainTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$OUGPOTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$GroupMembershipTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$PasswordExpirationTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$PasswordExpireSoonTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$userphaventloggedonrecentlytable = New-Object 'System.Collections.Generic.List[System.Object]'
+$EnterpriseAdminTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$NewCreatedUsersTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$GroupProtectionTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$OUProtectionTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$GPOTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ADObjectTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ProtectedUsersTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ComputersTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ComputerProtectedTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$ComputersEnabledTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$DefaultComputersinDefaultOUTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$DefaultUsersinDefaultOUTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$TOPUserTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$TOPGroupsTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$TOPComputersTable = New-Object 'System.Collections.Generic.List[System.Object]'
+$GraphComputerOS = New-Object 'System.Collections.Generic.List[System.Object]'
+
+#Get all users right away. Instead of doing several lookups, we will use this object to look up all the information needed.
+$AllUsers = Get-ADUser -Filter * -Properties *
+
+$GPOs = Get-GPO -All | Select-Object DisplayName, GPOStatus, ModificationTime, @{ Label = "ComputerVersion"; Expression = { $_.computer.dsversion } }, @{ Label = "UserVersion"; Expression = { $_.user.dsversion } }
+
+<###########################
+ Dashboard
+############################>
+Write-Host "Analyse sur le rapport du tableau de bord ........................................ " -ForegroundColor Green -NoNewline
+
+$dte = (Get-Date).AddDays(- $ADModNumber)
+
+$ADObjs = Get-ADObject -Filter { whenchanged -gt $dte -and ObjectClass -ne "domainDNS" -and ObjectClass -ne "rIDManager" -and ObjectClass -ne "rIDSet" } -Properties *
+$Compteur = 0
+
+Foreach ($ADObj in $ADObjs) {
+ $Compteur++
+ Write-Progress -Id 0 -Activity "Analyse : " -Status "Processing $($Compteur) of $($ADObjs.count)" -CurrentOperation $ADObj -PercentComplete (($Compteur / $ADObjs.count) * 100)
+
+ If ($ADObj.ObjectClass -eq "GroupPolicyContainer") {
+ $Name = $ADObj.DisplayName
+ }
+ Else{
+ $Name = $ADObj.Name
+ }
+
+ $obj = [PSCustomObject]@{
+ "Name" = $Name
+ "Type Object" = $ADObj.ObjectClass
+ "Date changement" = $ADObj.WhenChanged
+ }
+ $ADObjectTable.Add($obj)
+}
+
+$ADRecycleBinStatus = (Get-ADOptionalFeature -Filter 'name -like "Recycle Bin Feature"').EnabledScopes
+
+If ($ADRecycleBinStatus.Count -lt 1) {
+ $ADRecycleBin = "Desactive"
+}
+Else {
+ $ADRecycleBin = "Actif"
+}
+
+#Company Information
+$ADInfo = Get-ADDomain
+$ForestObj = Get-ADForest
+$DomainControllerobj = Get-ADDomain
+$Forest = $ADInfo.Forest
+$InfrastructureMaster = $DomainControllerobj.InfrastructureMaster
+$RIDMaster = $DomainControllerobj.RIDMaster
+$PDCEmulator = $DomainControllerobj.PDCEmulator
+$DomainNamingMaster = $ForestObj.DomainNamingMaster
+$SchemaMaster = $ForestObj.SchemaMaster
+
+$obj = [PSCustomObject]@{
+ "Domaine" = $Forest
+ "Corbeille AD" = $ADRecycleBin
+ "Maitre de l'infrastructure" = $InfrastructureMaster
+ "Maitre RID" = $RIDMaster
+ "Emulateur PDC" = $PDCEmulator
+ "Maitre des noms de domaine" = $DomainNamingMaster
+ "Maitre Schema" = $SchemaMaster
+}
+
+$CompanyInfoTable.Add($obj)
+
+#Get newly created users
+$When = ((Get-Date).AddDays(- $UserCreatedDays)).Date
+$NewUsers = $AllUsers | Where-Object { $_.whenCreated -ge $When }
+
+Foreach ($Newuser in $Newusers) {
+ $obj = [PSCustomObject]@{
+ "Name" = $Newuser.Name
+ "Actif" = $Newuser.Enabled
+ "Date creation" = $Newuser.whenCreated
+ }
+ $NewCreatedUsersTable.Add($obj)
+}
+
+#Get Domain Admins
+$DomainAdminMembers = Get-ADGroupMember "Admins du domaine"
+
+Foreach ($DomainAdminMember in $DomainAdminMembers) {
+ $Name = $DomainAdminMember.Name
+ $Type = $DomainAdminMember.ObjectClass
+ $Enabled = ($AllUsers | Where-Object { $_.Name -eq $Name }).Enabled
+
+ $obj = [PSCustomObject]@{
+ "Name" = $Name
+ "Actif" = $Enabled
+ "Type" = $Type
+ }
+ $DomainAdminTable.Add($obj)
+}
+
+#Get Enterprise Admins
+$EnterpriseAdminsMembers = Get-ADGroupMember "Administrateurs de l’entreprise"
+
+If (($EnterpriseAdminsMembers).Count -eq 0) {
+ $EnterpriseAdminsMember = [PSCustomObject]@{
+ Information = "Informations : Aucun utilisateur n'a ete trouve dans le groupe Administrateurs de l’entreprise"
+ }
+}
+Else {
+ Foreach ($EnterpriseAdminsMember in $EnterpriseAdminsMembers) {
+ $Name = $EnterpriseAdminsMember.Name
+ $Type = $EnterpriseAdminsMember.ObjectClass
+ $Enabled = ($AllUsers | Where-Object { $_.Name -eq $Name }).Enabled
+
+ $obj = [PSCustomObject]@{
+ "Name" = $Name
+ "Actif" = $Enabled
+ "Type" = $Type
+ }
+ $EnterpriseAdminTable.Add($obj)
+ }
+}
+
+$DefaultComputersOU = (Get-ADDomain).computerscontainer
+$DefaultComputers = Get-ADComputer -Filter * -Properties * -SearchBase "$DefaultComputersOU"
+
+Foreach ($DefaultComputer in $DefaultComputers) {
+ $obj = [PSCustomObject]@{
+ "Name" = $DefaultComputer.Name
+ "Actif" = $DefaultComputer.Enabled
+ "Systeme d'exploitation" = $DefaultComputer.OperatingSystem
+ "Date modification" = $DefaultComputer.Modified
+ "Dernier mot de passe defini" = $DefaultComputer.PasswordLastSet
+ "Protege contre la suppression" = $DefaultComputer.ProtectedFromAccidentalDeletion
+ }
+ $DefaultComputersinDefaultOUTable.Add($obj)
+}
+
+$DefaultUsersOU = (Get-ADDomain).UsersContainer
+$DefaultUsers = $Allusers | Where-Object { $_.DistinguishedName -like "*$($DefaultUsersOU)" } | Select-Object Name, UserPrincipalName, Enabled, ProtectedFromAccidentalDeletion, EmailAddress, @{ Name = 'lastlogon'; Expression = { LastLogonConvert $_.lastlogon } }, DistinguishedName
+
+Foreach ($DefaultUser in $DefaultUsers) {
+ $obj = [PSCustomObject]@{
+ "Name" = $DefaultUser.Name
+ "UserPrincipalName" = $DefaultUser.UserPrincipalName
+ "Actif" = $DefaultUser.Enabled
+ "Protege contre la suppression" = $DefaultUser.ProtectedFromAccidentalDeletion
+ "Derniere connexion" = $DefaultUser.LastLogon
+ "Adresse mail" = $DefaultUser.EmailAddress
+ }
+ $DefaultUsersinDefaultOUTable.Add($obj)
+}
+
+#Expiring Accounts
+$LooseUsers = Search-ADAccount -AccountExpiring -UsersOnly
+
+Foreach ($LooseUser in $LooseUsers) {
+ $NameLoose = $LooseUser.Name
+ $UPNLoose = $LooseUser.UserPrincipalName
+ $ExpirationDate = $LooseUser.AccountExpirationDate
+ $enabled = $LooseUser.Enabled
+
+ $obj = [PSCustomObject]@{
+ "Name" = $NameLoose
+ "UserPrincipalName" = $UPNLoose
+ "Date expiration" = $ExpirationDate
+ "Actif" = $enabled
+ }
+ $ExpiringAccountsTable.Add($obj)
+}
+
+If (($ExpiringAccountsTable).Count -eq 0) {
+ $ExpiringAccountsTable = [PSCustomObject]@{
+ Information = "Informations : Aucun utilisateur n'expire bientot"
+ }
+}
+
+#Analyse journaux audti sécurité
+$SecurityLogs = Get-EventLog -Newest 7 -LogName "Security" | Where-Object { $_.Message -like "*An account*" }
+
+Foreach ($SecurityLog in $SecurityLogs) {
+ $TimeGenerated = $SecurityLog.TimeGenerated
+ $EntryType = $SecurityLog.EntryType
+ $Recipient = $SecurityLog.Message
+
+ $obj = [PSCustomObject]@{
+ "Time" = $TimeGenerated
+ "Type" = $EntryType
+ "Message" = $Recipient
+ }
+ $SecurityEventTable.Add($obj)
+}
+
+If (($securityeventtable).Count -eq 0) {
+ $securityeventtable = [PSCustomObject]@{
+ Information = "Informations : Aucun journal de securite recent"
+ }
+}
+
+#Tenant Domain
+$Domains = Get-ADForest | Select-Object -ExpandProperty upnsuffixes | ForEach-Object{
+ $obj = [PSCustomObject]@{
+ "UPN Suffixes" = $_
+ Valid = "True"
+ }
+ $DomainTable.Add($obj)
+}
+
+Write-Host "Terminé !" -ForegroundColor White
+
+Write-Progress -Id 0 -Activity "Analyse : " -completed
+
+<###########################
+ Groups
+############################>
+Write-Host "Rapport sur les groupes .......................................................... " -ForegroundColor Green -NoNewline
+
+#Get groups and sort in alphabetical order
+$Groups = Get-ADGroup -Filter * -Properties *
+$SecurityCount = 0
+$MailSecurityCount = 0
+$CustomGroup = 0
+$DefaultGroup = 0
+$Groupswithmemebrship = 0
+$Groupswithnomembership = 0
+$GroupsProtected = 0
+$GroupsNotProtected = 0
+$Compteur = 0
+
+Foreach ($Group in $Groups) {
+ $Compteur++
+ Write-Progress -Id 1 -Activity "Analyse : " -Status "Processing $($Compteur) of $($Groups.count)" -CurrentOperation $Group -PercentComplete (($Compteur / $Groups.count) * 100)
+ $DefaultADGroup = 'False'
+ $Type = New-Object 'System.Collections.Generic.List[System.Object]'
+ $Gemail = (Get-ADGroup $Group -Properties mail).mail
+
+ If (($group.GroupCategory -eq "Security") -and ($Gemail -ne $Null)) {
+ $MailSecurityCount++
+ }
+
+ If (($group.GroupCategory -eq "Security") -and (($Gemail) -eq $Null)) {
+ $SecurityCount++
+ }
+
+ If ($Group.ProtectedFromAccidentalDeletion -eq $True) {
+ $GroupsProtected++
+ }
+ Else {
+ $GroupsNotProtected++
+ }
+
+ If ($DefaultSGs -contains $Group.Name) {
+ $DefaultADGroup = "True"
+ $DefaultGroup++
+ }
+ Else {
+ $CustomGroup++
+ }
+
+ If ($group.GroupCategory -eq "Distribution") {
+ $Type = "Groupe de distribution"
+ }
+
+ If (($group.GroupCategory -eq "Security") -and (($Gemail) -eq $Null)) {
+ $Type = "Groupe de securite"
+ }
+
+ If (($group.GroupCategory -eq "Security") -and (($Gemail) -ne $Null)) {
+ $Type = "Groupe de securite active par courrier"
+ }
+
+ If ($Group.Name -ne "Domain Users") {
+ $Users = (Get-ADGroupMember -Identity $Group | Sort-Object DisplayName | Select-Object -ExpandProperty Name) -join ", "
+
+ If (!($Users)) {
+ $Groupswithnomembership++
+ }
+ Else {
+ $Groupswithmemebrship++
+ }
+ }
+ Else {
+ $Users = "Skipped Domain Users Membership"
+ }
+
+ $OwnerDN = Get-ADGroup -Filter { name -eq $Group.Name } -Properties managedBy | Select-Object -ExpandProperty ManagedBy
+ $Manager = $AllUsers | Where-Object { $_.distinguishedname -eq $OwnerDN } | Select-Object -ExpandProperty Name
+
+ $obj = [PSCustomObject]@{
+ "Name" = $Group.name
+ "Type" = $Type
+ "Membres" = $users
+ "Gere par" = $Manager
+ "Adresse mail" = $GEmail
+ "Protege contre la suppression" = $Group.ProtectedFromAccidentalDeletion
+ "Default AD Groupe" = $DefaultADGroup
+ }
+ $table.Add($obj)
+}
+
+If (($table).Count -eq 0) {
+ $table = [PSCustomObject]@{
+ Information = "Information : Aucun groupe n'a ete trouve"
+ }
+}
+
+#TOP groups table
+$obj1 = [PSCustomObject]@{
+ "Total Groupes" = $Groups.Count
+ "Groupes de securite compatibles avec la messagerie" = $MailSecurityCount
+ "Groupes de securite" = $SecurityCount
+ "Groupes de distribution" = $DistroCount
+}
+
+$TOPGroupsTable.Add($obj1)
+
+$obj1 = [PSCustomObject]@{
+ 'Name' = "Groupes de securite compatibles avec la messagerie"
+ 'Count' = $MailSecurityCount
+}
+
+$GroupTypetable.Add($obj1)
+
+$obj1 = [PSCustomObject]@{
+ 'Name' = "Groupes de securite"
+ 'Count' = $SecurityCount
+}
+
+$GroupTypetable.Add($obj1)
+$DistroCount = ($Groups | Where-Object { $_.GroupCategory -eq "Distribution" }).Count
+
+$obj1 = [PSCustomObject]@{
+ 'Name' = "Groupes de distribution"
+ 'Count' = $DistroCount
+}
+
+$GroupTypetable.Add($obj1)
+
+#Default Group Pie Chart
+$obj1 = [PSCustomObject]@{
+ 'Name' = "Groupes par defaut"
+ 'Count' = $DefaultGroup
+}
+
+$DefaultGrouptable.Add($obj1)
+
+$obj1 = [PSCustomObject]@{
+ 'Name' = "Groupes personnalises"
+ 'Count' = $CustomGroup
+}
+
+$DefaultGrouptable.Add($obj1)
+
+#Group Protection Pie Chart
+$obj1 = [PSCustomObject]@{
+ 'Name' = "Protege"
+ 'Count' = $GroupsProtected
+}
+
+$GroupProtectionTable.Add($obj1)
+
+$obj1 = [PSCustomObject]@{
+ 'Name' = "Non Protege"
+ 'Count' = $GroupsNotProtected
+}
+
+$GroupProtectionTable.Add($obj1)
+
+#Groups with membership vs no membership pie chart
+$objmem = [PSCustomObject]@{
+ 'Name' = "Avec les membres"
+ 'Count' = $Groupswithmemebrship
+}
+
+$GroupMembershipTable.Add($objmem)
+
+$objmem = [PSCustomObject]@{
+ 'Name' = "Aucun membre"
+ 'Count' = $Groupswithnomembership
+}
+
+$GroupMembershipTable.Add($objmem)
+
+Write-Host "Terminé !" -ForegroundColor White
+
+Write-Progress -Id 1 -Activity "Analyse : " -completed
+
+<###########################
+ Organizational Units
+############################>
+Write-Host "Analyse sur le rapport sur les unités organisation ............................... " -ForegroundColor Green -NoNewline
+
+#Get all OUs'
+$OUs = Get-ADOrganizationalUnit -Filter * -Properties *
+$OUwithLinked = 0
+$OUwithnoLink = 0
+$OUProtected = 0
+$OUNotProtected = 0
+$Compteur = 0
+
+Foreach ($OU in $OUs) {
+ $Compteur++
+ Write-Progress -Id 2 -Activity "Analyse : " -Status "Processing $($Compteur) of $($OUs.count)" -CurrentOperation $OU -PercentComplete (($Compteur / $OUs.count) * 100)
+ $LinkedGPOs = New-Object 'System.Collections.Generic.List[System.Object]'
+
+ If (($OU.linkedgrouppolicyobjects).length -lt 1) {
+ $LinkedGPOs = "None"
+ $OUwithnoLink++
+ }
+ Else {
+ $OUwithLinked++
+ $GPOslinks = $OU.linkedgrouppolicyobjects
+
+ Foreach ($GPOlink in $GPOslinks) {
+ $Split1 = $GPOlink -split "{" | Select-Object -Last 1
+ $Split2 = $Split1 -split "}" | Select-Object -First 1
+ $LinkedGPOs.Add((Get-GPO -Guid $Split2 -ErrorAction SilentlyContinue).DisplayName)
+ }
+ }
+
+ If ($OU.ProtectedFromAccidentalDeletion -eq $True) {
+ $OUProtected++
+ }
+ Else {
+ $OUNotProtected++
+ }
+
+ $LinkedGPOs = $LinkedGPOs -join ", "
+ $obj = [PSCustomObject]@{
+ "Name" = $OU.Name
+ "Linked GPOs" = $LinkedGPOs
+ "Date modification" = $OU.WhenChanged
+ "Protege contre la suppression" = $OU.ProtectedFromAccidentalDeletion
+ }
+ $OUTable.Add($obj)
+}
+
+If (($OUTable).Count -eq 0) {
+ $OUTable = [PSCustomObject]@{
+ Information = "Information : Aucune unite organisationnelle n'a ete trouvee"
+ }
+}
+
+#OUs with no GPO Linked
+$obj1 = [PSCustomObject]@{
+ 'Name' = "OU sans GPOs lies"
+ 'Count' = $OUwithnoLink
+}
+
+$OUGPOTable.Add($obj1)
+
+$obj2 = [PSCustomObject]@{
+ 'Name' = "OU avec GPO's lies"
+ 'Count' = $OUwithLinked
+}
+
+$OUGPOTable.Add($obj2)
+
+#OUs Protected Pie Chart
+$obj1 = [PSCustomObject]@{
+ 'Name' = "Protege"
+ 'Count' = $OUProtected
+}
+
+$OUProtectionTable.Add($obj1)
+
+$obj2 = [PSCustomObject]@{
+ 'Name' = "Non protege"
+ 'Count' = $OUNotProtected
+}
+
+$OUProtectionTable.Add($obj2)
+
+Write-Host "Terminé !" -ForegroundColor White
+
+Write-Progress -Id 2 -Activity "Analyse : " -completed
+
+<###########################
+ USERS
+############################>
+Write-Host "Analyse sur le rapport des utilisateurs .......................................... " -ForegroundColor Green -NoNewline
+
+$UserEnabled = 0
+$UserDisabled = 0
+$UserPasswordExpires = 0
+$UserPasswordNeverExpires = 0
+$ProtectedUsers = 0
+$NonProtectedUsers = 0
+$Compteur = 0
+
+$UsersWithPasswordsExpiringInUnderAWeek = 0
+$UsersNotLoggedInOver30Days = 0
+$AccountsExpiringSoon = 0
+
+Foreach ($User in $AllUsers) {
+ $Compteur++
+ Write-Progress -Id 3 -Activity "Analyse : " -Status "Processing $($Compteur) of $($AllUsers.count)" -CurrentOperation $User -PercentComplete (($Compteur / $AllUsers.count) * 100)
+
+ $AttVar = $User | Select-Object Enabled, PasswordExpired, PasswordLastSet, PasswordNeverExpires, PasswordNotRequired, Name, SamAccountName, EmailAddress, AccountExpirationDate, @{ Name = 'lastlogon'; Expression = { LastLogonConvert $_.lastlogon } }, DistinguishedName
+ $maxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
+
+ If ((($AttVar.PasswordNeverExpires) -eq $False) -and (($AttVar.Enabled) -ne $false)) {
+ #Get Password last set date
+ $passwordSetDate = ($User | ForEach-Object { $_.PasswordLastSet })
+
+ If ($null -eq $passwordSetDate) {
+ $daystoexpire = "Utilisateur ne s'est jamais connecte"
+ }
+ Else {
+ #Check for Fine Grained Passwords
+ $PasswordPol = (Get-ADUserResultantPasswordPolicy $user)
+
+ If (($PasswordPol) -ne $null) {
+ $maxPasswordAge = ($PasswordPol).MaxPasswordAge
+ }
+
+ $expireson = $passwordsetdate + $maxPasswordAge
+ $today = (Get-Date)
+
+ #Gets the count on how many days until the password expires and stores it in the $daystoexpire var
+ $daystoexpire = (New-TimeSpan -Start $today -End $Expireson).Days
+ }
+ }
+ Else {
+ $daystoexpire = "N/A"
+ }
+
+ #Get users that haven't logged on in X amount of days, var is set at start of script
+ If (($User.Enabled -eq $True) -and ($User.LastLogonDate -lt (Get-Date).AddDays(- $Days)) -and ($User.LastLogonDate -ne $NULL)) {
+ $obj = [PSCustomObject]@{
+ "Name" = $User.Name
+ "UserPrincipalName" = $User.UserPrincipalName
+ "Actif" = $AttVar.Enabled
+ "Protege contre la suppression" = $User.ProtectedFromAccidentalDeletion
+ "Derniere connexion" = $AttVar.lastlogon
+ "Mot de passe n'expire jamais" = $AttVar.PasswordNeverExpires
+ "Jours avant l'expiration du mot de passe" = $daystoexpire
+ }
+ $userphaventloggedonrecentlytable.Add($obj)
+ }
+
+ If (($userphaventloggedonrecentlytable).Count -eq 0) {
+ $userphaventloggedonrecentlytable = [PSCustomObject]@{
+ Information = "Information : Aucun utilisateur n'a ete trouve comme n'ayant pas ete connecte en $Days jours"
+ }
+ }
+
+ #Items for protected vs non protected users
+ If ($User.ProtectedFromAccidentalDeletion -eq $False) {
+ $NonProtectedUsers++
+ }
+ Else {
+ $ProtectedUsers++
+ }
+
+ #Items for the enabled vs disabled users pie chart
+ If (($AttVar.PasswordNeverExpires) -ne $false) {
+ $UserPasswordNeverExpires++
+ }
+ Else {
+ $UserPasswordExpires++
+ }
+
+ #Items for password expiration pie chart
+ If (($AttVar.Enabled) -ne $false) {
+ $UserEnabled++
+ }
+ Else {
+ $UserDisabled++
+ }
+
+ $Name = $User.Name
+ $UPN = $User.UserPrincipalName
+ $Enabled = $AttVar.Enabled
+ $EmailAddress = $AttVar.EmailAddress
+ $AccountExpiration = $AttVar.AccountExpirationDate
+ $PasswordExpired = $AttVar.PasswordExpired
+ $PasswordLastSet = $AttVar.PasswordLastSet
+ $PasswordNeverExpires = $AttVar.PasswordNeverExpires
+ $daysUntilPWExpire = $daystoexpire
+
+ $obj = [PSCustomObject]@{
+ "Name" = $Name
+ "UserPrincipalName" = $UPN
+ "Actif" = $Enabled
+ "Protege contre la suppression" = $User.ProtectedFromAccidentalDeletion
+ "Derniere connexion" = $LastLogon
+ "Adresse mail " = $EmailAddress
+ "Expiration du compte" = $AccountExpiration
+ "Changer du mot de passe Prochaine" = $PasswordExpired
+ "Dernier mot de passe defini" = $PasswordLastSet
+ "Mot de passe n'expire jamais" = $PasswordNeverExpires
+ "Jours avant l'expiration du mot de passe" = $daystoexpire
+ }
+
+ $usertable.Add($obj)
+
+ If ($daystoexpire -lt $DaysUntilPWExpireINT) {
+ $obj = [PSCustomObject]@{
+ 'Name' = $Name
+ "Jours avant l'expiration du mot de passe" = $daystoexpire
+ }
+ $PasswordExpireSoonTable.Add($obj)
+ }
+}
+
+If (($usertable).Count -eq 0) {
+ $usertable = [PSCustomObject]@{
+ Information = "Information : Aucun utilisateur n'a ete trouve"
+ }
+}
+
+#Data for users enabled vs disabled pie graph
+$objULic = [PSCustomObject]@{
+ 'Name' = "Actif"
+ 'Count' = $UserEnabled
+}
+
+$EnabledDisabledUsersTable.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ 'Name' = "Desactiver"
+ 'Count' = $UserDisabled
+}
+
+$EnabledDisabledUsersTable.Add($objULic)
+
+#Data for users password expires pie graph
+$objULic = [PSCustomObject]@{
+ 'Name' = "Expiration du mot de passe"
+ 'Count' = $UserPasswordExpires
+}
+
+$PasswordExpirationTable.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ 'Name' = "Le mot de passe n'expire jamais"
+ 'Count' = $UserPasswordNeverExpires
+}
+
+$PasswordExpirationTable.Add($objULic)
+
+#Data for protected users pie graph
+$objULic = [PSCustomObject]@{
+ 'Name' = "Protege"
+ 'Count' = $ProtectedUsers
+}
+
+$ProtectedUsersTable.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ 'Name' = "Non protege"
+ 'Count' = $NonProtectedUsers
+}
+
+$ProtectedUsersTable.Add($objULic)
+
+#TOP User table
+If (($ExpiringAccountsTable).Count -gt 0) {
+ $objULic = [PSCustomObject]@{
+ "Total des utilisateurs" = $AllUsers.Count
+ "Utilisateurs dont les mots de passe expirent dans moins de $DaysUntilPWExpireINT jours" = $PasswordExpireSoonTable.Count
+ "Comptes arrivant a expiration" = $ExpiringAccountsTable.Count
+ "Utilisateurs non connectes depuis $Days jours" = $userphaventloggedonrecentlytable.Count
+ }
+ $TOPUserTable.Add($objULic)
+}
+Else {
+ $objULic = [PSCustomObject]@{
+ "Total des utilisateurs" = $AllUsers.Count
+ "Utilisateurs dont les mots de passe expirent dans moins de $DaysUntilPWExpireINT jours" = $PasswordExpireSoonTable.Count
+ "Comptes arrivant a expiration" = "0"
+ "Utilisateurs non connectes depuis $Days jours" = $userphaventloggedonrecentlytable.Count
+ }
+ $TOPUserTable.Add($objULic)
+}
+
+#Get-ADReplAccount -All -Server $env:ComputerName -NamingContext $(Get-ADDomain | select -ExpandProperty DistinguishedName) | Test-PasswordQuality -IncludeDisabledAccounts
+
+
+Write-Host "Terminé !" -ForegroundColor White
+
+Write-Progress -Id 3 -Activity "Done" -completed
+
+<###########################
+ Group Policy Object
+############################>
+Write-Host "Rapport sur les GPOs ............................................................. " -ForegroundColor Green -NoNewline
+
+$Compteur = 0
+
+$GPOTable = New-Object 'System.Collections.Generic.List[System.Object]'
+
+Foreach ($GPO in $GPOs) {
+ $Compteur++
+ Write-Progress -Id 4 -Activity "Analyse : " -Status "Processing $($Compteur) of $($GPOs.count)" -CurrentOperation $GPO -PercentComplete (($Compteur / $GPOs.count) * 100)
+
+ $obj = [PSCustomObject]@{
+ "Name" = $GPO.DisplayName
+ "Status" = $GPO.GpoStatus
+ "Date modification" = $GPO.ModificationTime
+ "User Version" = $GPO.UserVersion
+ "Computer Version" = $GPO.ComputerVersion
+ }
+
+ $GPOTable.Add($obj)
+}
+Write-Host "Terminé !" -ForegroundColor White
+
+Write-Progress -Id 4 -Activity "Done" -completed
+
+<###########################
+ Computers
+############################>
+Write-Host "Rapport sur le travail sur ordinateur ............................................ " -ForegroundColor Green -NoNewline
+
+$Computers = Get-ADComputer -Filter * -Properties *
+$ComputersProtected = 0
+$ComputersNotProtected = 0
+$ComputerEnabled = 0
+$ComputerDisabled = 0
+$Server2022 = 0
+$Server2019 = 0
+$Server2016 = 0
+$Server2012 = 0
+$Server2012R2 = 0
+$Server2008R2 = 0
+$Windows11 = 0
+$Windows10 = 0
+$Windows8 = 0
+$Windows7 = 0
+$Server2012R2 = 0
+$Compteur = 0
+
+Foreach ($Computer in $Computers) {
+ $Compteur++
+ Write-Progress -Id 5 -Activity "Analyse : " -Status "Processing $($Compteur) of $($Computers.count)" -CurrentOperation $Computer -PercentComplete (($Compteur / $Computers.count) * 100)
+
+ If ($Computer.ProtectedFromAccidentalDeletion -eq $True) {
+ $ComputersProtected++
+ }
+ Else {
+ $ComputersNotProtected++
+ }
+
+ If ($Computer.Enabled -eq $True) {
+ $ComputerEnabled++
+ }
+ Else {
+ $ComputerDisabled++
+ }
+
+ $obj = [PSCustomObject]@{
+ "ComputerName" = $Computer.Name
+ "Actif" = $Computer.Enabled
+ "Systeme d'exploitation" = $Computer.OperatingSystem
+ "Description" = $Computer.Description
+ "Date modification" = $Computer.Modified
+ "Date dernier mot de passe defini" = $Computer.PasswordLastSet
+ "Protege contre la suppression" = $Computer.ProtectedFromAccidentalDeletion
+ }
+
+ $ComputersTable.Add($obj)
+
+ If ($Computer.OperatingSystem -like "*Server 2022*") {
+ $Server2022++
+ }
+ Elseif ($Computer.OperatingSystem -like "*Server 2019*") {
+ $Server2019++
+ }
+ Elseif ($Computer.OperatingSystem -like "*Server 2016*") {
+ $Server2016++
+ }
+ Elseif ($Computer.OperatingSystem -like "*Server 2012 R2*") {
+ $Server2012R2++
+ }
+ Elseif ($Computer.OperatingSystem -like "*Server 2012*") {
+ $Server2012++
+ }
+ Elseif ($Computer.OperatingSystem -like "*Server 2008 R2*") {
+ $Server2008R2++
+ }
+ Elseif ($Computer.OperatingSystem -like "*Windows 11*") {
+ $Windows11++
+ }
+ Elseif ($Computer.OperatingSystem -like "*Windows 10*") {
+ $Windows10++
+ }
+ Elseif ($Computer.OperatingSystem -like "*Windows 8*") {
+ $Windows8++
+ }
+ Elseif ($Computer.OperatingSystem -like "*Windows 7*") {
+ $Windows7++
+ }
+}
+
+If (($ComputersTable).Count -eq 0) {
+ $ComputersTable = [PSCustomObject]@{
+ Information = "Information : Aucun ordinateur n'a ete trouve"
+ }
+}
+
+#Data for TOP Computers data table
+$objULic = [PSCustomObject]@{
+ "Total Ordinateurs" = $Computers.Count
+ "Serveur 2022" = $Server2022
+ "Serveur 2019" = $Server2019
+ "Serveur 2016" = $Server2016
+ "Serveur 2012 R2" = $Server2012R2
+ "Serveur 2012" = $Server2012
+ "Serveur 2008 R2" = $Server2008R2
+ "Windows 11" = $Windows11
+ "Windows 10" = $Windows10
+ "Windows 8" = $Windows8
+ "Windows 7" = $Windows7
+}
+
+$TOPComputersTable.Add($objULic)
+
+#Pie chart breaking down OS for computer obj
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Serveur 2022"
+ "Count" = $Server2022
+}
+
+$GraphComputerOS.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Serveur 2019"
+ "Count" = $Server2019
+}
+
+$GraphComputerOS.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Serveur 2016"
+ "Count" = $Server2016
+}
+
+$GraphComputerOS.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Serveur 2012 R2"
+ "Count" = $Server2012R2
+}
+
+$GraphComputerOS.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Serveur 2012"
+ "Count" = $Server2012
+}
+
+$GraphComputerOS.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Serveur 2008 R2"
+ "Count" = $Server2008R2
+}
+
+$GraphComputerOS.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Windows 11"
+ "Count" = $Windows11
+}
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Windows 10"
+ "Count" = $Windows10
+}
+
+$GraphComputerOS.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Windows 8"
+ "Count" = $Windows8
+}
+
+$GraphComputerOS.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Windows 7"
+ "Count" = $Windows7
+}
+
+$GraphComputerOS.Add($objULic)
+
+#Data for protected Computers pie graph
+$objULic = [PSCustomObject]@{
+ "Name" = "Protege"
+ "Count" = $ComputerProtected
+}
+
+$ComputerProtectedTable.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Non protege"
+ "Count" = $ComputersNotProtected
+}
+
+$ComputerProtectedTable.Add($objULic)
+
+#Data for enabled/vs Computers pie graph
+$objULic = [PSCustomObject]@{
+ "Name" = "Actif"
+ "Count" = $ComputerEnabled
+}
+
+$ComputersEnabledTable.Add($objULic)
+
+$objULic = [PSCustomObject]@{
+ "Name" = "Desactiver"
+ "Count" = $ComputerDisabled
+}
+
+$ComputersEnabledTable.Add($objULic)
+
+Write-Host "Terminé !" -ForegroundColor White
+
+Write-Progress -Id 5 -Activity "Done" -completed
+
+$tabarray = @("Tableau de bord", "Groupes", "Unites d Organisation", "Utilisateurs", "Groupe Police Objet", "Ordinateurs")
+
+Write-Host "Compilation du rapport ........................................................... " -ForegroundColor Green -NoNewline
+
+##--OU Protection PIE CHART--##
+#Basic Properties
+$PO12 = Get-HTMLPieChartObject
+$PO12.Title = "Unites organisation protegees contre la suppression"
+$PO12.Size.Height = 250
+$PO12.Size.width = 250
+$PO12.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PO12.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PO12.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PO12.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PO12.DataDefinition.DataNameColumnName = 'Name'
+$PO12.DataDefinition.DataValueColumnName = 'Count'
+
+##--Computer OS Breakdown PIE CHART--##
+$PieObjectComputerObjOS = Get-HTMLPieChartObject
+$PieObjectComputerObjOS.Title = "Computer Operating Systems"
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectComputerObjOS.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectComputerObjOS.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectComputerObjOS.ChartStyle.ColorSchemeName = 'Random'
+
+##--Computers Protection PIE CHART--##
+#Basic Properties
+$PieObjectComputersProtected = Get-HTMLPieChartObject
+$PieObjectComputersProtected.Title = "Ordinateurs proteges contre la suppression"
+$PieObjectComputersProtected.Size.Height = 250
+$PieObjectComputersProtected.Size.width = 250
+$PieObjectComputersProtected.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectComputersProtected.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectComputersProtected.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectComputersProtected.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectComputersProtected.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectComputersProtected.DataDefinition.DataValueColumnName = 'Count'
+
+##--Computers Enabled PIE CHART--##
+#Basic Properties
+$PieObjectComputersEnabled = Get-HTMLPieChartObject
+$PieObjectComputersEnabled.Title = "Ordinateurs actives et desactives"
+$PieObjectComputersEnabled.Size.Height = 250
+$PieObjectComputersEnabled.Size.width = 250
+$PieObjectComputersEnabled.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectComputersEnabled.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectComputersEnabled.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectComputersEnabled.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectComputersEnabled.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectComputersEnabled.DataDefinition.DataValueColumnName = 'Count'
+
+##--USERS Protection PIE CHART--##
+#Basic Properties
+$PieObjectProtectedUsers = Get-HTMLPieChartObject
+$PieObjectProtectedUsers.Title = "Utilisateurs proteges contre la suppression"
+$PieObjectProtectedUsers.Size.Height = 250
+$PieObjectProtectedUsers.Size.width = 250
+$PieObjectProtectedUsers.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectProtectedUsers.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectProtectedUsers.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectProtectedUsers.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectProtectedUsers.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectProtectedUsers.DataDefinition.DataValueColumnName = 'Count'
+
+#Basic Properties
+$PieObjectOUGPOLinks = Get-HTMLPieChartObject
+$PieObjectOUGPOLinks.Title = "Liens OU/GPO"
+$PieObjectOUGPOLinks.Size.Height = 250
+$PieObjectOUGPOLinks.Size.width = 250
+$PieObjectOUGPOLinks.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectOUGPOLinks.ChartStyle.ColorSchemeName = "ColorScheme4"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectOUGPOLinks.ChartStyle.ColorSchemeName = "Generated5"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectOUGPOLinks.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectOUGPOLinks.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectOUGPOLinks.DataDefinition.DataValueColumnName = 'Count'
+
+#Basic Properties
+$PieObject4 = Get-HTMLPieChartObject
+$PieObject4.Title = "Office 365 Unassigned Licenses"
+$PieObject4.Size.Height = 250
+$PieObject4.Size.width = 250
+$PieObject4.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObject4.ChartStyle.ColorSchemeName = "ColorScheme4"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObject4.ChartStyle.ColorSchemeName = "Generated4"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObject4.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObject4.DataDefinition.DataNameColumnName = 'Name'
+$PieObject4.DataDefinition.DataValueColumnName = 'Unassigned Licenses'
+
+#Basic Properties
+$PieObjectGroupType = Get-HTMLPieChartObject
+$PieObjectGroupType.Title = "Types de groupes"
+$PieObjectGroupType.Size.Height = 250
+$PieObjectGroupType.Size.width = 250
+$PieObjectGroupType.ChartStyle.ChartType = 'doughnut'
+
+#Pie Chart Groups with members vs no members
+$PieObjectGroupMembersType = Get-HTMLPieChartObject
+$PieObjectGroupMembersType.Title = "Adhesion au groupe"
+$PieObjectGroupMembersType.Size.Height = 250
+$PieObjectGroupMembersType.Size.width = 250
+$PieObjectGroupMembersType.ChartStyle.ChartType = 'doughnut'
+$PieObjectGroupMembersType.ChartStyle.ColorSchemeName = "ColorScheme4"
+$PieObjectGroupMembersType.ChartStyle.ColorSchemeName = "Generated8"
+$PieObjectGroupMembersType.ChartStyle.ColorSchemeName = 'Random'
+$PieObjectGroupMembersType.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectGroupMembersType.DataDefinition.DataValueColumnName = 'Count'
+
+#Basic Properties
+$PieObjectGroupType2 = Get-HTMLPieChartObject
+$PieObjectGroupType2.Title = "Groupes personnalises et groupes par defaut"
+$PieObjectGroupType2.Size.Height = 250
+$PieObjectGroupType2.Size.width = 250
+$PieObjectGroupType2.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectGroupType.ChartStyle.ColorSchemeName = "ColorScheme4"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectGroupType.ChartStyle.ColorSchemeName = "Generated8"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectGroupType.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectGroupType.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectGroupType.DataDefinition.DataValueColumnName = 'Count'
+
+##--Enabled users vs Disabled Users PIE CHART--##
+#Basic Properties
+$EnabledDisabledUsersPieObject = Get-HTMLPieChartObject
+$EnabledDisabledUsersPieObject.Title = "Utilisateurs actives et desactives"
+$EnabledDisabledUsersPieObject.Size.Height = 250
+$EnabledDisabledUsersPieObject.Size.width = 250
+$EnabledDisabledUsersPieObject.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$EnabledDisabledUsersPieObject.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$EnabledDisabledUsersPieObject.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$EnabledDisabledUsersPieObject.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$EnabledDisabledUsersPieObject.DataDefinition.DataNameColumnName = 'Name'
+$EnabledDisabledUsersPieObject.DataDefinition.DataValueColumnName = 'Count'
+
+##--PasswordNeverExpires PIE CHART--##
+#Basic Properties
+$PWExpiresUsersTable = Get-HTMLPieChartObject
+$PWExpiresUsersTable.Title = "Expiration Mot De Passe"
+$PWExpiresUsersTable.Size.Height = 250
+$PWExpiresUsersTable.Size.Width = 250
+$PWExpiresUsersTable.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PWExpiresUsersTable.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PWExpiresUsersTable.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PWExpiresUsersTable.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PWExpiresUsersTable.DataDefinition.DataNameColumnName = 'Name'
+$PWExpiresUsersTable.DataDefinition.DataValueColumnName = 'Count'
+
+##--Group Protection PIE CHART--##
+#Basic Properties
+$PieObjectGroupProtection = Get-HTMLPieChartObject
+$PieObjectGroupProtection.Title = "Groupes proteges contre la suppression"
+$PieObjectGroupProtection.Size.Height = 250
+$PieObjectGroupProtection.Size.width = 250
+$PieObjectGroupProtection.ChartStyle.ChartType = 'doughnut'
+
+#These file exist in the module directoy, There are 4 schemes by default
+$PieObjectGroupProtection.ChartStyle.ColorSchemeName = "ColorScheme3"
+
+#There are 8 generated schemes, randomly generated at runtime
+$PieObjectGroupProtection.ChartStyle.ColorSchemeName = "Generated3"
+
+#you can also ask for a random scheme. Which also happens ifyou have too many records for the scheme
+$PieObjectGroupProtection.ChartStyle.ColorSchemeName = 'Random'
+
+#Data defintion you can reference any column from name and value from the dataset.
+#Name and Count are the default to work with the Group function.
+$PieObjectGroupProtection.DataDefinition.DataNameColumnName = 'Name'
+$PieObjectGroupProtection.DataDefinition.DataValueColumnName = 'Count'
+
+#Tableau de bord Report
+$FinalReport = New-Object 'System.Collections.Generic.List[System.Object]'
+$FinalReport.Add($(Get-HTMLOpenPage -TitleText $ReportTitle -LeftLogoString $CompanyLogo -RightLogoString $CompanyLogo))
+$FinalReport.Add($(Get-HTMLTabHeader -TabNames $tabarray))
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[0] -TabHeading ("Rapport: " + (Get-Date -Format dd-MM-yyyy))))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Informations sur l'entreprise"))
+$FinalReport.Add($(Get-HTMLContentTable $CompanyInfoTable))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Groupes"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Administrateurs de domaines"))
+$FinalReport.Add($(Get-HTMLContentDataTable $DomainAdminTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Administrateurs d'entreprise"))
+$FinalReport.Add($(Get-HTMLContentDataTable $EnterpriseAdminTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Objets dans les OU par defaut"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Ordinateurs"))
+$FinalReport.Add($(Get-HTMLContentDataTable $DefaultComputersinDefaultOUTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Utilisateurs"))
+$FinalReport.Add($(Get-HTMLContentDataTable $DefaultUsersinDefaultOUTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Objets AD modifies en dernier $ADModNumber jours"))
+$FinalReport.Add($(Get-HTMLContentDataTable $ADObjectTable))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Objets en voie d'expiration"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Utilisateurs dont les mots de passe expirent dans $DaysUntilPWExpireINT jours"))
+$FinalReport.Add($(Get-HTMLContentDataTable $PasswordExpireSoonTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Comptes expirant bientot"))
+$FinalReport.Add($(Get-HTMLContentDataTable $ExpiringAccountsTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Comptes"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Utilisateurs non connectes depuis $Days jours"))
+$FinalReport.Add($(Get-HTMLContentDataTable $userphaventloggedonrecentlytable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Comptes crees en $UserCreatedDays jours"))
+$FinalReport.Add($(Get-HTMLContentDataTable $NewCreatedUsersTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Journaux de securite"))
+$FinalReport.Add($(Get-HTMLContentDataTable $securityeventtable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "UPN Suffixes"))
+$FinalReport.Add($(Get-HTMLContentTable $DomainTable))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLTabContentClose))
+
+#Groups Report
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[1] -TabHeading ("Rapport: " + (Get-Date -Format dd-MM-yyyy))))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Apercu des groupes"))
+$FinalReport.Add($(Get-HTMLContentTable $TOPGroupsTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Groupes Active Directory"))
+$FinalReport.Add($(Get-HTMLContentDataTable $Table -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Administrateurs de domaines"))
+$FinalReport.Add($(Get-HTMLContentDataTable $DomainAdminTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Administrateurs d'entreprise"))
+$FinalReport.Add($(Get-HTMLContentDataTable $EnterpriseAdminTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Graphiques Groupes Active Directory"))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 1 -ColumnCount 4))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectGroupType -DataSet $GroupTypetable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 2 -ColumnCount 4))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectGroupType2 -DataSet $DefaultGrouptable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 3 -ColumnCount 4))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectGroupMembersType -DataSet $GroupMembershipTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 4 -ColumnCount 4))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectGroupProtection -DataSet $GroupProtectionTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLTabContentClose))
+
+#Organizational Unit Report
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[2] -TabHeading ("Rapport: " + (Get-Date -Format dd-MM-yyyy))))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Unites organisations"))
+$FinalReport.Add($(Get-HTMLContentDataTable $OUTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Graphiques unites organisations"))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 1 -ColumnCount 2))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectOUGPOLinks -DataSet $OUGPOTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 2 -ColumnCount 2))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PO12 -DataSet $OUProtectionTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentclose))
+$FinalReport.Add($(Get-HTMLTabContentClose))
+
+#Users Report
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[3] -TabHeading ("Rapport: " + (Get-Date -Format dd-MM-yyyy))))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Apercu des utilisateurs"))
+$FinalReport.Add($(Get-HTMLContentTable $TOPUserTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Utilisateurs d'Active Directory"))
+$FinalReport.Add($(Get-HTMLContentDataTable $UserTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Groupe de mot de passe"))
+$FinalReport.Add($(Get-HTMLContentDataTable $$UserPasswordTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Articles en voie d'expiration"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Utilisateurs dont les mots de passe expirent dans $DaysUntilPWExpireINT jours"))
+$FinalReport.Add($(Get-HTMLContentDataTable $PasswordExpireSoonTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Comptes expirant bientot"))
+$FinalReport.Add($(Get-HTMLContentDataTable $ExpiringAccountsTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Comptes"))
+$FinalReport.Add($(Get-HTMLColumn1of2))
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Utilisateurs non connectes depuis $Days jours"))
+$FinalReport.Add($(Get-HTMLContentDataTable $userphaventloggedonrecentlytable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumn2of2))
+
+$FinalReport.Add($(Get-HTMLContentOpen -BackgroundShade 1 -HeaderText "Comptes crees en $UserCreatedDays jours"))
+$FinalReport.Add($(Get-HTMLContentDataTable $NewCreatedUsersTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Graphiques utilisateurs"))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 1 -ColumnCount 3))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $EnabledDisabledUsersPieObject -DataSet $EnabledDisabledUsersTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 2 -ColumnCount 3))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PWExpiresUsersTable -DataSet $PasswordExpirationTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 3 -ColumnCount 3))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectProtectedUsers -DataSet $ProtectedUsersTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLTabContentClose))
+
+#GPO Report
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[4] -TabHeading ("Rapport: " + (Get-Date -Format dd-MM-yyyy))))
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Police Groupe Objet"))
+$FinalReport.Add($(Get-HTMLContentDataTable $GPOTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+$FinalReport.Add($(Get-HTMLTabContentClose))
+
+#Computers Report
+$FinalReport.Add($(Get-HTMLTabContentopen -TabName $tabarray[5] -TabHeading ("Rapport: " + (Get-Date -Format dd-MM-yyyy))))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Apercu des ordinateurs"))
+$FinalReport.Add($(Get-HTMLContentTable $TOPComputersTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Ordinateurs"))
+$FinalReport.Add($(Get-HTMLContentDataTable $ComputersTable -HideFooter))
+$FinalReport.Add($(Get-HTMLContentClose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Graphiques d'ordinateurs"))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 1 -ColumnCount 2))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectComputersProtected -DataSet $ComputerProtectedTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLColumnOpen -ColumnNumber 2 -ColumnCount 2))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectComputersEnabled -DataSet $ComputersEnabledTable))
+$FinalReport.Add($(Get-HTMLColumnClose))
+$FinalReport.Add($(Get-HTMLContentclose))
+
+$FinalReport.Add($(Get-HTMLContentOpen -HeaderText "Repartition des systemes d'exploitation des ordinateurs"))
+$FinalReport.Add($(Get-HTMLPieChart -ChartObject $PieObjectComputerObjOS -DataSet $GraphComputerOS))
+$FinalReport.Add($(Get-HTMLContentclose))
+
+$FinalReport.Add($(Get-HTMLTabContentClose))
+$FinalReport.Add($(Get-HTMLClosePage))
+
+$ReportName = ("$Day-$Month-$Year-$Hour-$Minute-AD Report")
+
+#Save-HTMLReport -ReportContent $FinalReport -ShowReport -ReportName $ReportName -ReportPath $ReportSavePath
+Add-Content $ReportSavePath$ReportName".html" $FinalReport
+
+Write-Host "Terminé !" -ForegroundColor White
+
+#---------------------------------------------------------[Fin Du Script]----------------------------------------------------------
+
+Stop-Transcript
\ No newline at end of file
diff --git a/Get-AdComputerInventory.ps1 b/Get-AdComputerInventory.ps1
new file mode 100644
index 0000000..d4bb7e5
--- /dev/null
+++ b/Get-AdComputerInventory.ps1
@@ -0,0 +1,50 @@
+$Computers = Get-ADComputer -filter * | Select-Object -ExpandProperty Name
+# Get-ADComputer -Filter { OperatingSystem -NotLike '*Server*' } -Properties OperatingSystem
+
+Foreach ($computer in $computers) {
+
+ if (!(Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet)) {
+ write-host "cannot reach $computer offline" -f red
+ }
+ else {
+ $outtbl = @()
+ Try {
+ $sr = Get-WmiObject win32_bios -ComputerName $Computer -ErrorAction Stop
+ $Xr = Get-WmiObject –class Win32_processor -ComputerName $computer -ErrorAction Stop
+ $ld = get-adcomputer $computer -properties Name, Lastlogondate, operatingsystem, ipv4Address, enabled, description, DistinguishedName -ErrorAction Stop
+ $r = "{0} GB" -f ((Get-WmiObject Win32_PhysicalMemory -ComputerName $computer | Measure-Object Capacity -Sum).Sum / 1GB)
+ $x = gwmi win32_computersystem -ComputerName $computer | Select-Object @{Name = "Type"; Expression = { if (($_.pcsystemtype -eq '2') ) {
+ 'Laptop'
+ }
+ Else {
+ 'Desktop Or Other something else'
+ }
+ }
+ }, Manufacturer, @{Name = "Model"; Expression = { if (($_.model -eq "$null") ) { 'Virtual' } Else { $_.model } } }, username -ErrorAction Stop
+ $t = New-Object PSObject -Property @{
+ serialnumber = $sr.serialnumber
+ computername = $ld.name
+ Ipaddress = $ld.ipv4Address
+ Enabled = $ld.Enabled
+ Description = $ld.description
+ Ou = $ld.DistinguishedName.split(',')[1].split('=')[1]
+ Type = $x.type
+ Manufacturer = $x.Manufacturer
+ Model = $x.Model
+ Ram = $R
+ ProcessorName = ($xr.name | Out-String).Trim()
+ NumberOfCores = ($xr.NumberOfCores | Out-String).Trim()
+ NumberOfLogicalProcessors = ($xr.NumberOfLogicalProcessors | Out-String).Trim()
+ Addresswidth = ($xr.Addresswidth | Out-String).Trim()
+ Operatingsystem = $ld.operatingsystem
+ Lastlogondate = $ld.lastlogondate
+ LoggedinUser = $x.username
+ }
+ $outtbl += $t
+ }
+ catch [Exception] {
+ "Error communicating with $computer, skipping to next"
+ }
+ $outtbl | Select-Object Computername, enabled, description, ipAddress, Ou, Type, Serialnumber, Manufacturer, Model, Ram, ProcessorName, NumberOfCores, NumberOfLogicalProcessors, Addresswidth, Operatingsystem, loggedinuser, Lastlogondate
+ }
+}
\ No newline at end of file
diff --git a/creation-automatique.ps1 b/creation-automatique.ps1
new file mode 100644
index 0000000..8bfa764
--- /dev/null
+++ b/creation-automatique.ps1
@@ -0,0 +1,409 @@
+# Fonction pour les requetes SQL
+Function QuerySQLServer([string]$DBServer, [string]$DBName, [string]$Query) {
+ Try {
+ $ErrorActionPreference = "Stop"
+
+ $resultsDataTable = New-Object System.Data.DataTable
+
+ $cn = new-object System.Data.SqlClient.SqlConnection("Data Source=$DBServer;Integrated Security=SSPI;Initial Catalog=$DBName")
+ $cn.open()
+
+ $cmd = new-object "System.Data.SqlClient.SqlCommand" ($Query , $cn)
+ $reader = $cmd.ExecuteReader()
+
+ $resultsDataTable.Load($reader)
+
+ $cn.Close()
+
+ return $resultsDataTable
+ }
+ Catch {
+ write-host $_.Exception.Message
+ $_.Exception.Message >> "d:\tmp\error.log"
+ }
+ Finally {
+ $ErrorActionPreference = "Continue"
+ }
+}
+
+# Fonction pour les requetes dans GLPI
+Function QueryGLPI([string]$Titre, [string]$Message, [string]$Categorie) {
+ Write-host $Titre
+ Write-host $Message
+ Write-host $Categorie
+
+ $UtilisateurGLPI = Get-GlpiToolsUsers -UserName helpdesk
+
+ $Ticket = New-GlpiToolsTicket -Name $Titre -Content $Message -Type Request -itilcategories_id $Categorie -Priority Medium -requester_id $UtilisateurGLPI.ID
+
+ Update-GlpiToolsTicket -ticket_id $Ticket.id -requester_id $UtilisateurGLPI.ID -technician_id '12'
+}
+
+cls
+
+$ListeException = $null
+$ListeVide = $null
+$FichierException = "D:\tmp\Exception.txt"
+$FichierVide = "D:\tmp\vide.txt"
+$percentCompleteA = $null
+$percentCompleteB = $null
+$CounterA = $null
+$CounterB = $null
+$EtatUtilisateur = $null
+
+$DateDesactivation = Get-Date -Format "dd/MM/yyyy"
+$DateExpiration = (Get-Date $DateDesactivation).AddDays(-1)
+$DateDelete = (Get-Date $DateDesactivation).AddDays(-30)
+
+$DateTmp = (Get-Date).AddDays(-1)
+$DateCreation = Get-Date $DateTmp -Format "yyyy-dd-MM"
+
+Start-Transcript -path "D:\tmp\MyTranscript-$DateCreation.txt"
+
+$OuPath = "OU=Utilisateurs,DC=fr,DC=dgs,DC=group"
+$ListeUserAD = Get-ADUser -Filter * -SearchBase $OuPath -Properties cn,description,sAMAccountName
+
+## Desactivation ou Update de compte
+
+Foreach ($UserAD in $ListeUserAD) {
+ # Barre de progression
+ $percentCompleteA = $(($CounterA / $ListeUserAD.Count) * 100 )
+
+ $ProgressA = @{
+ Activity = "Getting folder information for '$($UserAD.Name)'."
+ Status = "Processing $CounterA of $($ListeUserAD.Count)"
+ PercentComplete = $([math]::Round($percentCompleteA, 2))
+ }
+
+ Write-Progress @ProgressA -Id 1
+
+ # recherche inforamtiopn 1 utilisateur
+ $InfoUser = Get-ADUser -identity $UserAD.sAMAccountName -properties 'msDS-cloudExtensionAttribute1','msDS-cloudExtensionAttribute2',mail,employeeID,EmployeeNumber,EmployeeType,Title,Initials
+
+ $C = $UserAD.C
+ $City = $UserAD.City
+ $Cn = $UserAD.Cn
+ $Company = $UserAD.Company
+ $department = $UserAD.Department
+ $displayName = $UserAD.DisplayName
+ $EmailAddress = $UserAD.EmailAddress
+ $Enable = $UserAD.Enabled
+ $GivenName = $UserAD.GivenName
+ $Name = $UserAD.Name
+ $Office = $UserAD.Office
+ $Organization = $UserAD.Organization
+ $PostalCode = $UserAD.PostalCode
+ $sn = $UserAD.Sn
+ $Surname = $UserAD.Surname
+
+ $ExtensionAttribute1 = $InfoUser.'msDS-cloudExtensionAttribute1'
+ $ExtensionAttribute2 = $InfoUser.'msDS-cloudExtensionAttribute2'
+ $Mail = $InfoUser.Mail
+ $EmployeeID = $InfoUser.employeeID
+ $EmployeeNumber = $InfoUser.EmployeeNumber
+ $EmployeeType = $InfoUser.EmployeeType
+ $Title = $InfoUser.Title
+ $Initials = $InfoUser.Initials
+
+ # Est-ce qu'il est actif
+ If ($Enable -eq "True") {
+ # Est-ce qu'il est interne
+ If($EmployeeType -eq "Interne") {
+ # Est-ce qu'il a un ID
+ If ($EmployeeID) {
+ # Si l'employeeID est bien présent
+ $EtatUtilisateurD = QuerySQLServer "SWDHBBDDP01.fr.dgs.group" "dhb_prd" "SELECT NOM,PRN,NOM_PAT,LIB_EMP,ADR_EML,MTR,NUM_ALC,COD_SEX,FLG_AGT,DAT_SRT FROM [dhb_prd].[dhbref].[TAB_RH_SAL] WHERE MTR = '$EmployeeID' And DAT_SRT IS NOT NULL"
+ $EtatUtilisateurU = QuerySQLServer "SWDHBBDDP01.fr.dgs.group" "dhb_prd" "SELECT NOM,PRN,NOM_PAT,LIB_EMP,ADR_EML,MTR,NUM_ALC,COD_SEX,FLG_AGT,DAT_SRT FROM [dhb_prd].[dhbref].[TAB_RH_SAL] WHERE MTR = '$EmployeeID'"
+
+ # Desactivation du compte ?
+ If ($EtatUtilisateurD) {
+ If ($DateExpiration -gt $EtatUtilisateurD.DAT_SRT) {
+
+ Get-ADUser -Identity $UserAD.sAMAccountName | Move-ADObject -TargetPath "OU=_A_SUPPRIMER,OU=Utilisateurs,DC=fr,DC=dgs,DC=group"
+ Set-ADAccountExpiration -Identity $UserAD.sAMAccountName -DateTime $dateExpiration
+ Set-ADUser -Identity $Utilisateur -Clear msDS-cloudExtensionAttribute2
+ Set-ADUser -Identity $UserAD.sAMAccountName -Add @{'msDS-cloudExtensionAttribute2' = "$DateExpiration"}
+ Disable-ADAccount -Identity $UserAD.sAMAccountName
+
+ $TitreA = "Désactivation du compte Active Directory : $UserAD.sAMAccountName "
+
+ $MessageA = "Bonjour, `r`n `r`n" `
+ +"Nous venons de désactiver le compte de : $DisplayName `r`n `r`n" `
+ +"Prénom : $GivenName `r`n" `
+ +"Nom : $Surname `r`n" `
+ +"Mail : $Mail `r`n" `
+ +"Matricule : $EmployeeID `r`n" `
+ +"Fonction : $Title `r`n" `
+ +"`r`n" `
+ +"Conformément à la politique entreprise le compte restera en état désactivé pendant 30 jours avant d'être supprimé `r`n" `
+ +"La suppression du compte GMAIL se fait également en automatique."
+
+ QueryGLPI $TitreA $MessageA "213"
+ }
+ }
+
+ # Mise en place de la civilité
+ If ($EtatUtilisateurU) {
+ If (($Initials -ne "Mr") -And ($EtatUtilisateurU.COD_SEX -eq "H")) {
+
+ Set-ADUser -Identity $UserAD.sAMAccountName -Clear Initials
+ Set-ADUser -Identity $UserAD.sAMAccountName -Initials "Mr"
+
+ Write-Host " - Update civilité - Mr"
+ }
+ ElseIf (($Initials -ne "Mme") -And ($EtatUtilisateurU.COD_SEX -eq "F")) {
+
+ Set-ADUser -Identity $UserAD.sAMAccountName -Clear Initials
+ Set-ADUser -Identity $UserAD.sAMAccountName -Initials "Mme"
+
+ Write-Host " - Update civilité - Mme"
+ }
+ Else {
+ # Write-Host " - Pas de modification"
+ }
+ }
+
+ # Mise en place du Employee Number
+ If ($EmployeeNumber -ne $EtatUtilisateurU.NUM_ALC) {
+ If ($EmployeeID.substring(0, 1) -eq 0 ) {
+ $tmp = ($EtatUtilisateurU.NUM_ALC).Remove(0,1)
+ If (!($EmployeeNumber -eq $tmp)) {
+ Write-host " - 2erreur : "$EtatUtilisateurU.MTR" > $EmployeeID | "$EtatUtilisateurU.NUM_ALC" > $EmployeeNumber"
+ }
+ }
+ Else {
+ Write-host " - erreur : "$EtatUtilisateurU.MTR" > $EmployeeID | "$EtatUtilisateurU.NUM_ALC" > $EmployeeNumber"
+ }
+ }
+
+ # Controle du status
+ If (($ExtensionAttribute1 -eq $null) -Or ($ExtensionAttribute1 -ne $EmployeeType)) {
+ Set-ADUser -Identity $UserAD.sAMAccountName -Add @{'msDS-cloudExtensionAttribute1' = $EmployeeType}
+ }
+ }
+ Else {
+ # Si l'employé ID n'est pas présent
+ $EtatUtilisateurID = QuerySQLServer "SWDHBBDDP01.fr.dgs.group" "dhb_prd" "SELECT NOM,PRN,NOM_PAT,LIB_EMP,ADR_EML,MTR,NUM_ALC,COD_SEX,FLG_AGT,DAT_SRT FROM [dhb_prd].[dhbref].[TAB_RH_SAL] WHERE NOM = '$Surname' And PRN ='$GivenName' And DAT_SRT IS NULL"
+
+ If ($EtatUtilisateurID) {
+
+ Set-ADUser -Identity $UserAD.sAMAccountName -EmployeeID $EtatUtilisateurID.MTR
+
+ Write-Host " - Mise en place de employee ID"
+ }
+ }
+ }
+ Else {
+ # Passage d'un externe en interne
+ }
+ }
+ $EtatUtilisateurD = $null
+ $EtatUtilisateurU = $null
+
+ $CounterA++
+}
+
+## Suppression des comptes de plus 1 mois
+
+$OuPathDelete = "OU=_A_SUPPRIMER,OU=Utilisateurs,DC=fr,DC=dgs,DC=group"
+$ListeUserADDelete = Get-ADUser -Filter * -SearchBase $OuPathDelete -Properties cn,description,sAMAccountName
+
+Foreach ($UserADDelete in $ListeUserADDelete) {
+ # Barre de progression
+ $percentCompleteB = $(($CounterB / $ListeUserADDelete.Count) * 100 )
+
+ $ProgressB = @{
+ Activity = "Getting folder information for '$($UserADDelete.Name)'."
+ Status = "Processing $CounterB of $($ListeUserADDelete.Count)"
+ PercentComplete = $([math]::Round($percentCompleteB, 2))
+ }
+
+ Write-Progress @ProgressB -Id 1
+
+ $InfoUserDelete = Get-ADUser -identity $UserADDelete.sAMAccountName -properties 'msDS-cloudExtensionAttribute2'
+
+ $ExtensionAttributeDelete2 = $InfoUserDelete.'msDS-cloudExtensionAttribute2'
+
+ If ($Enable -eq $False) {
+ If ($DateDelete -gt $ExtensionAttributeDelete2) {
+ Remove-ADUser -Identity $UserADDelete.sAMAccountName
+
+ $TitreB = "Suppression du compte Active Directory : $UserAD.sAMAccountName "
+
+ $MessageB = "Bonjour, `r`n `r`n" `
+ +"Nous venons de supprimer le compte de : $DisplayName `r`n `r`n" `
+ +"Prénom : $GivenName `r`n" `
+ +"Nom : $Surname `r`n" `
+ +"Mail : $Mail `r`n" `
+ +"Matricule : $EmployeeID `r`n" `
+ +"Fonction : $Title `r`n" `
+ +"`r`n" `
+ +"Conformément à la politique entreprise le compte est resté en état désactivé pendant 30 jours `r`n" `
+ +"La suppression du compte GMAIL se fait également en automatique."
+
+ QueryGLPI $TitreB $MessageB "213"
+ }
+ }
+ $EtatUtilisateur = $null
+
+ $CounterB++
+}
+
+## Creation compte
+
+$ListeNouveau = QuerySQLServer "SWDHBBDDP01.fr.dgs.group" "dhb_prd" "SELECT NOM,PRN,NOM_PAT,LIB_EMP,ADR_EML,MTR,NUM_ALC,COD_SEX,FLG_AGT,DAT_SRT,NUM_MAG_AGT FROM [dhb_prd].[dhbref].[TAB_RH_SAL] WHERE DTH_CRE >= '$DateCreation' ORDER BY DTH_CRE DESC"
+$ListeNouveau | Format-Table
+Pause
+If ($ListeNouveau) {
+ Foreach ($NouveauUtilisateur in $ListeNouveau) {
+
+ $InfoUserNew = Get-ADUser -Filter * | Where-Object {$_.GivenName -like $NouveauUtilisateur.PRN -and $_.Surname -like $NouveauUtilisateur.NOM}
+
+ If ($InfoUserNew) {
+ $InfoUserNewA = Get-ADUser -identity $InfoUserNew.sAMAccountName -properties 'msDS-cloudExtensionAttribute1','msDS-cloudExtensionAttribute2',mail,employeeID,EmployeeNumber,EmployeeType,Title,Initials
+ }
+
+ $C = $InfoUserNew.c
+ $City = $InfoUserNew.City
+ $Cn = $InfoUserNew.cn
+ $Company = $InfoUserNew.company
+ $department = $InfoUserNew.department
+ $DisplayName = $InfoUserNew.displayName
+ $EmailAddress = $InfoUserNew.EmailAddress
+ $Enable = $InfoUserNew.Enabled
+ $GivenName = $InfoUserNew.GivenName
+ $Name = $InfoUserNew.Name
+ $Office = $InfoUserNew.Office
+ $Organization = $InfoUserNew.Organization
+ $PostalCode = $InfoUserNew.PostalCode
+ $sn = $InfoUserNew.sn
+ $Surname = $InfoUserNew.Surname
+
+ $ExtensionAttribute1 = $InfoUserNewA.'msDS-cloudExtensionAttribute1'
+ $ExtensionAttribute2 = $InfoUserNewA.'msDS-cloudExtensionAttribute2'
+ $Mail = $InfoUserNewA.Mail
+ $EmployeeID = $InfoUserNewA.employeeID
+ $EmployeeNumber = $InfoUserNewA.EmployeeNumber
+ $EmployeeType = $InfoUserNewA.EmployeeType
+ $Title = $InfoUserNewA.Title
+ $Initials = $InfoUserNewA.Initials
+
+ If ($NouveauUtilisateur.MTR -eq $EmployeeID) {
+ Write-host "Existe deja"
+ }
+ Else {
+ Write-Host "A creer"
+
+ $Surnametmp = ($NouveauUtilisateur.NOM).ToLower()
+ $GivenNametmp = ($NouveauUtilisateur.PRN).ToLower()
+ $Titletmp = ($NouveauUtilisateur.LIB_EMP).ToLower()
+ $Initialstmp = $NouveauUtilisateur.COD_SEX
+ $CodeMagtmp = $NouveauUtilisateur.NUM_MAG_AGT
+
+ $SurnameNew = (Get-Culture).TextInfo.ToTitleCase($Surnametmp)
+ $GivenNameNew = (Get-Culture).TextInfo.ToTitleCase($GivenNametmp)
+ $TitleNew = (Get-Culture).TextInfo.ToTitleCase($Titletmp)
+ $EmployeeIDNew = $NouveauUtilisateur.MTR
+
+ If ($NouveauUtilisateur.NOM_PAT) {
+ $SurnamePattmp = ($NouveauUtilisateur.NOM_PAT).ToLower()
+ $SurnamePatNew = (Get-Culture).TextInfo.ToTitleCase($SurnamePattmp)
+ }
+
+ $NameNew = $GivenNameNew+" "+$SurnameNew
+ $DisplayNameNew = $GivenNameNew+" "+$SurnameNew+" "+$SurnamePatNew
+ $SamAccountNameNew = (("$GivenNameNew.$SurnameNew").Replace(' ','').Replace('é','e').Replace('è','e').Replace('ç','c').Replace('ï','i').Replace('î','i').Replace('ë','e').Replace('ö','o').Replace('ô','o')).ToLower()
+ $UserPrincipalNameNew = (("$GivenNameNew.$SurnameNew@fr.dgs.group").Replace(' ','').Replace('é','e').Replace('è','e').Replace('ç','c').Replace('ï','i').Replace('î','i').Replace('ë','e').Replace('ö','o').Replace('ô','o')).ToLower()
+ $MailNew = (("$GivenNameNew.$SurnameNew@saint-maclou.com").Replace(' ','').Replace('é','e').Replace('è','e').Replace('ç','c').Replace('ï','i').Replace('î','i').Replace('ë','e').Replace('ö','o').Replace('ô','o')).ToLower()
+ $Password = ([System.Guid]::NewGuid()).ToString()
+
+
+ If ($SamAccountNameNew.Length -gt 20) {
+ $tmp = ($SamAccountNameNew).substring(0, 20)
+ $SamAccountNameNew = $tmp
+ }
+
+ Write-host $NameNew
+ Write-host $DisplayNameNew
+ Write-host $SamAccountNameNew
+ Write-host $UserPrincipalNameNew
+ Write-host $MailNew
+ Write-host $Password
+ Write-host $EmployeeIDNew
+ Write-host $TitleNew
+
+ New-ADUser -Name "$NameNew" -DisplayName "$DisplayNameNew" -GivenName "$GivenNameNew" -Surname "$SurnameNew" -SamAccountName "$SamAccountNameNew" -UserPrincipalName "$UserPrincipalNameNew" -Path "OU=_ARRIVER,OU=Utilisateurs,DC=fr,DC=dgs,DC=group" -AccountPassword (ConvertTo-SecureString "$Password" -AsPlainText -force) -Enabled $true -EmailAddress $MailNew
+
+ sleep 10
+
+ Set-ADUser -Identity $SamAccountNameNew -replace @{c="FR";co="France";countrycode=250}
+ Set-ADUser -Identity $SamAccountNameNew -Add @{'msDS-cloudExtensionAttribute1' = "Interne"}
+ Set-ADUser -Identity $SamAccountNameNew -Add @{'EmployeeType' = "Interne"}
+ Set-ADUser -Identity $SamAccountNameNew -Add @{'EmployeeID' = "$EmployeeIDNew"}
+ Set-ADUser -Identity $SamAccountNameNew -Title "$TitleNew"
+ Set-ADUser -Identity $SamAccountNameNew -Description "A remplir par le Helpdesk"
+ Set-ADUser -Identity $SamAccountNameNew -City "A remplir par le Helpdesk"
+
+ If ($Initialstmp -eq "H") {
+ Set-ADUser -Identity $SamAccountNameNew -Initials "Mr"
+ Write-Host "Update civilite - Mr"
+ }
+ ElseIf ($Initialstmp -eq "F") {
+ Set-ADUser -Identity $SamAccountNameNew -Initials "Mme"
+ Write-Host "Update civilite - Mme"
+ }
+ Else {
+
+ }
+
+ $TitreC = "Creation du compte de : $DisplayNameNew "
+
+ $MessageC = "Bonjour, `r`n `r`n" `
+ +"la creation de compte automatique a cree le compte de : $DisplayNameNew `r`n `r`n" `
+ +"Prenom : $GivenNameNew `r`n" `
+ +"Nom : $SurnameNew `r`n" `
+ +"Mail : $MailNew `r`n" `
+ +"Password temporaire : $Password `r`n" `
+ +"Matricule : $EmployeeIDNew `r`n" `
+ +"Fonction : $TitleNew `r`n"
+
+ QueryGLPI $TitreC $MessageC "104"
+
+ $C = $Null
+ $City = $Null
+ $Cn = $Null
+ $Company = $Null
+ $department = $Null
+ $DisplayName = $Null
+ $EmailAddress = $Null
+ $Enable = $Null
+ $GivenName = $Null
+ $Name = $Null
+ $Office = $Null
+ $Organization = $Null
+ $PostalCode = $Null
+ $sn = $Null
+ $Surname = $Null
+ $ExtensionAttribute1 = $Null
+ $ExtensionAttribute2 = $Null
+ $Mail = $Null
+ $EmployeeID = $Null
+ $EmployeeNumber = $Null
+ $EmployeeType = $Null
+ $Title = $Null
+ $Initials = $Null
+ $Surnametmp = $Null
+ $GivenNametmp = $Null
+ $Titletmp = $Null
+ $Initialstmp = $Null
+ $CodeMagtmp = $Null
+ $SurnameNew = $Null
+ $SurnamePatNew = $Null
+ $GivenNameNew = $Null
+ $TitleNew = $Null
+ $EmployeeIDNew = $Null
+ }
+ }
+}
+
+Stop-Transcript