Improve Get-GPOZaurrBroken

This commit is contained in:
Przemyslaw Klys
2020-12-23 09:32:07 +01:00
parent a36a470538
commit 15538e8559
2 changed files with 93 additions and 73 deletions
+35 -57
View File
@@ -9,8 +9,7 @@
) )
$Differences = @{ } $Differences = @{ }
$SysvolHash = @{ } $SysvolHash = @{ }
$GPOGUIDS = ConvertFrom-DistinguishedName -DistinguishedName $GPOs.DistinguishedName
$GPOGUIDS = $GPOs.ID.GUID
$SysVolPath = "\\$($Server)\SYSVOL\$Domain\Policies" $SysVolPath = "\\$($Server)\SYSVOL\$Domain\Policies"
Write-Verbose "Get-GPOZaurrBroken - Processing SYSVOL from \\$($Server)\SYSVOL\$Domain\Policies" Write-Verbose "Get-GPOZaurrBroken - Processing SYSVOL from \\$($Server)\SYSVOL\$Domain\Policies"
try { try {
@@ -19,10 +18,10 @@
$Sysvol = $Null $Sysvol = $Null
} }
foreach ($_ in $SYSVOL) { foreach ($_ in $SYSVOL) {
$GUID = $_.Name -replace '{' -replace '}' $GUID = $_.Name
$SysvolHash[$GUID] = $_ $SysvolHash[$GUID] = $_
} }
$Files = $SYSVOL.Name -replace '{' -replace '}' $Files = $SYSVOL.Name
if ($Files) { if ($Files) {
$Comparing = Compare-Object -ReferenceObject $GPOGUIDS -DifferenceObject $Files -IncludeEqual $Comparing = Compare-Object -ReferenceObject $GPOGUIDS -DifferenceObject $Files -IncludeEqual
foreach ($_ in $Comparing) { foreach ($_ in $Comparing) {
@@ -30,8 +29,10 @@
# we skip policy definitions # we skip policy definitions
continue continue
} }
$ADStatus = $PoliciesAD[$_.InputObject]
if ($_.SideIndicator -eq '==') { if ($_.SideIndicator -eq '==') {
$Found = 'Exists' #$Found = 'Exists'
$Found = $ADStatus
} elseif ($_.SideIndicator -eq '<=') { } elseif ($_.SideIndicator -eq '<=') {
$Found = 'Not available on SYSVOL' $Found = 'Not available on SYSVOL'
} elseif ($_.SideIndicator -eq '=>') { } elseif ($_.SideIndicator -eq '=>') {
@@ -41,8 +42,10 @@
$Found = 'Not available in AD' $Found = 'Not available in AD'
} }
} else { } else {
# This shouldn't happen at all
$Found = 'Orphaned GPO' $Found = 'Orphaned GPO'
} }
$Differences[$_.InputObject] = $Found $Differences[$_.InputObject] = $Found
} }
} }
@@ -50,85 +53,60 @@
$Count = 0 $Count = 0
foreach ($GPO in $GPOS) { foreach ($GPO in $GPOS) {
$Count++ $Count++
Write-Verbose "Get-GPOZaurrBroken - Processing [$($GPO.DomainName)]($Count/$($GPOS.Count)) $($GPO.DisplayName)" $GPOGuid = ConvertFrom-DistinguishedName -DistinguishedName $GPO.DistinguishedName
if ($null -ne $SysvolHash[$GPO.Id.GUID].FullName) { if ($GPO.DisplayName) {
$FullPath = $SysvolHash[$GPO.Id.GUID].FullName $GPODisplayName = $GPO.DisplayName
try { $GPOName = $GPO.Name
$ACL = Get-Acl -Path $SysvolHash[$GPO.Id.GUID].FullName -ErrorAction Stop -Verbose:$false Write-Verbose "Get-GPOZaurrBroken - Processing [$($Domain)]($Count/$($GPOS.Count)) $($GPO.DisplayName)"
$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
}
} else { } else {
$FullPath = -join ($SysVolPath, "\{$($GPO.Id.Guid)}") $GPOName = $GPOGuid
$ACL = $null $GPODisplayName = $GPOGuid
$Owner = '' 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' $ErrorMessage = 'Not found on SYSVOL'
} }
if ($null -eq $Differences[$GPO.Id.Guid]) { if ($null -eq $Differences[$GPOGuid]) {
$SysVolStatus = 'Unknown Issue' $SysVolStatus = 'Unknown issue'
} else { } else {
$SysVolStatus = $Differences[$GPO.Id.Guid] $SysVolStatus = $Differences[$GPOGuid]
} }
[PSCustomObject] @{ [PSCustomObject] @{
DisplayName = $GPO.DisplayName DisplayName = $GPODisplayName
Status = $SysVolStatus Status = $SysVolStatus
DomainName = $GPO.DomainName DomainName = $Domain
SysvolServer = $Server SysvolServer = $Server
SysvolStatus = $SysVolStatus ObjectClass = $GPO.ObjectClass
GpoStatus = $GPO.GpoStatus Id = $GPOName
Owner = $GPO.Owner
FileOwner = $Owner
Id = $GPO.Id.Guid
Path = $FullPath Path = $FullPath
DistinguishedName = -join ("CN={", $GPO.Id.Guid, "},", $PoliciesSearchBase) DistinguishedName = -join ("CN=", $GPOGuid, ",", $PoliciesSearchBase)
Description = $GPO.Description Description = $GPO.Description
CreationTime = $GPO.CreationTime CreationTime = $GPO.Created
ModificationTime = $GPO.ModificationTime ModificationTime = $GPO.Modified
UserVersion = $GPO.UserVersion
ComputerVersion = $GPO.ComputerVersion
WmiFilter = $GPO.WmiFilter
Error = $ErrorMessage 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 # 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" Write-Verbose "Get-GPOZaurrBroken - Processing SYSVOL differences"
foreach ($_ in $Differences.Keys) { foreach ($_ in $Differences.Keys) {
if ($Differences[$_] -in 'Not available in AD', 'Permissions issue') { if ($Differences[$_] -in 'Not available in AD') {
$FullPath = $SysvolHash[$_].FullName $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] @{ [PSCustomObject] @{
DisplayName = $SysvolHash[$_].BaseName DisplayName = $SysvolHash[$_].BaseName
Status = $Differences[$_] Status = $Differences[$_]
DomainName = $Domain DomainName = $Domain
SysvolServer = $Server SysvolServer = $Server
SysvolStatus = 'Exists' #$Differences[$GPO.Id.Guid] ObjectClass = ''
GpoStatus = $Differences[$_]
Owner = ''
FileOwner = $Owner
Id = $_ Id = $_
Path = $FullPath Path = $FullPath
DistinguishedName = -join ("CN={", $_, "},", $PoliciesSearchBase) DistinguishedName = -join ("CN=", $_, ",", $PoliciesSearchBase)
Description = $null Description = $null
CreationTime = $SysvolHash[$_].CreationTime CreationTime = $SysvolHash[$_].CreationTime
ModificationTime = $SysvolHash[$_].LastWriteTime ModificationTime = $SysvolHash[$_].LastWriteTime
UserVersion = $null
ComputerVersion = $null
WmiFilter = $null
Error = $ErrorMessage Error = $ErrorMessage
} }
} }
+58 -16
View File
@@ -1,4 +1,47 @@
function Get-GPOZaurrBroken { 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')] [alias('Get-GPOZaurrSysvol')]
[cmdletBinding()] [cmdletBinding()]
param( param(
@@ -8,7 +51,6 @@
[alias('Domain', 'Domains')][string[]] $IncludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains,
[alias('DomainControllers')][string[]] $IncludeDomainControllers, [alias('DomainControllers')][string[]] $IncludeDomainControllers,
[switch] $SkipRODC, [switch] $SkipRODC,
[Array] $GPOs,
[System.Collections.IDictionary] $ExtendedForestInformation, [System.Collections.IDictionary] $ExtendedForestInformation,
[switch] $VerifyDomainControllers [switch] $VerifyDomainControllers
) )
@@ -21,31 +63,31 @@
$PoliciesAD = @{} $PoliciesAD = @{}
if ($SystemsContainer) { if ($SystemsContainer) {
$PoliciesSearchBase = -join ("CN=Policies,", $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) { foreach ($Policy in $PoliciesInAD) {
$GUIDFromDN = ConvertFrom-DistinguishedName -DistinguishedName $Policy.DistinguishedName $GUIDFromDN = ConvertFrom-DistinguishedName -DistinguishedName $Policy.DistinguishedName
$GUIDFromDN = $GUIDFromDN -replace '{' -replace '}' if ($Policy.ObjectClass -eq 'Container') {
$GUID = $Policy.Name -replace '{' -replace '}' # This usually means GPO deletion process somehow failed and while object itself stayed it isn't groupPolicyContainer anymore
if ($GUID -and $GUIDFromDN) { $PoliciesAD[$GUIDFromDN] = 'ObjectClass issue'
$PoliciesAD[$GUIDFromDN] = 'Exists'
} else { } 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 { if ($PoliciesInAD.Count -ge 2) {
[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 (-not $VerifyDomainControllers) { 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 { } else {
foreach ($Server in $ForestInformation['DomainDomainControllers']["$Domain"]) { foreach ($Server in $ForestInformation['DomainDomainControllers']["$Domain"]) {
Write-Verbose "Get-GPOZaurrBroken - Processing $Domain \ $($Server.HostName.Trim())" 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 { } else {