How do I find all GPOs that can be deleted?

I often find myself opening the Group Policy Management Console, and wondering “are all of these GPOs actually being used?”.  And if not, how do we go about finding GPOs that could be cleaned-up?

The script below is a little long, and though heavily commented, the summary of the logic is:

  • Find all GPOs linked to every AD site with enabled links
  • Find all GPOs linked to the root domain with enabled links
  • Find all GPOs linked to every OU with enabled links
  • Get a global list of every GPO and subtracts all of the above linked GPOs found
  • Add to the list of candidates for deletion any GPO without any objects/permission within the Security Filtering area. (this is the bit that takes the longest)
  • Add to the list of candidates for deletion any GPO with all settings disabled

And at the end of all that, we can display the names of the GPOs that are not in use and could be removed.  EG:


There’s some logic in this that I’m not totally thrilled with (though it does work), where we try and find out if a GPO is link-enabled.

Specifically, raw GPO links look like this:


The string between the curly braces is the GUID of the GPO.  The number following the semi-colon is the status of the link.

  • 0 means link enabled, not enforced
  • 1 means link disabled, enforced
  • 2 means link enabled, enforced
  • 3 means link disabled, not enforced

(we’re not concerned here with the enforced status, just if the link is enabled – options 0 and 2).

To extract out the GUID and the status, I used two different regex operations, which creates two parallel (loosely linked) lists, like this:


So, to find which GPOs are being used, we need to find the array index where the link option value is either 0 or 2, and use that same index number in the GUID array to find the correspodning GPO (this is why the $foreachcount variable, below, is in use).   Again, I’m not thrilled with this method that I came up with – but it does work.


The script is also domain agnostic, and a copy/paste into your environment should work without modification. Here’s the script:



Leave a Reply

Your email address will not be published. Required fields are marked *