Group Policy Objects (GPOs) are ideal for setting various configuration items on a vast number of devices in an Active Directory domain. A single GPO can enforce a change across all devices in a domain in a few minutes. GPOs are an excellent way to manage systems in bulk from a single place but over time, you can end up with with hundreds or even thousands of these GPOs. In this blog post, you're going to learn how to use PowerShell to get GPOs linked to OUs in Active Directory.

After a few years of consistent GPO additions and modifications, there comes a point to where it can become a mess! GPOs can contain settings that don't apply anymore, they can conflict with one another when not applied in the right order and what was once linked to some different organizational units (OUs) aren't linked anymore.

In this article, you're going to learn one way to clean up GPOs; finding unlinked GPOs.

Unlinked GPOs are those GPOs that are no longer applicable to computers or users. Since GPOs must be linked to one or more OUs to apply configuration changes, GPOs are that aren't linked to a single OU have no effect at all.

To keep Active Directory clean, it's important to discover and remove these GPOs if they are no longer needed. Unfortunately, there's no good way to do this through the Group Policy Management console but we can make it happen with PowerShell and the Active Directory module.

Assuming you're on an Active Directory-joined computer with appropriate rights and have the ActiveDirectory module installed, you'll first need to find all of the GPO links in the Active Directory environment. To do this, use the Get-GPO cmdlet with the All parameter.

$gpos = Get-Gpo -All

Once you have all GPOs assigned to the $gpos variable, you can then begin looking into each one. To do that, you'll use a foreach loop. Because the objects output by Get-Gpo don't have the property to determine what OU they are linked to, you're forced to use another cmdlet called Get-GPOReport. Use the ID property on each object represented by $gpo.ID and pass that to the Get-GPOReport cmdlet.

Since you'll need to do some further examination of the output of Get-GPOReport, be sure to output the report in XML. This allows you to easily reference properties on the $gpoReport variable.

Notice below that I'm casting $gpoReport to an XML object. By doing this, it will allow you to reference various nodes inside of the XML rather than having to parse it like a string.

foreach ($gpo in $gpos) {
    [xml]$gpoReport = Get-GPOReport -Guid $gpo.ID -ReportType xml
}

You now have the code to create an XML GPO report for each GPO in the environment. At this point, check the GPO.LinksTo property on each $gpoReport to determine if it is null or not. If it's null then that means no OU has been linked to it. Knowing this, you can then only include those GPOs that aren't linked to an OU using a simple if/then condition.

foreach ($gpo in $gpos) {
    [xml]$gpoReport = Get-GPOReport -Guid $gpo.ID -ReportType xml
    if (-not $gpoReport.GPO.LinksTo) {
        $gpo.DisplayName
    }
}

This foreach loop will now check each GPO in the Active Directory environment, get the report in XML format, check the appropriate property on the XML report and if there is no OU defined in this property, will output the name of the GPO.

Note that you may also use the Where-Object cmdlet too. I'll leave that up as a challenge for you!

Summary

By using a small PowerShell script, you will now get a list of all of the GPOs that aren't linked to an OU. At that time, you can then decide to remove them, if necessary.

For a sampling of tons of Active Directory scripts including some nice ones to manage GPOs, check out Active Directory Scripts Galore: Come and Get It!.

Join the Jar Tippers on Patreon

It takes a lot of time to write detailed blog posts like this one. In a single-income family, this blog is one way I depend on to keep the lights on. I'd be eternally grateful if you could become a Patreon patron today!

Become a Patron!