As an organization grows so does its Active Directory (AD) environment and Group Policy (GP) infrastructure. Group Policy Objects (GPOs) can soon get out of hand and before you know it, you’ve got dozens of unlinked GPOs cluttering up your environment. Let’s change that.
In this tutorial, you’re going to learn how to discover all of those unlinked GPOs with both the Group Policy Management Console (GPMC) and PowerShell.
Let’s get crackin’!
Prerequisites
This article will be a tutorial with hands-on examples. If you’d like to follow along, make sure you have:
- An Active directory domain. This article will be using a domain called HomeLab.Local.
- A Windows computer joined to the AD domain. This tutorial will use Windows 10.
- The Windows computer has the Remote Server Administration Tools (RSAT) package installed.
Where do unlinked GPOs come from?
When you create a GPO and define all the settings, you intend to apply to client computers, that GPO isn’t actually doing anything. To actually affect client computers, the GPO must be linked to an AD organizational unit (OU).
As time progresses and more admins create GPOs, forget to link them, unlink GPOs from OUs, intend to remove them but never do, GPOs just sitting around doing nothing can proliferate. Especially in large organizations, unlinked GPOs can grow into the hundreds if not properly maintained.
Finding Unlinked GPOs in the GPMC
You can find unlinked GPOs in one of two ways; via the GPMC or via PowerShell. If you only have a handful of unlinked GPOs, it may make sense to use the GPMC rather than creating a PowerShell script.
To find unlinked GPOs via the GPMC:
1. Open the GPMC by going to your start menu and typing “group policy management”. When the GPMC program shows up, open it.
2. In the GPMC, go to Forest: <your forest name —> Domains —> <your domain name>, right-click on the domain name and click Search. This action will bring up the Search for Group Policy Objects dialog box.
3. Click on the Search Item dropdown and select the GPO-links. This search item will search for GPOs that are linked to an OU. Change the Condition dropdown to be Exist In and the domain to be your domain.
In the below screenshot, the combination of these settings will search for all GPOs linked to at least one OU in the homelab.local domain.
When you’re complete, click on Add to add the criteria. It will show up under the All search criteria section.
4. Now, click on the Search button to find all of the GPOs matching the search criteria.
5. As shown in the below screenshot, only linked GPOs are shown in the search results.
6. To find the unlinked GPOs, now manually compare all GPOs with the linked GPOs as shown below. In the screenshot below, only three GPOs are linked. By looking under the Group Policy Objects node, you can see two of the GPOs don’t show up (UnlinkedGPO1 and UnlinkedGPO2). That means they are not linked.
This task will be time-consuming and why you’ll see how to perform this task via PowerShell in the next section.
Linked GPOs will have a link when they are assigned to an AD object like OU, as shown below. If you have only a handful GPOs, you can look for GPOs which has a link, and the ones which don’t have a link. GPOs which doesn’t have a link are unlinked GPOs.
Finding Unlinked GPOs with PowerShell
Scouring through GPOs in the GPMC may work for a few GPOs, but you’re going to struggle if you’ve got hundreds or thousands of GPOs managing thousands of endpoints. In that case, it’s time to automate this process and to build a handy PowerShell tool.
Assuming you’re on your local domain-joined Windows PC with RSAT installed:
1. Open a Windows PowerShell console.
2. Import the GroupPolicy module. The GroupPolicy module comes with RSAT and should already be installed on your system. This module contains all of the commands necessary to work with GPOs in PowerShell.
Import-Module GroupPolicy
3. Run the Get-GPO
PowerShell cmdlet using the All
parameter. This cmdlet queries AD and returns all of the GPOs found.
Get-GPO -All
4. Now that you can query all of the GPOs in the domain, you must now figure out which ones are unlinked. To do that, run the Get-GPOReport
cmdlet. This cmdlet allows you to provide a name and a type of output to return.
Get-GPOReport -Name SomeGPO -ReportType XML
Manually copy and paste one of the GPO names found in the step above and run this command. You’ll see that the cmdlet returns an XML report of all the settings the GPOs has. In particular, notice the section called LinksTo
, as shown below. This section contains the SOMPath
XML node that represents the path to the OU it’s linked to.
The
Get-GPOReport
cmdlet can only get a report for a single GPO at a time. This cmdlet only includes search results from domains and not from AD sites.
5. Now that you know how to find all GPOs with Get-GPO
and the cmdlet to discover what they’re linked to (Get-GPOReport
), combine them by copying and pasting the PowerShell command below in your console.
The command below queries all GPOs in the domain (Get-GPO
) and then generates an XML report for each of them (Get-GPOReport
), only allowing the ones that don’t have a <LinksTo>
string in the report (Select-String
) to be returned.
Get-GPO -All | Where-Object {
$_ | Get-GPOReport -ReportType XML | Select-String -NotMatch "<LinksTo>"
}
In the tutorial’s environment, you can see in the below example UnlinkedGPO1 and UnlinkedGPO2 are not linked to any OU.
Building an Unlinked GPO PowerShell Tool
Let’s now put everything you’ve learned together and build a PowerShell script you might use in the real world.
1. Open your favorite code editor and copy/paste the following PowerShell script into it. Save the script as Remove-UnlinkedGPO.ps1. This script:
- Creates a folder with the current date to store unlinked GPO reports.
- Finds all GPOs in AD that are unlinked.
- Creates an HTML report for each unlinked GPO and saves it to disk.
- Creates and appends to a text file a list of all unlinked GPOs.
- Removes each unlinked GPO with a confirmation step using the
Remove-GPO
cmdlet.
You can also download the Remove-UnlinkedGPO.ps1 script via GitHub.
Import-Module GroupPolicy
$Date = Get-Date -Format dd_MM_yyyy
$BackupDir = "c:\GPOBackup\$Date"
## Creates a directory to store the GPO reports
if (-Not(Test-Path -Path $BackupDir)) {
New-Item -ItemType Directory $BackupDir -Force
}
# Get all GPOs with the gpo report type as XML and also look for the section in the xml report.
# Consider only the GPOs that doesnt have section.
Get-GPO -All | Where-Object { $_ | Get-GPOReport -ReportType XML | Select-String -NotMatch "<LinksTo>" } | ForEach-Object {
# Backup the GPO, HTML report and saving the GPO details to text file are optional.
Backup-GPO -Name $_.DisplayName -Path $BackupDir
# Run the report and save as an HTML report to disk
Get-GPOReport -Name $_.DisplayName -ReportType Html -Path "$BackupDir\$($_.DisplayName).html"
# Create and append to a text file called UnlinkedGPOs.txt in the backup folder that
# contains each GPO object that Get-GPO returns
$_ | Select-Object * | Out-File "$BackupDir\UnLinkedGPOs.txt" -Append
# Remove the GPO but first prompt before removing
$_.Displayname | Remove-GPO -Confirm
}
2. Execute the Remove-UnlinkedGPO.ps1 script.
3. If an unlinked GPO is found, the script will prompt you to remove it. This prompt comes from the Remove-GPO
cmdlet using the Confirm
switch. To confirm the removal of that single GPO, click Yes otherwise, click Yes to All to remove all unlinked GPOs with no further confirmation.
After the script is complete and it found at least one unlinked GPO, you should see in the C:\GPOBackup\<date> folder the GPO contents as a GUID folder along with the HTML reports and the UnlinkedGPOs.txt file.
4. Now, open one of the GPO HTML reports with a web browser. You can see below the report contains all the settings defined in that GPO. In the below example, the UnlinkedGPO1 GPO contains settings for the PowerShell execution policy.
5. Finally, open the UnlinkedGPOs.txt file. You’ll see below that it contains the same output you received from the Get-GPO
cmdlet.
Conclusion
You should now know how to find all of those unlinked GPOs in your AD environment using the GPMC and PowerShell.
Which way do you prefer? Can you think of a way to improve the PowerShell script covered?