138 lines
5.2 KiB
PowerShell
138 lines
5.2 KiB
PowerShell
# ------------------------------------------------------------------
|
||
# 1. Set up environment
|
||
# ------------------------------------------------------------------
|
||
$ComputerName = "FRRF1LLC1103011"
|
||
$printDebug = $false
|
||
|
||
Import-Module GroupPolicy -ErrorAction Stop
|
||
Import-Module ActiveDirectory -ErrorAction Stop
|
||
|
||
# ------------------------------------------------------------------
|
||
# 2. Resolve the computer’s DN and build the OU chain (leaf → root)
|
||
# ------------------------------------------------------------------
|
||
try {
|
||
$comp = Get-ADComputer -Identity $ComputerName -Properties DistinguishedName
|
||
}
|
||
catch {
|
||
Write-Error "Cannot find computer '$ComputerName' in AD: $_"
|
||
exit 1
|
||
}
|
||
|
||
$ouDN = $comp.DistinguishedName -replace '^CN=.*?,', ''
|
||
$ouChain = @()
|
||
$currentDN = $ouDN
|
||
|
||
while ($currentDN -match '^OU=') {
|
||
$ouChain += $currentDN
|
||
$currentDN = $currentDN.Substring($currentDN.IndexOf(',') + 1)
|
||
}
|
||
$ouChain += $currentDN # root
|
||
|
||
if ($printDebug) {
|
||
Write-Host "OU chain (leaf → root):" -ForegroundColor Cyan
|
||
$ouChain | ForEach-Object { Write-Host $_ }
|
||
}
|
||
|
||
# ------------------------------------------------------------------
|
||
# 3. Gather GPO links for each OU in the chain
|
||
# ------------------------------------------------------------------
|
||
$gpoLinks = @{} # Hashtable: GPO_GUID => array of OU DNs
|
||
|
||
foreach ($ouDN in $ouChain) {
|
||
try {
|
||
$ouObj = Get-ADObject -Identity $ouDN -Properties gPLink -ErrorAction Stop
|
||
}
|
||
catch {
|
||
Write-Warning "Could not read OU '$ouDN': $_"
|
||
continue
|
||
}
|
||
|
||
if ($ouObj.gPLink) {
|
||
foreach ($link in ($ouObj.gPLink -split ';')) {
|
||
$clean = $link.Trim(' ', '[', ']')
|
||
if ($clean -match '\{(?<guid>[0-9a-fA-F-]+)\}') {
|
||
$guid = $Matches.guid
|
||
if (-not $gpoLinks.ContainsKey($guid)) { $gpoLinks[$guid] = @() }
|
||
$gpoLinks[$guid] += $ouDN
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (-not $gpoLinks.Count) {
|
||
Write-Host "No GPOs linked to any OU in the hierarchy." -ForegroundColor Yellow
|
||
exit
|
||
}
|
||
|
||
Write-Host "Found $($gpoLinks.Count) distinct GPO(s) linked in this OU tree." -ForegroundColor Green
|
||
|
||
# ------------------------------------------------------------------
|
||
# 4. Collect all restricted‑group entries into an array
|
||
# ------------------------------------------------------------------
|
||
$results = @() # Will hold objects: @{ADGroup=; LocalGroup=; GPO=}
|
||
|
||
$uniqueGpoGuids = $gpoLinks.Keys | Sort-Object -Unique
|
||
$ou_counter = 0
|
||
$totalItems = $uniqueGpoGuids.Count
|
||
$startTime = Get-Date
|
||
|
||
foreach ($gpoGuid in $uniqueGpoGuids) {
|
||
$ou_counter++
|
||
|
||
try { $gpo = Get-GPO -Guid $gpoGuid -ErrorAction Stop }
|
||
catch { Write-Warning "Unable to retrieve GPO $gpoGuid – skipping."; continue }
|
||
|
||
$percentComplete = [int]( ($ou_counter / $gpoLinks.Count) * 100 )
|
||
if ($ou_counter -gt 0) {
|
||
$elapsed = (Get-Date) - $startTime
|
||
$averagePerItem = $elapsed.TotalSeconds / $ou_counter
|
||
$remainingItems = $totalItems - $ou_counter
|
||
$secondsRemaining = [int]($averagePerItem * $remainingItems)
|
||
} else { $secondsRemaining = -1 }
|
||
|
||
Write-Progress -Activity "Processing GPOs" `
|
||
-Status $gpo.DisplayName `
|
||
-PercentComplete $percentComplete `
|
||
-SecondsRemaining $secondsRemaining `
|
||
-CurrentOperation "GPO $ou_counter of $totalItems"
|
||
|
||
# Grab XML report
|
||
try { $xmlString = Get-GPOReport -Guid $gpoGuid -ReportType Xml -ErrorAction Stop }
|
||
catch { Write-Warning "Unable to generate XML report for $($gpo.DisplayName)."; continue }
|
||
|
||
$xml = [xml]$xmlString
|
||
$ns = New-Object System.Xml.XmlNamespaceManager ($xml.NameTable)
|
||
$ns.AddNamespace('m', 'http://www.microsoft.com/GroupPolicy/Settings')
|
||
$ns.AddNamespace('q1', 'http://www.microsoft.com/GroupPolicy/Settings/Security')
|
||
$ns.AddNamespace('t', 'http://www.microsoft.com/GroupPolicy/Types')
|
||
$ns.AddNamespace('xsi','http://www.w3.org/2001/XMLSchema-instance')
|
||
|
||
$restrictedGroups = $xml.SelectNodes('//q1:RestrictedGroups', $ns)
|
||
|
||
foreach ($rg in $restrictedGroups) {
|
||
$adGroupName = ($rg.SelectSingleNode('q1:GroupName/t:Name', $ns)).InnerText
|
||
$localGroupName = ($rg.SelectSingleNode('q1:Memberof/t:Name', $ns)).InnerText
|
||
|
||
$results += [pscustomobject]@{
|
||
ADGroup = $adGroupName
|
||
LocalGroup = $localGroupName
|
||
GPO = $gpo.DisplayName
|
||
}
|
||
}
|
||
}
|
||
|
||
$elapsed = (Get-Date) - $startTime
|
||
$elapsedSeconds = [math]::Round($elapsed.TotalSeconds, 2)
|
||
Write-Host "`nFinished processing $totalItems GPO(s) after $elapsedSeconds seconds." -ForegroundColor Green
|
||
|
||
# ------------------------------------------------------------------
|
||
# 5. Print two separate lists
|
||
# ------------------------------------------------------------------
|
||
Write-Host "`n=== AD groups that are members of BUILTIN\Administrators ===" -ForegroundColor Yellow
|
||
$results | Where-Object {$_.LocalGroup -eq 'BUILTIN\Administrators'} |
|
||
Select-Object -ExpandProperty ADGroup | Sort-Object | Get-Unique
|
||
|
||
Write-Host "`n=== AD groups that are members of vnc_admin ===" -ForegroundColor Yellow
|
||
$results | Where-Object {$_.LocalGroup -eq 'vnc_admin'} |
|
||
Select-Object -ExpandProperty ADGroup | Sort-Object | Get-Unique
|