Compare commits

...

48 Commits

Author SHA1 Message Date
Przemyslaw Klys 826460f739 readme update 2020-11-09 09:45:28 +01:00
Przemyslaw Klys 9e34f4297c Update 2020-11-09 09:45:04 +01:00
Przemyslaw Klys 0da051a53b PSD1 update 2020-11-08 17:40:55 +01:00
Przemyslaw Klys f5fb9e2dee Update 2020-11-08 17:40:36 +01:00
Przemyslaw Klys 9784370c77 PSD1 update 2020-11-08 17:11:23 +01:00
Przemyslaw Klys 83363bb843 Update 2020-11-08 17:11:17 +01:00
Przemyslaw Klys 6c2e774b10 Update readme 2020-11-08 15:25:53 +01:00
Przemyslaw Klys b70cccf0db Update 2020-11-08 15:25:26 +01:00
Przemyslaw Klys 1788615711 Update 2020-11-07 23:06:02 +01:00
Przemyslaw Klys 0a863de36b Update readme/psd1 2020-11-07 22:50:08 +01:00
Przemyslaw Klys 31dd7e31a6 Update 2020-11-07 22:35:08 +01:00
Przemyslaw Klys 46a927b92d Update 2020-11-07 17:00:45 +01:00
Przemyslaw Klys 3e5c5fd6fb Update 2020-11-07 16:13:02 +01:00
Przemyslaw Klys 2a92c90b0c Update 2020-11-07 16:07:00 +01:00
Przemyslaw Klys 0f8fbb1220 Update 2020-11-07 10:35:55 +01:00
Przemyslaw Klys c37dd91403 Update 2020-11-07 10:23:16 +01:00
Przemyslaw Klys bc55cd09c5 Update 2020-11-06 13:30:05 +01:00
Przemyslaw Klys 2331e449b1 Update 2020-11-06 13:28:57 +01:00
Przemyslaw Klys d27760931e Update 2020-11-06 13:08:38 +01:00
Przemyslaw Klys 64bc1ff092 Update readme 2020-11-06 11:17:56 +01:00
Przemyslaw Klys 6f92043c3a Update 2020-11-06 11:17:01 +01:00
Przemyslaw Klys 7ce5027abd Update 2020-11-06 10:35:11 +01:00
Przemyslaw Klys 0c8e23f6af Update 2020-11-05 23:36:24 +01:00
Przemyslaw Klys 731ba420b9 Update 2020-11-05 22:46:10 +01:00
Przemyslaw Klys 8790be18bb Update 2020-11-05 21:32:31 +01:00
Przemyslaw Klys e495411714 Update 2020-11-05 19:44:48 +01:00
Przemyslaw Klys fab33b8d14 Update 2020-11-05 19:21:00 +01:00
Przemyslaw Klys e00c93b798 Update 2020-11-05 18:28:51 +01:00
Przemyslaw Klys ee5e03d666 Update 2020-11-05 18:06:49 +01:00
Przemyslaw Klys ad2067c714 Update 2020-11-05 16:36:48 +01:00
Przemyslaw Klys b5e4fd07dc Update 2020-11-05 11:28:58 +01:00
Przemyslaw Klys 981e71483a Update 2020-11-04 20:55:47 +01:00
Przemyslaw Klys 26e9486894 Update psd1 2020-11-03 09:06:15 +01:00
Przemyslaw Klys 2f874b8dc8 Update 2020-10-29 20:24:26 +01:00
Przemyslaw Klys f1e211e560 Update readme 2020-10-29 18:48:43 +01:00
Przemyslaw Klys 4d82b51577 Update 2020-10-29 18:47:13 +01:00
Przemyslaw Klys ee9effa4f1 Update examples 2020-10-29 11:35:33 +01:00
Przemyslaw Klys af673cef1c Update readme 2020-10-29 11:34:16 +01:00
Przemyslaw Klys 38bf93fef3 Update 2020-10-29 11:32:25 +01:00
Przemyslaw Klys 3811bf68ad Update 2020-10-28 18:25:39 +01:00
Przemyslaw Klys e5d897d354 Update 2020-10-28 18:25:21 +01:00
Przemyslaw Klys 901be9fc66 Update PSD1 2020-10-28 11:40:35 +01:00
Przemyslaw Klys 57f9d02635 Update 2020-10-28 11:40:22 +01:00
Przemyslaw Klys d7e1ccd2f2 Update 2020-10-28 11:37:38 +01:00
Przemyslaw Klys 8d7ef2512d Update 2020-10-27 11:09:06 +01:00
Przemyslaw Klys ed417e7d9e Update 2020-10-24 20:30:35 +02:00
Przemyslaw Klys df07cc5a6b Update 2020-10-24 20:29:34 +02:00
Przemyslaw Klys 68f7a9292c Update 2020-10-23 18:53:47 +02:00
50 changed files with 2281 additions and 898 deletions
+2 -2
View File
@@ -2,8 +2,8 @@
# Backup GPOs
$BackupPath = "$Env:UserProfile\Desktop\GPO"
$GPOSummary = Backup-GPOZaurr -BackupPath $BackupPath -Verbose -Type Unlinked -BackupDated #-LimitProcessing 1
$GPOSummary = Backup-GPOZaurr -BackupPath $BackupPath -Verbose -Type All #-BackupDated #-LimitProcessing 1
$GPOSummary | Format-Table -AutoSize
# Confirm GPOs are backed up properly
## Confirm GPOs are backed up properly
Get-GPOZaurrBackupInformation -BackupFolder $GPOSummary[0].BackupDirectory | Format-Table -a
@@ -1,5 +1,4 @@
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
# Remove GPOS
$BackupPath = "$Env:UserProfile\Desktop\GPO"
Remove-GPOZaurr -Type Empty, Unlinked -BackupPath $BackupPath -BackupDated -LimitProcessing 2 -Verbose -WhatIf
Remove-GPOZaurr -Type Empty, Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -BackupDated -LimitProcessing 2 -Verbose -WhatIf
+1 -1
View File
@@ -5,7 +5,7 @@ $Report = Get-GPOZaurrOwner -IncludeSysvol
$Report | ConvertTo-Excel -FilePath $Env:UserProfile\Desktop\GPOOwners.xlsx -ExcelWorkSheetName 'GPO Owners Before' -AutoFilter -AutoFit
# Step 2 - Fix owners / use WhatIf and LimitProcessing / verify changes before/after
Set-GPOZaurrOwner -Type NotAdministrative -Verbose -LimitProcessing 2 -WhatIf
Set-GPOZaurrOwner -Type All -Verbose:$true -WhatIf # -LimitProcessing 2
# Step 3
$Report = Get-GPOZaurrOwner -IncludeSysvol
+2 -2
View File
@@ -1,7 +1,7 @@
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
#$Output = Invoke-GPOZaurr -GPOPath 'C:\Support\GitHub\GpoZaurr\Ignore\GPODefender'
$Output = Invoke-GPOZaurr -GPOPath 'C:\Support\GitHub\GpoZaurr\Ignore\GPOExport' -Type WindowsHelloForBusiness
#$Output = Invoke-GPOZaurrContent -GPOPath 'C:\Support\GitHub\GpoZaurr\Ignore\GPODefender'
$Output = Invoke-GPOZaurrContent -GPOPath 'C:\Support\GitHub\GpoZaurr\Ignore\GPOExport' -Type WindowsHelloForBusiness
$Output | Format-Table
$Output.WindowsDefenderExploitGuard | Format-Table
$Output.Reports.RegistrySetting | Format-Table
+1 -1
View File
@@ -2,7 +2,7 @@
# This gets the same thing as earlier examples
# with a difference where one entry per gpo and all settings for that GPO is stored under settings property.
$Output = Invoke-GPOZaurr -SingleObject -Verbose
$Output = Invoke-GPOZaurrContent -SingleObject -Verbose
$Output | Format-Table
$Output.Reports.RegistrySettings | Format-Table *
$Output.Reports.RegistrySettings[0].Settings | Format-Table *
+1 -1
View File
@@ -4,7 +4,7 @@
# This will allow you to process offline data more than once
# It's useful for when you want to request different types
$Output = Invoke-GPOZaurr -GPOPath $ENV:USERPROFILE\Desktop\GPOExportAudit -Extended -Verbose
$Output = Invoke-GPOZaurrContent -GPOPath $ENV:USERPROFILE\Desktop\GPOExportAudit -Extended -Verbose
$Output | Format-Table *
$Output.Reports | Format-Table
+1 -1
View File
@@ -1,4 +1,4 @@
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
$Output = Invoke-GPOZaurr -Verbose -OutputType HTML, Object -Open
$Output = Invoke-GPOZaurrContent -Verbose -OutputType HTML, Object -Open
$Output | Format-Table
+1 -1
View File
@@ -1,7 +1,7 @@
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
# Asses GPO based on exported data
$Output = Invoke-GPOZaurr -GPOPath $Env:USERPROFILE\Desktop\GPOExport_2020.10.12 -Verbose -Type RegistrySetting
$Output = Invoke-GPOZaurrContent -GPOPath $Env:USERPROFILE\Desktop\GPOExport_2020.10.12 -Verbose -Type RegistrySetting
$Output | Format-Table *
+1 -1
View File
@@ -1,4 +1,4 @@
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
$Output = Invoke-GPOZaurr -GPOPath $ENV:USERPROFILE\Desktop\GPOTestingUserAccess -Verbose
$Output = Invoke-GPOZaurrContent -GPOPath $ENV:USERPROFILE\Desktop\GPOTestingUserAccess -Verbose
$Output | Format-Table *
+7
View File
@@ -0,0 +1,7 @@
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
$Output = Invoke-GPOZaurr -FilePath $PSScriptRoot\Reports\GPOZaurr.html -Type GPOList -PassThru
$Output
Write-Color -Text 'Output of nested report' -Color DarkYellow -LinesBefore 1 -LinesAfter 1
$Output.GPOList
+3
View File
@@ -0,0 +1,3 @@
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
Invoke-GPOZaurr -FilePath $PSScriptRoot\Reports\GPOZaurr.html -Type NetLogonPermissions, GPOOrphans, GPOList, GPOConsistency, GPOOwners, GPODuplicates
+15
View File
@@ -0,0 +1,15 @@
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
$Types = @(
@{ Name = 'GPOOwners'; Path = "$PSScriptRoot\Reports\GPOOwners.html" }
@{ Name = 'GPOConsistency'; Path = "$PSScriptRoot\Reports\GPOConsistency.html" }
@{ Name = 'GPODuplicates'; Path = "$PSScriptRoot\Reports\GPODuplicates.html" }
@{ Name = 'GPOList'; Path = "$PSScriptRoot\Reports\GPOList.html" }
@{ Name = 'GPOOrphans'; Path = "$PSScriptRoot\Reports\GPOOrphans.html" }
@{ Name = 'GPOPassword'; Path = "$PSScriptRoot\Reports\GPOPassword.html" }
@{ Name = 'NetLogonPermissions'; Path = "$PSScriptRoot\Reports\NetLogonPermissions.html" }
)
foreach ($Type in $Types) {
Invoke-GPOZaurr -FilePath $Type.Path -Type $Type.Name -HideHTML
}
@@ -0,0 +1,3 @@
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
Repair-GPOZaurrNetLogonOwner -WhatIf:$true -Verbose -IncludeDomains ad.evotec.pl
+3
View File
@@ -0,0 +1,3 @@
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
Get-GPOZaurrPassword -GPOPath 'C:\Users\przemyslaw.klys\Desktop\GPOExport_2020.10.12'
+2
View File
@@ -0,0 +1,2 @@
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
+8 -8
View File
@@ -1,34 +1,34 @@
@{
AliasesToExport = @('Get-GPOZaurrSysvol', 'Get-GPOZaurrFilesPolicyDefinitions', 'Find-GPO', 'Remove-GPOZaurrOrphaned')
AliasesToExport = @('Get-GPOZaurrSysvol', 'Get-GPOZaurrFilesPolicyDefinitions', 'Show-GPOZaurr', 'Show-GPO', 'Find-GPO', 'Remove-GPOZaurrOrphaned')
Author = 'Przemyslaw Klys'
CmdletsToExport = @()
CompanyName = 'Evotec'
CompatiblePSEditions = @('Desktop')
Copyright = '(c) 2011 - 2020 Przemyslaw Klys @ Evotec. All rights reserved.'
Description = 'Group Policy Eater is a PowerShell module that aims to gather information about Group Policies but also allows fixing issues that you may find in them.'
FunctionsToExport = @('Add-GPOPermission', 'Add-GPOZaurrPermission', 'Backup-GPOZaurr', 'Clear-GPOZaurrSysvolDFSR', 'ConvertFrom-CSExtension', 'Find-CSExtension', 'Get-GPOZaurr', 'Get-GPOZaurrAD', 'Get-GPOZaurrBackupInformation', 'Get-GPOZaurrBroken', 'Get-GPOZaurrDictionary', 'Get-GPOZaurrFiles', 'Get-GPOZaurrFilesPolicyDefinition', 'Get-GPOZaurrFolders', 'Get-GPOZaurrInheritance', 'Get-GPOZaurrLegacyFiles', 'Get-GPOZaurrLink', 'Get-GPOZaurrLinkSummary', 'Get-GPOZaurrNetLogon', 'Get-GPOZaurrOwner', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermission', 'Get-GPOZaurrPermissionConsistency', 'Get-GPOZaurrPermissionRoot', 'Get-GPOZaurrPermissionSummary', 'Get-GPOZaurrSysvolDFSR', 'Get-GPOZaurrWMI', 'Invoke-GPOZaurr', 'Invoke-GPOZaurrPermission', 'Invoke-GPOZaurrSupport', 'New-GPOZaurrWMI', 'Remove-GPOPermission', 'Remove-GPOZaurr', 'Remove-GPOZaurrBroken', 'Remove-GPOZaurrFolders', 'Remove-GPOZaurrLegacyFiles', 'Remove-GPOZaurrPermission', 'Remove-GPOZaurrWMI', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Set-GPOOwner', 'Set-GPOZaurrOwner', 'Show-GPOZaurr')
FunctionsToExport = @('Add-GPOPermission', 'Add-GPOZaurrPermission', 'Backup-GPOZaurr', 'Clear-GPOZaurrSysvolDFSR', 'ConvertFrom-CSExtension', 'Find-CSExtension', 'Get-GPOZaurr', 'Get-GPOZaurrAD', 'Get-GPOZaurrBackupInformation', 'Get-GPOZaurrBroken', 'Get-GPOZaurrDictionary', 'Get-GPOZaurrDuplicateObject', 'Get-GPOZaurrFiles', 'Get-GPOZaurrFilesPolicyDefinition', 'Get-GPOZaurrFolders', 'Get-GPOZaurrInheritance', 'Get-GPOZaurrLegacyFiles', 'Get-GPOZaurrLink', 'Get-GPOZaurrLinkSummary', 'Get-GPOZaurrNetLogon', 'Get-GPOZaurrOwner', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermission', 'Get-GPOZaurrPermissionConsistency', 'Get-GPOZaurrPermissionRoot', 'Get-GPOZaurrPermissionSummary', 'Get-GPOZaurrSysvolDFSR', 'Get-GPOZaurrWMI', 'Invoke-GPOZaurr', 'Invoke-GPOZaurrContent', 'Invoke-GPOZaurrPermission', 'Invoke-GPOZaurrSupport', 'New-GPOZaurrWMI', 'Optimize-GPOZaurr', 'Remove-GPOPermission', 'Remove-GPOZaurr', 'Remove-GPOZaurrBroken', 'Remove-GPOZaurrDuplicateObject', 'Remove-GPOZaurrFolders', 'Remove-GPOZaurrLegacyFiles', 'Remove-GPOZaurrPermission', 'Remove-GPOZaurrWMI', 'Repair-GPOZaurrNetLogonOwner', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Set-GPOOwner', 'Set-GPOZaurrOwner')
GUID = 'f7d4c9e4-0298-4f51-ad77-e8e3febebbde'
ModuleVersion = '0.0.67'
ModuleVersion = '0.0.77'
PowerShellVersion = '5.1'
PrivateData = @{
PSData = @{
Tags = @('Windows', 'ActiveDirectory', 'GPO', 'GroupPolicy')
ProjectUri = 'https://github.com/EvotecIT/GPOZaurr'
ExternalModuleDependencies = @('CimCmdlets', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility')
ExternalModuleDependencies = @('CimCmdlets', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility', 'Microsoft.PowerShell.Security')
}
}
RequiredModules = @(@{
ModuleVersion = '0.0.186'
ModuleVersion = '0.0.187'
ModuleName = 'PSSharedGoods'
Guid = 'ee272aa8-baaa-4edf-9f45-b6d6f7d844fe'
}, @{
ModuleVersion = '0.0.99'
ModuleVersion = '0.0.102'
ModuleName = 'ADEssentials'
Guid = '9fc9fd61-7f11-4f4b-a527-084086f1905f'
}, @{
ModuleVersion = '0.0.116'
ModuleVersion = '0.0.120'
ModuleName = 'PSWriteHTML'
Guid = 'a7bdf640-f5cb-4acf-9de0-365b322d245c'
}, 'CimCmdlets', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility')
}, 'CimCmdlets', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility', 'Microsoft.PowerShell.Security')
RootModule = 'GPOZaurr.psm1'
}
+37
View File
@@ -0,0 +1,37 @@
function Find-GPOPassword {
[cmdletBinding()]
param(
[string] $Path
)
#Convert XML in a String file
[string]$XMLString = Get-Content -LiteralPath $Path
#Check if Cpassword Exist in the file
if ($XMLString.Contains("cpassword")) {
#Take the Cpassword Value from XML String file
[string]$Cpassword = [regex]::matches($XMLString, '(cpassword=).+?(?=\")')
$Cpassword = $Cpassword.split('(\")')[1]
#Check if Cpassword has a value
if ($Cpassword.Length -gt 20 -and $Cpassword -notlike '*cpassword*') {
$Mod = ($Cpassword.length % 4)
switch ($Mod) {
'1' { $Cpassword = $Cpassword.Substring(0, $Cpassword.Length - 1) }
'2' { $Cpassword += ('=' * (4 - $Mod)) }
'3' { $Cpassword += ('=' * (4 - $Mod)) }
}
$Base64Decoded = [Convert]::FromBase64String($Cpassword)
$AesObject = [System.Security.Cryptography.AesCryptoServiceProvider]::new()
#Use th AES Key
[Byte[]] $AesKey = @(0x4e, 0x99, 0x06, 0xe8, 0xfc, 0xb6, 0x6c, 0xc9, 0xfa, 0xf4, 0x93, 0x10, 0x62, 0x0f, 0xfe, 0xe8, 0xf4, 0x96, 0xe8, 0x06, 0xcc, 0x05, 0x79, 0x90, 0x20, 0x9b, 0x09, 0xa4, 0x33, 0xb6, 0x6c, 0x1b)
$AesIV = New-Object Byte[]($AesObject.IV.Length)
$AesObject.IV = $AesIV
$AesObject.Key = $AesKey
$DecryptorObject = $AesObject.CreateDecryptor()
[Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
#Convert Hash variable in a String valute
$Password = [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
} else {
$Password = ''
}
}
$Password
}
+24
View File
@@ -0,0 +1,24 @@
function Get-GitHubVersion {
[cmdletBinding()]
param(
[Parameter(Mandatory)][string] $Cmdlet,
[Parameter(Mandatory)][string] $RepositoryOwner,
[Parameter(Mandatory)][string] $RepositoryName
)
$App = Get-Command -Name $Cmdlet -ErrorAction SilentlyContinue
if ($App) {
[Array] $GitHubReleases = (Get-GitHubLatestRelease -Url "https://api.github.com/repos/$RepositoryOwner/$RepositoryName/releases" -Verbose:$false)
$LatestVersion = $GitHubReleases[0]
if (-not $LatestVersion.Errors) {
if ($App.Version -eq $LatestVersion.Version) {
"Current/Latest: $($LatestVersion.Version) at $($LatestVersion.PublishDate)"
} elseif ($App.Version -lt $LatestVersion.Version) {
"Current: $($App.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Update?"
} elseif ($App.Version -gt $LatestVersion.Version) {
"Current: $($App.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Lucky you!"
}
} else {
"Current: $($App.Version)"
}
}
}
+39 -3
View File
@@ -6,7 +6,8 @@
[switch] $PermissionsOnly,
[switch] $OwnerOnly,
[System.Collections.IDictionary] $ADAdministrativeGroups,
[string] $Splitter = [System.Environment]::NewLine
[string] $Splitter = [System.Environment]::NewLine,
[switch] $ReturnObject
)
if ($XMLContent.GPO.LinksTo) {
$LinkSplit = ([Array] $XMLContent.GPO.LinksTo).Where( { $_.Enabled -eq $true }, 'Split')
@@ -54,6 +55,36 @@
} elseif ($UserEnabled -eq $false -and $ComputerEnabled -eq $true) {
$Enabled = 'User configuration settings disabled'
}
$ComputerSettingsAvailable = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true }
$UserSettingsAvailable = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true }
$ComputerProblem = $false
if ($ComputerEnabled -eq $true -and $ComputerSettingsAvailable -eq $true) {
$ComputerOptimized = $true
} elseif ($ComputerEnabled -eq $true -and $ComputerSettingsAvailable -eq $false) {
$ComputerOptimized = $false
} elseif ($ComputerEnabled -eq $false -and $ComputerSettingsAvailable -eq $false) {
$ComputerOptimized = $true
} else {
# Enabled $false, but ComputerData is there.
$ComputerOptimized = $false
$ComputerProblem = $true
}
$UserProblem = $false
if ($UserEnabled -eq $true -and $UserSettingsAvailable -eq $true) {
$UserOptimized = $true
} elseif ($UserEnabled -eq $true -and $UserSettingsAvailable -eq $false) {
$UserOptimized = $false
} elseif ($UserEnabled -eq $false -and $UserSettingsAvailable -eq $false) {
$UserOptimized = $true
} else {
# Enabled $false, but UserData is there.
$UserOptimized = $false
$UserProblem = $true
}
if (-not $PermissionsOnly) {
if ($XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text') {
$AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($XMLContent.GPO.SecurityDescriptor.Owner.Name.'#text')"]
@@ -123,9 +154,13 @@
'LinksDisabledCount' = $LinksDisabledCount
'Enabled' = $Enabled
'ComputerEnabled' = $ComputerEnabled
'ComputerOptimized' = $ComputerOptimized
'ComputerProblem' = $ComputerProblem
'UserEnabled' = $UserEnabled
'ComputerSettingsAvailable' = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true }
'UserSettingsAvailable' = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true }
'UserOptimized' = $UserOptimized
'UserProblem' = $UserProblem
'ComputerSettingsAvailable' = $ComputerSettingsAvailable
'UserSettingsAvailable' = $UserSettingsAvailable
'ComputerSettingsStatus' = if ($XMLContent.GPO.Computer.VersionDirectory -eq 0 -and $XMLContent.GPO.Computer.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" }
'ComputerSetttingsVersionIdentical' = if ($XMLContent.GPO.Computer.VersionDirectory -eq $XMLContent.GPO.Computer.VersionSysvol) { $true } else { $false }
'ComputerSettings' = $XMLContent.GPO.Computer.ExtensionData.Extension
@@ -181,6 +216,7 @@
}
}
}
'GPOObject' = $GPO
}
}
}
+33
View File
@@ -0,0 +1,33 @@
$GPOZaurrAnalysis = [ordered] @{
Name = 'Group Policy Content'
Enabled = $true
ActionRequired = $null
Data = $null
Execute = {
Invoke-GPOZaurrContent
}
Processing = {
}
Variables = @{
}
Overview = {
}
Solution = {
foreach ($Key in $Script:Reporting['GPOAnalysis']['Data'].Keys) {
New-HTMLTab -Name $Key {
New-HTMLTable -DataTable $Script:Reporting['GPOAnalysis']['Data'][$Key] -Filtering -Title $Key
}
}
if ($Script:Reporting['GPOAnalysis']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['GPOAnalysis']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
}
}
@@ -0,0 +1,29 @@
$GPOZaurrBlockedInheritance = [ordered] @{
Name = 'Group Policy Blocked Inhertiance'
Enabled = $true
ActionRequired = $null
Data = $null
Execute = {
Get-GPOZaurrInheritance -IncludeBlockedObjects -OnlyBlockedInheritance
}
Processing = {
}
Variables = @{
}
Overview = {
}
Solution = {
New-HTMLTable -DataTable $Script:Reporting['GPOBlockedInheritance']['Data'] -Filtering
if ($Script:Reporting['GPOBlockedInheritance']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['GPOBlockedInheritance']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
}
}
+168
View File
@@ -0,0 +1,168 @@
$GPOZaurrConsistency = [ordered] @{
Name = 'GPO Permissions Consistency'
Enabled = $true
ActionRequired = $null
Data = $null
Execute = { Get-GPOZaurrPermissionConsistency -Type All -VerifyInheritance }
Processing = {
foreach ($GPO in $Script:Reporting['GPOConsistency']['Data']) {
if ($GPO.ACLConsistent -eq $true) {
$Script:Reporting['GPOConsistency']['Variables']['Consistent']++
} else {
$Script:Reporting['GPOConsistency']['Variables']['Inconsistent']++
}
if ($GPO.ACLConsistentInside -eq $true) {
$Script:Reporting['GPOConsistency']['Variables']['ConsistentInside']++
} else {
$Script:Reporting['GPOConsistency']['Variables']['InconsistentInside']++
}
}
if ($Script:Reporting['GPOConsistency']['Variables']['Inconsistent'].Count -gt 0 -or $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside'].Count -gt 0 ) {
$Script:Reporting['GPOConsistency']['ActionRequired'] = $true
} else {
$Script:Reporting['GPOConsistency']['ActionRequired'] = $false
}
}
Variables = @{
Consistent = 0
Inconsistent = 0
ConsistentInside = 0
InconsistentInside = 0
}
Overview = {
New-HTMLPanel {
New-HTMLText -Text 'Following chart presents ', 'permissions consistency between Active Directory and SYSVOL for Group Policies' -FontSize 10pt -FontWeight normal, bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Top level permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['Consistent'] -FontWeight normal, bold
New-HTMLListItem -Text 'Inherited permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'] -FontWeight normal, bold
New-HTMLListItem -Text 'Inconsistent top level permissions: ', $Script:Reporting['GPOConsistency']['Variables']['Inconsistent'] -FontWeight normal, bold
New-HTMLListItem -Text "Inconsistent inherited permissions: ", $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside'] -FontWeight normal, bold
} -FontSize 10pt
New-HTMLText -FontSize 10pt -Text 'Having incosistent permissions on AD in comparison to those on SYSVOL can lead to uncontrolled ability to modify them.'
New-HTMLChart {
New-ChartLegend -Names 'Bad', 'Good' -Color PaleGreen, Salmon
New-ChartBarOptions -Type barStacked
New-ChartLegend -Name 'Consistent', 'Inconsistent'
New-ChartBar -Name 'TopLevel' -Value $Script:Reporting['GPOConsistency']['Variables']['Consistent'], $Script:Reporting['GPOConsistency']['Variables']['Inconsistent']
New-ChartBar -Name 'Inherited' -Value $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'], $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside']
} -Title 'Permissions Consistency' -TitleAlignment center
}
}
Summary = {
New-HTMLText -FontSize 10pt -TextBlock {
"When GPO is created it creates an entry in Active Directory (metadata) and SYSVOL (content). "
"Two different places meens two different sets of permissions. Group Policy module is making sure the data in both places is correct. "
"However, for different reasons it's not nessecary the case and often permissions go out of sync between AD and SYSVOL. "
"This test verifies consistency of policies between AD and SYSVOL in two ways. "
"It checks top level permissions for a GPO, and then checks if all files within said GPO are inheriting permissions or have different permissions in place. "
}
New-HTMLText -Text 'Following list presents ', 'permissions consistency between Active Directory and SYSVOL for Group Policies' -FontSize 10pt -FontWeight normal, bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Top level permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['Consistent'] -FontWeight normal, bold
New-HTMLListItem -Text 'Inherited permissions consistency: ', $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'] -FontWeight normal, bold
New-HTMLListItem -Text 'Inconsistent top level permissions: ', $Script:Reporting['GPOConsistency']['Variables']['Inconsistent'] -FontWeight normal, bold
New-HTMLListItem -Text "Inconsistent inherited permissions: ", $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside'] -FontWeight normal, bold
} -FontSize 10pt
New-HTMLText -FontSize 10pt -Text 'Having incosistent permissions on AD in comparison to those on SYSVOL can lead to uncontrolled ability to modify them. Please notice that if ', `
' Not available ', 'is visible in the table you should first fix related, more pressing issue, before fixing permissions inconsistency.' -FontWeight normal, bold, normal
}
Solution = {
New-HTMLSection -Invisible {
New-HTMLPanel {
& $Script:GPOConfiguration['GPOConsistency']['Summary']
}
New-HTMLPanel {
New-HTMLChart {
New-ChartBarOptions -Type barStacked
New-ChartLegend -Name 'Consistent', 'Inconsistent' -Color PaleGreen, Salmon
New-ChartBar -Name 'TopLevel' -Value $Script:Reporting['GPOConsistency']['Variables']['Consistent'], $Script:Reporting['GPOConsistency']['Variables']['Inconsistent']
New-ChartBar -Name 'Inherited' -Value $Script:Reporting['GPOConsistency']['Variables']['ConsistentInside'], $Script:Reporting['GPOConsistency']['Variables']['InconsistentInside']
} -Title 'Permissions Consistency' -TitleAlignment center
}
}
New-HTMLSection -Name 'Group Policy Permissions Consistency' {
New-HTMLTable -DataTable $Script:Reporting['GPOConsistency']['Data'] -Filtering {
New-HTMLTableCondition -Name 'ACLConsistent' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string
New-HTMLTableCondition -Name 'ACLConsistentInside' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string
New-HTMLTableCondition -Name 'ACLConsistent' -Value $true -BackgroundColor PaleGreen -TextTransform capitalize -ComparisonType string
New-HTMLTableCondition -Name 'ACLConsistentInside' -Value $true -BackgroundColor PaleGreen -TextTransform capitalize -ComparisonType string
New-HTMLTableCondition -Name 'ACLConsistent' -Value 'Not available' -BackgroundColor Crimson -ComparisonType string
New-HTMLTableCondition -Name 'ACLConsistentInside' -Value 'Not available' -BackgroundColor Crimson -ComparisonType string
} -PagingOptions 10, 20, 30, 40, 50
}
if ($Script:Reporting['GPOConsistency']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['GPOConsistency']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
New-HTMLSection -Name 'Steps to fix - Permissions Consistency' {
New-HTMLContainer {
New-HTMLSpanStyle -FontSize 10pt {
New-HTMLText -Text 'Following steps will guide you how to fix permissions consistency'
New-HTMLWizard {
New-HTMLWizardStep -Name 'Prepare environment' {
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
New-HTMLCodeBlock -Code {
Install-Module GPOZaurr -Force
Import-Module GPOZaurr -Force
} -Style powershell
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
}
New-HTMLWizardStep -Name 'Prepare report' {
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding fixing permissions inconsistencies. To generate new report please use:"
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrPermissionsInconsistentBefore.html -Verbose -Type GPOConsistency
}
New-HTMLText -Text {
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
}
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
New-HTMLCodeBlock -Code {
$GPOOutput = Get-GPOZaurrPermissionConsistency
$GPOOutput | Format-Table # do your actions as desired
}
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
}
New-HTMLWizardStep -Name 'Fix inconsistent permissions' {
New-HTMLText -Text "Following command when executed fixes inconsistent permissions."
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
New-HTMLText -Text "Make sure to fill in TargetDomain to match your Domain Admin permission account"
New-HTMLCodeBlock -Code {
Repair-GPOZaurrPermissionConsistency -IncludeDomains "TargetDomain" -Verbose -WhatIf
}
New-HTMLText -TextBlock {
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. Once happy with results please follow with command: "
}
New-HTMLCodeBlock -Code {
Repair-GPOZaurrPermissionConsistency -LimitProcessing 2 -IncludeDomains "TargetDomain"
}
New-HTMLText -TextBlock {
"This command when executed repairs only first X inconsistent permissions. Use LimitProcessing parameter to prevent mass fixing and increase the counter when no errors occur."
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
}
New-HTMLText -Text "If there's nothing else to be fixed, we can skip to next step step"
}
New-HTMLWizardStep -Name 'Fix inconsistent downlevel permissions' {
New-HTMLText -Text "Unfortunetly this step is manual until automation is developed. "
New-HTMLText -Text "If there are inconsistent permissions found inside GPO one has to fix them manually by going into SYSVOL and making sure inheritance is enabled, and that permissions are consistent across all files."
}
New-HTMLWizardStep -Name 'Verification report' {
New-HTMLText -TextBlock {
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
}
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrPermissionsInconsistentAfter.html -Verbose -Type GPOConsistency
}
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
}
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
}
}
}
}
}
+122
View File
@@ -0,0 +1,122 @@
$GPOZaurrDuplicates = [ordered] @{
Name = 'Duplicate (CNF) Group Policies'
Enabled = $true
Action = $null
Data = $null
Execute = {
Get-GPOZaurrDuplicateObject
}
Processing = {
$Script:Reporting['GPODuplicates']['Variables']['RequireDeletion'] = $Script:Reporting['GPODuplicates']['Data'].Count
if ($Script:Reporting['GPODuplicates']['Data'].Count -gt 0) {
$Script:Reporting['GPODuplicates']['ActionRequired'] = $true
} else {
$Script:Reporting['GPODuplicates']['ActionRequired'] = $false
}
}
Variables = @{
RequireDeletion = 0
}
Overview = {
}
Resources = @(
'https://social.technet.microsoft.com/wiki/contents/articles/15435.active-directory-duplicate-object-name-resolution.aspx'
'https://kickthatcomputer.wordpress.com/2014/11/22/seek-and-destroy-duplicate-ad-objects-with-cnf-in-the-name/'
)
Summary = {
New-HTMLText -FontSize 10pt -TextBlock {
"CNF objects, Conflict objects or Duplicate Objects are created in Active Directory when there is simultaneous creation of an AD object under the same container "
"on two separate Domain Controllers near about the same time or before the replication occurs. "
"This results in a conflict and the same is exhibited by a CNF (Duplicate) object. "
"While it doesn't nessecary has a huge impact on Active Directory it's important to keep Active Directory in proper, healthy state. "
}
New-HTMLText -Text 'As it stands currently there are ', $Script:Reporting['GPODuplicates']['Data'].Count, ' CNF (Duplicate) Group Policy objects to be deleted.' -FontSize 10pt -FontWeight normal, bold, normal
}
Solution = {
New-HTMLSection -Invisible {
New-HTMLPanel {
& $Script:GPOConfiguration['GPODuplicates']['Summary']
}
New-HTMLPanel {
New-HTMLChart {
New-ChartLegend -Names 'Bad' -Color PaleGreen, Salmon
New-ChartBar -Name 'Duplicate (CNF) object' -Value $Script:Reporting['GPODuplicates']['Data'].Count
} -Title 'Duplicate (CNF) Objects' -TitleAlignment center
}
}
New-HTMLSection -Name 'Group Policy CNF (Duplicate) Objects' {
New-HTMLTable -DataTable $Script:Reporting['GPODuplicates']['Data'] -Filtering {
} -PagingOptions 10, 20, 30, 40, 50
}
if ($Script:Reporting['GPODuplicates']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['GPODuplicates']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
New-HTMLSection -Name 'Steps to fix - Remove duplicate (CNF) objects' {
New-HTMLContainer {
New-HTMLSpanStyle -FontSize 10pt {
New-HTMLText -Text 'Following steps will guide you how to remove duplicate objects'
New-HTMLWizard {
New-HTMLWizardStep -Name 'Prepare environment' {
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
New-HTMLCodeBlock -Code {
Install-Module GPOZaurr -Force
Import-Module GPOZaurr -Force
} -Style powershell
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
}
New-HTMLWizardStep -Name 'Prepare report' {
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding fixing duplicate GPO objects. To generate new report please use:"
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrDuplicateObjectsBefore.html -Verbose -Type GPODuplicates
}
New-HTMLText -Text {
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
}
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
New-HTMLCodeBlock -Code {
$GPOOutput = Get-GPOZaurrDuplicateObject
$GPOOutput | Format-Table # do your actions as desired
}
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
}
New-HTMLWizardStep -Name 'Remove CNF objects' {
New-HTMLText -Text "Following command when executed, runs internally command that lists all duplicate objects."
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
New-HTMLCodeBlock -Code {
Remove-GPOZaurrDuplicateObject -WhatIf -Verbose
}
New-HTMLText -TextBlock {
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data. Once happy with results please follow with command: "
}
New-HTMLCodeBlock -Code {
Remove-GPOZaurrDuplicateObject -Verbose -LimitProcessing 2
}
New-HTMLText -TextBlock {
"This command when executed removes only first X duplicate objects. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur."
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
}
}
New-HTMLWizardStep -Name 'Verification report' {
New-HTMLText -TextBlock {
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
}
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrDuplicateObjectsAfter.html -Verbose -Type GPODuplicates
}
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
}
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
}
}
}
}
}
+29
View File
@@ -0,0 +1,29 @@
$GPOZaurrFiles = [ordered] @{
Name = 'SYSVOL (NetLogon) Files List'
Enabled = $true
ActionRequired = $null
Data = $null
Execute = {
Get-GPOZaurrFiles
}
Processing = {
}
Variables = @{
}
Overview = {
}
Solution = {
New-HTMLTable -DataTable $Script:Reporting['GPOFiles']['Data'] -Filtering
if ($Script:Reporting['GPOFiles']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['GPOFiles']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
}
}
+285
View File
@@ -0,0 +1,285 @@
$GPOZaurrList = [ordered] @{
Name = 'Group Policy Summary'
Enabled = $true
Action = $null
Data = $null
Execute = {
Get-GPOZaurr
}
Processing = {
foreach ($GPO in $Script:Reporting['GPOList']['Data']) {
if ($GPO.Linked -eq $false -and $GPO.Empty -eq $true) {
# Not linked, Empty
$Script:Reporting['GPOList']['Variables']['GPOEmptyAndUnlinked']++
$Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++
$Script:Reporting['GPOList']['Variables']['GPONotLinked']++
$Script:Reporting['GPOList']['Variables']['GPOEmpty']++
} elseif ($GPO.Linked -eq $true -and $GPO.Empty -eq $true) {
# Linked, But EMPTY
$Script:Reporting['GPOList']['Variables']['GPOLinkedButEmpty']++
$Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++
$Script:Reporting['GPOList']['Variables']['GPOEmpty']++
$Script:Reporting['GPOList']['Variables']['GPOLinked']++
} elseif ($GPO.Linked -eq $false) {
# Not linked, but not EMPTY
$Script:Reporting['GPOList']['Variables']['GPONotLinked']++
$Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++
$Script:Reporting['GPOList']['Variables']['GPONotEmpty']++
} elseif ($GPO.Empty -eq $true) {
# Linked, But EMPTY
$Script:Reporting['GPOList']['Variables']['GPOEmpty']++
$Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']++
$Script:Reporting['GPOList']['Variables']['GPOLinked']++
} else {
# Linked, not EMPTY
$Script:Reporting['GPOList']['Variables']['GPOValid']++
$Script:Reporting['GPOList']['Variables']['GPOLinked']++
$Script:Reporting['GPOList']['Variables']['GPONotEmpty']++
}
if ($GPO.LinksDisabledCount -eq $GPO.LinksCount -and $GPO.LinksCount -gt 0) {
$Script:Reporting['GPOList']['Variables']['GPOLinkedButLinkDisabled']++
}
if ($GPO.ComputerOptimized -eq $true) {
$Script:Reporting['GPOList']['Variables']['ComputerOptimizedYes']++
} else {
$Script:Reporting['GPOList']['Variables']['ComputerOptimizedNo']++
}
if ($GPO.ComputerProblem -eq $true) {
$Script:Reporting['GPOList']['Variables']['ComputerProblemYes']++
} else {
$Script:Reporting['GPOList']['Variables']['ComputerProblemNo']++
}
if ($GPO.UserOptimized -eq $true) {
$Script:Reporting['GPOList']['Variables']['UserOptimizedYes']++
} else {
$Script:Reporting['GPOList']['Variables']['UserOptimizedNo']++
}
if ($GPO.UserProblem -eq $true) {
$Script:Reporting['GPOList']['Variables']['UserProblemYes']++
} else {
$Script:Reporting['GPOList']['Variables']['UserProblemNo']++
}
if ($GPO.UserProblem -or $GPO.ComputerProblem) {
$Script:Reporting['GPOList']['Variables']['GPOWithProblems']++
}
}
$Script:Reporting['GPOList']['Variables']['GPOTotal'] = $Script:Reporting['GPOList']['Data'].Count
if ($Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked'].Count -gt 0) {
$Script:Reporting['GPOList']['ActionRequired'] = $true
} else {
$Script:Reporting['GPOList']['ActionRequired'] = $false
}
}
Variables = @{
GPOWithProblems = 0
ComputerOptimizedYes = 0
ComputerOptimizedNo = 0
ComputerProblemYes = 0
ComputerProblemNo = 0
UserOptimizedYes = 0
UserOptimizedNo = 0
UserProblemYes = 0
UserProblemNo = 0
GPONotLinked = 0
GPOLinked = 0
GPOEmpty = 0
GPONotEmpty = 0
GPOEmptyAndUnlinked = 0
GPOEmptyOrUnlinked = 0
GPOLinkedButEmpty = 0
GPOValid = 0
GPOLinkedButLinkDisabled = 0
GPOTotal = 0
}
Overview = {
New-HTMLPanel {
New-HTMLText -Text 'Following chart presents ', 'Linked / Empty and Unlinked Group Policies' -FontSize 10pt -FontWeight normal, bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies total: ', $Script:Reporting['GPOList']['Variables']['GPOTotal'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies valid: ", $Script:Reporting['GPOList']['Variables']['GPOValid'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies to delete: ", $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked'] -FontWeight normal, bold {
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies that are unlinked (are not doing anything currently): ', $Script:Reporting['GPOList']['Variables']['GPONotLinked'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies that are empty (have no settings): ", $Script:Reporting['GPOList']['Variables']['GPOEmpty'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies that are linked, but empty: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButEmpty'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies that are linked, but link disabled: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButLinkDisabled'] -FontWeight normal, bold
}
}
}
New-HTMLText -FontSize 10pt -Text 'Usually empty or unlinked Group Policies are safe to delete.'
New-HTMLChart -Title 'Group Policies Summary' {
New-ChartBarOptions -Type barStacked
#New-ChartLegend -Names 'Unlinked', 'Linked', 'Empty', 'Total' -Color Salmon, PaleGreen, PaleVioletRed, PaleTurquoise
New-ChartLegend -Names 'Good', 'Bad' -Color PaleGreen, Salmon
#New-ChartBar -Name 'Group Policies' -Value $Script:Reporting['GPOList']['Variables']['GPONotLinked'], $Script:Reporting['GPOList']['Variables']['GPOLinked'], $Script:Reporting['GPOList']['Variables']['GPOEmpty'], $Script:Reporting['GPOList']['Variables']['GPOTotal']
New-ChartBar -Name 'Linked' -Value $Script:Reporting['GPOList']['Variables']['GPOLinked'], $Script:Reporting['GPOList']['Variables']['GPONotLinked']
New-ChartBar -Name 'Empty' -Value $Script:Reporting['GPOList']['Variables']['GPONotEmpty'], $Script:Reporting['GPOList']['Variables']['GPOEmpty']
New-ChartBar -Name 'Valid' -Value $Script:Reporting['GPOList']['Variables']['GPOValid'], $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']
} -TitleAlignment center
}
}
Summary = {
New-HTMLText -TextBlock {
"Over time Administrators add more and more group policies, as business requirements change. "
"Due to neglection or thinking it may serve it's purpose later on a lot of Group Policies often have no value at all. "
"Either the Group Policy is not linked to anything and just stays unlinked forever, or GPO is linked, but the link (links) are disabled. "
"Additionally sometimes new GPO is created without any settings or the settings are removed over time, but GPO stays in place. "
} -FontSize 10pt
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies total: ', $Script:Reporting['GPOList']['Variables']['GPOTotal'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies valid: ", $Script:Reporting['GPOList']['Variables']['GPOValid'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies to delete: ", $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked'] -FontWeight normal, bold {
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies that are unlinked (are not doing anything currently): ', $Script:Reporting['GPOList']['Variables']['GPONotLinked'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies that are empty (have no settings): ", $Script:Reporting['GPOList']['Variables']['GPOEmpty'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies that are linked, but empty: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButEmpty'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies that are linked, but link disabled: ", $Script:Reporting['GPOList']['Variables']['GPOLinkedButLinkDisabled'] -FontWeight normal, bold
}
}
} -FontSize 10pt
New-HTMLText -Text "Additionally, we're reviewing Group Policies that have their section disabled, but contain data. Please review them and make sure this configuration is as expected!" -FontSize 10pt
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies with problems: ', $Script:Reporting['GPOList']['Variables']['GPOWithProblems'] -FontWeight normal, bold {
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies that have content (computer), but are disabled: ', $Script:Reporting['GPOList']['Variables']['ComputerProblemYes'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies that have content (user), but are disabled: ", $Script:Reporting['GPOList']['Variables']['UserProblemYes'] -FontWeight normal, bold
}
}
} -FontSize 10pt
New-HTMLText -Text "For best performance it's recommended that if there are no settings of certain kind (Computer or User settings) it's best to disable them. " -FontSize 10pt
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies with optimization: ' -FontWeight normal, bold {
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies that are optimized (computer) ', $Script:Reporting['GPOList']['Variables']['ComputerOptimizedYes'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies that are optimized (user): ", $Script:Reporting['GPOList']['Variables']['UserOptimizedYes'] -FontWeight normal, bold
}
}
New-HTMLListItem -Text 'Group Policies without optimization: ' -FontWeight normal, bold {
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies that are not optimized (computer): ', $Script:Reporting['GPOList']['Variables']['ComputerOptimizedNo'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies that are not optimized (user): ", $Script:Reporting['GPOList']['Variables']['UserOptimizedNo'] -FontWeight normal, bold
}
}
} -FontSize 10pt
New-HTMLText -TextBlock {
'All empty or unlinked Group Policies can be automatically deleted. Please review output in the table and follow steps below table to cleanup Group Policies. '
'GPOs that have content, but are disabled require manual intervention. '
"If performance is an issue you should consider disabling user or computer sections of GPO when those are not used. "
} -FontSize 10pt
}
Solution = {
New-HTMLSection -Invisible {
New-HTMLPanel {
& $Script:GPOConfiguration['GPOList']['Summary']
}
New-HTMLPanel {
New-HTMLChart -Title 'Group Policies Empty & Unlinked' {
New-ChartBarOptions -Type barStacked
New-ChartLegend -Names 'Yes', 'No' -Color SpringGreen, Salmon
New-ChartBar -Name 'Linked' -Value $Script:Reporting['GPOList']['Variables']['GPOLinked'], $Script:Reporting['GPOList']['Variables']['GPONotLinked']
New-ChartBar -Name 'Empty' -Value $Script:Reporting['GPOList']['Variables']['GPONotEmpty'], $Script:Reporting['GPOList']['Variables']['GPOEmpty']
New-ChartBar -Name 'Valid' -Value $Script:Reporting['GPOList']['Variables']['GPOValid'], $Script:Reporting['GPOList']['Variables']['GPOEmptyOrUnlinked']
New-ChartBar -Name 'With problem (computers)' -Value $Script:Reporting['GPOList']['Variables']['ComputerProblemNo'], $Script:Reporting['GPOList']['Variables']['ComputerProblemYes']
New-ChartBar -Name 'With problem (users)' -Value $Script:Reporting['GPOList']['Variables']['UserProblemNo'], $Script:Reporting['GPOList']['Variables']['UserProblemYes']
New-ChartBar -Name 'Optimized Computers' -Value $Script:Reporting['GPOList']['Variables']['ComputerOptimizedYes'], $Script:Reporting['GPOList']['Variables']['ComputerOptimizedNo']
New-ChartBar -Name 'Optimized Users' -Value $Script:Reporting['GPOList']['Variables']['UserOptimizedYes'], $Script:Reporting['GPOList']['Variables']['UserOptimizedNo']
} -TitleAlignment center
}
}
New-HTMLSection -Name 'Group Policies List' {
New-HTMLTable -DataTable $Script:Reporting['GPOList']['Data'] -Filtering {
New-HTMLTableCondition -Name 'Empty' -Value $true -BackgroundColor Salmon -ComparisonType string
New-HTMLTableCondition -Name 'Linked' -Value $false -BackgroundColor Salmon -ComparisonType string
New-HTMLTableCondition -Name 'ComputerProblem' -Value $true -BackgroundColor Salmon -ComparisonType string
New-HTMLTableCondition -Name 'UserProblem' -Value $true -BackgroundColor Salmon -ComparisonType string
New-HTMLTableCondition -Name 'ComputerOptimized' -Value $false -BackgroundColor Salmon -ComparisonType string
New-HTMLTableCondition -Name 'UserOptimized' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string
# reverse
New-HTMLTableCondition -Name 'Empty' -Value $false -BackgroundColor SpringGreen -ComparisonType string
New-HTMLTableCondition -Name 'Linked' -Value $true -BackgroundColor SpringGreen -ComparisonType string
New-HTMLTableCondition -Name 'ComputerProblem' -Value $false -BackgroundColor SpringGreen -ComparisonType string
New-HTMLTableCondition -Name 'UserProblem' -Value $false -BackgroundColor SpringGreen -ComparisonType string
New-HTMLTableCondition -Name 'ComputerOptimized' -Value $true -BackgroundColor SpringGreen -ComparisonType string
New-HTMLTableCondition -Name 'UserOptimized' -Value $true -BackgroundColor SpringGreen -TextTransform capitalize -ComparisonType string
} -PagingOptions 10, 20, 30, 40, 50
}
if ($Script:Reporting['GPOList']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['GPOList']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
New-HTMLSection -Name 'Steps to fix - Empty & Unlinked Group Policies' {
New-HTMLContainer {
New-HTMLSpanStyle -FontSize 10pt {
New-HTMLText -Text 'Following steps will guide you how to remove empty or unlinked group policies'
New-HTMLWizard {
New-HTMLWizardStep -Name 'Prepare environment' {
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
New-HTMLCodeBlock -Code {
Install-Module GPOZaurr -Force
Import-Module GPOZaurr -Force
} -Style powershell
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
}
New-HTMLWizardStep -Name 'Prepare report' {
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:"
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrEmptyUnlinked.html -Verbose -Type GPOList
}
New-HTMLText -TextBlock {
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
}
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
New-HTMLCodeBlock -Code {
$GPOOutput = Get-GPOZaurr
$GPOOutput | Format-Table
}
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
}
New-HTMLWizardStep -Name 'Remove GPOs that are EMPTY or UNLINKED' {
New-HTMLText -Text @(
"Following command when executed removes every ",
"EMPTY"
" or "
"NOT LINKED"
" Group Policy. Make sure when running it for the first time to run it with ",
"WhatIf",
" parameter as shown below to prevent accidental removal.",
"Make sure to use BackupPath which will make sure that for each GPO that is about to be deleted a backup is made to folder on a desktop."
) -FontWeight normal, bold, normal, bold, normal, bold, normal, normal -Color Black, Red, Black, Red, Black
New-HTMLCodeBlock -Code {
Remove-GPOZaurr -Type Empty, Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -Verbose -WhatIf
}
New-HTMLText -TextBlock {
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. Once happy with results please follow with command: "
}
New-HTMLCodeBlock -Code {
Remove-GPOZaurr -Type Empty, Unlinked -BackupPath "$Env:UserProfile\Desktop\GPO" -LimitProcessing 2 -Verbose
}
New-HTMLText -TextBlock {
"This command when executed deletes only first empty or unlinked GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur."
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
"Please make sure to check if backup is made as well before going all in."
}
New-HTMLText -Text "If there's nothing else to be deleted on SYSVOL side, we can skip to next step step"
}
New-HTMLWizardStep -Name 'Verification report' {
New-HTMLText -TextBlock {
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
}
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrEmptyUnlinkedAfter.html -Verbose -Type GPOList
}
New-HTMLText -Text "If there are no more empty or unlinked GPOs in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
}
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
}
}
}
}
}
@@ -0,0 +1,187 @@
$GPOZaurrNetLogonPermissions = [ordered] @{
Name = 'NetLogon Permissions'
Enabled = $true
ActionRequired = $null
Data = $null
Execute = {
Get-GPOZaurrNetLogon
}
Processing = {
foreach ($File in $Script:Reporting['NetLogonPermissions']['Data']) {
if ($File.FileSystemRights -eq 'Owner') {
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwners']++
if ($File.PrincipalType -eq 'WellKnownAdministrative') {
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrative']++
} elseif ($File.PrincipalType -eq 'Administrative') {
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrative']++
} else {
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersNotAdministrative']++
}
if ($File.PrincipalSid -eq 'S-1-5-32-544') {
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators']++
} elseif ($File.PrincipalType -in 'WellKnownAdministrative', 'Administrative') {
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrativeNotAdministrators']++
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix']++
} else {
$Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix']++
}
$Script:Reporting['NetLogonPermissions']['Variables']['Owner'].Add($File)
} else {
$Script:Reporting['NetLogonPermissions']['Variables']['NonOwner'].Add($File)
}
}
if ($Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'].Count -gt 0) {
$Script:Reporting['NetLogonPermissions']['ActionRequired'] = $true
} else {
$Script:Reporting['NetLogonPermissions']['ActionRequired'] = $false
}
}
Variables = @{
NetLogonOwners = 0
NetLogonOwnersAdministrators = 0
NetLogonOwnersNotAdministrative = 0
NetLogonOwnersAdministrative = 0
NetLogonOwnersAdministrativeNotAdministrators = 0
NetLogonOwnersToFix = 0
Owner = [System.Collections.Generic.List[PSCustomObject]]::new()
NonOwner = [System.Collections.Generic.List[PSCustomObject]]::new()
}
Overview = {
New-HTMLPanel {
New-HTMLText -Text 'Following chart presents ', 'NetLogon Summary' -FontSize 10pt -FontWeight normal, bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'NetLogon Files in Total: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwners'] -FontWeight normal, bold
New-HTMLListItem -Text 'NetLogon BUILTIN\Administrators as Owner: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -FontWeight normal, bold
New-HTMLListItem -Text "NetLogon Owners requiring change: ", $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -FontWeight normal, bold {
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Not Administrative: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersNotAdministrative'] -FontWeight normal, bold
New-HTMLListItem -Text 'Administrative, but not BUILTIN\Administrators: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrativeNotAdministrators'] -FontWeight normal, bold
}
}
} -FontSize 10pt
#New-HTMLText -FontSize 10pt -Text 'Those problems must be resolved before doing other clenaup activities.'
New-HTMLChart {
New-ChartPie -Name 'Correct Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -Color LightGreen
New-ChartPie -Name 'Incorrect Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -Color Crimson
} -Title 'NetLogon Owners' -TitleAlignment center
}
New-HTMLPanel {
}
}
Summary = {
New-HTMLText -TextBlock {
"NetLogon is crucial part of Active Directory. Files stored there are available on each and every computer or server in the company. "
"Keeping those files clean and secure is very important task. "
"It's important that NetLogon file owners are set to BUILTIN\Administrators (SID: S-1-5-32-544). "
"Owners have full control over the file object. Current owner of the file may be an Administrator but it doesn't guarentee that he/she will be in the future. "
"That's why as a best-practice it's recommended to change any non-administrative owners to BUILTIN\Administrators, and even Administrative accounts should be replaced with it. "
} -FontSize 10pt
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'NetLogon Files in Total: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwners'] -FontWeight normal, bold
New-HTMLListItem -Text 'NetLogon BUILTIN\Administrators as Owner: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -FontWeight normal, bold
New-HTMLListItem -Text "NetLogon Owners requiring change: ", $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -FontWeight normal, bold {
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Not Administrative: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersNotAdministrative'] -FontWeight normal, bold
New-HTMLListItem -Text 'Administrative, but not BUILTIN\Administrators: ', $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrativeNotAdministrators'] -FontWeight normal, bold
}
}
} -FontSize 10pt
New-HTMLText -Text "Follow the steps below table to get NetLogon Owners into compliant state." -FontSize 10pt
}
Solution = {
New-HTMLTab -Name 'NetLogon Owners' {
New-HTMLSection -Invisible {
New-HTMLPanel {
& $Script:GPOConfiguration['NetLogonPermissions']['Summary']
}
New-HTMLPanel {
New-HTMLChart {
New-ChartPie -Name 'Correct Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersAdministrators'] -Color LightGreen
New-ChartPie -Name 'Incorrect Owners' -Value $Script:Reporting['NetLogonPermissions']['Variables']['NetLogonOwnersToFix'] -Color Crimson
} -Title 'NetLogon Owners' -TitleAlignment center
}
}
New-HTMLSection -Name 'NetLogon Files List' {
New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['Variables']['Owner'] -Filtering {
New-HTMLTableCondition -Name 'PrincipalSid' -Value "S-1-5-32-544" -BackgroundColor LightGreen -ComparisonType string
New-HTMLTableCondition -Name 'PrincipalSid' -Value "S-1-5-32-544" -BackgroundColor Salmon -ComparisonType string -Operator ne
New-HTMLTableCondition -Name 'PrincipalType' -Value "WellKnownAdministrative" -BackgroundColor LightGreen -ComparisonType string -Operator eq
}
}
if ($Script:Reporting['NetLogonPermissions']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
New-HTMLSection -Name 'Steps to fix NetLogon Owners ' {
New-HTMLContainer {
New-HTMLSpanStyle -FontSize 10pt {
New-HTMLText -Text 'Following steps will guide you how to fix NetLogon Owners and make them compliant.'
New-HTMLWizard {
New-HTMLWizardStep -Name 'Prepare environment' {
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
New-HTMLCodeBlock -Code {
Install-Module GPOZaurr -Force
Import-Module GPOZaurr -Force
} -Style powershell
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
}
New-HTMLWizardStep -Name 'Prepare report' {
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:"
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrNetLogonBefore.html -Verbose -Type NetLogon
}
New-HTMLText -TextBlock {
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
}
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
New-HTMLCodeBlock -Code {
$NetLogonOutput = Get-GPOZaurrNetLogon -Verbose
$NetLogonOutput | Format-Table
}
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
}
New-HTMLWizardStep -Name 'Set non-compliant file owners to BUILTIN\Administrators' {
New-HTMLText -Text "Following command when executed runs internally command that lists all file owners and if it doesn't match changes it BUILTIN\Administrators. It doesn't change compliant owners."
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
New-HTMLCodeBlock -Code {
Repair-GPOZaurrNetLogonOwner -Verbose -WhatIf
}
New-HTMLText -TextBlock {
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data. Once happy with results please follow with command: "
}
New-HTMLCodeBlock -Code {
Repair-GPOZaurrNetLogonOwner -Verbose -LimitProcessing 2
}
New-HTMLText -TextBlock {
"This command when executed sets new owner only on first X non-compliant NetLogon files. Use LimitProcessing parameter to prevent mass change and increase the counter when no errors occur."
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
}
}
New-HTMLWizardStep -Name 'Verification report' {
New-HTMLText -TextBlock {
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
}
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrNetLogonAfter.html -Verbose -Type NetLogon
}
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
}
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
}
}
}
}
New-HTMLTab -Name 'NetLogon Permissions' {
New-HTMLSection -Name 'NetLogon Files List' {
New-HTMLTable -DataTable $Script:Reporting['NetLogonPermissions']['Variables']['NonOwner'] -Filtering
}
}
}
}
+188
View File
@@ -0,0 +1,188 @@
$GPOZaurrOrphans = [ordered] @{
Name = 'Orphaned Group Policies'
Enabled = $true
ActionRequired = $null
Data = $null
Execute = {
Get-GPOZaurrBroken
}
Processing = {
foreach ($GPO in $Script:Reporting['GPOOrphans']['Data']) {
if ($GPO.Status -eq 'Not available in AD') {
$Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD']++
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted']++
} elseif ($GPO.Status -eq 'Not available on SYSVOL') {
$Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol']++
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted']++
} elseif ($GPO.Status -eq 'Permissions issue') {
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue']++
}
}
if ($Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted'].Count -gt 0) {
$Script:Reporting['GPOOrphans']['ActionRequired'] = $true
} else {
$Script:Reporting['GPOOrphans']['ActionRequired'] = $false
}
}
Variables = @{
NotAvailableInAD = 0
NotAvailableOnSysvol = 0
NotAvailablePermissionIssue = 0
ToBeDeleted = 0
}
Overview = {
New-HTMLPanel {
New-HTMLText -TextBlock {
"Group Policies are stored in two places - Active Directory (metadata) and SYSVOL (content)."
"Since those are managed in different ways, replicated in different ways it's possible because of different issues they get out of sync."
} -LineBreak
New-HTMLText -Text "For example:"
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'USN Rollback in AD could cause group policies to reappar in Active Directory, yet SYSVOL data would be unavailable'
New-HTMLListItem -Text 'Group Policy deletion failing to delete GPO content'
New-HTMLListItem -Text 'DFSR replication failing between DCs'
}
New-HTMLText -Text 'Following chart presents ', 'Broken / Orphaned Group Policies' -FontSize 10pt -FontWeight normal, bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'] -FontWeight normal, bold
New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue'] -FontWeight normal, bold
} -FontSize 10pt
New-HTMLText -FontSize 10pt -Text 'Those problems must be resolved before doing other clenaup activities.'
New-HTMLChart {
New-ChartBarOptions -Type barStacked
New-ChartLegend -Name 'Not in AD', 'Not on SYSVOL', 'Permissions Issue' -Color Crimson, LightCoral, IndianRed
New-ChartBar -Name 'Orphans' -Value $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue']
} -Title 'Broken / Orphaned Group Policies' -TitleAlignment center
}
}
Summary = {
New-HTMLText -TextBlock {
"Group Policies are stored in two places - Active Directory (metadata) and SYSVOL (content)."
"Since those are managed in different ways, replicated in different ways it's possible because of different issues they get out of sync."
} -FontSize 10pt
New-HTMLText -Text "For example:" -FontSize 10pt -FontWeight bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'USN Rollback in AD could cause already deleted Group Policies to reapper in Active Directory, yet SYSVOL data would be unavailable'
New-HTMLListItem -Text 'Group Policy deletion failing to delete GPO content'
New-HTMLListItem -Text 'Permission issue preventing deletion of GPO content'
New-HTMLListItem -Text 'Failing DFSR replication between DCs'
} -FontSize 10pt
New-HTMLText -Text 'Following problems were detected:' -FontSize 10pt -FontWeight bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'] -FontWeight normal, bold
New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue'] -FontWeight normal, bold
} -FontSize 10pt
New-HTMLText -Text "Please review output in table and follow the steps below table to get Active Directory Group Policies in healthy state." -FontSize 10pt
}
Solution = {
New-HTMLSection -Invisible {
New-HTMLPanel {
& $Script:GPOConfiguration['GPOOrphans']['Summary']
}
New-HTMLPanel {
New-HTMLChart {
New-ChartBarOptions -Type barStacked
New-ChartLegend -Name 'Not in AD', 'Not on SYSVOL', 'Permissions Issue' -Color Crimson, LightCoral, IndianRed
New-ChartBar -Name 'Orphans' -Value $Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol'], $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue']
} -Title 'Broken / Orphaned Group Policies' -TitleAlignment center
}
}
New-HTMLSection -Name 'Health State of Group Policies' {
New-HTMLTable -DataTable $Script:Reporting['GPOOrphans']['Data'] -Filtering {
New-HTMLTableCondition -Name 'Status' -Value "Not available in AD" -BackgroundColor Salmon -ComparisonType string
New-HTMLTableCondition -Name 'Status' -Value "Not available on SYSVOL" -BackgroundColor LightCoral -ComparisonType string
New-HTMLTableCondition -Name 'Status' -Value "Permissions issue" -BackgroundColor MediumVioletRed -ComparisonType string -Color White
} -PagingOptions 10, 20, 30, 40, 50
}
if ($Script:Reporting['GPOOrphans']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['GPOOrphans']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
New-HTMLSection -Name 'Steps to fix - Not available on SYSVOL / Active Directory' {
New-HTMLContainer {
New-HTMLSpanStyle -FontSize 10pt {
New-HTMLText -Text 'Following steps will guide you how to fix GPOs which are not available on SYSVOL or AD.'
New-HTMLWizard {
New-HTMLWizardStep -Name 'Prepare environment' {
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
New-HTMLCodeBlock -Code {
Install-Module GPOZaurr -Force
Import-Module GPOZaurr -Force
} -Style powershell
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
}
New-HTMLWizardStep -Name 'Prepare report' {
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:"
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrBrokenGpoBefore.html -Verbose -Type GPOOrphans
}
New-HTMLText -TextBlock {
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
}
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
New-HTMLCodeBlock -Code {
$GPOOutput = Get-GPOZaurrBroken
$GPOOutput | Format-Table
}
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
}
New-HTMLWizardStep -Name 'Fix GPOs not available on SYSVOL' {
New-HTMLText -Text "Following command when executed runs cleanup procedure that removes all broken GPOs on SYSVOL side."
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
New-HTMLCodeBlock -Code {
Remove-GPOZaurrBroken -Type SYSVOL -WhatIf
}
New-HTMLText -TextBlock {
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. Once happy with results please follow with command: "
}
New-HTMLCodeBlock -Code {
Remove-GPOZaurrBroken -Type SYSVOL -LimitProcessing 2 -BackupPath $Env:UserProfile\Desktop\GPOSYSVOLBackup
}
New-HTMLText -TextBlock {
"This command when executed deletes only first X broken GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur."
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
}
New-HTMLText -Text "If there's nothing else to be deleted on SYSVOL side, we can skip to next step step"
}
New-HTMLWizardStep -Name 'Fix GPOs not available on AD' {
New-HTMLText -Text "Following command when executed runs cleanup procedure that removes all broken GPOs on Active Directory side."
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
New-HTMLCodeBlock -Code {
Remove-GPOZaurrBroken -Type AD -WhatIf
}
New-HTMLText -TextBlock {
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. Once happy with results please follow with command: "
}
New-HTMLCodeBlock -Code {
Remove-GPOZaurrBroken -Type AD -LimitProcessing 2 -BackupPath $Env:UserProfile\Desktop\GPOSYSVOLBackup
}
New-HTMLText -TextBlock {
"This command when executed deletes only first X broken GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur."
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
}
New-HTMLText -Text "If there's nothing else to be deleted on AD side, we can skip to next step step"
}
New-HTMLWizardStep -Name 'Verification report' {
New-HTMLText -TextBlock {
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
}
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrBrokenGpoAfter.html -Verbose -Type GPOOrphans
}
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
}
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
}
}
}
}
}
+176
View File
@@ -0,0 +1,176 @@
$GPOZaurrOwners = [ordered] @{
Name = 'Group Policy Owners'
Enabled = $true
ActionRequired = $null
Data = $null
Execute = { Get-GPOZaurrOwner -IncludeSysvol }
Processing = {
foreach ($GPO in $Script:Reporting['GPOOwners']['Data']) {
if ($GPO.IsOwnerConsistent) {
$Script:Reporting['GPOOwners']['Variables']['IsConsistent']++
} else {
$Script:Reporting['GPOOwners']['Variables']['IsNotConsistent']++
}
if ($GPO.IsOwnerAdministrative) {
$Script:Reporting['GPOOwners']['Variables']['IsAdministrative']++
} else {
$Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative']++
}
if (($GPO.IsOwnerAdministrative -eq $false -or $GPO.IsOwnerConsistent -eq $false) -and $GPO.SysvolExists -eq $true) {
$Script:Reporting['GPOOwners']['Variables']['WillFix']++
} elseif ($GPO.SysvolExists -eq $false) {
$Script:Reporting['GPOOwners']['Variables']['RequiresDiffFix']++
} else {
$Script:Reporting['GPOOwners']['Variables']['WillNotTouch']++
}
}
if ($Script:Reporting['GPOOwners']['Variables']['WillFix'].Count -gt 0) {
$Script:Reporting['GPOOwners']['ActionRequired'] = $true
} else {
$Script:Reporting['GPOOwners']['ActionRequired'] = $false
}
}
Variables = @{
IsAdministrative = 0
IsNotAdministrative = 0
IsConsistent = 0
IsNotConsistent = 0
WillFix = 0
RequiresDiffFix = 0
WillNotTouch = 0
}
Overview = {
New-HTMLPanel {
New-HTMLText -Text 'Following chart presents Group Policy owners and whether they are administrative and consistent. By design an owner of Group Policy should be Domain Admins or Enterprise Admins group only to prevent malicious takeover. ', `
"It's also important that owner in Active Directory matches owner on SYSVOL (file system)." -FontSize 10pt
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'] -FontWeight normal, bold
New-HTMLListItem -Text 'Non-Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative'] -FontWeight normal, bold
New-HTMLListItem -Text "Owners consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsConsistent'] -FontWeight normal, bold
New-HTMLListItem -Text "Owners not-consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent'] -FontWeight normal, bold
} -FontSize 10pt
New-HTMLChart {
New-ChartBarOptions -Type barStacked
New-ChartLegend -Name 'Yes', 'No' -Color PaleGreen, Orchid
New-ChartBar -Name 'Is administrative' -Value $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'], $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative']
New-ChartBar -Name 'Is consistent' -Value $Script:Reporting['GPOOwners']['Variables']['IsConsistent'], $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent']
} -Title 'Group Policy Owners' -TitleAlignment center
}
}
Summary = {
New-HTMLText -FontSize 10pt -TextBlock {
"By default GPO creation is usually maintained by Domain Admins or Enterprise Admins. "
"When GPO is created by member of Domain Admins or Enterprise Admins group the GPO Owner is set to Domain Admins. "
"When GPO is created by member of Group Policy Creator Owners or other group has delegated rights to create a GPO the owner of said GPO is not Domain Admins group but is assigned to relevant user. "
"GPO Owners should be Domain Admins or Enterprise Admins to prevent abuse. If that isn't so it means owner is able to fully control GPO and potentially change it's settings in uncontrolled way. "
"While at the moment of creation of new GPO it's not a problem, in long term it's possible such person may no longer be admin, yet keep their rights over GPO. "
}
New-HTMLText -FontSize 10pt -TextBlock {
"As you're aware Group Policies are stored in 2 places. In Active Directory (metadata) and SYSVOL (settings). This means that there are 2 places where GPO Owners exists. "
"This also means that for multiple reasons AD and SYSVOL can be out of sync when it comes to their permissions which can lead to uncontrolled ability to modify them. "
"Ownership in Active Directory and Ownership of SYSVOL for said GPO are required to be the same. "
}
New-HTMLText -Text "Here's a short summary of ", "Group Policy Owners", ": " -FontSize 10pt -FontWeight normal, bold, normal
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'] -FontWeight normal, bold
New-HTMLListItem -Text 'Non-Administrative Owners: ', $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative'] -FontWeight normal, bold
New-HTMLListItem -Text "Owners consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsConsistent'] -FontWeight normal, bold
New-HTMLListItem -Text "Owners not-consistent in AD and SYSVOL: ", $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent'] -FontWeight normal, bold
} -FontSize 10pt
New-HTMLText -FontSize 10pt -Text "This gives us: "
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies requiring owner change: ', $Script:Reporting['GPOOwners']['Variables']['WillFix'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies which can't be fixed (no SYSVOL?): ", $Script:Reporting['GPOOwners']['Variables']['RequiresDiffFix'] -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies unaffected: ", $Script:Reporting['GPOOwners']['Variables']['WillNotTouch'] -FontWeight normal, bold
} -FontSize 10pt
}
Solution = {
New-HTMLSection -Invisible {
New-HTMLPanel {
& $Script:GPOConfiguration['GPOOwners']['Summary']
}
New-HTMLPanel {
New-HTMLChart {
New-ChartBarOptions -Type barStacked
New-ChartLegend -Name 'Yes', 'No' -Color LightGreen, Salmon
New-ChartBar -Name 'Is administrative' -Value $Script:Reporting['GPOOwners']['Variables']['IsAdministrative'], $Script:Reporting['GPOOwners']['Variables']['IsNotAdministrative']
New-ChartBar -Name 'Is consistent' -Value $Script:Reporting['GPOOwners']['Variables']['IsConsistent'], $Script:Reporting['GPOOwners']['Variables']['IsNotConsistent']
} -Title 'Group Policy Owners' -TitleAlignment center
}
}
New-HTMLSection -Name 'Group Policy Owners' {
New-HTMLTable -DataTable $Script:Reporting['GPOOwners']['Data'] -Filtering {
New-HTMLTableCondition -Name 'IsOwnerConsistent' -Value $false -BackgroundColor Salmon -ComparisonType string -Row
New-HTMLTableCondition -Name 'IsOwnerAdministrative' -Value $false -BackgroundColor Salmon -ComparisonType string -Row
} -PagingOptions 10, 20, 30, 40, 50
}
if ($Script:Reporting['GPOOwners']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['GPOOwners']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
New-HTMLSection -Name 'Steps to fix Group Policy Owners' {
New-HTMLContainer {
New-HTMLSpanStyle -FontSize 10pt {
New-HTMLText -Text 'Following steps will guide you how to fix group policy owners'
New-HTMLWizard {
New-HTMLWizardStep -Name 'Prepare environment' {
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
New-HTMLCodeBlock -Code {
Install-Module GPOZaurr -Force
Import-Module GPOZaurr -Force
} -Style powershell
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
}
New-HTMLWizardStep -Name 'Prepare report' {
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with fixing Group Policy Owners. To generate new report please use:"
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOOwnersBefore.html -Verbose -Type GPOOwners
}
New-HTMLText -TextBlock {
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
}
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
New-HTMLCodeBlock -Code {
$OwnersGPO = Get-GPOZaurrOwner -IncludeSysvol -Verbose
$OwnersGPO | Format-Table
}
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
}
New-HTMLWizardStep -Name 'Set GPO Owners to Administrative (Domain Admins)' {
New-HTMLText -Text "Following command will find any GPO which doesn't have proper GPO Owner (be it due to inconsistency or not being Domain Admin) and will enforce new GPO Owner. "
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
New-HTMLCodeBlock -Code {
Set-GPOZaurrOwner -Type All -Verbose -WhatIf
}
New-HTMLText -TextBlock {
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be changed matches expected data. Once happy with results please follow with command: "
}
New-HTMLCodeBlock -Code {
Set-GPOZaurrOwner -Type All -Verbose -LimitProcessing 2
}
New-HTMLText -TextBlock {
"This command when executed sets new owner only on first X non-compliant GPO Owners for AD/SYSVOL. Use LimitProcessing parameter to prevent mass change and increase the counter when no errors occur."
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
}
}
New-HTMLWizardStep -Name 'Verification report' {
New-HTMLText -TextBlock {
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
}
New-HTMLCodeBlock -Code {
Invoke-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrGPOOwnersAfter.html -Verbose -Type GPOOwners
}
New-HTMLText -Text "If everything is healthy in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
}
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
}
}
}
}
}
+30
View File
@@ -0,0 +1,30 @@
$GPOZaurrPassword = [ordered] @{
Name = 'Group Policy Passwords'
Enabled = $true
Action = $null
Data = $null
Execute = {
Get-GPOZaurrPassword
}
Processing = {
}
Variables = @{
}
Overview = {
}
Solution = {
New-HTMLTable -DataTable $Script:Reporting['GPOPassword']['Data'] -Filtering
if ($Script:Reporting['GPOPassword']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['GPOPassword']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
}
}
+29
View File
@@ -0,0 +1,29 @@
$GPOZaurrPermissions = [ordered] @{
Name = 'Group Policy Permissions'
Enabled = $true
Action = $null
Data = $null
Execute = {
Get-GPOZaurrPermission -Type All -IncludePermissionType GpoEditDeleteModifySecurity, GpoEdit, GpoCustom #-IncludeOwner
}
Processing = {
}
Variables = @{
}
Overview = {
}
Solution = {
New-HTMLTable -DataTable $Script:Reporting['GPOPermissions']['Data'] -Filtering
if ($Script:Reporting['GPOPermissions']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['GPOPermissions']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
}
}
@@ -0,0 +1,29 @@
$GPOZaurrPermissionsRoot = [ordered] @{
Name = 'Group Policies Root Permissions'
Enabled = $true
Action = $null
Data = $null
Execute = {
Get-GPOZaurrPermissionRoot -SkipNames
}
Processing = {
}
Variables = @{
}
Overview = {
}
Solution = {
New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRoot']['Data'] -Filtering
if ($Script:Reporting['GPOPermissionsRoot']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['GPOPermissionsRoot']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
}
}
@@ -0,0 +1,29 @@
$GPOZaurrSysVolLegacyFiles = [ordered] @{
Name = 'SYSVOL Legacy ADM Files'
Enabled = $false
Action = $null
Data = $null
Execute = {
Get-GPOZaurrLegacyFiles
}
Processing = {
}
Variables = @{
}
Overview = {
}
Solution = {
New-HTMLTable -DataTable $Script:Reporting['SysVolLegacyFiles']['Data'] -Filtering
if ($Script:Reporting['SysVolLegacyFiles']['WarningsAndErrors']) {
New-HTMLSection -Name 'Warnings & Errors to Review' {
New-HTMLTable -DataTable $Script:Reporting['SysVolLegacyFiles']['WarningsAndErrors'] -Filtering {
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
}
}
}
}
}
+1
View File
@@ -1,4 +1,5 @@
function New-GPOZaurrReportConsole {
[cmdletBinding()]
param(
[System.Collections.IDictionary] $Results
)
+24
View File
@@ -0,0 +1,24 @@
function Reset-GPOZaurrStatus {
param(
)
#if (-not $Script:GPOConfigurationClean) {
# $Script:GPOConfigurationClean = Copy-Dictionary -Dictionary $Script:GPOConfiguration
#} else {
# $Script:GPOConfiguration = Copy-Dictionary -Dictionary $Script:GPOConfigurationClean
#}
if (-not $Script:DefaultTypes) {
$Script:DefaultTypes = foreach ($T in $Script:GPOConfiguration.Keys) {
if ($Script:GPOConfiguration[$T].Enabled) {
$T
}
}
} else {
foreach ($T in $Script:GPOConfiguration.Keys) {
$Script:GPOConfiguration[$T]['Enabled'] = $false
}
foreach ($T in $Script:DefaultTypes) {
$Script:GPOConfiguration[$T]['Enabled'] = $true
}
}
}
+15
View File
@@ -0,0 +1,15 @@
$Script:GPOConfiguration = [ordered] @{
GPOOrphans = $GPOZaurrOrphans
GPOOwners = $GPOZaurrOwners
GPOConsistency = $GPOZaurrConsistency
GPODuplicates = $GPOZaurrDuplicates
GPOList = $GPOZaurrList
GPOPassword = $GPOZaurrPassword
GPOPermissions = $GPOZaurrPermissions
GPOPermissionsRoot = $GPOZaurrPermissionsRoot
GPOFiles = $GPOZaurrFiles
GPOBlockedInheritance = $GPOZaurrBlockedInheritance
GPOAnalysis = $GPOZaurrAnalysis
NetLogonPermissions = $GPOZaurrNetLogonPermissions
SysVolLegacyFiles = $GPOZaurrSysVolLegacyFiles
}
+4 -4
View File
@@ -13,7 +13,7 @@
[switch] $PermissionsOnly,
[switch] $OwnerOnly,
[switch] $Limited,
[switch] $ReturnObject,
[System.Collections.IDictionary] $ADAdministrativeGroups
)
Begin {
@@ -43,7 +43,7 @@
Write-Warning "Get-GPOZaurr - Failed to get GPOReport: $($_.Exception.Message). Skipping."
continue
}
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups -ReturnObject:$ReturnObject.IsPresent
} else {
$_
}
@@ -61,7 +61,7 @@
Write-Warning "Get-GPOZaurr - Failed to get GPOReport: $($_.Exception.Message). Skipping."
continue
}
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups -ReturnObject:$ReturnObject.IsPresent
} else {
$_
}
@@ -79,7 +79,7 @@
Write-Warning "Get-GPOZaurr - Failed to get GPOReport: $($_.Exception.Message). Skipping."
continue
}
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups
Get-XMLGPO -OwnerOnly:$OwnerOnly.IsPresent -XMLContent $XMLContent -GPO $_ -PermissionsOnly:$PermissionsOnly.IsPresent -ADAdministrativeGroups $ADAdministrativeGroups -ReturnObject:$ReturnObject.IsPresent
} else {
$_
}
+21
View File
@@ -0,0 +1,21 @@
function Get-GPOZaurrDuplicateObject {
[cmdletBinding()]
param(
[alias('ForestName')][string] $Forest,
[string[]] $ExcludeDomains,
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
[System.Collections.IDictionary] $ExtendedForestInformation
)
$getWinADDuplicateObjectSplat = @{
Forest = $Forest
IncludeDomains = $IncludeDomains
ExcludeDomains = $ExcludeDomains
ExtendedForestInformation = $ExtendedForestInformation
PartialMatchDistinguishedName = "*,CN=Policies,CN=System,DC=*"
Extended = $true
}
$DuplicateObjects = Get-WinADDuplicateObject @getWinADDuplicateObjectSplat
$DuplicateObjects
}
+1 -1
View File
@@ -20,7 +20,7 @@
}
}
foreach ($e in $err) {
Write-Warning "Get-GPOZaurrLegacy - $($e.Exception.Message) ($($e.CategoryInfo.Reason))"
Write-Warning "Get-GPOZaurrLegacyFiles - $($e.Exception.Message) ($($e.CategoryInfo.Reason))"
}
}
}
+45 -29
View File
@@ -2,66 +2,82 @@
[cmdletBinding(DefaultParameterSetName = 'Default')]
param(
[parameter(ParameterSetName = 'OwnerOnly')][switch] $OwnerOnly,
[parameter(ParameterSetName = 'SkipOwner')][switch] $SkipOwner
[parameter(ParameterSetName = 'SkipOwner')][switch] $SkipOwner,
[alias('ForestName')][string] $Forest,
[string[]] $ExcludeDomains,
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
[System.Collections.IDictionary] $ExtendedForestInformation
)
$ForestInformation = Get-WinADForestDetails -Extended
$ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
$FilesAll = foreach ($Domain in $ForestInformation.Domains) {
$Path = -join ("\\", $Domain, '\Netlogon')
$Files = Get-ChildItem -LiteralPath $Path -Recurse
foreach ($_ in $Files) {
$ACL = Get-Acl -Path $_.FullName
$PathOnSysvol = -join ("\\", $Domain, "\SYSVOL\", $Domain, "\Scripts")
$Files = Get-ChildItem -LiteralPath $Path -Recurse -Force -ErrorVariable Err -ErrorAction SilentlyContinue
foreach ($e in $err) {
Write-Warning "Get-GPOZaurrNetLogon - Listing file failed with error $($e.Exception.Message) ($($e.CategoryInfo.Reason))"
}
foreach ($File in $Files) {
try {
$ACL = Get-Acl -Path $File.FullName -ErrorAction Stop
} catch {
Write-Warning "Get-GPOZaurrNetLogon - ACL reading failed for $($File.FullName) with error $($_.Exception.Message) ($($_.CategoryInfo.Reason))"
}
if ($ACL.Owner) {
$IdentityOwner = Convert-Identity -Identity $ACL.Owner
$IdentityOwner = Convert-Identity -Identity $ACL.Owner -Verbose:$false
} else {
$IdentityOwner = [PSCustomObject] @{ SID = ''; Type = 'Uknown' }
}
if (-not $OwnerOnly) {
if (-not $SkipOwner) {
[PSCustomObject] @{
FullName = $_.FullName
Extension = $_.Extension
CreationTime = $_.CreationTime
LastAccessTime = $_.LastAccessTime
LastWriteTime = $_.LastWriteTime
Attributes = $_.Attributes
FullName = $File.FullName
Extension = $File.Extension
CreationTime = $File.CreationTime
LastAccessTime = $File.LastAccessTime
LastWriteTime = $File.LastWriteTime
Attributes = $File.Attributes
AccessControlType = 'Allow' # : Allow
Principal = $IdentityOwner.Name # : BUILTIN\Administrators
PrincipalSid = $IdentityOwner.SID
PrincipalType = $IdentityOwner.Type
FileSystemRights = 'Owner' # : FullControl
IsInherited = $false
FullNameOnSysVol = $File.FullName.Replace($Path, $PathOnSysvol)
#Owner = $ACL.Owner
}
}
$FilePermission = Get-FilePermissions -Path $_.FullName -ACLS $ACL
$FilePermission = Get-FilePermissions -Path $_.FullName -ACLS $ACL -Verbose:$false
foreach ($Perm in $FilePermission) {
$Identity = Convert-Identity -Identity $Perm.Principal
$Identity = Convert-Identity -Identity $Perm.Principal -Verbose:$false
[PSCustomObject] @{
FullName = $_.FullName
Extension = $_.Extension
CreationTime = $_.CreationTime
LastAccessTime = $_.LastAccessTime
LastWriteTime = $_.LastWriteTime
Attributes = $_.Attributes
FullName = $File.FullName
Extension = $File.Extension
CreationTime = $File.CreationTime
LastAccessTime = $File.LastAccessTime
LastWriteTime = $File.LastWriteTime
Attributes = $File.Attributes
AccessControlType = $Perm.AccessControlType # : Allow
Principal = $Identity.Name # : BUILTIN\Administrators
PrincipalSid = $Identity.SID
PrincipalType = $Identity.Type
FileSystemRights = $Perm.FileSystemRights # : FullControl
IsInherited = $Perm.IsInherited # : True
FullNameOnSysVol = $File.FullName.Replace($Path, $PathOnSysvol)
}
}
} else {
[PSCustomObject] @{
FullName = $_.FullName
Extension = $_.Extension
CreationTime = $_.CreationTime
LastAccessTime = $_.LastAccessTime
LastWriteTime = $_.LastWriteTime
Attributes = $_.Attributes
Owner = $IdentityOwner.Name
OwnerSid = $IdentityOwner.SID
OwnerType = $IdentityOwner.Type
FullName = $File.FullName
Extension = $File.Extension
CreationTime = $File.CreationTime
LastAccessTime = $File.LastAccessTime
LastWriteTime = $File.LastWriteTime
Attributes = $File.Attributes
Owner = $IdentityOwner.Name
OwnerSid = $IdentityOwner.SID
OwnerType = $IdentityOwner.Type
FullNameOnSysVol = $File.FullName.Replace($Path, $PathOnSysvol)
}
}
}
+57 -3
View File
@@ -1,10 +1,54 @@
function Get-GPOZaurrOwner {
<#
.SYNOPSIS
Gets owners of GPOs from Active Directory and SYSVOL
.DESCRIPTION
Gets owners of GPOs from Active Directory and SYSVOL
.PARAMETER GPOName
Name of GPO. By default all GPOs are returned
.PARAMETER GPOGuid
GUID of GPO. By default all GPOs are returned
.PARAMETER IncludeSysvol
Includes Owner from SYSVOL as well
.PARAMETER SkipBroken
Doesn't display GPOs that have no SYSVOL content (orphaned GPOs)
.PARAMETER Forest
Target different Forest
.PARAMETER ExcludeDomains
Exclude domain from search, by default whole forest is scanned
.PARAMETER IncludeDomains
Include only specific domains, by default whole forest is scanned
.PARAMETER ExtendedForestInformation
Ability to provide Forest Information from another command to speed up processing
.PARAMETER ADAdministrativeGroups
Ability to provide AD Administrative Groups from another command to speed up processing
.EXAMPLE
Get-GPOZaurrOwner -Verbose -IncludeSysvol
.EXAMPLE
Get-GPOZaurrOwner -Verbose -IncludeSysvol -SkipBroken
.NOTES
General notes
#>
[cmdletbinding(DefaultParameterSetName = 'Default')]
param(
[Parameter(ParameterSetName = 'GPOName')][string] $GPOName,
[Parameter(ParameterSetName = 'GPOGUID')][alias('GUID', 'GPOID')][string] $GPOGuid,
[switch] $IncludeSysvol,
[switch] $SkipBroken,
[alias('ForestName')][string] $Forest,
[string[]] $ExcludeDomains,
@@ -17,6 +61,7 @@
if (-not $ADAdministrativeGroups) {
$ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
}
$Count = 0
}
Process {
$getGPOZaurrADSplat = @{
@@ -32,8 +77,9 @@
}
$Objects = Get-GPOZaurrAD @getGPOZaurrADSplat
foreach ($_ in $Objects) {
Write-Verbose "Get-GPOZaurrOwner - Processing GPO: $($_.DisplayName) from domain: $($_.DomainName)"
$ACL = Get-ADACLOwner -ADObject $_.GPODistinguishedName -Resolve -ADAdministrativeGroups $ADAdministrativeGroups
$Count++
Write-Verbose "Get-GPOZaurrOwner - Processing GPO [$Count/$($Objects.Count)]: $($_.DisplayName) from domain: $($_.DomainName)"
$ACL = Get-ADACLOwner -ADObject $_.GPODistinguishedName -Resolve -ADAdministrativeGroups $ADAdministrativeGroups -Verbose:$false
$Object = [ordered] @{
DisplayName = $_.DisplayName
DomainName = $_.DomainName
@@ -43,16 +89,24 @@
OwnerType = $ACL.OwnerType
}
if ($IncludeSysvol) {
$FileOwner = Get-FileOwner -JustPath -Path $_.Path -Resolve
$FileOwner = Get-FileOwner -JustPath -Path $_.Path -Resolve -Verbose:$false
$Object['SysvolOwner'] = $FileOwner.OwnerName
$Object['SysvolSid'] = $FileOwner.OwnerSid
$Object['SysvolType'] = $FileOwner.OwnerType
$Object['SysvolPath'] = $_.Path
$Object['IsOwnerConsistent'] = if ($ACL.OwnerName -eq $FileOwner.OwnerName) { $true } else { $false }
$Object['IsOwnerAdministrative'] = if ($Object['SysvolType'] -eq 'Administrative' -and $Object['OwnerType'] -eq 'Administrative') { $true } else { $false }
if (Test-Path -LiteralPath $Object['SysvolPath']) {
$Object['SysvolExists'] = $true
} else {
$Object['SysvolExists'] = $false
}
} else {
$Object['IsOwnerAdministrative'] = if ($Object['OwnerType'] -eq 'Administrative') { $true } else { $false }
}
if ($SkipBroken -and $Object['SysvolExists'] -eq $false) {
continue
}
$Object['DistinguishedName'] = $_.GPODistinguishedName
[PSCUstomObject] $Object
}
+84 -103
View File
@@ -1,4 +1,35 @@
function Get-GPOZaurrPassword {
<#
.SYNOPSIS
Tries to find CPassword in Group Policies or given path and translate it to readable value
.DESCRIPTION
Tries to find CPassword in Group Policies or given path and translate it to readable value
.PARAMETER Forest
Specify forest name. By default current forest is used
.PARAMETER ExcludeDomains
Exclude Domain or Domains
.PARAMETER IncludeDomains
Include only certain Domain or Domains
.PARAMETER ExtendedForestInformation
Ability to provide extended forest information in advanced scenarios
.PARAMETER GPOPath
Path where Group Policy content is located or where backup is located
.EXAMPLE
Get-GPOZaurrPassword -GPOPath 'C:\Users\przemyslaw.klys\Desktop\GPOExport_2020.10.12'
.EXAMPLE
Get-GPOZaurrPassword
.NOTES
General notes
#>
[cmdletBinding()]
param(
[alias('ForestName')][string] $Forest,
@@ -7,115 +38,65 @@
[System.Collections.IDictionary] $ExtendedForestInformation,
[string[]] $GPOPath
)
if (-not $GPOPath) {
if (-not $ExtendedForestInformation) {
$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
} else {
$ForestInformation = $ExtendedForestInformation
}
[Array] $GPOPath = foreach ($Domain in $ForestInformation.Domains) {
-join ('\\', $Domain, '\SYSVOL\', $Domain, '\Policies')
}
}
if (-not $GPOPath) {
return
}
foreach ($Path in $GPOPath) {
#Extract the all XML files in the Folders
$Items = Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml
$Output = foreach ($XMLFileName in $Items) {
#Convert XML in a String file
[string]$XMLString = Get-Content ($XMLFileName.FullName)
#Check if Cpassword Exist in the file
if ($XMLString.Contains("cpassword")) {
#Take the Cpassword Value from XML String file
[string]$Cpassword = [regex]::matches($XMLString, '(cpassword=).+?(?=\")')
$Cpassword = $Cpassword.split('(\")')[1]
#Check if Cpassword has a value
if ($Cpassword.Length -gt 20 -and $Cpassword -notlike '*cpassword*') {
$Mod = ($Cpassword.length % 4)
switch ($Mod) {
'1' { $Cpassword = $Cpassword.Substring(0, $Cpassword.Length - 1) }
'2' { $Cpassword += ('=' * (4 - $Mod)) }
'3' { $Cpassword += ('=' * (4 - $Mod)) }
}
$Base64Decoded = [Convert]::FromBase64String($Cpassword)
$AesObject = [System.Security.Cryptography.AesCryptoServiceProvider]::new()
#Use th AES Key
[Byte[]] $AesKey = @(0x4e, 0x99, 0x06, 0xe8, 0xfc, 0xb6, 0x6c, 0xc9, 0xfa, 0xf4, 0x93, 0x10, 0x62, 0x0f, 0xfe, 0xe8, 0xf4, 0x96, 0xe8, 0x06, 0xcc, 0x05, 0x79, 0x90, 0x20, 0x9b, 0x09, 0xa4, 0x33, 0xb6, 0x6c, 0x1b)
$AesIV = New-Object Byte[]($AesObject.IV.Length)
$AesObject.IV = $AesIV
$AesObject.Key = $AesKey
$DecryptorObject = $AesObject.CreateDecryptor()
[Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
#Convert Hash variable in a String valute
$Password = [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
} else {
$Password = ''
}
#[string]$GPOguid = [regex]::matches($XMLFileName.DirectoryName, '(?<=\{).+?(?=\})')
#$GPODetail = Get-GPO -guid $GPOguid
[xml] $XMLContent = $XMLString
#if (-not $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.Properties.cpassword -and -not $XMLContent.gpo.User.ExtensionData.Extension.DriveMapSettings.Drive.Properties.cpassword) {
#Write-Host ''
#}
if ($GPOPath) {
foreach ($Path in $GPOPath) {
$Items = Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml -ErrorAction SilentlyContinue -ErrorVariable err
$Output = foreach ($XMLFileName in $Items) {
$Password = Find-GPOPassword -Path $XMLFileName.FullName
if ($Password) {
$PasswordStatus = $true
} else {
$PasswordStatus = $false
if ($XMLFileName.FullName -match '{\w{8}-\w{4}-\w{4}-\w{4}-\w{12}}') {
$GPOGUID = $matches[0]
}
[PSCustomObject] @{
RootPath = $Path
PasswordFile = $XMLFileName.FullName
GUID = $GPOGUID
Password = $Password
}
}
[PsCustomObject] @{
'Name' = $XMLContent.GPO.Name
'Links' = $XMLContent.GPO.LinksTo #| Select-Object -ExpandProperty SOMPath
'Enabled' = $XMLContent.GPO.GpoStatus
'PasswordStatus' = $PasswordStatus
#'GPO' = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups
'User' = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.name
'Cpassword' = $XMLContent.gpo.Computer.ExtensionData.Extension.LocalUsersAndGroups.User.Properties.cpassword
'CpasswordMap' = $XMLContent.gpo.User.ExtensionData.Extension.DriveMapSettings.Drive.Properties.cpassword
'Password' = $Password
'GUID' = $XMLContent.GPO.Identifier.Identifier.InnerText
'Domain' = $XMLContent.GPO.Identifier.Domain
'ComputerSettingsAvailable' = if ($null -eq $XMLContent.GPO.Computer.ExtensionData) { $false } else { $true }
'ComputerSettingsStatus' = if ($XMLContent.GPO.Computer.VersionDirectory -eq 0 -and $XMLContent.GPO.Computer.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" }
'ComputerEnabled' = [bool] $XMLContent.GPO.Computer.Enabled
'ComputerSetttingsVersionIdentical' = if ($XMLContent.GPO.Computer.VersionDirectory -eq $XMLContent.GPO.Computer.VersionSysvol) { $true } else { $false }
'ComputerSettings' = $XMLContent.GPO.Computer.ExtensionData.Extension
'UserSettingsAvailable' = if ($null -eq $XMLContent.GPO.User.ExtensionData) { $false } else { $true }
'UserEnabled' = [bool] $XMLContent.GPO.User.Enabled
'UserSettingsStatus' = if ($XMLContent.GPO.User.VersionDirectory -eq 0 -and $XMLContent.GPO.User.VersionSysvol -eq 0) { "NeverModified" } else { "Modified" }
'UserSettingsVersionIdentical' = if ($XMLContent.GPO.User.VersionDirectory -eq $XMLContent.GPO.User.VersionSysvol) { $true } else { $false }
'UserSettings' = $XMLContent.GPO.User.ExtensionData.Extension
'CreationTime' = [DateTime] $XMLContent.GPO.CreatedTime
'ModificationTime' = [DateTime] $XMLContent.GPO.ModifiedTime
'ReadTime' = [DateTime] $XMLContent.GPO.ReadTime
'WMIFilter' = $GPO.WmiFilter.name
'WMIFilterDescription' = $GPO.WmiFilter.Description
'Path' = $GPO.Path
#'SDDL' = if ($Splitter -ne '') { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' -join $Splitter } else { $XMLContent.GPO.SecurityDescriptor.SDDL.'#text' }
'ACL' = $XMLContent.GPO.SecurityDescriptor.Permissions.TrusteePermissions | ForEach-Object -Process {
}
}
} else {
$ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
foreach ($Domain in $ForestInformation.Domains) {
$Path = -join ('\\', $Domain, '\SYSVOL\', $Domain, '\Policies')
#Extract the all XML files in the Folder
$Items = Get-ChildItem -LiteralPath $Path -Recurse -Filter *.xml -ErrorAction SilentlyContinue -ErrorVariable err
$Output = foreach ($XMLFileName in $Items) {
$Password = Find-GPOPassword -Path $XMLFileName.FullName
if ($Password) {
# match regex
if ($XMLFileName.FullName -match '{\w{8}-\w{4}-\w{4}-\w{4}-\w{12}}') {
$GPOGUID = $matches[0]
$GPO = Get-GPOZaurrAD -GPOGuid $GPOGUID -IncludeDomains $Domain
[PSCustomObject] @{
'User' = $_.trustee.name.'#Text'
'Permission Type' = $_.type.PermissionType
'Inherited' = $_.Inherited
'Permissions' = $_.Standard.GPOGroupedAccessEnum
DisplayName = $GPO.DisplayName
DomainName = $GPO.DomainName
GUID = $GPO.GUID
PasswordFile = $XMLFileName.FullName
Password = $Password
Created = $GPO.Created
Modified = $GPO.Modified
Description = $GPO.Description
}
} else {
[PSCustomObject] @{
DisplayName = ''
DomainName = ''
GUID = ''
PasswordFile = $XMLFileName.FullName
Password = $Password
Created = ''
Modified = ''
Description = ''
}
}
}
#Write-Host "I find a Password [ " $Password " ] The GPO named:" $GPODetail" and th file is:" $XMLFileName
} #if($XMLContent.Contains("cpassword")
}
foreach ($e in $err) {
Write-Warning "Get-GPOZaurrPassword - $($e.Exception.Message) ($($e.CategoryInfo.Reason))"
}
$Output
}
$Output
}
}
+31 -18
View File
@@ -44,32 +44,45 @@
$GroupPolicies = Get-GPO @getGPOSplat
$Count = 0
$GroupPolicies | ForEach-Object -Process {
$GPO = $_
$Count++
Write-Verbose "Get-GPOZaurrPermissionConsistency - Processing [$($_.DomainName)]($Count/$($GroupPolicies.Count)) $($_.DisplayName)"
try {
$IsConsistent = $_.IsAclConsistent()
$ErrorMessage = ''
} catch {
$ErrorMessage = $_.Exception.Message
Write-Warning "Get-GPOZaurrPermissionConsistency - Failed to get consistency: $($_.Exception.Message)."
$IsConsistent = 'Not available'
$SysVolpath = -join ('\\', $Domain, '\sysvol\', $Domain, '\Policies\{', $GPO.ID.GUID, '}')
if (Test-Path -LiteralPath $SysVolpath) {
try {
$IsConsistent = $GPO.IsAclConsistent()
$ErrorMessage = ''
} catch {
$ErrorMessage = $_.Exception.Message
Write-Warning "Get-GPOZaurrPermissionConsistency - Processing $($GPO.DisplayName) / $($GPO.DomainName) failed to get consistency with error: $($_.Exception.Message)."
$IsConsistent = 'Not available'
}
} else {
Write-Warning "Get-GPOZaurrPermissionConsistency - Processing $($GPO.DisplayName) / $($GPO.DomainName) failed as path $SysvolPath doesn't exists!"
$IsConsistent = $false
}
$SysVolpath = -join ('\\', $Domain, '\sysvol\', $Domain, '\Policies\{', $_.ID.GUID, '}')
if ($VerifyInheritance) {
$FolderPermissions = Get-WinADSharePermission -Path $SysVolpath -Verbose:$false
if ($FolderPermissions) {
[Array] $NotInheritedPermissions = foreach ($File in $FolderPermissions) {
if ($File.Path -ne $SysVolpath -and $File.IsInherited -eq $false) {
$File
if ($IsConsistent -eq $true) {
$FolderPermissions = Get-WinADSharePermission -Path $SysVolpath -Verbose:$false
if ($FolderPermissions) {
[Array] $NotInheritedPermissions = foreach ($File in $FolderPermissions) {
if ($File.Path -ne $SysVolpath -and $File.IsInherited -eq $false) {
$File
}
}
if ($NotInheritedPermissions.Count -eq 0) {
$ACLConsistentInside = $true
} else {
$ACLConsistentInside = $false
}
}
if ($NotInheritedPermissions.Count -eq 0) {
$ACLConsistentInside = $true
} else {
$ACLConsistentInside = $false
$ACLConsistentInside = 'Not available'
$NotInheritedPermissions = $null
}
} else {
$ACLConsistentInside = 'Not available'
# Since top level permissions are inconsistent we don't even try to asses inside permissions
$ACLConsistentInside = $IsConsistent
$NotInheritedPermissions = $null
}
}
$Object = [ordered] @{
+111 -235
View File
@@ -1,263 +1,139 @@
function Invoke-GPOZaurr {
[alias('Find-GPO')]
[cmdletBinding(DefaultParameterSetName = 'Default')]
[alias('Show-GPOZaurr', 'Show-GPO')]
[cmdletBinding()]
param(
[Parameter(ParameterSetName = 'Default')][alias('ForestName')][string] $Forest,
[Parameter(ParameterSetName = 'Default')][string[]] $ExcludeDomains,
[Parameter(ParameterSetName = 'Default')][alias('Domain', 'Domains')][string[]] $IncludeDomains,
[Parameter(ParameterSetName = 'Default')][System.Collections.IDictionary] $ExtendedForestInformation,
[Parameter(ParameterSetName = 'Local')][string] $GPOPath,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[string] $FilePath,
[string[]] $Type,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[string] $Splitter = [System.Environment]::NewLine,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $FullObjects,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[ValidateSet('HTML', 'Object')][string[]] $OutputType = 'Object',
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[string] $OutputPath,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $Open,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $Online,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $CategoriesOnly,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $SingleObject,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $SkipNormalize,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $SkipCleanup,
[switch] $Extended
[switch] $PassThru,
[switch] $HideHTML
)
if ($Type.Count -eq 0) {
$Type = $Script:GPODitionary.Keys
Reset-GPOZaurrStatus # This makes sure types are at it's proper status
$Script:Reporting = [ordered] @{}
$Script:Reporting['Version'] = Get-GitHubVersion -Cmdlet 'Invoke-GPOZaurr' -RepositoryOwner 'evotecit' -RepositoryName 'GPOZaurr'
Write-Color '[i]', "[GPOZaurr] ", 'Version', ' [Informative] ', $Script:Reporting['Version'] -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
# Verify requested types are supported
$Supported = [System.Collections.Generic.List[string]]::new()
[Array] $NotSupported = foreach ($T in $Type) {
if ($T -notin $Script:GPOConfiguration.Keys ) {
$T
} else {
$Supported.Add($T)
}
}
if ($GPOPath) {
Write-Verbose "Invoke-GPOZaurr - Reading GPOs from $GPOPath"
if (Test-Path -LiteralPath $GPOPath) {
$GPOFiles = Get-ChildItem -LiteralPath $GPOPath -Recurse -File -Filter *.xml
[Array] $GPOs = foreach ($File in $GPOFiles) {
if ($File.Name -ne 'GPOList.xml') {
try {
[xml] $GPORead = Get-Content -LiteralPath $File.FullName
} catch {
Write-Warning "Invoke-GPOZaurr - Couldn't process $($File.FullName) error: $($_.Exception.message)"
continue
}
if ($Supported) {
Write-Color '[i]', "[GPOZaurr] ", 'Supported types', ' [Informative] ', "Chosen by user: ", ($Supported -join ', ') -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta
}
if ($NotSupported) {
Write-Color '[i]', "[GPOZaurr] ", 'Not supported types', ' [Informative] ', "Following types are not supported: ", ($NotSupported -join ', ') -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta
Write-Color '[i]', "[GPOZaurr] ", 'Not supported types', ' [Informative] ', "Please use one/multiple from the list: ", ($Script:GPOConfiguration.Keys -join ', ') -Color Yellow, DarkGray, Yellow, DarkGray, Yellow, Magenta
return
}
# Lets make sure we only enable those types which are requestd by user
if ($Type) {
foreach ($T in $Script:GPOConfiguration.Keys) {
$Script:GPOConfiguration[$T].Enabled = $false
}
# Lets enable all requested ones
foreach ($T in $Type) {
$Script:GPOConfiguration[$T].Enabled = $true
}
}
# Build data
foreach ($T in $Script:GPOConfiguration.Keys) {
if ($Script:GPOConfiguration[$T].Enabled -eq $true) {
$Script:Reporting[$T] = [ordered] @{
Name = $Script:GPOConfiguration[$T].Name
ActionRequired = $null
Data = $null
WarningsAndErrors = $null
Time = $null
Summary = $null
Variables = Copy-Dictionary -Dictionary $Script:GPOConfiguration[$T]['Variables']
}
$TimeLogGPOList = Start-TimeLog
Write-Color -Text '[i]', '[Start] ', $($Script:GPOConfiguration[$T]['Name']) -Color Yellow, DarkGray, Yellow
$Script:Reporting[$T]['Data'] = Invoke-Command -ScriptBlock $Script:GPOConfiguration[$T]['Execute'] -WarningVariable CommandWarnings -ErrorVariable CommandErrors
Invoke-Command -ScriptBlock $Script:GPOConfiguration[$T]['Processing']
$Script:Reporting[$T]['WarningsAndErrors'] = @(
foreach ($War in $CommandWarnings) {
[PSCustomObject] @{
DisplayName = $GPORead.GPO.Name
DomainName = $GPORead.GPO.Identifier.Domain.'#text'
GUID = $GPORead.GPO.Identifier.Identifier.'#text' -replace '{' -replace '}'
GPOOutput = $GPORead
Type = 'Warning'
Comment = $War
Reason = ''
TargetName = ''
}
}
}
} else {
Write-Warning "Invoke-GPOZaurr - $GPOPath doesn't exists."
return
}
} else {
Write-Verbose "Invoke-GPOZaurr - Query AD for GPOs"
[Array] $GPOs = Get-GPOZaurrAD -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
}
# This caches single reports.
$TemporaryCachedSingleReports = [ordered] @{}
$TemporaryCachedSingleReports['ReportsSingle'] = [ordered] @{}
# This will be returned
$Output = [ordered] @{}
$Output['Reports'] = [ordered] @{}
$Output['CategoriesFull'] = [ordered] @{}
Write-Verbose "Invoke-GPOZaurr - Loading GPO Report to Categories"
[Array] $GPOCategories = foreach ($GPO in $GPOs) {
if ($GPOPath) {
$GPOOutput = $GPO.GPOOutput
} else {
[xml] $GPOOutput = Get-GPOReport -Guid $GPO.GUID -Domain $GPO.DomainName -ReportType Xml
}
Get-GPOCategories -GPO $GPO -GPOOutput $GPOOutput.GPO -Splitter $Splitter -FullObjects:$FullObjects -CachedCategories $Output['CategoriesFull']
}
$Output['Categories'] = $GPOCategories | Select-Object -Property * -ExcludeProperty DataSet
if ($CategoriesOnly) {
# Return Categories only
return $Output['Categories']
}
# We check our dictionary for reports that are based on reports to make sure we run CodeSingle separatly
[Array] $FindRequiredSingle = foreach ($Key in $Script:GPODitionary.Keys) {
$Script:GPODitionary[$Key].ByReports.Report
}
# Build reports based on categories
if ($Output['CategoriesFull'].Count -gt 0) {
foreach ($Report in $Type) {
Write-Verbose "Invoke-GPOZaurr - Processing report type $Report"
foreach ($CategoryType in $Script:GPODitionary[$Report].Types) {
$Category = $CategoryType.Category
$Settings = $CategoryType.Settings
# Those are checks for making sure we have data to be even able to process it
if (-not $Output['CategoriesFull'][$Category]) {
continue
}
if (-not $Output['CategoriesFull'][$Category][$Settings]) {
continue
}
# Translation
$CategorizedGPO = $Output['CategoriesFull'][$Category][$Settings]
foreach ($GPO in $CategorizedGPO) {
if (-not $Output['Reports'][$Report]) {
$Output['Reports'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
}
# Create temporary storage for "single gpo" reports
# it's required if we want to base reports on other reports later on
if (-not $TemporaryCachedSingleReports['ReportsSingle'][$Report]) {
$TemporaryCachedSingleReports['ReportsSingle'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
}
# Make sure translated gpo is null
$TranslatedGpo = $null
if ($SingleObject -or ($Report -in $FindRequiredSingle)) {
# We either create 1 GPO with multiple settings to return it as user requested it
# Or we process it only because we need to base it for reports based on other reports
if (-not $Script:GPODitionary[$Report]['CodeSingle']) {
# sometimes code and code single are identical. To not define things two times, one can just skip it
If ($Script:GPODitionary[$Report]['Code']) {
$Script:GPODitionary[$Report]['CodeSingle'] = $Script:GPODitionary[$Report]['Code']
}
}
if ($Script:GPODitionary[$Report]['CodeSingle']) {
#Write-Verbose "Invoke-GPOZaurr - Processing $Report single entry mode"
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['CodeSingle']
if ($Report -in $FindRequiredSingle) {
foreach ($T in $TranslatedGpo) {
$TemporaryCachedSingleReports['ReportsSingle'][$Report].Add($T)
}
}
if ($SingleObject) {
foreach ($T in $TranslatedGpo) {
$Output['Reports'][$Report].Add($T)
}
}
}
}
if (-not $SingleObject) {
# We want each GPO to be listed multiple times if it makes sense for reporting
# think drive mapping - showing 1 mapping of a drive per object even if there are 50 drive mappings within 1 gpo
# this would result in 50 objects created
if ($Script:GPODitionary[$Report]['Code']) {
#Write-Verbose "Invoke-GPOZaurr - Processing $Report multi entry mode"
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['Code']
foreach ($T in $TranslatedGpo) {
$Output['Reports'][$Report].Add($T)
}
}
foreach ($Err in $CommandErrors) {
[PSCustomObject] @{
Type = 'Error'
Comment = $Err
Reason = $Err.CategoryInfo.Reason
TargetName = $Err.CategoryInfo.TargetName
}
}
}
}
}
# Those reports are based on other reports (for example already processed registry settings)
# This is useful where going thru registry collections may not be efficient enough to try and read it directly again
foreach ($Report in $Type) {
foreach ($ReportType in $Script:GPODitionary[$Report].ByReports) {
if (-not $Output['Reports'][$Report]) {
$Output['Reports'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
}
$FindReport = $ReportType.Report
Write-Verbose "Invoke-GPOZaurr - Processing reports based on other report $Report ($FindReport)"
foreach ($GPO in $TemporaryCachedSingleReports['ReportsSingle'][$FindReport]) {
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['CodeReport']
foreach ($T in $TranslatedGpo) {
$Output['Reports'][$Report].Add($T)
}
}
}
}
# Normalize - meaning that before we return each GPO report we make sure that each entry has the same column names regardless which one is first.
# Normally if you would have a GPO with just 2 entries for given subject (say LAPS), and then another GPO having 5 settings for the same type
# and you would display them one after another - all entries would be shown using first object which has less properties then 2nd or 3rd object
# to make sure all objects are having same (even empty) properties we "normalize" it
if (-not $SkipNormalize) {
foreach ($Report in [string[]] $Output['Reports'].Keys) {
$FirstProperties = 'DisplayName', 'DomainName', 'GUID', 'GpoType'
#$EndProperties = 'CreatedTime', 'ModifiedTime', 'ReadTime', 'Filters', 'Linked', 'LinksCount', 'Links'
$EndProperties = 'Filters', 'Linked', 'LinksCount', 'Links'
$Properties = $Output['Reports'][$Report] | Select-Properties -ExcludeProperty ($FirstProperties + $EndProperties) -AllProperties -WarningAction SilentlyContinue
$DisplayProperties = @(
$FirstProperties
foreach ($Property in $Properties) {
$Property
}
$EndProperties
)
$Output['Reports'][$Report] = $Output['Reports'][$Report] | Select-Object -Property $DisplayProperties
if ($Script:GPOConfiguration[$T]['Summary']) {
$Script:Reporting[$T]['Summary'] = Invoke-Command -ScriptBlock $Script:GPOConfiguration[$T]['Summary']
}
$TimeEndGPOList = Stop-TimeLog -Time $TimeLogGPOList -Option OneLiner
$Script:Reporting[$T]['Time'] = $TimeEndGPOList
Write-Color -Text '[i]', '[End ] ', $($Script:GPOConfiguration[$T]['Name']), " [Time to execute: $TimeEndGPOList]" -Color Yellow, DarkGray, Yellow, DarkGray
}
}
$Output['PoliciesTotal'] = $Output.Reports.Policies.PolicyCategory | Group-Object | Select-Object Name, Count | Sort-Object -Property Name #-Descending
#$Output['PoliciesTotal'] = $Output.Reports.Policies.PolicyCategory | Group-Object | Select-Object Name, Count | Sort-Object -Property Count -Descending
# Generate pretty HTML
$TimeLogHTML = Start-TimeLog
Write-Color -Text '[i]', '[HTML ] ', 'Generating HTML report' -Color Yellow, DarkGray, Yellow
New-HTML -Author 'Przemysław Kłys' -TitleText 'GPOZaurr Report' {
New-HTMLTabStyle -BorderRadius 0px -TextTransform capitalize -BackgroundColorActive SlateGrey
New-HTMLSectionStyle -BorderRadius 0px -HeaderBackGroundColor Grey -RemoveShadow
New-HTMLPanelStyle -BorderRadius 0px
New-HTMLTableOption -DataStore JavaScript -BoolAsString
if (-not $SkipCleanup) {
Write-Verbose "Invoke-GPOZaurr - Cleaning up output"
Remove-EmptyValue -Hashtable $Output -Recursive
}
if ($Extended) {
$Output
} else {
if ($Output.Reports) {
if ($OutputType -eq 'Object') {
$Output.Reports
New-HTMLHeader {
New-HTMLSection -Invisible {
New-HTMLSection {
New-HTMLText -Text "Report generated on $(Get-Date)" -Color Blue
} -JustifyContent flex-start -Invisible
New-HTMLSection {
New-HTMLText -Text "GPOZaurr - $($Script:Reporting['Version'])" -Color Blue
} -JustifyContent flex-end -Invisible
}
if ($OutputType -eq 'HTML') {
if (-not $OutputPath) {
$OutputPath = Get-FileName -Extension 'html' -Temporary
Write-Warning "Invoke-GPOZaurr - OutputPath not given. Using $OutputPath"
}
if ($Type.Count -eq 1) {
foreach ($T in $Script:GPOConfiguration.Keys) {
if ($Script:GPOConfiguration[$T].Enabled -eq $true) {
& $Script:GPOConfiguration[$T]['Solution']
}
Write-Verbose "Invoke-GPOZaurr - Generating HTML output"
New-HTML {
New-HTMLSectionStyle -BorderRadius 0px -HeaderBackGroundColor Grey -RemoveShadow
New-HTMLTabStyle -BorderRadius 0px -TextTransform capitalize -BackgroundColorActive SlateGrey
New-HTMLTableOption -DataStore JavaScript
foreach ($Key in $Output.Reports.Keys) {
New-HTMLTab -Name $Key {
Write-Verbose "Invoke-GPOZaurr - Generating HTML Table for $Key"
New-HTMLTable -DataTable $Output.Reports[$Key] -Filtering -Title $Key
}
}
} -FilePath $OutputPath -ShowHTML:$Open -Online:$Online
}
} else {
Write-Warning "Invoke-GPOZaurr - There was no data output for requested types."
foreach ($T in $Script:GPOConfiguration.Keys) {
if ($Script:GPOConfiguration[$T].Enabled -eq $true) {
New-HTMLTab -Name $Script:GPOConfiguration[$T]['Name'] {
& $Script:GPOConfiguration[$T]['Solution']
}
}
}
}
} -Online -ShowHTML:(-not $HideHTML) -FilePath $FilePath
$TimeLogEndHTML = Stop-TimeLog -Time $TimeLogHTML -Option OneLiner
Write-Color -Text '[i]', '[HTML ] ', 'Generating HTML report', " [Time to execute: $TimeLogEndHTML]" -Color Yellow, DarkGray, Yellow, DarkGray
if ($PassThru) {
$Script:Reporting
}
Reset-GPOZaurrStatus # This makes sure types are at it's proper status
}
[scriptblock] $SourcesAutoCompleter = {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
$Script:GPODitionary.Keys | Sort-Object | Where-Object { $_ -like "*$wordToComplete*" }
$Script:GPOConfiguration.Keys | Sort-Object | Where-Object { $_ -like "*$wordToComplete*" }
}
Register-ArgumentCompleter -CommandName Invoke-GPOZaurr -ParameterName Type -ScriptBlock $SourcesAutoCompleter
+263
View File
@@ -0,0 +1,263 @@
function Invoke-GPOZaurrContent {
[alias('Find-GPO')]
[cmdletBinding(DefaultParameterSetName = 'Default')]
param(
[Parameter(ParameterSetName = 'Default')][alias('ForestName')][string] $Forest,
[Parameter(ParameterSetName = 'Default')][string[]] $ExcludeDomains,
[Parameter(ParameterSetName = 'Default')][alias('Domain', 'Domains')][string[]] $IncludeDomains,
[Parameter(ParameterSetName = 'Default')][System.Collections.IDictionary] $ExtendedForestInformation,
[Parameter(ParameterSetName = 'Local')][string] $GPOPath,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[string[]] $Type,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[string] $Splitter = [System.Environment]::NewLine,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $FullObjects,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[ValidateSet('HTML', 'Object')][string[]] $OutputType = 'Object',
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[string] $OutputPath,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $Open,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $Online,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $CategoriesOnly,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $SingleObject,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $SkipNormalize,
[Parameter(ParameterSetName = 'Default')]
[Parameter(ParameterSetName = 'Local')]
[switch] $SkipCleanup,
[switch] $Extended
)
if ($Type.Count -eq 0) {
$Type = $Script:GPODitionary.Keys
}
if ($GPOPath) {
Write-Verbose "Invoke-GPOZaurrContent - Reading GPOs from $GPOPath"
if (Test-Path -LiteralPath $GPOPath) {
$GPOFiles = Get-ChildItem -LiteralPath $GPOPath -Recurse -File -Filter *.xml
[Array] $GPOs = foreach ($File in $GPOFiles) {
if ($File.Name -ne 'GPOList.xml') {
try {
[xml] $GPORead = Get-Content -LiteralPath $File.FullName
} catch {
Write-Warning "Invoke-GPOZaurrContent - Couldn't process $($File.FullName) error: $($_.Exception.message)"
continue
}
[PSCustomObject] @{
DisplayName = $GPORead.GPO.Name
DomainName = $GPORead.GPO.Identifier.Domain.'#text'
GUID = $GPORead.GPO.Identifier.Identifier.'#text' -replace '{' -replace '}'
GPOOutput = $GPORead
}
}
}
} else {
Write-Warning "Invoke-GPOZaurrContent - $GPOPath doesn't exists."
return
}
} else {
Write-Verbose "Invoke-GPOZaurrContent - Query AD for GPOs"
[Array] $GPOs = Get-GPOZaurrAD -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
}
# This caches single reports.
$TemporaryCachedSingleReports = [ordered] @{}
$TemporaryCachedSingleReports['ReportsSingle'] = [ordered] @{}
# This will be returned
$Output = [ordered] @{}
$Output['Reports'] = [ordered] @{}
$Output['CategoriesFull'] = [ordered] @{}
Write-Verbose "Invoke-GPOZaurrContent - Loading GPO Report to Categories"
[Array] $GPOCategories = foreach ($GPO in $GPOs) {
if ($GPOPath) {
$GPOOutput = $GPO.GPOOutput
} else {
[xml] $GPOOutput = Get-GPOReport -Guid $GPO.GUID -Domain $GPO.DomainName -ReportType Xml
}
Get-GPOCategories -GPO $GPO -GPOOutput $GPOOutput.GPO -Splitter $Splitter -FullObjects:$FullObjects -CachedCategories $Output['CategoriesFull']
}
$Output['Categories'] = $GPOCategories | Select-Object -Property * -ExcludeProperty DataSet
if ($CategoriesOnly) {
# Return Categories only
return $Output['Categories']
}
# We check our dictionary for reports that are based on reports to make sure we run CodeSingle separatly
[Array] $FindRequiredSingle = foreach ($Key in $Script:GPODitionary.Keys) {
$Script:GPODitionary[$Key].ByReports.Report
}
# Build reports based on categories
if ($Output['CategoriesFull'].Count -gt 0) {
foreach ($Report in $Type) {
Write-Verbose "Invoke-GPOZaurrContent - Processing report type $Report"
foreach ($CategoryType in $Script:GPODitionary[$Report].Types) {
$Category = $CategoryType.Category
$Settings = $CategoryType.Settings
# Those are checks for making sure we have data to be even able to process it
if (-not $Output['CategoriesFull'][$Category]) {
continue
}
if (-not $Output['CategoriesFull'][$Category][$Settings]) {
continue
}
# Translation
$CategorizedGPO = $Output['CategoriesFull'][$Category][$Settings]
foreach ($GPO in $CategorizedGPO) {
if (-not $Output['Reports'][$Report]) {
$Output['Reports'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
}
# Create temporary storage for "single gpo" reports
# it's required if we want to base reports on other reports later on
if (-not $TemporaryCachedSingleReports['ReportsSingle'][$Report]) {
$TemporaryCachedSingleReports['ReportsSingle'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
}
# Make sure translated gpo is null
$TranslatedGpo = $null
if ($SingleObject -or ($Report -in $FindRequiredSingle)) {
# We either create 1 GPO with multiple settings to return it as user requested it
# Or we process it only because we need to base it for reports based on other reports
if (-not $Script:GPODitionary[$Report]['CodeSingle']) {
# sometimes code and code single are identical. To not define things two times, one can just skip it
If ($Script:GPODitionary[$Report]['Code']) {
$Script:GPODitionary[$Report]['CodeSingle'] = $Script:GPODitionary[$Report]['Code']
}
}
if ($Script:GPODitionary[$Report]['CodeSingle']) {
#Write-Verbose "Invoke-GPOZaurrContent - Processing $Report single entry mode"
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['CodeSingle']
if ($Report -in $FindRequiredSingle) {
foreach ($T in $TranslatedGpo) {
$TemporaryCachedSingleReports['ReportsSingle'][$Report].Add($T)
}
}
if ($SingleObject) {
foreach ($T in $TranslatedGpo) {
$Output['Reports'][$Report].Add($T)
}
}
}
}
if (-not $SingleObject) {
# We want each GPO to be listed multiple times if it makes sense for reporting
# think drive mapping - showing 1 mapping of a drive per object even if there are 50 drive mappings within 1 gpo
# this would result in 50 objects created
if ($Script:GPODitionary[$Report]['Code']) {
#Write-Verbose "Invoke-GPOZaurrContent - Processing $Report multi entry mode"
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['Code']
foreach ($T in $TranslatedGpo) {
$Output['Reports'][$Report].Add($T)
}
}
}
}
}
}
}
# Those reports are based on other reports (for example already processed registry settings)
# This is useful where going thru registry collections may not be efficient enough to try and read it directly again
foreach ($Report in $Type) {
foreach ($ReportType in $Script:GPODitionary[$Report].ByReports) {
if (-not $Output['Reports'][$Report]) {
$Output['Reports'][$Report] = [System.Collections.Generic.List[PSCustomObject]]::new()
}
$FindReport = $ReportType.Report
Write-Verbose "Invoke-GPOZaurrContent - Processing reports based on other report $Report ($FindReport)"
foreach ($GPO in $TemporaryCachedSingleReports['ReportsSingle'][$FindReport]) {
$TranslatedGpo = Invoke-Command -ScriptBlock $Script:GPODitionary[$Report]['CodeReport']
foreach ($T in $TranslatedGpo) {
$Output['Reports'][$Report].Add($T)
}
}
}
}
# Normalize - meaning that before we return each GPO report we make sure that each entry has the same column names regardless which one is first.
# Normally if you would have a GPO with just 2 entries for given subject (say LAPS), and then another GPO having 5 settings for the same type
# and you would display them one after another - all entries would be shown using first object which has less properties then 2nd or 3rd object
# to make sure all objects are having same (even empty) properties we "normalize" it
if (-not $SkipNormalize) {
foreach ($Report in [string[]] $Output['Reports'].Keys) {
$FirstProperties = 'DisplayName', 'DomainName', 'GUID', 'GpoType'
#$EndProperties = 'CreatedTime', 'ModifiedTime', 'ReadTime', 'Filters', 'Linked', 'LinksCount', 'Links'
$EndProperties = 'Filters', 'Linked', 'LinksCount', 'Links'
$Properties = $Output['Reports'][$Report] | Select-Properties -ExcludeProperty ($FirstProperties + $EndProperties) -AllProperties -WarningAction SilentlyContinue
$DisplayProperties = @(
$FirstProperties
foreach ($Property in $Properties) {
$Property
}
$EndProperties
)
$Output['Reports'][$Report] = $Output['Reports'][$Report] | Select-Object -Property $DisplayProperties
}
}
$Output['PoliciesTotal'] = $Output.Reports.Policies.PolicyCategory | Group-Object | Select-Object Name, Count | Sort-Object -Property Name #-Descending
#$Output['PoliciesTotal'] = $Output.Reports.Policies.PolicyCategory | Group-Object | Select-Object Name, Count | Sort-Object -Property Count -Descending
if (-not $SkipCleanup) {
Write-Verbose "Invoke-GPOZaurrContent - Cleaning up output"
Remove-EmptyValue -Hashtable $Output -Recursive
}
if ($Extended) {
$Output
} else {
if ($Output.Reports) {
if ($OutputType -eq 'Object') {
$Output.Reports
}
if ($OutputType -eq 'HTML') {
if (-not $OutputPath) {
$OutputPath = Get-FileName -Extension 'html' -Temporary
Write-Warning "Invoke-GPOZaurrContent - OutputPath not given. Using $OutputPath"
}
Write-Verbose "Invoke-GPOZaurrContent - Generating HTML output"
New-HTML {
New-HTMLSectionStyle -BorderRadius 0px -HeaderBackGroundColor Grey -RemoveShadow
New-HTMLTabStyle -BorderRadius 0px -TextTransform capitalize -BackgroundColorActive SlateGrey
New-HTMLTableOption -DataStore JavaScript
foreach ($Key in $Output.Reports.Keys) {
New-HTMLTab -Name $Key {
Write-Verbose "Invoke-GPOZaurrContent - Generating HTML Table for $Key"
New-HTMLTable -DataTable $Output.Reports[$Key] -Filtering -Title $Key
}
}
} -FilePath $OutputPath -ShowHTML:$Open -Online:$Online
}
} else {
Write-Warning "Invoke-GPOZaurrContent - There was no data output for requested types."
}
}
}
[scriptblock] $SourcesAutoCompleter = {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
$Script:GPODitionary.Keys | Sort-Object | Where-Object { $_ -like "*$wordToComplete*" }
}
Register-ArgumentCompleter -CommandName Invoke-GPOZaurrContent -ParameterName Type -ScriptBlock $SourcesAutoCompleter
+19
View File
@@ -0,0 +1,19 @@
function Optimize-GPOZaurr {
[cmdletBinding()]
param(
)
$GPOS = Get-GPOZaurr -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation
foreach ($GPO in $GPOS) {
if ($GPO.UserSettingsAvailable -eq $false -and $GPO.ComputerSettingsAvailable -eq $false) {
if ($GPO.Enabled -ne 'All setttings disabled') {
# $GPO
}
} elseif ($GPO.UserSettingsAvailable -eq $false) {
} elseif ($GPO.ComputerSettingsAvailable -eq $false) {
}
}
}
+26
View File
@@ -0,0 +1,26 @@
function Remove-GPOZaurrDuplicateObject {
[cmdletBinding(SupportsShouldProcess)]
param(
[int] $LimitProcessing = [int32]::MaxValue,
[alias('ForestName')][string] $Forest,
[string[]] $ExcludeDomains,
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
[System.Collections.IDictionary] $ExtendedForestInformation
)
$getGPOZaurrDuplicateObjectSplat = @{
Forest = $Forest
IncludeDomains = $IncludeDomains
ExcludeDomains = $ExcludeDomains
ExtendedForestInformation = $ExtendedForestInformation
}
$DuplicateGpoObjects = Get-GPOZaurrDuplicateObject @getGPOZaurrDuplicateObjectSplat
foreach ($Duplicate in $DuplicateGpoObjects | Select-Object -First $LimitProcessing) {
try {
Remove-ADObject -Identity $Duplicate.ObjectGUID -Recursive -ErrorAction Stop -Server $Duplicate.DomainName
} catch {
Write-Warning "Remove-GPOZaurrDuplicateObject - Deleting $($Duplicate.ConflictDN) / $($Duplicate.DomainName) via GUID: $($Duplicate.ObjectGUID) failed with error: $($_.Exception.Message)"
}
}
}
+36
View File
@@ -0,0 +1,36 @@
function Repair-GPOZaurrNetLogonOwner {
[cmdletBinding(SupportsShouldProcess)]
param(
[alias('ForestName')][string] $Forest,
[string[]] $ExcludeDomains,
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
[System.Collections.IDictionary] $ExtendedForestInformation,
[string] $Principal = 'S-1-5-32-544',
[int] $LimitProcessing = [int32]::MaxValue
)
$Identity = Convert-Identity -Identity $Principal -Verbose:$false
if ($Identity.Error) {
Write-Warning "Repair-GPOZaurrNetLogonOwner - couldn't convert Identity $Principal to desired name. Error: $($Identity.Error)"
return
}
$Principal = $Identity.Name
$getGPOZaurrNetLogonSplat = @{
OwnerOnly = $true
Forest = $Forest
IncludeDomains = $IncludeDomains
ExcludeDomains = $ExcludeDomains
ExtendedForestInformation = $ExtendedForestInformation
}
Get-GPOZaurrNetLogon @getGPOZaurrNetLogonSplat | Select-Object -First $LimitProcessing | Where-Object {
if ($_.OwnerSid -ne 'S-1-5-32-544') {
$_
}
} | ForEach-Object {
if ($PSCmdlet.ShouldProcess($_.FullName, "Setting NetLogon Owner to $($Principal)")) {
Set-FileOwner -JustPath -Path $_.FullName -Owner $Principal -Verbose:$true -WhatIf:$WhatIfPreference
}
}
}
+17 -14
View File
@@ -1,10 +1,10 @@
function Set-GPOZaurrOwner {
<#
.SYNOPSIS
Short description
Sets GPO Owner to Domain Admins or other choosen account
.DESCRIPTION
Long description
Sets GPO Owner to Domain Admins or other choosen account. GPO Owner is set in AD and SYSVOL unless specified otherwise. If account doesn't require change, no change is done.
.PARAMETER Type
Unknown - finds unknown Owners and sets them to Administrative (Domain Admins) or chosen principal
@@ -12,34 +12,34 @@
NotAdministrative - combination of Unknown/NotMatching and NotAdministrative - replace with chosen principal or Domain Admins if not specified
All - if Owner is known it checks if it's Administrative, if it sn't it fixes that. If owner is unknown it fixes it
.PARAMETER GPOName
Parameter description
Name of GPO. By default all GPOs are targetted
.PARAMETER GPOGuid
Parameter description
GUID of GPO. By default all GPOs are targetted
.PARAMETER Forest
Parameter description
Target different Forest
.PARAMETER ExcludeDomains
Parameter description
Exclude domain from search, by default whole forest is scanned
.PARAMETER IncludeDomains
Parameter description
Include only specific domains, by default whole forest is scanned
.PARAMETER ExtendedForestInformation
Parameter description
Ability to provide Forest Information from another command to speed up processing
.PARAMETER Principal
Parameter description
.PARAMETER SkipSysvol
Parameter description
Set GPO Owner only in Active Directory. By default GPO Owner is being set in both places
.PARAMETER LimitProcessing
Parameter description
Allows to specify maximum number of items that will be fixed in a single run. It doesn't affect amount of GPOs processed
.EXAMPLE
An example
Set-GPOZaurrOwner -Type All -Verbose -WhatIf -LimitProcessing 2
.NOTES
General notes
@@ -92,13 +92,16 @@
ExtendedForestInformation = $ExtendedForestInformation
ADAdministrativeGroups = $ADAdministrativeGroups
Verbose = $VerbosePreference
SkipBroken = $true
}
if ($GPOName) {
$getGPOZaurrOwnerSplat['GPOName'] = $GPOName
} elseif ($GPOGuid) {
$getGPOZaurrOwnerSplat['GPOGuid'] = $GPOGUiD
}
$Count = 0
Get-GPOZaurrOwner @getGPOZaurrOwnerSplat | Where-Object {
$Count++
if ($_.Owner) {
$AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($_.Owner)"]
} else {
@@ -161,15 +164,15 @@
}
if ($Action -eq 'OnlyGPO') {
Write-Verbose "Set-GPOZaurrOwner - Changing GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.Owner) (SID: $($GPO.OwnerSID)) to $DefaultPrincipal"
Set-ADACLOwner -ADObject $GPO.DistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference
Set-ADACLOwner -ADObject $GPO.DistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference
} elseif ($Action -eq 'OnlyFileSystem') {
if (-not $SkipSysvol) {
Write-Verbose "Set-GPOZaurrOwner - Changing Sysvol Owner GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.SysvolOwner) (SID: $($GPO.SysvolSid)) to $DefaultPrincipal"
Set-FileOwner -JustPath -Path $GPO.SysvolPath -Owner $DefaultPrincipal -Verbose:$true -WhatIf:$WhatIfPreference
Set-FileOwner -JustPath -Path $GPO.SysvolPath -Owner $DefaultPrincipal -Verbose:$true -WhatIf:$WhatIfPreference
}
} else {
Write-Verbose "Set-GPOZaurrOwner - Changing GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.Owner) (SID: $($GPO.OwnerSID)) to $DefaultPrincipal"
Set-ADACLOwner -ADObject $GPO.DistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference
Set-ADACLOwner -ADObject $GPO.DistinguishedName -Principal $DefaultPrincipal -Verbose:$false -WhatIf:$WhatIfPreference
if (-not $SkipSysvol) {
Write-Verbose "Set-GPOZaurrOwner - Changing Sysvol Owner GPO: $($GPO.DisplayName) from domain: $($GPO.DomainName) from owner $($GPO.SysvolOwner) (SID: $($GPO.SysvolSid)) to $DefaultPrincipal"
Set-FileOwner -JustPath -Path $GPO.SysvolPath -Owner $DefaultPrincipal -Verbose:$true -WhatIf:$WhatIfPreference
-466
View File
@@ -1,466 +0,0 @@
function Show-GPOZaurr {
[cmdletBinding()]
param(
[string] $FilePath,
[ValidateSet(
'GPOList', 'GPOOrphans', 'GPOPermissions', 'GPOPermissionsRoot', 'GPOFiles',
'GPOConsistency', 'GPOOwners', 'GPOAnalysis', 'NetLogon'
)][string[]] $Type
)
$Script:Reporting = [ordered] @{
}
# Provide version check for easy use
$GPOZaurrVersion = Get-Command -Name 'Show-GPOZaurr' -ErrorAction SilentlyContinue
[Array] $GitHubReleases = (Get-GitHubLatestRelease -Url "https://api.github.com/repos/evotecit/GpoZaurr/releases" -Verbose:$false)
$LatestVersion = $GitHubReleases[0]
if (-not $LatestVersion.Errors) {
if ($GPOZaurrVersion.Version -eq $LatestVersion.Version) {
$Script:Reporting['Version'] = "GPOZaurr Current/Latest: $($LatestVersion.Version) at $($LatestVersion.PublishDate)"
} elseif ($GPOZaurrVersion.Version -lt $LatestVersion.Version) {
$Script:Reporting['Version'] = "GPOZaurr Current: $($GPOZaurrVersion.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Update?"
} elseif ($GPOZaurrVersion.Version -gt $LatestVersion.Version) {
$Script:Reporting['Version'] = "GPOZaurr Current: $($GPOZaurrVersion.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Lucky you!"
}
} else {
$Script:Reporting['Version'] = "GPOZaurr Current: $($GPOZaurrVersion.Version)"
}
# Gather data
$TimeLog = Start-TimeLog
if ($Type -contains 'GPOList' -or $null -eq $Type) {
$TimeLogGPOList = Start-TimeLog
Write-Verbose -Message "Show-GPOZaurr - Processing GPO List"
$GPOSummary = Get-GPOZaurr
$GPOLinkedStatus = $GPOSummary.Where( { $_.Linked -eq $true }, 'split')
[Array] $GPONotLinked = $GPOLinkedStatus[1]
[Array] $GPOLinked = $GPOLinkedStatus[0]
$GPOEmptyStatus = $GPOSummary.Where( { $_.Empty -eq $true }, 'split' )
[Array] $GPOEmpty = $GPOEmptyStatus[0]
[Array] $GPONotEmpty = $GPOEmptyStatus[1]
$GPOTotal = $GPOSummary.Count
$TimeEndGPOList = Stop-TimeLog -Time $TimeLog -Option OneLiner
}
if ($Type -contains 'GPOOrphans' -or $null -eq $Type) {
#Write-Color -Text "[Info] ", "Processing GPOOrphans" -Color Yellow, White
Write-Verbose -Message "Show-GPOZaurr - Processing GPO Sysvol"
$GPOOrphans = Get-GPOZaurrBroken
$NotAvailableInAD = [System.Collections.Generic.List[PSCustomObject]]::new()
$NotAvailableOnSysvol = [System.Collections.Generic.List[PSCustomObject]]::new()
$NotAvailablePermissionIssue = [System.Collections.Generic.List[PSCustomObject]]::new()
foreach ($_ in $GPOOrphans) {
if ($_.Status -eq 'Not available in AD') {
$NotAvailableInAD.Add($NotAvailableInAD)
} elseif ($_.Status -eq 'Not available on SYSVOL') {
$NotAvailableOnSysvol.Add($NotAvailableInAD)
} elseif ( $_.Status -eq 'Permissions issue') {
$NotAvailablePermissionIssue.Add($NotAvailableInAD)
}
}
}
if ($Type -contains 'GPOPermissions' -or $null -eq $Type) {
#Write-Color -Text "[Info] ", "Processing GPOPermissions" -Color Yellow, White
Write-Verbose -Message "Show-GPOZaurr - Processing GPO Permissions"
$GPOPermissions = Get-GPOZaurrPermission -Type All -IncludePermissionType GpoEditDeleteModifySecurity, GpoEdit, GpoCustom -IncludeOwner
}
if ($Type -contains 'GPOConsistency' -or $null -eq $Type) {
Write-Verbose -Message "Show-GPOZaurr - Processing GPO Permissions Consistency"
$GPOPermissionsConsistency = Get-GPOZaurrPermissionConsistency -Type All -VerifyInheritance
[Array] $Inconsistent = $GPOPermissionsConsistency.Where( { $_.ACLConsistent -eq $true } , 'split' )
[Array] $InconsistentInside = $GPOPermissionsConsistency.Where( { $_.ACLConsistentInside -eq $true }, 'split' )
}
if ($Type -contains 'GPOPermissionsRoot' -or $null -eq $Type) {
Write-Verbose -Message "Show-GPOZaurr - Processing GPO Permissions Root"
$GPOPermissionsRoot = Get-GPOZaurrPermissionRoot -SkipNames
}
if ($Type -contains 'GPOOwners' -or $null -eq $Type) {
Write-Verbose "Show-GPOZaurr - Processing GPO Owners"
$GPOOwners = Get-GPOZaurrOwner -IncludeSysvol
$IsOwnerConsistent = $GPOOwners.Where( { $_.IsOwnerConsistent -eq $true } , 'split' )
$IsOwnerAdministrative = $GPOOwners.Where( { $_.IsOwnerAdministrative -eq $true } , 'split' )
}
if ($Type -contains 'NetLogon' -or $null -eq $Type) {
Write-Verbose "Get-GPOZaurrNetLogon - Processing NETLOGON Share"
$Netlogon = Get-GPOZaurrNetLogon
}
if ($Type -contains 'GPOAnalysis' -or $null -eq $Type) {
Write-Verbose "Show-GPOZaurr - Processing GPO Analysis"
$GPOContent = Invoke-GPOZaurr
}
if ($Type -contains 'GPOFiles') {
Write-Verbose "Show-GPOZaurr - Processing GPOFiles"
$GPOFiles = Get-GPOZaurrFiles
}
$TimeEnd = Stop-TimeLog -Time $TimeLog -Option OneLiner
# Generate pretty HTML
Write-Verbose "Show-GPOZaurr - Generating HTML"
New-HTML {
New-HTMLTabStyle -BorderRadius 0px -TextTransform capitalize -BackgroundColorActive SlateGrey
New-HTMLSectionStyle -BorderRadius 0px -HeaderBackGroundColor Grey -RemoveShadow
New-HTMLPanelStyle -BorderRadius 0px
New-HTMLTableOption -DataStore JavaScript -BoolAsString
New-HTMLHeader {
New-HTMLSection -Invisible {
New-HTMLSection {
New-HTMLText -Text "Report generated on $(Get-Date)" -Color Blue
} -JustifyContent flex-start -Invisible
New-HTMLSection {
New-HTMLText -Text $Script:Reporting['Version'] -Color Blue
} -JustifyContent flex-end -Invisible
}
}
New-HTMLTab -Name 'Overview' {
if ($Type -contains 'GPOConsistency' -or $Type -contains 'GPOList' -or $null -eq $Type) {
New-HTMLSection -Invisible {
if ($Type -contains 'GPOList' -or $null -eq $Type) {
New-HTMLPanel {
New-HTMLText -Text 'Following chart presents ', 'Linked / Empty and Unlinked Group Policies' -FontSize 10pt -FontWeight normal, bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies total: ', $GPOTotal -FontWeight normal, bold
New-HTMLListItem -Text 'Group Policies linked: ', $GPOLinked.Count -FontWeight normal, bold
New-HTMLListItem -Text 'Group Policies that are unlinked (are not doing anything currently): ', $GPONotLinked.Count -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies that are empty (have no settings): ", $GPOEmpty.Count -FontWeight normal, bold
} -FontSize 10pt
New-HTMLText -FontSize 10pt -Text 'Usually empty or unlinked Group Policies are safe to delete.'
New-HTMLChart -Title 'Group Policies Summary' {
New-ChartBarOptions -Type barStacked
#New-ChartLegend -Names 'Unlinked', 'Linked', 'Empty', 'Total' -Color Salmon, PaleGreen, PaleVioletRed, PaleTurquoise
New-ChartLegend -Names 'Good', 'Bad' -Color PaleGreen, Salmon
#New-ChartBar -Name 'Group Policies' -Value $GPONotLinked.Count, $GPOLinked.Count, $GPOEmpty.Count, $GPOTotal
New-ChartBar -Name 'Linked' -Value $GPOLinked.Count, $GPONotLinked.Count
New-ChartBar -Name 'Empty' -Value $GPONotEmpty.Count, $GPOEmpty.Count
} -TitleAlignment center
}
}
if ($Type -contains 'GPOConsistency' -or $null -eq $Type) {
New-HTMLPanel {
New-HTMLText -Text 'Following chart presents ', 'permissions consistency between Active Directory and SYSVOL for Group Policies' -FontSize 10pt -FontWeight normal, bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Top level permissions consistency: ', $Inconsistent[0].Count -FontWeight normal, bold
New-HTMLListItem -Text 'Inherited permissions consistency: ', $InconsistentInside[0].Count -FontWeight normal, bold
New-HTMLListItem -Text 'Inconsistent top level permissions: ', $Inconsistent[1].Count -FontWeight normal, bold
New-HTMLListItem -Text "Inconsistent inherited permissions: ", $InconsistentInside[1].Count -FontWeight normal, bold
} -FontSize 10pt
New-HTMLText -FontSize 10pt -Text 'Having incosistent permissions on AD in comparison to those on SYSVOL can lead to uncontrolled ability to modify them.'
New-HTMLChart {
New-ChartLegend -Names 'Bad', 'Good' -Color PaleGreen, Salmon
New-ChartBarOptions -Type barStacked
New-ChartLegend -Name 'Consistent', 'Inconsistent'
New-ChartBar -Name 'TopLevel' -Value $Inconsistent[0].Count, $Inconsistent[1].Count
New-ChartBar -Name 'Inherited' -Value $InconsistentInside[0].Count, $InconsistentInside[1].Count
} -Title 'Permissions Consistency' -TitleAlignment center
}
}
}
}
if ($Type -contains 'GPOOwners' -or $Type -contains 'GPOOrphans' -or $null -eq $Type) {
New-HTMLSection -Invisible {
if ($Type -contains 'GPOOwners' -or $null -eq $Type) {
New-HTMLPanel {
New-HTMLText -Text 'Following chart presents Group Policy owners and whether they are administrative and consistent. By design an owner of Group Policy should be Domain Admins or Enterprise Admins group only to prevent malicious takeover. ', `
"It's also important that owner in Active Directory matches owner on SYSVOL (file system)."
New-HTMLChart {
New-ChartBarOptions -Type barStacked
New-ChartLegend -Name 'Yes', 'No' -Color PaleGreen, Orchid
New-ChartBar -Name 'Is administrative' -Value $IsOwnerAdministrative[0].Count, $IsOwnerAdministrative[1].Count
New-ChartBar -Name 'Is consistent' -Value $IsOwnerConsistent[0].Count, $IsOwnerConsistent[1].Count
} -Title 'Group Policy Owners'
}
}
if ($Type -contains 'GPOOrphans' -or $null -eq $Type) {
New-HTMLPanel {
New-HTMLText -Text 'Following chart presents ', 'Broken / Orphaned Group Policies' -FontSize 10pt -FontWeight normal, bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $NotAvailableInAD.Count -FontWeight normal, bold
New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $NotAvailableOnSysvol.Count -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $NotAvailablePermissionIssue.Count -FontWeight normal, bold
} -FontSize 10pt
New-HTMLText -FontSize 10pt -Text 'Those problems must be resolved before doing other clenaup activities.'
New-HTMLChart {
New-ChartBarOptions -Type barStacked
New-ChartLegend -Name 'Not in AD', 'Not on SYSVOL', 'Permissions Issue' -Color Crimson, LightCoral, IndianRed
New-ChartBar -Name 'Orphans' -Value $NotAvailableInAD.Count, $NotAvailableOnSysvol.Count, $NotAvailablePermissionIssue.Count
} -Title 'Broken / Orphaned Group Policies' -TitleAlignment center
}
}
}
}
}
if ($Type -contains 'GPOList' -or $null -eq $Type) {
New-HTMLTab -Name 'Group Policies Summary' {
New-HTMLPanel {
New-HTMLText -Text 'Following table shows a list of group policies. ', 'By using following table you can easily find which GPOs can be safely deleted because those are empty or unlinked.' -FontSize 10pt -FontWeight normal, bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies total: ', $GPOTotal -FontWeight normal, bold
New-HTMLListItem -Text 'Group Policies linked: ', $GPOLinked.Count -FontWeight normal, bold
New-HTMLListItem -Text 'Group Policies that are unlinked (are not doing anything currently): ', $GPONotLinked.Count -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies that are empty (have no settings): ", $GPOEmpty.Count -FontWeight normal, bold
} -FontSize 10pt
}
New-HTMLSection -Name 'Group Policies List' {
New-HTMLTable -DataTable $GPOSummary -Filtering {
New-HTMLTableCondition -Name 'Empty' -Value $true -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string
New-HTMLTableCondition -Name 'Linked' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string
} -PagingOptions 10, 20, 30, 40, 50
}
New-HTMLSection -Name 'Steps to fix - Empty & Unlinked Group Policies' {
New-HTMLContainer {
New-HTMLSpanStyle -FontSize 10pt {
New-HTMLText -Text 'Following steps will guide you how to remove empty or unlinked group policies'
New-HTMLWizard {
New-HTMLWizardStep {
}
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
}
}
}
}
}
if ($Type -contains 'GPOOrphans' -or $null -eq $Type) {
New-HTMLTab -Name 'Health State' {
New-HTMLPanel {
New-HTMLText -TextBlock {
"Following table shows list of all group policies and their status in AD and SYSVOL. Due to different reasons it's "
"possible that "
} -FontSize 10pt
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $NotAvailableInAD.Count -FontWeight normal, bold
New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $NotAvailableOnSysvol.Count -FontWeight normal, bold
New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $NotAvailablePermissionIssue.Count -FontWeight normal, bold
} -FontSize 10pt
New-HTMLText -Text "Follow the steps below table to get Active Directory Group Policies in healthy state." -FontSize 10pt
}
New-HTMLSection -Name 'Health State of Group Policies' {
New-HTMLTable -DataTable $GPOOrphans -Filtering {
New-HTMLTableCondition -Name 'Status' -Value "Not available in AD" -BackgroundColor Salmon -ComparisonType string
New-HTMLTableCondition -Name 'Status' -Value "Not available on SYSVOL" -BackgroundColor LightCoral -ComparisonType string
New-HTMLTableCondition -Name 'Status' -Value "Permissions issue" -BackgroundColor MediumVioletRed -ComparisonType string -Color White
} -PagingOptions 10, 20, 30, 40, 50
}
New-HTMLSection -Name 'Steps to fix - Not available on SYSVOL / Active Directory' {
New-HTMLContainer {
New-HTMLSpanStyle -FontSize 10pt {
New-HTMLText -Text 'Following steps will guide you how to fix GPOs which are not available on SYSVOL or AD.'
New-HTMLWizard {
New-HTMLWizardStep -Name 'Prepare environment' {
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
New-HTMLCodeBlock -Code {
Install-Module GPOZaurr -Force
Import-Module GPOZaurr -Force
} -Style powershell
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
}
New-HTMLWizardStep -Name 'Prepare report' {
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding with removal. To generate new report please use:"
New-HTMLCodeBlock -Code {
Show-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrBrokenGpoBefore.html -Verbose -Type GPOOrphans
}
New-HTMLText -Text {
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
}
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
New-HTMLCodeBlock -Code {
$GPOOutput = Get-GPOZaurrBroken
$GPOOutput | Format-Table
}
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
}
New-HTMLWizardStep -Name 'Fix GPOs not available on SYSVOL' {
New-HTMLText -Text "Following command when executed runs cleanup procedure that removes all broken GPOs on SYSVOL side."
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
New-HTMLCodeBlock -Code {
Remove-GPOZaurrBroken -Type SYSVOL -WhatIf
}
New-HTMLText -TextBlock {
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. Once happy with results please follow with command: "
}
New-HTMLCodeBlock -Code {
Remove-GPOZaurrBroken -Type SYSVOL -LimitProcessing 2 -BackupPath $Env:UserProfile\Desktop\GPOSYSVOLBackup
}
New-HTMLText -TextBlock {
"This command when executed deletes only first X broken GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur."
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
}
New-HTMLText -Text "If there's nothing else to be deleted on SYSVOL side, we can skip to next step step"
}
New-HTMLWizardStep -Name 'Fix GPOs not available on AD' {
New-HTMLText -Text "Following command when executed runs cleanup procedure that removes all broken GPOs on Active Directory side."
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
New-HTMLCodeBlock -Code {
Remove-GPOZaurrBroken -Type AD -WhatIf
}
New-HTMLText -TextBlock {
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. Once happy with results please follow with command: "
}
New-HTMLCodeBlock -Code {
Remove-GPOZaurrBroken -Type AD -LimitProcessing 2 -BackupPath $Env:UserProfile\Desktop\GPOSYSVOLBackup
}
New-HTMLText -TextBlock {
"This command when executed deletes only first X broken GPOs. Use LimitProcessing parameter to prevent mass delete and increase the counter when no errors occur."
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
}
New-HTMLText -Text "If there's nothing else to be deleted on AD side, we can skip to next step step"
}
New-HTMLWizardStep -Name 'Verification report' {
New-HTMLText -TextBlock {
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
}
New-HTMLCodeBlock -Code {
Show-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrBrokenGpoAfter.html -Verbose -Type GPOOrphans
}
New-HTMLText -Text "If everything is health in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
}
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
}
}
}
}
}
if ($Type -contains 'NetLogon' -or $Type -contains 'GPOFiles' -or $null -eq $Type) {
New-HTMLTab -Name 'Files (SysVol / NetLogon)' {
if ($Type -contains 'NetLogon' -or $null -eq $Type) {
New-HTMLTab -Name 'NetLogon Permissions' {
New-HTMLTable -DataTable $Netlogon -Filtering
}
}
if ($Type -contains 'GPOFiles' -or $null -eq $Type) {
New-HTMLTab -Name 'SysVol Files Assesment' {
New-HTMLTable -DataTable $GPOFiles -Filtering
}
}
}
}
if ($Type -contains 'GPOPermissionsRoot' -or $Type -contains 'GPOOwners' -or
$Type -contains 'GPOPermissions' -or $Type -contains 'GPOConsistency' -or
$null -eq $Type
) {
New-HTMLTab -Name 'Permissions' {
if ($Type -contains 'GPOPermissionsRoot' -or $null -eq $Type) {
New-HTMLTab -Name 'Root' {
New-HTMLTable -DataTable $GPOPermissionsRoot -Filtering
}
}
if ($Type -contains 'GPOOwners' -or $null -eq $Type) {
New-HTMLTab -Name 'Owners' {
New-HTMLTable -DataTable $GPOOwners -Filtering
}
}
if ($Type -contains 'GPOPermissions' -or $null -eq $Type) {
New-HTMLTab -Name 'Edit & Modify' {
New-HTMLTable -DataTable $GPOPermissions -Filtering
}
}
if ($Type -contains 'GPOConsistency' -or $null -eq $Type) {
New-HTMLTab -Name 'Permissions Consistency' {
New-HTMLPanel {
New-HTMLText -Text 'Following table presents ', 'permissions consistency between Active Directory and SYSVOL for Group Policies' -FontSize 10pt -FontWeight normal, bold
New-HTMLList -Type Unordered {
New-HTMLListItem -Text 'Top level permissions consistency: ', $Inconsistent[0].Count -FontWeight normal, bold
New-HTMLListItem -Text 'Inherited permissions consistency: ', $InconsistentInside[0].Count -FontWeight normal, bold
New-HTMLListItem -Text 'Inconsistent top level permissions: ', $Inconsistent[1].Count -FontWeight normal, bold
New-HTMLListItem -Text "Inconsistent inherited permissions: ", $InconsistentInside[1].Count -FontWeight normal, bold
} -FontSize 10pt
New-HTMLText -FontSize 10pt -Text 'Having incosistent permissions on AD in comparison to those on SYSVOL can lead to uncontrolled ability to modify them. Please notice that if ', `
' Not available ', 'is visible in the table you should first fix related, more pressing issue, before fixing permissions inconsistency.' -FontWeight normal, bold, normal
}
New-HTMLSection -Name 'Group Policy Permissions Consistency' {
New-HTMLTable -DataTable $GPOPermissionsConsistency -Filtering {
New-HTMLTableCondition -Name 'ACLConsistent' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string
New-HTMLTableCondition -Name 'ACLConsistentInside' -Value $false -BackgroundColor Salmon -TextTransform capitalize -ComparisonType string
New-HTMLTableCondition -Name 'ACLConsistent' -Value $true -BackgroundColor PaleGreen -TextTransform capitalize -ComparisonType string
New-HTMLTableCondition -Name 'ACLConsistentInside' -Value $true -BackgroundColor PaleGreen -TextTransform capitalize -ComparisonType string
New-HTMLTableCondition -Name 'ACLConsistent' -Value 'Not available' -BackgroundColor Crimson -ComparisonType string
New-HTMLTableCondition -Name 'ACLConsistentInside' -Value 'Not available' -BackgroundColor Crimson -ComparisonType string
} -PagingOptions 10, 20, 30, 40, 50
}
New-HTMLSection -Name 'Steps to fix - Permissions Consistency' {
New-HTMLContainer {
New-HTMLSpanStyle -FontSize 10pt {
New-HTMLText -Text 'Following steps will guide you how to fix permissions consistency'
New-HTMLWizard {
New-HTMLWizardStep -Name 'Prepare environment' {
New-HTMLText -Text "To be able to execute actions in automated way please install required modules. Those modules will be installed straight from Microsoft PowerShell Gallery."
New-HTMLCodeBlock -Code {
Install-Module GPOZaurr -Force
Import-Module GPOZaurr -Force
} -Style powershell
New-HTMLText -Text "Using force makes sure newest version is downloaded from PowerShellGallery regardless of what is currently installed. Once installed you're ready for next step."
}
New-HTMLWizardStep -Name 'Prepare report' {
New-HTMLText -Text "Depending when this report was run you may want to prepare new report before proceeding fixing permissions inconsistencies. To generate new report please use:"
New-HTMLCodeBlock -Code {
Show-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrPermissionsInconsistentBefore.html -Verbose -Type GPOConsistency
}
New-HTMLText -Text {
"When executed it will take a while to generate all data and provide you with new report depending on size of environment."
"Once confirmed that data is still showing issues and requires fixing please proceed with next step."
}
New-HTMLText -Text "Alternatively if you prefer working with console you can run: "
New-HTMLCodeBlock -Code {
$GPOOutput = Get-GPOZaurrPermissionConsistency
$GPOOutput | Format-Table # do your actions as desired
}
New-HTMLText -Text "It provides same data as you see in table above just doesn't prettify it for you."
}
New-HTMLWizardStep -Name 'Fix inconsistent permissions' {
New-HTMLText -Text "Following command when executed fixes inconsistent permissions."
New-HTMLText -Text "Make sure when running it for the first time to run it with ", "WhatIf", " parameter as shown below to prevent accidental removal." -FontWeight normal, bold, normal -Color Black, Red, Black
New-HTMLText -Text "Make sure to fill in TargetDomain to match your Domain Admin permission account"
New-HTMLCodeBlock -Code {
Repair-GPOZaurrPermissionConsistency -IncludeDomains "TargetDomain" -Verbose -WhatIf
}
New-HTMLText -TextBlock {
"After execution please make sure there are no errors, make sure to review provided output, and confirm that what is about to be deleted matches expected data. Once happy with results please follow with command: "
}
New-HTMLCodeBlock -Code {
Repair-GPOZaurrPermissionConsistency -LimitProcessing 2 -IncludeDomains "TargetDomain"
}
New-HTMLText -TextBlock {
"This command when executed repairs only first X inconsistent permissions. Use LimitProcessing parameter to prevent mass fixing and increase the counter when no errors occur."
"Repeat step above as much as needed increasing LimitProcessing count till there's nothing left. In case of any issues please review and action accordingly."
}
New-HTMLText -Text "If there's nothing else to be fixed, we can skip to next step step"
}
New-HTMLWizardStep -Name 'Fix inconsistent downlevel permissions' {
New-HTMLText -Text "Unfortunetly this step is manual until automation is developed. "
New-HTMLText -Text "If there are inconsistent permissions found inside GPO one has to fix them manually by going into SYSVOL and making sure inheritance is enabled, and that permissions are consistent across all files."
}
New-HTMLWizardStep -Name 'Verification report' {
New-HTMLText -TextBlock {
"Once cleanup task was executed properly, we need to verify that report now shows no problems."
}
New-HTMLCodeBlock -Code {
Show-GPOZaurr -FilePath $Env:UserProfile\Desktop\GPOZaurrPermissionsInconsistentAfter.html -Verbose -Type GPOConsistency
}
New-HTMLText -Text "If everything is health in the report you're done! Enjoy rest of the day!" -Color BlueDiamond
}
} -RemoveDoneStepOnNavigateBack -Theme arrows -ToolbarButtonPosition center
}
}
}
}
}
}
}
if ($Type -contains 'GPOAnalysis' -or $null -eq $Type) {
New-HTMLTab -Name 'Analysis' {
foreach ($Key in $GPOContent.Keys) {
New-HTMLTab -Name $Key {
New-HTMLTable -DataTable $GPOContent[$Key] -Filtering -Title $Key
}
}
}
}
} -Online -ShowHTML -FilePath $FilePath
}
+40 -2
View File
@@ -19,7 +19,15 @@
# GPOZaurr
## To install
## Table of Contents
- [GPOZaurr](#gpozaurr)
- [Table of Contents](#table-of-contents)
- [Installing](#installing)
- [Updating](#updating)
- [Changelog](#changelog)
## Installing
GPOZaurr requires `RSAT` installed to provide results. If you don't have them you can install them as below. Keep in mind it also installs GUI tools so it shouldn't be installed on user workstations.
@@ -37,7 +45,7 @@ Install-Module -Name GPOZaurr -AllowClobber -Force
Force and AllowClobber aren't necessary, but they do skip errors in case some appear.
## And to update
## Updating
```powershell
Update-Module -Name GPOZaurr
@@ -49,6 +57,36 @@ That's it. Whenever there's a new version, you run the command, and you can enjo
## Changelog
- 0.0.77 - 9.11.2020
- Improved `Invoke-GPOZaurr` (WIP)
- 0.0.76 - 8.11.2020
- Improved `Get-GPOZaurrNetLogon` to better handle errors
- 0.0.75 - 8.11.2020
- Improved `Get-GPOZaurrPermissionConsistency` to stop checking consistency if path doesn't exists
- 0.0.74 - 8.11.2020
- Improved `Invoke-GPOZaurr` (WIP)
- 0.0.73 - 7.11.2020
- Improved `Invoke-GPOZaurr` (WIP)
- Improved `Get-GPOZaurr`
- 0.0.72 - 6.11.2020
- Improved `Invoke-GPOZaurr` (WIP)
- 0.0.71 - 3.11.2020
- Improved `Invoke-GPOZaurr` (WIP)
- 0.0.70 - 29.10.2020
- Added `Get-GPOZaurrDuplicateObject`
- Added `Remove-GPOZaurrDuplicateObject`
- 0.0.69 - 29.10.2020
- Improved `Invoke-GPOZaurr` (WIP)
- Improved `Get-GPOZaurrNetLogon`
- Improved `Get-GPOZaurrOwner`
- Improved `Set-GPOZaurrOwner`
- Added `Repair-GPOZaurrNetLogonOwner`
- Improved `Invoke-GPOZaurr` (WIP)
- 0.0.68 - 28.10.2020
- Renamed `Show-GPOZaurr` to `Invoke-GPOZaurr`
- Renamed `Invoke-GPOZaurr` to `Invoke-GPOZaurrContent`
- Improvements to `Get-GPOZaurrPermissionConsistency` - don't check for inherited permissions if top level ones are inconsistent
- Improved `Invoke-GPOZaurr` (WIP)
- 0.0.67 - 22.10.2020
- Improved `Show-GPOZaurr` (WIP)
- 0.0.66 - 22.10.2020