Created
January 13, 2026 13:23
-
-
Save Torxsmind/b64ac5dfcf635c69a891827ef54c5994 to your computer and use it in GitHub Desktop.
AD Unlinked GPOs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .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