Skip to content

Instantly share code, notes, and snippets.

@Torxsmind
Created January 13, 2026 13:23
Show Gist options
  • Select an option

  • Save Torxsmind/b64ac5dfcf635c69a891827ef54c5994 to your computer and use it in GitHub Desktop.

Select an option

Save Torxsmind/b64ac5dfcf635c69a891827ef54c5994 to your computer and use it in GitHub Desktop.
AD Unlinked GPOs
<#
.SYNOPSIS
List GPOs in the current domain that are not linked to any OU,
show a count at the top, and display results sorted by "Date Last Modified" (oldest → newest).
.OUTPUTS
Console count + table: GPO Name, Date Created, Date Last Modified, Status, Linked OUs (None)
.NOTES
OU-only linkage is considered (domain root and site links are ignored).
Run in Windows PowerShell 5.x with RSAT: Active Directory + Group Policy Management Tools.
#>
[CmdletBinding()]
param(
[string]$Domain = (Get-ADDomain).DNSRoot,
[string]$ExportCsvPath
)
# Import modules (soft check with clear error)
try {
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
throw "ActiveDirectory module not available. Install RSAT: Active Directory."
}
if (-not (Get-Module -ListAvailable -Name GroupPolicy)) {
throw "GroupPolicy module not available. Install RSAT: Group Policy Management Tools (GPMC)."
}
Import-Module ActiveDirectory -ErrorAction Stop
Import-Module GroupPolicy -ErrorAction Stop
} catch {
Write-Error $_.Exception.Message
return
}
function Get-GpoGuidsFromGpLink {
param([string]$gpLink)
if ([string]::IsNullOrWhiteSpace($gpLink)) { return @() }
$guids = @()
foreach ($segment in ($gpLink -split '\]\[')) {
if ($segment -match '\{([0-9A-Fa-f\-]{36})\}') {
$guids += $matches[1].ToLower()
}
}
$guids | Where-Object { $_ } | Select-Object -Unique
}
# Collect set of GPO GUIDs that are linked to any OU
$ouLinkedGuids = Get-ADOrganizationalUnit -Filter * -Server $Domain -Properties gPLink |
ForEach-Object { Get-GpoGuidsFromGpLink -gpLink $_.gPLink } |
Where-Object { $_ } |
Select-Object -Unique
# Get all GPOs
$allGpos = Get-GPO -All -Domain $Domain
# Identify GPOs not linked to any OU and project with Linked OUs column (None)
$unlinked = foreach ($gpo in $allGpos) {
$guid = $gpo.Id.Guid.ToString().ToLower()
if ($guid -notin $ouLinkedGuids) {
[pscustomobject]@{
'GPO Name' = $gpo.DisplayName
'Date Created' = $gpo.CreationTime
'Date Last Modified' = $gpo.ModificationTime
'Status' = $gpo.GpoStatus # AllSettingsEnabled | UserSettingsDisabled | ...
'Linked OUs' = 'None'
}
}
}
# Sort oldest → newest by Date Last Modified
$unlinked = $unlinked | Sort-Object 'Date Last Modified'
# Print count first
$unlinkedCount = ($unlinked | Measure-Object).Count
Write-Host ("Unlinked GPOs: {0}" -f $unlinkedCount)
# Show as a table
$unlinked | Format-Table 'GPO Name','Date Created','Date Last Modified','Status','Linked OUs' -AutoSize
# Optional CSV export (keeps the sorted order)
if ($ExportCsvPath) {
try {
$unlinked | Export-Csv -Path $ExportCsvPath -NoTypeInformation -Encoding UTF8
Write-Host "Unlinked GPO results written to $ExportCsvPath"
} catch {
Write-Error "Failed to write unlinked GPO CSV: $($_.Exception.Message)"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment