Public/Get-EmptyGPOs.ps1
|
function Get-EmptyGPOs { <# .SYNOPSIS Finds Group Policy Objects with no settings configured. .DESCRIPTION Retrieves every GPO in the domain and examines the XML report to determine whether Computer Configuration and User Configuration contain any extension data (actual policy settings). GPOs where both sides are empty clutter GPMC and add to processing overhead without providing any value. When -IncludeDisabledSections is specified, the function also flags GPOs where one configuration side is disabled but still contains settings -- a common misconfiguration that hides active policies. This function is read-only and never modifies or deletes GPOs. .PARAMETER IncludeDisabledSections Also report GPOs where a configuration section (Computer or User) is disabled but still has settings configured. These settings are dormant and may indicate a misconfiguration. .EXAMPLE Get-EmptyGPOs Returns GPOs where both Computer and User configurations have zero settings. .EXAMPLE Get-EmptyGPOs -IncludeDisabledSections Also includes GPOs with disabled-but-configured sections. .OUTPUTS [PSCustomObject] with properties: DisplayName, Id, ComputerSettingsConfigured, UserSettingsConfigured, ComputerEnabled, UserEnabled, Finding #> [CmdletBinding()] param( [Parameter()] [switch]$IncludeDisabledSections ) begin { Write-Verbose 'Get-EmptyGPOs: Starting scan for empty Group Policy Objects' if (-not (Get-Module -ListAvailable -Name GroupPolicy)) { throw 'The GroupPolicy RSAT module is required but not installed. Install RSAT tools and try again.' } Import-Module GroupPolicy -ErrorAction Stop -Verbose:$false } process { try { $AllGPOs = @(Get-GPO -All -ErrorAction Stop) Write-Verbose "Get-EmptyGPOs: Retrieved $($AllGPOs.Count) GPOs from domain" } catch { Write-Error "Failed to retrieve GPOs: $_" return } $EmptyCount = 0 foreach ($GPO in $AllGPOs) { Write-Verbose "Get-EmptyGPOs: Checking settings for '$($GPO.DisplayName)'" try { [xml]$Report = Get-GPOReport -Guid $GPO.Id -ReportType Xml -ErrorAction Stop } catch { Write-Warning "Get-EmptyGPOs: Could not generate report for '$($GPO.DisplayName)': $_" continue } # Determine whether each configuration side has extension data $ComputerExtensions = $Report.GPO.Computer.ExtensionData $UserExtensions = $Report.GPO.User.ExtensionData $ComputerHasSettings = ($null -ne $ComputerExtensions) -and (@($ComputerExtensions).Count -gt 0) $UserHasSettings = ($null -ne $UserExtensions) -and (@($UserExtensions).Count -gt 0) # Determine enabled state from the GPO object $ComputerEnabled = $GPO.GpoStatus -in @('AllSettingsEnabled', 'UserSettingsDisabled') $UserEnabled = $GPO.GpoStatus -in @('AllSettingsEnabled', 'ComputerSettingsDisabled') # Case 1: Completely empty GPO (no settings on either side) if (-not $ComputerHasSettings -and -not $UserHasSettings) { $EmptyCount++ [PSCustomObject]@{ DisplayName = $GPO.DisplayName Id = $GPO.Id.ToString() ComputerSettingsConfigured = $false UserSettingsConfigured = $false ComputerEnabled = $ComputerEnabled UserEnabled = $UserEnabled Finding = 'EMPTY' } } # Case 2: Disabled section that still has settings (only with switch) elseif ($IncludeDisabledSections) { $Findings = @() if ($ComputerHasSettings -and -not $ComputerEnabled) { $Findings += 'COMPUTER_DISABLED_WITH_SETTINGS' } if ($UserHasSettings -and -not $UserEnabled) { $Findings += 'USER_DISABLED_WITH_SETTINGS' } if ($Findings.Count -gt 0) { $EmptyCount++ [PSCustomObject]@{ DisplayName = $GPO.DisplayName Id = $GPO.Id.ToString() ComputerSettingsConfigured = $ComputerHasSettings UserSettingsConfigured = $UserHasSettings ComputerEnabled = $ComputerEnabled UserEnabled = $UserEnabled Finding = $Findings -join '; ' } } } } Write-Verbose "Get-EmptyGPOs: Scan complete. Found $EmptyCount finding(s) out of $($AllGPOs.Count) total GPOs." } } |