Dude, where’s my GPO? Using PowerShell to find all of your Group Policy links.

Stratego Stratego

I only played Stratego once as a kid when I was over at a friend’s house. Strategy, challenge and mystery made the game captivating. A few years ago I bought a vintage 1961 edition on eBay so I could teach my sons to play. As it turns out this game has been around for centuries among various cultures around the world. Pieces are ranked from 1 to 9, with 1 being the most powerful (unless you have a newer edition where the numbers have been revised). The objective is to capture the opponent’s flag while avoiding the bombs hidden among the ranks. By now you are probably wondering what this has to do with PowerShell or Active Directory.  Keep reading and find out.

GPO Scavenger Hunt

Using the Get-GPOReport cmdlet from the Group Policy PowerShell module you can easily create a report of all your Group Policy Objects (GPOs) like this:


 PS C:\> Import-Module GroupPolicy
PS C:\> Get-GPOReport -All -ReportType HTML -Path .\GPO.html


This is a great report, but it can be a bit overwhelming.  What if you want a simple spreadsheet listing of the same information?

Back around 2002 I wrote some code for this in VBScript.  You see, back then Group Policy Management Console (GPMC) had not been released yet.  At that time you created GPOs through AD Users and Computers (ADUC).  I was working with Windows 2000 Server designing OUs and GPOs for a large company’s Windows 2000 implementation.  I needed a report to show me where all of these policies were linked, and I also needed to know where inheritance was blocked and where policies were set to “No Override” (now called “Enforced”).  After some recent conversations with peers I decided to rewrite this script in PowerShell.

Understanding GPOs in Active Directory

First off, technically speaking Group Policy is not a feature of Active Directory (AD).  However, so much of the infrastructure rides on AD that most people associate the two.  If you need some help understanding GPOs start here:  Everything you need to get started with Group Policy.

In GPO terms, domains, OUs, and sites are called Scopes of Management (SOMs).  They have an attribute called gPLink that lists all of the GPOs applied to the object.  The gPLink attribute contains a list of GPO distinguished names and a gPLinkOptions value for each one.  This numeric value is a bit switch flagging LinkEnabled status and Enforced status.  SOMs have an attribute call gPOptions which specifies whether Block Inheritance is enabled.  To understand these in more detail you can explore the following links:

Remember that Stratego reference?

imageThe gPLink attribute is a string concatenation that looks something like this:

[LDAP://cn={7BE35F55-E3DF-4D1C-8C3A-38F81F451D86},cn=policies, cn=system,DC=wingtiptoys,DC=local;2][LDAP://cn={046584E4-F1CD-457E-8366-F48B7492FBA2}, cn=policies,cn=system,DC=wingtiptoys,DC=local;0] [LDAP://cn={12845926-AE1B-49C4-A33A-756FF72DCC6B},cn=policies, cn=system,DC=wingtiptoys,DC=local;1]

As you can see, each GPO distinguished name is enclosed with square brackets, and there is a numeric digit following it as described above.  The important point is that GPOs are listed in this string in order of precedence.  In the GPMC GUI you will see multiple linked policies listed top to bottom, 1 through x, with lower numbers winning over higher numbers.  This is just like Stratego.  The #1 policy (Stratego’s marshall) wins over all the others.  Lower numbers always win.

Where’s the PowerShell?

Understanding how policies tie into Active Directory, now you can see that we need a couple targeted LDAP queries to get all of the gPLink and gPOptions attributes from the SOMs.  Then we parse out the attributes and put them into a report.  I am over-simplifying it just a bit, but here are the queries:


 # Empty array to hold all possible GPO links            
$gPLinks = @()            
# GPOs linked to the root of the domain            
# !!! Get-ADDomain does not return the gPLink attribute            
$gPLinks += Get-ADObject -Identity (Get-ADDomain).distinguishedName ` 
-Properties name, distinguishedName, gPLink, gPOptions            
# GPOs linked to OUs            
# !!! Get-GPO does not return the gPLink attribute            
$gPLinks += Get-ADOrganizationalUnit -Filter * -Properties name, ` 
distinguishedName, gPLink, gPOptions            
# GPOs linked to sites            
$gPLinks += Get-ADObject -LDAPFilter '(objectClass=site)' ` 
-SearchBase "CN=Sites,$((Get-ADRootDSE).configurationNamingContext)" ` 
-SearchScope OneLevel -Properties name, distinguishedName, gPLink, gPOptions            


Notice in the script comments that Get-ADDomain and Get-GPO do not return the gPLink attribute.  We have to specifically query for this attribute using other cmdlets.  Also notice that I am not filtering out SOMs without a gPLink attribute.  I want to collect all OUs and sites to round out the report, not just the ones with policies linked.

You can download the full script from the TechNet Script Gallery.  The output is a CSV file (gPLink_Report.csv) listing all of the linked policies, their locations, and link configurations.  The report contains the following columns:

  • SOM – Scope of Management, the domain, OU, or site where the policy is linked.  The OUs are tabbed to represent the tree structure for easy viewing.
  • DistinguishedName – Full path to the SOM.
  • BlockInheritance – True if the OU is blocking policy inheritance.  This comes from the gPOptions attribute.
  • LinkEnabled – This is represented by bit 1 of the gPLinkOptions numeric in the gPLink string.
  • Enforced – This is represented by bit 2 of the gPLinkOptions numeric in the gPLink string. This setting is ignored for the policy if not enabled (bit 1).
  • Precedence – Stratego ranking of multiple policies linked to the same SOM.
  • DisplayName – Friendly name of the policy.
  • GPOStatus – All settings enabled. User settings disabled. Computer settings disabled. All settings disabled. Note that this is different than the enabled/disabled status on the gPLink itself.
  • WMIFilter – Name of the WMI filter on the policy, if configured.
  • GUID – The GPO GUID that can be used to identify the policy in ADSIEDIT or the SYSVOL share.
  • PolicyDN – The full path to the group policy container object in Active Directory.

This script has the following prerequisites:

  • PowerShell v2 or above
  • Remote Server Administration Tools (RSAT)
  • AD PowerShell module
  • Group Policy module

Unlike Get-GPOReport this script does NOT include any of the following other related information:

  • Policy filtering by permissions
  • GPOs that are not linked
  • Individual policy settings

With this handy CSV spreadsheet you can easily get an overview of your policies, where they are blocked, and where they are enforced.  Perhaps the helpdesk could use a copy of the script and report when they are troubleshooting group policy calls (in addition to RSOP, of course).

Group Policy Health Check (GPOHC)

As a Microsoft Premier Field Engineer I am part of a global team with many specialties.  Some of my peers are true group policy experts.  You can have one of them visit your company, teach you the ins and outs of group policy, and help optimize your GPOs.  They have many other tricks up their sleeve beyond today’s reporting script.  This service is called the Group Policy Health Check (GPOHC) .  Use the links at the bottom of this blog post or contact me for more information if you would like one of these at your company.

The Download

Download today’s script at the TechNet Script Gallery.

UPDATE:  For a new, improved version of this script go to this post.