declarative-ad-framework/gpo/Get-UnmanagedGPOs.ps1
Damien Coles f172d00514 Initial release: Declarative AD Framework v2.1.0
Infrastructure-as-code framework for Active Directory objects and Group Policy.
Sanitized from production deployment for public sharing.
2026-02-19 17:02:42 +00:00

136 lines
4.6 KiB
PowerShell

# 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