mirror of
https://github.com/EvotecIT/GPOZaurr.git
synced 2026-07-26 20:00:09 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7adfd1ef65 | |||
| 828c221ac9 | |||
| 8e80fd423d | |||
| f52fd60fef | |||
| 9594afb246 | |||
| 7c52284b48 | |||
| 2fd64952af | |||
| 15538e8559 | |||
| a36a470538 | |||
| 0a5d65761c | |||
| 7ed3cb6dad | |||
| 4d9edde212 |
@@ -1,10 +1,10 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
# Find broken GPOs
|
||||
Get-GPOZaurrBroken -Verbose -IncludeDomains 'ad.evotec.pl' | Format-Table
|
||||
Get-GPOZaurrBroken -Verbose | Format-Table # -IncludeDomains 'ad.evotec.pl' | Format-Table
|
||||
|
||||
# this allows you to process X amount of orphaned folders/files (good for testing)
|
||||
Remove-GPOZaurrBroken -Verbose -WhatIf #-IncludeDomains 'ad.evotec.pl' #-LimitProcessing 2
|
||||
Remove-GPOZaurrBroken -Verbose -WhatIf -Type AD, SYSVOL -LimitProcessing 1 #-IncludeDomains 'ad.evotec.pl' #-LimitProcessing 2
|
||||
|
||||
# this runs for whole SYSVOL and checks things against GPOS
|
||||
Remove-GPOZaurrBroken -Verbose -IncludeDomains 'ad.evotec.xyz' -BackupPath $Env:UserProfile\Desktop\MyBackup1 -WhatIf
|
||||
Remove-GPOZaurrBroken -Verbose -IncludeDomains 'ad.evotec.xyz' -BackupPath $Env:UserProfile\Desktop\MyBackup1 -WhatIf -Type AD, SYSVOL
|
||||
@@ -1,4 +1,4 @@
|
||||
Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force
|
||||
|
||||
#$Output = Invoke-GPOZaurr -FilePath $PSScriptRoot\Reports\GPOZaurr.html -PassThru -Type GPOConsistency, GPOList, GPODuplicates, GPOOrphans, GPOOwners, NetLogonOwners, GPOPermissionsRead, GPOPermissionsAdministrative,GPOPermissionsUnknown
|
||||
Invoke-GPOZaurr -FilePath $PSScriptRoot\Reports\GPOZaurr.html -Type GPOPermissions
|
||||
Invoke-GPOZaurr -FilePath $PSScriptRoot\Reports\GPOZaurr.html -Type GPOPermissions,GPOPermissionsRead
|
||||
+2
-2
@@ -8,7 +8,7 @@
|
||||
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-GPOZaurrDuplicateObject', 'Get-GPOZaurrFiles', 'Get-GPOZaurrFilesPolicyDefinition', 'Get-GPOZaurrFolders', 'Get-GPOZaurrInheritance', 'Get-GPOZaurrLegacyFiles', 'Get-GPOZaurrLink', 'Get-GPOZaurrLinkSummary', 'Get-GPOZaurrNetLogon', 'Get-GPOZaurrOwner', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermission', 'Get-GPOZaurrPermissionAnalysis', 'Get-GPOZaurrPermissionConsistency', 'Get-GPOZaurrPermissionIssue', '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-GPOZaurrPermission', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Set-GPOOwner', 'Set-GPOZaurrOwner', 'Set-GPOZaurrStatus', 'Skip-GroupPolicy')
|
||||
GUID = 'f7d4c9e4-0298-4f51-ad77-e8e3febebbde'
|
||||
ModuleVersion = '0.0.99'
|
||||
ModuleVersion = '0.0.101'
|
||||
PowerShellVersion = '5.1'
|
||||
PrivateData = @{
|
||||
PSData = @{
|
||||
@@ -18,7 +18,7 @@
|
||||
}
|
||||
}
|
||||
RequiredModules = @(@{
|
||||
ModuleVersion = '0.0.190'
|
||||
ModuleVersion = '0.0.192'
|
||||
ModuleName = 'PSSharedGoods'
|
||||
Guid = 'ee272aa8-baaa-4edf-9f45-b6d6f7d844fe'
|
||||
}, @{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
$GPOZaurrOrphans = [ordered] @{
|
||||
Name = 'Orphaned Group Policies'
|
||||
Name = 'Broken Group Policies'
|
||||
Enabled = $true
|
||||
ActionRequired = $null
|
||||
Data = $null
|
||||
@@ -7,41 +7,52 @@
|
||||
Get-GPOZaurrBroken -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
}
|
||||
Processing = {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'] = @{}
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'] = @{}
|
||||
foreach ($GPO in $Script:Reporting['GPOOrphans']['Data']) {
|
||||
if (-not $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName] = 0
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'] = @{}
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssuePerDomain'] = @{}
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssuePerDomain'] = @{}
|
||||
foreach ($GPO in $Script:Reporting['GPOBroken']['Data']) {
|
||||
if (-not $Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName] = 0
|
||||
if (-not $Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssuePerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssuePerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if ($GPO.Status -eq 'Not available in AD') {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailableInAD']++
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted']++
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]++
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableInAD']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeleted']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]++
|
||||
} elseif ($GPO.Status -eq 'Not available on SYSVOL') {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailableOnSysvol']++
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted']++
|
||||
$Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]++
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableOnSysvol']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeleted']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]++
|
||||
} elseif ($GPO.Status -eq 'Permissions issue') {
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssue']++
|
||||
$Script:Reporting['GPOOrphans']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]++
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssue']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssuePerDomain'][$GPO.DomainName]++
|
||||
} elseif ($GPO.Status -eq 'ObjectClass issue') {
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssue']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeleted']++
|
||||
$Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssuePerDomain'][$GPO.DomainName]++
|
||||
$Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOOrphans']['Variables']['ToBeDeleted'] -gt 0) {
|
||||
$Script:Reporting['GPOOrphans']['ActionRequired'] = $true
|
||||
if ($Script:Reporting['GPOBroken']['Variables']['ToBeDeleted'] -gt 0) {
|
||||
$Script:Reporting['GPOBroken']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOOrphans']['ActionRequired'] = $false
|
||||
$Script:Reporting['GPOBroken']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
NotAvailableInAD = 0
|
||||
NotAvailableOnSysvol = 0
|
||||
NotAvailablePermissionIssue = 0
|
||||
NotAvailablePermissionIssuePerDomain = $null
|
||||
ToBeDeleted = 0
|
||||
ToBeDeletedPerDomain = $null
|
||||
NotAvailableInAD = 0
|
||||
NotAvailableOnSysvol = 0
|
||||
NotAvailablePermissionIssue = 0
|
||||
NotAvailablePermissionIssuePerDomain = $null
|
||||
ToBeDeleted = 0
|
||||
ToBeDeletedPerDomain = $null
|
||||
NotAvailableObjectClassIssue = 0
|
||||
NotAvailableObjectClassIssuePerDomain = $null
|
||||
}
|
||||
Overview = {
|
||||
<#
|
||||
@@ -58,15 +69,15 @@
|
||||
}
|
||||
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
|
||||
New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableInAD'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableOnSysvol'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $Script:Reporting['GPOBroken']['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']
|
||||
New-ChartBar -Name 'Orphans' -Value $Script:Reporting['GPOBroken']['Variables']['NotAvailableInAD'], $Script:Reporting['GPOBroken']['Variables']['NotAvailableOnSysvol'], $Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssue']
|
||||
} -Title 'Broken / Orphaned Group Policies' -TitleAlignment center
|
||||
}
|
||||
#>
|
||||
@@ -85,14 +96,15 @@
|
||||
} -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
|
||||
New-HTMLListItem -Text 'Group Policies on SYSVOL, but no details in AD: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableInAD'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Group Policies in AD, but no content on SYSVOL: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableOnSysvol'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text 'Group Policies which exists, but have wrong ObjectClass: ', $Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssue'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which couldn't be assed due to permissions issue: ", $Script:Reporting['GPOBroken']['Variables']['NotAvailablePermissionIssue'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
foreach ($Domain in $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOOrphans']['Variables']['ToBeDeletedPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
foreach ($Domain in $Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text "$Domain requires ", $Script:Reporting['GPOBroken']['Variables']['ToBeDeletedPerDomain'][$Domain], " changes." -FontWeight normal, bold, normal
|
||||
}
|
||||
} -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
|
||||
@@ -100,25 +112,26 @@
|
||||
Solution = {
|
||||
New-HTMLSection -Invisible {
|
||||
New-HTMLPanel {
|
||||
& $Script:GPOConfiguration['GPOOrphans']['Summary']
|
||||
& $Script:GPOConfiguration['GPOBroken']['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']
|
||||
New-ChartLegend -Name 'Not in AD', 'Not on SYSVOL', 'ObjectClass Issue', 'Permissions Issue' -Color Crimson, LightCoral, MediumOrchid, IndianRed
|
||||
New-ChartBar -Name 'Broken' -Value $Script:Reporting['GPOBroken']['Variables']['NotAvailableInAD'], $Script:Reporting['GPOBroken']['Variables']['NotAvailableOnSysvol'], $Script:Reporting['GPOBroken']['Variables']['NotAvailableObjectClassIssue'], $Script:Reporting['GPOBroken']['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-HTMLTable -DataTable $Script:Reporting['GPOBroken']['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 "ObjectClass issue" -BackgroundColor MediumOrchid -ComparisonType string
|
||||
New-HTMLTableCondition -Name 'Status' -Value "Permissions issue" -BackgroundColor MediumVioletRed -ComparisonType string -Color White
|
||||
} -PagingOptions 10, 20, 30, 40, 50
|
||||
}
|
||||
if ($Script:Reporting['Settings']['HideSteps'] -eq $false) {
|
||||
New-HTMLSection -Name 'Steps to fix - Not available on SYSVOL / Active Directory' {
|
||||
New-HTMLSection -Name 'Steps to fix - Not available on SYSVOL / Active Directory / ObjectClass issue' {
|
||||
New-HTMLContainer {
|
||||
New-HTMLSpanStyle -FontSize 10pt {
|
||||
New-HTMLWizard {
|
||||
@@ -243,6 +256,44 @@
|
||||
"If there's nothing else to be deleted on AD side, we can skip to next step step. "
|
||||
}
|
||||
}
|
||||
New-HTMLWizardStep -Name 'Fix GPOs of wrong ObjectClass' {
|
||||
New-HTMLText -Text @(
|
||||
"Following command when executed runs cleanup procedure that removes all GPOs which have ObjectClass of Container, rather than required groupPolicyContainer. "
|
||||
"Make sure when running it for the first time to run it with ",
|
||||
"WhatIf",
|
||||
" parameter as shown below to prevent accidental removal."
|
||||
'When run it will remove GPO metadata from AD, and any files/folders from SYSVOL.'
|
||||
) -FontWeight normal, normal, bold, normal -Color Black, Black, Red, Black
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type ObjectClass -WhatIf -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type ObjectClass -WhatIf -IncludeDomains 'YourDomainYouHavePermissionsFor' -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 deleted matches expected data. "
|
||||
"Keep in mind that there is no backup for this as backup process doesn't see GPOs that are of wrong ObjectClass. "
|
||||
"Once you delete it, it's gone. "
|
||||
} -LineBreak
|
||||
New-HTMLText -Text 'Once happy with results please follow with command (this will start deletion process): ' -LineBreak -FontWeight bold
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type ObjectClass -LimitProcessing 2 -Verbose
|
||||
}
|
||||
New-HTMLText -TextBlock {
|
||||
"Alternatively for multi-domain scenario, if you have limited Domain Admin credentials to a single domain please use following command: "
|
||||
}
|
||||
New-HTMLCodeBlock -Code {
|
||||
Remove-GPOZaurrBroken -Type ObjectClass -LimitProcessing 2 -IncludeDomains 'YourDomainYouHavePermissionsFor' -Verbose
|
||||
}
|
||||
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. "
|
||||
"If there's nothing else to be deleted, 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."
|
||||
@@ -257,9 +308,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOOrphans']['WarningsAndErrors']) {
|
||||
if ($Script:Reporting['GPOBroken']['WarningsAndErrors']) {
|
||||
New-HTMLSection -Name 'Warnings & Errors to Review' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOOrphans']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOBroken']['WarningsAndErrors'] -Filtering {
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Warning' -BackgroundColor SandyBrown -ComparisonType string -Row
|
||||
New-HTMLTableCondition -Name 'Type' -Value 'Error' -BackgroundColor Salmon -ComparisonType string -Row
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
}
|
||||
$Object['PermissionsPerRow'] = $Object['Permissions'] | ForEach-Object { $_ }
|
||||
$Object['PermissionsAnalysis'] = Get-GPOZaurrPermissionAnalysis -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -Permissions $Object.Permissions
|
||||
$Object['PermissionsIssues'] = Get-GPOZaurrPermissionIssue -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
$Object
|
||||
}
|
||||
Processing = {
|
||||
@@ -28,6 +29,31 @@
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotFixPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillFixPerDomain'] = @{}
|
||||
|
||||
$Script:Reporting['GPOPermissions']['Variables']['ReadPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['CouldNotReadPerDomain'] = @{}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['TotalPerDomain'] = @{}
|
||||
|
||||
foreach ($GPO in $Script:Reporting['GPOPermissions']['Data'].PermissionsIssues) {
|
||||
# Create Per Domain Variables
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['ReadPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['ReadPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['TotalPerDomain'][$GPO.DomainName]) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['TotalPerDomain'][$GPO.DomainName] = 0
|
||||
}
|
||||
if ($GPO.PermissionIssue) {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['CouldNotRead']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['CouldNotReadPerDomain'][$GPO.DomainName]++
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissions']['Variables']['Read']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['ReadPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
$Script:Reporting['GPOPermissions']['Variables']['Total']++
|
||||
$Script:Reporting['GPOPermissions']['Variables']['TotalPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
foreach ($GPO in $Script:Reporting['GPOPermissions']['Data'].PermissionsAnalysis) {
|
||||
# Create Per Domain Variables
|
||||
if (-not $Script:Reporting['GPOPermissions']['Variables']['WillFixPerDomain'][$GPO.DomainName]) {
|
||||
@@ -99,13 +125,21 @@
|
||||
$Script:Reporting['GPOPermissions']['Variables']['WillNotFixPerDomain'][$GPO.DomainName]++
|
||||
}
|
||||
}
|
||||
if ($Script:Reporting['GPOPermissions']['Variables']['WillFix'] -gt 0) {
|
||||
if ($Script:Reporting['GPOPermissions']['Variables']['WillFix'] -gt 0 -or $Script:Reporting['GPOPermissions']['Variables']['CouldNotRead'] -gt 0) {
|
||||
$Script:Reporting['GPOPermissions']['ActionRequired'] = $true
|
||||
} else {
|
||||
$Script:Reporting['GPOPermissions']['ActionRequired'] = $false
|
||||
}
|
||||
}
|
||||
Variables = @{
|
||||
# Issues / Couldnt read report
|
||||
Read = 0
|
||||
ReadPerDomain = $null
|
||||
CouldNotRead = 0
|
||||
CouldNotReadPerDomain = $null
|
||||
TotalPerDomain = $null
|
||||
Total = 0
|
||||
# Permissions analysis
|
||||
WillFix = 0
|
||||
WillNotFix = 0
|
||||
WillFixAdministrative = 0
|
||||
@@ -150,7 +184,34 @@
|
||||
New-HTMLListItem -Text "After MS16-072 is installed, user group policies are retrieved by using the computer's security context."
|
||||
} -FontSize 10pt
|
||||
|
||||
New-HTMLText -FontSize 10pt -Text "Assesment results " -FontWeight bold
|
||||
New-HTMLText -FontSize 10pt -Text @(
|
||||
"Unfortunetly it's not as simple as it sounds. While checking for permissions mostly works fine, it's possible a Group Policy has totally removed account permissions from being able to asses any of it. ",
|
||||
"The account we're using "
|
||||
"$($Env:USERDOMAIN)\$($Env:USERNAME.ToUpper())",
|
||||
" may simply not have enough permisions to properly asses permissions for a GPO. "
|
||||
"Therefore we're using dual assesment for this situation, where first assesment is checking for GPO visibility or lack of it, and second assesment is checking for direct permissions assignement. "
|
||||
"We just were able to detect the problem, but hopefully higher level account (Domain Admin) should be able to provide full assesment. "
|
||||
) -FontWeight normal, normal, bold, normal -Color None, None, BlueDiamond, none, none
|
||||
|
||||
New-HTMLText -FontSize 10pt -Text "First assesment results: " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text "Group Policies couldn't read at all: ", $Script:Reporting['GPOPermissions']['Variables']['CouldNotRead'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies with permissions allowing read: ", $Script:Reporting['GPOPermissions']['Variables']['Read'] -FontWeight normal, bold
|
||||
} -FontSize 10pt
|
||||
New-HTMLText -Text 'Following domains require actions (permissions required):' -FontSize 10pt -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
foreach ($Domain in $Script:Reporting['GPOPermissions']['Variables']['CouldNotReadPerDomain'].Keys) {
|
||||
New-HTMLListItem -Text @(
|
||||
"$Domain requires ",
|
||||
$Script:Reporting['GPOPermissions']['Variables']['CouldNotReadPerDomain'][$Domain],
|
||||
" changes out of ",
|
||||
$Script:Reporting['GPOPermissions']['Variables']['TotalPerDomain'][$Domain],
|
||||
"."
|
||||
) -FontWeight normal, bold, normal
|
||||
}
|
||||
} -FontSize 10pt
|
||||
|
||||
New-HTMLText -FontSize 10pt -Text "Second Assesment results " -FontWeight bold
|
||||
New-HTMLList -Type Unordered {
|
||||
New-HTMLListItem -Text 'Group Policies requiring Authenticated Users with GpoRead permission: ', $Script:Reporting['GPOPermissions']['Variables']['WillFixAuthenticatedUsers'] -FontWeight normal, bold
|
||||
New-HTMLListItem -Text "Group Policies which don't require changes: ", $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAuthenticatedUsers'] -FontWeight normal, bold
|
||||
@@ -229,6 +290,7 @@
|
||||
New-HTMLChart {
|
||||
New-ChartBarOptions -Type barStacked
|
||||
New-ChartLegend -Name 'Yes', 'No' -Color SpringGreen, Salmon
|
||||
New-ChartBar -Name 'Visible Permissions' -Value $Script:Reporting['GPOPermissions']['Variables']['Read'], $Script:Reporting['GPOPermissions']['Variables']['CouldNotRead']
|
||||
New-ChartBar -Name 'Overall Permissions' -Value $Script:Reporting['GPOPermissions']['Variables']['WillNotFix'], $Script:Reporting['GPOPermissions']['Variables']['WillFix']
|
||||
New-ChartBar -Name 'Administrative Permissions' -Value $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAdministrative'], $Script:Reporting['GPOPermissions']['Variables']['WillFixAdministrative']
|
||||
New-ChartBar -Name 'Authenticated Users Permissions' -Value $Script:Reporting['GPOPermissions']['Variables']['WillNotTouchAdministrative'], $Script:Reporting['GPOPermissions']['Variables']['WillFixAuthenticatedUsers']
|
||||
@@ -237,6 +299,11 @@
|
||||
} -Title 'Group Policy Permissions' -TitleAlignment center
|
||||
}
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policy Visibility Analysis' {
|
||||
New-HTMLTable -DataTable $Script:Reporting['GPOPermissions']['Data'].PermissionsIssues -Filtering {
|
||||
New-HTMLTableCondition -Name 'PermissionIssue' -Value $true -BackgroundColor Salmon -ComparisonType string -Row
|
||||
} -PagingOptions 7, 15, 30, 45, 60 -DefaultSortColumn PermissionIssue -DefaultSortOrder Descending
|
||||
}
|
||||
New-HTMLSection -Name 'Group Policy Permissions Analysis' {
|
||||
New-HTMLContainer {
|
||||
New-HTMLText -Text 'Explanation to table columns:' -FontSize 10pt
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
Execute = {
|
||||
[ordered] @{
|
||||
Permissions = Get-GPOZaurrPermission -Type AuthenticatedUsers -ReturnSecurityWhenNoData -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
Issues = Get-GPOZaurrPermissionIssue
|
||||
Issues = Get-GPOZaurrPermissionIssue -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains
|
||||
}
|
||||
}
|
||||
Processing = {
|
||||
@@ -93,7 +93,7 @@
|
||||
} -FontStyle italic -FontSize 10pt -FontWeight bold -LineBreak
|
||||
New-HTMLText -FontSize 10pt -Text @(
|
||||
"There are two parts to this assesment. Reading all Group Policies Permissions that account ",
|
||||
$($Env:USERNAME.ToUpper()),
|
||||
"$($Env:USERDOMAIN)\$($Env:USERNAME.ToUpper())",
|
||||
" has permissions to read and provide detailed assesment about permissions. ",
|
||||
"Second assesment checks for permissions that this account is not able to read at all, and therefore it has no visibility about permissions set on it. "
|
||||
"We just were able to detect the problem, but hopefully higher level account (Domain Admin) should be able to provide full assesment. "
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
$Script:GPOConfiguration = [ordered] @{
|
||||
GPOOrphans = $GPOZaurrOrphans
|
||||
GPOBroken = $GPOZaurrOrphans
|
||||
GPOOwners = $GPOZaurrOwners
|
||||
GPOConsistency = $GPOZaurrConsistency
|
||||
GPODuplicates = $GPOZaurrDuplicates
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
)
|
||||
$Differences = @{ }
|
||||
$SysvolHash = @{ }
|
||||
|
||||
$GPOGUIDS = $GPOs.ID.GUID
|
||||
$GPOGUIDS = ConvertFrom-DistinguishedName -DistinguishedName $GPOs.DistinguishedName
|
||||
$SysVolPath = "\\$($Server)\SYSVOL\$Domain\Policies"
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing SYSVOL from \\$($Server)\SYSVOL\$Domain\Policies"
|
||||
try {
|
||||
@@ -19,10 +18,10 @@
|
||||
$Sysvol = $Null
|
||||
}
|
||||
foreach ($_ in $SYSVOL) {
|
||||
$GUID = $_.Name -replace '{' -replace '}'
|
||||
$GUID = $_.Name
|
||||
$SysvolHash[$GUID] = $_
|
||||
}
|
||||
$Files = $SYSVOL.Name -replace '{' -replace '}'
|
||||
$Files = $SYSVOL.Name
|
||||
if ($Files) {
|
||||
$Comparing = Compare-Object -ReferenceObject $GPOGUIDS -DifferenceObject $Files -IncludeEqual
|
||||
foreach ($_ in $Comparing) {
|
||||
@@ -30,8 +29,10 @@
|
||||
# we skip policy definitions
|
||||
continue
|
||||
}
|
||||
$ADStatus = $PoliciesAD[$_.InputObject]
|
||||
if ($_.SideIndicator -eq '==') {
|
||||
$Found = 'Exists'
|
||||
#$Found = 'Exists'
|
||||
$Found = $ADStatus
|
||||
} elseif ($_.SideIndicator -eq '<=') {
|
||||
$Found = 'Not available on SYSVOL'
|
||||
} elseif ($_.SideIndicator -eq '=>') {
|
||||
@@ -41,8 +42,10 @@
|
||||
$Found = 'Not available in AD'
|
||||
}
|
||||
} else {
|
||||
# This shouldn't happen at all
|
||||
$Found = 'Orphaned GPO'
|
||||
}
|
||||
|
||||
$Differences[$_.InputObject] = $Found
|
||||
}
|
||||
}
|
||||
@@ -50,85 +53,60 @@
|
||||
$Count = 0
|
||||
foreach ($GPO in $GPOS) {
|
||||
$Count++
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing [$($GPO.DomainName)]($Count/$($GPOS.Count)) $($GPO.DisplayName)"
|
||||
if ($null -ne $SysvolHash[$GPO.Id.GUID].FullName) {
|
||||
$FullPath = $SysvolHash[$GPO.Id.GUID].FullName
|
||||
try {
|
||||
$ACL = Get-Acl -Path $SysvolHash[$GPO.Id.GUID].FullName -ErrorAction Stop -Verbose:$false
|
||||
$Owner = $ACL.Owner
|
||||
$ErrorMessage = ''
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrBroken - ACL reading (1) failed for $FullPath with error: $($_.Exception.Message)"
|
||||
$ACL = $null
|
||||
$Owner = ''
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
}
|
||||
$GPOGuid = ConvertFrom-DistinguishedName -DistinguishedName $GPO.DistinguishedName
|
||||
if ($GPO.DisplayName) {
|
||||
$GPODisplayName = $GPO.DisplayName
|
||||
$GPOName = $GPO.Name
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing [$($Domain)]($Count/$($GPOS.Count)) $($GPO.DisplayName)"
|
||||
} else {
|
||||
$FullPath = -join ($SysVolPath, "\{$($GPO.Id.Guid)}")
|
||||
$ACL = $null
|
||||
$Owner = ''
|
||||
$GPOName = $GPOGuid
|
||||
$GPODisplayName = $GPOGuid
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing [$($Domain)]($Count/$($GPOS.Count)) $($GPOGuid)"
|
||||
}
|
||||
if ($null -ne $SysvolHash[$GPOGuid].FullName) {
|
||||
$FullPath = $SysvolHash[$GPOGuid].FullName
|
||||
$ErrorMessage = ''
|
||||
} else {
|
||||
$FullPath = -join ($SysVolPath, "\$($GPOGuid)")
|
||||
$ErrorMessage = 'Not found on SYSVOL'
|
||||
}
|
||||
if ($null -eq $Differences[$GPO.Id.Guid]) {
|
||||
$SysVolStatus = 'Unknown Issue'
|
||||
if ($null -eq $Differences[$GPOGuid]) {
|
||||
$SysVolStatus = 'Unknown issue'
|
||||
} else {
|
||||
$SysVolStatus = $Differences[$GPO.Id.Guid]
|
||||
$SysVolStatus = $Differences[$GPOGuid]
|
||||
}
|
||||
[PSCustomObject] @{
|
||||
DisplayName = $GPO.DisplayName
|
||||
DisplayName = $GPODisplayName
|
||||
Status = $SysVolStatus
|
||||
DomainName = $GPO.DomainName
|
||||
DomainName = $Domain
|
||||
SysvolServer = $Server
|
||||
SysvolStatus = $SysVolStatus
|
||||
GpoStatus = $GPO.GpoStatus
|
||||
Owner = $GPO.Owner
|
||||
FileOwner = $Owner
|
||||
Id = $GPO.Id.Guid
|
||||
ObjectClass = $GPO.ObjectClass
|
||||
Id = $GPOName
|
||||
Path = $FullPath
|
||||
DistinguishedName = -join ("CN={", $GPO.Id.Guid, "},", $PoliciesSearchBase)
|
||||
DistinguishedName = -join ("CN=", $GPOGuid, ",", $PoliciesSearchBase)
|
||||
Description = $GPO.Description
|
||||
CreationTime = $GPO.CreationTime
|
||||
ModificationTime = $GPO.ModificationTime
|
||||
UserVersion = $GPO.UserVersion
|
||||
ComputerVersion = $GPO.ComputerVersion
|
||||
WmiFilter = $GPO.WmiFilter
|
||||
CreationTime = $GPO.Created
|
||||
ModificationTime = $GPO.Modified
|
||||
Error = $ErrorMessage
|
||||
}
|
||||
}
|
||||
# Now we need to list thru Sysvol files and fine those that do not exists as GPO and create dummy GPO objects to show orphaned gpos
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing SYSVOL differences"
|
||||
foreach ($_ in $Differences.Keys) {
|
||||
if ($Differences[$_] -in 'Not available in AD', 'Permissions issue') {
|
||||
if ($Differences[$_] -in 'Not available in AD') {
|
||||
$FullPath = $SysvolHash[$_].FullName
|
||||
try {
|
||||
$ACL = Get-Acl -Path $FullPath -ErrorAction Stop
|
||||
$Owner = $ACL.Owner
|
||||
$ErrorMessage = ''
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrBroken - ACL reading (2) failed for $FullPath with error: $($_.Exception.Message)"
|
||||
$ACL = $null
|
||||
$Owner = $null
|
||||
$ErrorMessage = $_.Exception.Message
|
||||
}
|
||||
|
||||
[PSCustomObject] @{
|
||||
DisplayName = $SysvolHash[$_].BaseName
|
||||
Status = $Differences[$_]
|
||||
DomainName = $Domain
|
||||
SysvolServer = $Server
|
||||
SysvolStatus = 'Exists' #$Differences[$GPO.Id.Guid]
|
||||
GpoStatus = $Differences[$_]
|
||||
Owner = ''
|
||||
FileOwner = $Owner
|
||||
ObjectClass = ''
|
||||
Id = $_
|
||||
Path = $FullPath
|
||||
DistinguishedName = -join ("CN={", $_, "},", $PoliciesSearchBase)
|
||||
DistinguishedName = -join ("CN=", $_, ",", $PoliciesSearchBase)
|
||||
Description = $null
|
||||
CreationTime = $SysvolHash[$_].CreationTime
|
||||
ModificationTime = $SysvolHash[$_].LastWriteTime
|
||||
UserVersion = $null
|
||||
ComputerVersion = $null
|
||||
WmiFilter = $null
|
||||
Error = $ErrorMessage
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,47 @@
|
||||
function Get-GPOZaurrBroken {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Detects broken or otherwise damaged Group Policies
|
||||
|
||||
.DESCRIPTION
|
||||
Detects broken or otherwise damaged Group Policies providing insight whether GPO exists in both AD and SYSVOL.
|
||||
It provides few statuses:
|
||||
- Permissions issue - means account couldn't read GPO due to permissions
|
||||
- ObjectClass issue - means that ObjectClass is of type Container, rather than expected groupPolicyContainer
|
||||
- Not available on SYSVOL - means SYSVOL data is missing, yet AD metadata is available
|
||||
- Not available in AD - means AD metadata is missing, yet SYSVOL data is available
|
||||
- Exists - means AD metadata and SYSVOL data are available
|
||||
|
||||
.PARAMETER Forest
|
||||
Target different Forest, by default current forest is used
|
||||
|
||||
.PARAMETER ExcludeDomains
|
||||
Exclude domain from search, by default whole forest is scanned
|
||||
|
||||
.PARAMETER ExcludeDomainControllers
|
||||
Exclude specific domain controllers, by default there are no exclusions, as long as VerifyDomainControllers switch is enabled. Otherwise this parameter is ignored.
|
||||
|
||||
.PARAMETER IncludeDomains
|
||||
Include only specific domains, by default whole forest is scanned
|
||||
|
||||
.PARAMETER IncludeDomainControllers
|
||||
Include only specific domain controllers, by default all domain controllers are included, as long as VerifyDomainControllers switch is enabled. Otherwise this parameter is ignored.
|
||||
|
||||
.PARAMETER SkipRODC
|
||||
Skip Read-Only Domain Controllers. By default all domain controllers are included.
|
||||
|
||||
.PARAMETER ExtendedForestInformation
|
||||
Ability to provide Forest Information from another command to speed up processing
|
||||
|
||||
.PARAMETER VerifyDomainControllers
|
||||
Forces cmdlet to check GPO Existance on Domain Controllers rather then per domain
|
||||
|
||||
.EXAMPLE
|
||||
Get-GPOZaurrBroken -Verbose | Format-Table
|
||||
|
||||
.NOTES
|
||||
General notes
|
||||
#>
|
||||
[alias('Get-GPOZaurrSysvol')]
|
||||
[cmdletBinding()]
|
||||
param(
|
||||
@@ -8,7 +51,6 @@
|
||||
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
|
||||
[alias('DomainControllers')][string[]] $IncludeDomainControllers,
|
||||
[switch] $SkipRODC,
|
||||
[Array] $GPOs,
|
||||
[System.Collections.IDictionary] $ExtendedForestInformation,
|
||||
[switch] $VerifyDomainControllers
|
||||
)
|
||||
@@ -21,31 +63,31 @@
|
||||
$PoliciesAD = @{}
|
||||
if ($SystemsContainer) {
|
||||
$PoliciesSearchBase = -join ("CN=Policies,", $SystemsContainer)
|
||||
$PoliciesInAD = Get-ADObject -SearchBase $PoliciesSearchBase -SearchScope OneLevel -Filter * -Server $QueryServer
|
||||
$PoliciesInAD = Get-ADObject -SearchBase $PoliciesSearchBase -SearchScope OneLevel -Filter * -Server $QueryServer -Properties Name, gPCFileSysPath, DisplayName, DistinguishedName, Description, Created, Modified, ObjectClass, ObjectGUID
|
||||
foreach ($Policy in $PoliciesInAD) {
|
||||
$GUIDFromDN = ConvertFrom-DistinguishedName -DistinguishedName $Policy.DistinguishedName
|
||||
$GUIDFromDN = $GUIDFromDN -replace '{' -replace '}'
|
||||
$GUID = $Policy.Name -replace '{' -replace '}'
|
||||
if ($GUID -and $GUIDFromDN) {
|
||||
$PoliciesAD[$GUIDFromDN] = 'Exists'
|
||||
if ($Policy.ObjectClass -eq 'Container') {
|
||||
# This usually means GPO deletion process somehow failed and while object itself stayed it isn't groupPolicyContainer anymore
|
||||
$PoliciesAD[$GUIDFromDN] = 'ObjectClass issue'
|
||||
} else {
|
||||
$PoliciesAD[$GUIDFromDN] = 'Permissions issue'
|
||||
$GUID = $Policy.Name
|
||||
if ($GUID -and $GUIDFromDN) {
|
||||
$PoliciesAD[$GUIDFromDN] = 'Exists'
|
||||
} else {
|
||||
$PoliciesAD[$GUIDFromDN] = 'Permissions issue'
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Get-GPOZaurrBroken - Couldn't get GPOs from $Domain. Skipping"
|
||||
}
|
||||
Try {
|
||||
[Array]$GPOs = Get-GPO -All -Domain $Domain -Server $QueryServer
|
||||
} catch {
|
||||
Write-Warning "Get-GPOZaurrBroken - Couldn't get GPOs from $Domain. Error: $($_.Exception.Message)"
|
||||
continue
|
||||
}
|
||||
if ($GPOs.Count -ge 2) {
|
||||
if ($PoliciesInAD.Count -ge 2) {
|
||||
if (-not $VerifyDomainControllers) {
|
||||
Test-SysVolFolders -GPOs $GPOs -Server $Domain -Domain $Domain -PoliciesAD $PoliciesAD -PoliciesSearchBase $PoliciesSearchBase
|
||||
Test-SysVolFolders -GPOs $PoliciesInAD -Server $Domain -Domain $Domain -PoliciesAD $PoliciesAD -PoliciesSearchBase $PoliciesSearchBase
|
||||
} else {
|
||||
foreach ($Server in $ForestInformation['DomainDomainControllers']["$Domain"]) {
|
||||
Write-Verbose "Get-GPOZaurrBroken - Processing $Domain \ $($Server.HostName.Trim())"
|
||||
Test-SysVolFolders -GPOs $GPOs -Server $Server.Hostname -Domain $Domain -PoliciesAD $PoliciesAD -PoliciesSearchBase $PoliciesSearchBase
|
||||
Test-SysVolFolders -GPOs $PoliciesInAD -Server $Server.Hostname -Domain $Domain -PoliciesAD $PoliciesAD -PoliciesSearchBase $PoliciesSearchBase
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,54 @@
|
||||
function Remove-GPOZaurrBroken {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Finds and removes broken Group Policies from SYSVOL or AD or both.
|
||||
|
||||
.DESCRIPTION
|
||||
Finds and removes broken Group Policies from SYSVOL or AD or both. Assesment is based on Get-GPOZaurrBroken and there are 3 supported types:
|
||||
- AD - meaning GPOs which have no SYSVOL content will be deleted from AD
|
||||
- SYSVOL - meaning GPOs which have no AD content will be deleted from SYSVOL
|
||||
- ObjectClass - meaning GPOs which have ObjectClass category of Container rather than groupPolicyContainer will be deleted from AD & SYSVOL
|
||||
|
||||
.PARAMETER Type
|
||||
Choose one or more types to delete. Options are AD, ObjectClass, SYSVOL
|
||||
|
||||
.PARAMETER BackupPath
|
||||
Path to optional backup of SYSVOL content before deletion
|
||||
|
||||
.PARAMETER BackupDated
|
||||
Forces backup to be created within folder that has date in it
|
||||
|
||||
.PARAMETER LimitProcessing
|
||||
Allows to specify maximum number of items that will be fixed in a single run. It doesn't affect amount of GPOs processed
|
||||
|
||||
.PARAMETER Forest
|
||||
Target different Forest, by default current forest is used
|
||||
|
||||
.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
|
||||
|
||||
.EXAMPLE
|
||||
Remove-GPOZaurrBroken -Verbose -WhatIf -Type AD, SYSVOL
|
||||
|
||||
.EXAMPLE
|
||||
Remove-GPOZaurrBroken -Verbose -WhatIf -Type AD, SYSVOL -IncludeDomains 'ad.evotec.pl' -LimitProcessing 2
|
||||
|
||||
.EXAMPLE
|
||||
Remove-GPOZaurrBroken -Verbose -IncludeDomains 'ad.evotec.xyz' -BackupPath $Env:UserProfile\Desktop\MyBackup1 -WhatIf -Type AD, SYSVOL
|
||||
|
||||
.NOTES
|
||||
General notes
|
||||
#>
|
||||
[alias('Remove-GPOZaurrOrphaned')]
|
||||
[cmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[ValidateSet('SYSVOL', 'AD')][string[]] $Type = @('SYSVOL', 'AD'),
|
||||
[parameter(Mandatory, Position = 0)][ValidateSet('SYSVOL', 'AD', 'ObjectClass')][string[]] $Type,
|
||||
[string] $BackupPath,
|
||||
[switch] $BackupDated,
|
||||
[int] $LimitProcessing = [int32]::MaxValue,
|
||||
@@ -32,10 +78,15 @@
|
||||
$_
|
||||
}
|
||||
}
|
||||
if ($Type -contains 'ObjectClass') {
|
||||
if ($_.Status -eq 'ObjectClass issue') {
|
||||
$_
|
||||
}
|
||||
}
|
||||
} | Select-Object | Select-Object -First $LimitProcessing | ForEach-Object {
|
||||
$GPO = $_
|
||||
if ($GPO.Status -eq 'Not available in AD') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Processing [AD] $($GPO.Path)"
|
||||
if ($GPO.Status -in 'Not available in AD', 'ObjectClass issue') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Removing from SYSVOL [$($GPO.Status)] $($GPO.Path)"
|
||||
if ($BackupFinalPath) {
|
||||
Try {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Backing up $($GPO.Path)"
|
||||
@@ -47,15 +98,16 @@
|
||||
}
|
||||
}
|
||||
if ($BackupWorked -or $BackupFinalPath -eq '') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Deleting $($GPO.Path)"
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Removing $($GPO.Path)"
|
||||
try {
|
||||
Remove-Item -Recurse -Force -LiteralPath $GPO.Path -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Warning "Remove-GPOZaurrBroken - Failed to remove file $($GPO.Path): $($_.Exception.Message)."
|
||||
}
|
||||
}
|
||||
} elseif ($GPO.Status -eq 'Not available on SYSVOL') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Processing [SYSVOL] $($GPO.DistinguishedName)"
|
||||
}
|
||||
if ($GPO.Status -in 'Not available on SYSVOL', 'ObjectClass issue') {
|
||||
Write-Verbose "Remove-GPOZaurrBroken - Removing from AD [$($GPO.Status)] $($GPO.DistinguishedName)"
|
||||
try {
|
||||
$ExistingObject = Get-ADObject -Identity $GPO.DistinguishedName -Server $GPO.DomainName -ErrorAction Stop
|
||||
} catch {
|
||||
|
||||
@@ -57,7 +57,28 @@ That's it. Whenever there's a new version, you run the command, and you can enjo
|
||||
|
||||
## Changelog
|
||||
|
||||
- 0.0.99 - 13.10.2020
|
||||
- 0.0.101 - 23.12.2020
|
||||
- [x] Improved `Get-GPOZaurrBroken`
|
||||
- [x] It now detects `ObjectClass Issue`
|
||||
- [x] Heavily improved performance
|
||||
- [x] Removed some useless properties for this particular cmdlet
|
||||
- [x] All states: `Not available on SYSVOL`, `Not available in AD`, `Exists`, `Permissions Issue`, `ObjectClass Issue`
|
||||
- [x] Improved help
|
||||
- [x] Improved `Remove-GPOZaurrBroken`
|
||||
- [x] It now deals with `ObjectClass Issue`
|
||||
- [x] Heavily improved performance
|
||||
- [x] Removed some useless properties for this particular cmdlet
|
||||
- [x] Now requires manual type insert AD, SYSVOL or ObjectClass (or all of them). Before it was auto using AD/SYSVOL.
|
||||
- [x] Improved help
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] Type `GPOList`
|
||||
- [x] Renamed `GPOOrphans` to `GPOBroken`
|
||||
- [x] Improved `GPOBroken` with `ObjectClass issue`
|
||||
- 0.0.100 - 21.12.2020
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] Type `GPOPermissionsRead`
|
||||
- [x] Type `GPOPermissions`
|
||||
- 0.0.99 - 13.12.2020
|
||||
- [x] Improved `Invoke-GPOZaurr`
|
||||
- [x] Type `GPOList` - require GPO to be 7 days old for deletion to be proposed
|
||||
- [x] Type `GPOPermissions` - one stop for permissions
|
||||
|
||||
Reference in New Issue
Block a user