Group Policy PowerShell

GPO PowerShell analyze

Have you ever been in a situation where you needed to get an overview of your GPO’s?

Something like:

  • FInd all GPO’s with drive mapping
  • Find all GPO’s with software publishing
  • Find all GPO’s with user rights configuration

The Group Policy Management Console (GPMC) tool isn’t very useful for this type of thing. If you have dozens or hundreds of GPO’s it’s near impossible.

The most effective way to review all of your GPO settings is using PowerShell. This article is going to teach you how.

In 10 minutes you’ll be the go-to-guy for GPO analysis scripts – I promise you 😉

How to Install the GPO PowerShell Module

First thing to get started is to get your hands on the Group Policy PowerShell module.

Don’t make the mistake of working from your Domain Controller – it’s a bad (and lazy) habit.

To get up and running on your workstation install the RSAT Group Policy Management tools. This is super easy on Windows 10 version 1809 and later:

Get-WindowsCapability -Name "RSAT.GroupPolicy.Management.Tools*" -Online | Add-WindowsCapability -Online

On Windows 10 1709 or earlier check this article.

On Windows 8.1 go here for more info.

If you’re using a jump host (Windows Server), install the Group Policy PowerShell module by adding the “Group Policy Management” feature (for a detailed step-by-step guide on adding features to your server check this article).


There are basically only two commands in the GroupPolicy module you need to know to generate complex analysis of your Group Policy Objects.

The first one is Get-GPO.

Simply feed the name of the GPO to this command to grab a GPO object. The output will look similar to the following:

PS C:\> Get-GPO "Default Domain Policy"

DisplayName      : Default Domain Policy
DomainName       : easy365manager.local
Owner            : E365M\Domain Admins
Id               : 24c1692a-6fd1-4a85-b2e4-343968fe78e0
GpoStatus        : AllSettingsEnabled
Description      :
CreationTime     : 26-11-2004 13:22:37
ModificationTime : 03-12-2020 14:38:46
UserVersion      : AD Version: 69, SysVol Version: 69
ComputerVersion  : AD Version: 51, SysVol Version: 51
WmiFilter        :

We already have some basic info that might be useful in a report or otherwise:

  • The Guid of the GPO (in the Id attribute)
  • The owner
  • The creation time
  • The last modification time
  • The WmiFilter

To dive deeper into more properties of the GPO and the actual settings another command is needed:


The Get-GPOReport command extracts all the settings of a group policy. It can use different formats for the data but for easy analysis XML is preferred.

To get the data, you need to identify which GPO you want to look into, either by stating the name or the GUID of the GPO – or by piping the GPO into the command.

When you specify XML as the data format using the -ReportType switch, the output is a massive XML document. Put it in a variable so we can access it more easily:

$GPOSettings = Get-GPO "Default Domain Policy" | Get-GPOReport -ReportType XML

To let PowerShell know we’ve got XML data on our hands, prepend the variable with [xml]:

PS C:\> [xml]$GPOSettings

xml                             GPO
---                             ---
version="1.0" encoding="utf-16" GPO

As you see the top element is named “GPO”. Put this element into a new variable so we don’t have to do the explicit cast to XML on every call:

PS C:\> $GPO = ([xml]$GPOSettings).GPO
PS C:\> $GPO

xsd                 :
xsi                 :
xmlns               :
Identifier          : Identifier
Name                : Default Domain Policy
IncludeComments     : true
CreatedTime         : 2004-11-26T12:22:37
ModifiedTime        : 2020-12-03T12:38:46
ReadTime            : 2021-05-12T19:42:24.1554915Z
SecurityDescriptor  : SecurityDescriptor
FilterDataAvailable : true
Computer            : Computer
User                : User
LinksTo             : {LinksTo}

The output has more or less the same information as the object we retrieved earlier using the Get-GPO command.

But pay special attention to the Computer and User attributes – this is where the fun starts!

GPO ExtensionData

If anything is configured in the computer or user portion of the GPO the corresponding attribute will have a sub element called ExtensionData:

PS C:\> $GPO.Computer

VersionDirectory VersionSysvol Enabled ExtensionData
---------------- ------------- ------- -------------
51               51            true    {Security, Public Key, Name Resolution Policy, Registry}

The ExtensionData will have one or more Extension values depending on what types of settings have been configured in the GPO:

PS C:\> $GPO.Computer.ExtensionData

Extension Name
--------- ----
Extension Security
Extension Public Key
Extension Name Resolution Policy
Extension Registry

The various Extension attributes will have data that is custom to the type of extension. You can build a generic routine to iterate the Extension elements of the GPO report – but when it comes to the actual settings of the different types of Extension data, you need to build a custom routine for each type.

This is quite OK. When doing GPO analysis we’re often looking for particular information such as identifying all GPO’s with mapped drives or all GPO’s with software distribution.

Analyzing GPO Settings

For the sake of the example, let’s try to make a script that finds all GPO’s that have start-up scripts configured.

The method applied here can be used on any type of GPO settings that you want to analyze.

First step: Find the name of the extension by looking at one of the group policies that has a user logon script configured:

PS C:\> $Report = Get-GPO "Accounting_Scripts" | Get-GPOReport -ReportType xml
PS C:\> $GPO = ([xml]$Report).GPO
PS C:\> $GPO.User.ExtensionData

Extension Name
--------- ----
Extension Scripts

As seen, the name of the extension is “Scripts”. Therefore, to find all GPO’s with scripts we need to iterate all ExtensionData values of all GPO’s that have the name “Scripts”:

$Reports = Get-GPO -All | Get-GPOReport -ReportType Xml
ForEach ($Report In $Reports) {
  $GPO = ([xml]$Report).GPO
  ForEach ($ExtensionData In $GPO.User.ExtensionData) {
    If ($ExtensionData.Name -eq "Scripts") {
      # Do something ...

Now, let’s check what information is available to our script by looking at the data structure. Since, in our example GPO, only one extension exists we can access it like this:

PS C:\> $GPO.User.ExtensionData.Extension

q2                                                    type       Script
--                                                    ----       ------ q2:Scripts {q2:Script, q2:Script}

PS C:\> $GPO.User.ExtensionData.Extension.Script

Command    : ConfigureAcct.bat
Parameters : /SkipErrors
Type       : Logon
Order      : 0
RunOrder   : PSNotConfigured

Command  : CreateAcctShortcuts.bat
Type     : Logon
Order    : 1
RunOrder : PSNotConfigured

For each script we have a few valuable pieces of information:

  • Command
  • Parameters
  • Type

To get this information for all our GPO’s we can now put together the following code:

$Reports = Get-GPO -All | Get-GPOReport -ReportType Xml
$UserScripts = @()
ForEach ($Report In $Reports) {
  $GPO = ([xml]$Report).GPO
  ForEach ($ExtensionData In $GPO.User.ExtensionData) {
    If ($ExtensionData.Name -eq "Scripts") {
      $Scripts = $ExtensionData.Extension.Script
      ForEach ($Script In $Scripts){
        $ScriptInfo = New-Object PSObject -Property @{
          GPO        = $GPO.Name
          Command    = $Script.Command
          Parameters = $Script.Parameters
          Type       = $Script.Type
        $UserScripts += $ScriptInfo
Write-Output $UserScripts | ft GPO,Command,Parameters,Type

In my small test setup this generates the following output:

GPO                Command                 Parameters  Type
---                -------                 ----------  ----
Accounting_Scripts ConfigureAcct.bat       /SkipErrors Logon
Accounting_Scripts CreateAcctShortcuts.bat             Logon
Drive Mapping      Logon.bat                           Logon
Drive Mapping      SecondaryScript.bat     /Reset      Logon


And that’s it. I hope you now have a clear grasp on how to create your own Group Policy analysis scripts.

With the above approach you can now start gathering any type of information across all of your GPO’s. This is very useful if you want to clean up GPO’s, are doing a domain migration or restructuring your file servers.

For more examples using this approach check out these two articles: