# Get-UnmanagedGPOs.ps1 # Discovers GPOs in AD that are not managed by the framework, # and framework-defined GPOs that do not yet exist in AD. # # Usage: # .\Get-UnmanagedGPOs.ps1 [CmdletBinding()] param() $ErrorActionPreference = 'Stop' $ScriptRoot = $PSScriptRoot . (Join-Path $ScriptRoot 'lib\GPOHelper.ps1') # ------------------------------------------------------------------- # Discover managed GPO names from settings.ps1 files # ------------------------------------------------------------------- $gpoDirs = Get-ChildItem -Path $ScriptRoot -Directory | Where-Object { Test-Path (Join-Path $_.FullName 'settings.ps1') } $managedNames = @{} foreach ($dir in $gpoDirs) { $settings = & (Join-Path $dir.FullName 'settings.ps1') $managedNames[$settings.GPOName] = $dir.Name } Write-Host "Framework manages $($managedNames.Count) GPO(s).`n" -ForegroundColor Cyan # ------------------------------------------------------------------- # Enumerate all GPOs in AD # ------------------------------------------------------------------- $allGPOs = Get-GPO -All # ------------------------------------------------------------------- # Build OU link map (check known OUs for GPO links) # ------------------------------------------------------------------- $domainDN = (Get-ADDomain).DistinguishedName $checkTargets = @( $domainDN "OU=Domain Controllers,$domainDN" "OU=ExampleUsers,$domainDN" "OU=ExampleWorkstations,$domainDN" "OU=ExampleServers,$domainDN" "OU=ExampleAdmins,$domainDN" "OU=ExampleAdminWorkstations,$domainDN" ) # Map GPO GUID -> list of linked OU names $linkMap = @{} foreach ($target in $checkTargets) { try { $inheritance = Get-GPInheritance -Target $target -ErrorAction Stop } catch { continue } # Extract short OU name for display $ouDisplay = if ($target -eq $domainDN) { '(domain root)' } else { ($target -split ',')[0] -replace '^OU=', '' } foreach ($link in $inheritance.GpoLinks) { $gpoId = $link.GpoId.ToString() if (-not $linkMap.ContainsKey($gpoId)) { $linkMap[$gpoId] = @() } $linkMap[$gpoId] += $ouDisplay } } # ------------------------------------------------------------------- # Section 1: Unmanaged GPOs (in AD but not in framework) # ------------------------------------------------------------------- $unmanaged = @() foreach ($gpo in $allGPOs) { if (-not $managedNames.ContainsKey($gpo.DisplayName)) { $gpoId = $gpo.Id.ToString() $linkedTo = if ($linkMap.ContainsKey($gpoId)) { $linkMap[$gpoId] -join ', ' } else { '(not linked)' } $unmanaged += [PSCustomObject]@{ Name = $gpo.DisplayName Id = "{$gpoId}" CreationTime = $gpo.CreationTime.ToString('yyyy-MM-dd') ModificationTime = $gpo.ModificationTime.ToString('yyyy-MM-dd') LinkedTo = $linkedTo GpoStatus = $gpo.GpoStatus.ToString() } } } Write-Host '=== Unmanaged GPOs in AD ===' -ForegroundColor White if ($unmanaged.Count -eq 0) { Write-Host 'None found. All GPOs in AD are managed by the framework.' -ForegroundColor Green } else { Write-Host "$($unmanaged.Count) GPO(s) exist in AD without a settings.ps1:`n" -ForegroundColor Yellow $unmanaged | Format-Table Name, Id, CreationTime, ModificationTime, LinkedTo, GpoStatus -AutoSize } # ------------------------------------------------------------------- # Section 2: Framework GPOs not in AD (defined but not created) # ------------------------------------------------------------------- $adNames = @{} foreach ($gpo in $allGPOs) { $adNames[$gpo.DisplayName] = $true } $notInAD = @() foreach ($name in $managedNames.Keys) { if (-not $adNames.ContainsKey($name)) { $notInAD += [PSCustomObject]@{ GPOName = $name ConfigDir = $managedNames[$name] Status = 'Defined but not created in AD' } } } Write-Host '=== Framework GPOs Not in AD ===' -ForegroundColor White if ($notInAD.Count -eq 0) { Write-Host 'None. All framework GPOs exist in AD.' -ForegroundColor Green } else { Write-Host "$($notInAD.Count) framework GPO(s) not yet created:`n" -ForegroundColor Yellow $notInAD | Format-Table GPOName, ConfigDir, Status -AutoSize } # ------------------------------------------------------------------- # Summary # ------------------------------------------------------------------- Write-Host '' Write-Host "Summary: $($unmanaged.Count) unmanaged, $($notInAD.Count) not in AD, $($managedNames.Count) managed." -ForegroundColor Cyan