The PoSH Student

April 25, 2011

Finding un-linked GPOs

Filed under: PowerShell — Tags: , , — Nathan @ 5:08 pm

At my new job, I needed to do some GPO cleanup. There are a LOT of them, and many are not even linked to anything. I need to remove them, but didn’t want to go through them one-by-one to do it. PowerShell to the rescue!

For the impatient, here is the quick script to show the un-linked GPOs. The explanation of how to get there is below.

Import-Module grouppolicy
$allGPOs = Get-GPO -All | sort DisplayName
ForEach ($gpo in $allGPOs) {
	$xml = [xml](Get-GPOReport $gpo.Id xml)
	If (!$xml.GPO.LinksTo) {
		$gpo.DisplayName
		}
	}

First off, load the Group Policy module with Import-Module grouppolicy, and find what commands are available with Get-Command -Module grouppolicy:

CommandType     Name                                                Definition
-----------     ----                                                ----------
Cmdlet          Backup-GPO                                          Backup-GPO -Guid  -Path  [-Comment...
Cmdlet          Copy-GPO                                            Copy-GPO -SourceGuid  -TargetName ...
Cmdlet          Get-GPInheritance                                   Get-GPInheritance [-Target]  [-Domain <S...
Cmdlet          Get-GPO                                             Get-GPO [-Guid]  [[-Domain] ] [[-S...
Cmdlet          Get-GPOReport                                       Get-GPOReport [-Guid]  [-ReportType] <Repo...
Cmdlet          Get-GPPermissions                                   Get-GPPermissions -Guid  [-TargetName <Str...
Cmdlet          Get-GPPrefRegistryValue                             Get-GPPrefRegistryValue -Guid  -Context <G...
Cmdlet          Get-GPRegistryValue                                 Get-GPRegistryValue -Guid  -Key  [...
Cmdlet          Get-GPResultantSetOfPolicy                          Get-GPResultantSetOfPolicy [-Computer ] ...
Cmdlet          Get-GPStarterGPO                                    Get-GPStarterGPO -Guid  [-Domain ]...
Cmdlet          Import-GPO                                          Import-GPO -BackupId  -Path  [-Tar...
Cmdlet          New-GPLink                                          New-GPLink -Guid  -Target  [-LinkE...
Cmdlet          New-GPO                                             New-GPO [-Name]  [-Comment ] [-D...
Cmdlet          New-GPStarterGPO                                    New-GPStarterGPO [-Name]  [-Comment <Str...
Cmdlet          Remove-GPLink                                       Remove-GPLink -Guid  -Target  [-Do...
Cmdlet          Remove-GPO                                          Remove-GPO -Guid  [-Domain ] [-Ser...
Cmdlet          Remove-GPPrefRegistryValue                          Remove-GPPrefRegistryValue [[-Server] ] ...
Cmdlet          Remove-GPRegistryValue                              Remove-GPRegistryValue [-Guid]  [-Key] <St...
Cmdlet          Rename-GPO                                          Rename-GPO -Guid  -TargetName  [-D...
Cmdlet          Restore-GPO                                         Restore-GPO -BackupId  -Path  [-Do...
Cmdlet          Set-GPInheritance                                   Set-GPInheritance [-Target]  -IsBlocked ...
Cmdlet          Set-GPLink                                          Set-GPLink -Guid  -Target  [-LinkE...
Cmdlet          Set-GPPermissions                                   Set-GPPermissions -Guid  -PermissionLevel ...
Cmdlet          Set-GPPrefRegistryValue                             Set-GPPrefRegistryValue -Guid  -Context <G...
Cmdlet          Set-GPRegistryValue                                 Set-GPRegistryValue -Guid  -Key  [...

So, I apparently can create a new Link, or set or remove one, but not simply get one. Nice. And Get-GPO doesn’t include links either:

DisplayName      : test
DomainName       : mydomain.com
Owner            : MYDOMAIN\Domain Admins
Id               : 1be29c7a-6cdb-48f8-aaef-18db7ab79b25
GpoStatus        : AllSettingsEnabled
Description      :
CreationTime     : 9/16/2004 11:10:04 AM
ModificationTime : 1/25/2011 5:39:44 PM
UserVersion      : AD Version: 6, SysVol Version: 6
ComputerVersion  : AD Version: 2, SysVol Version: 2
WmiFilter        :

So I need more detail. Get-GPOReport has lots of detail, but it's in HTML or XML format. Since PowerShell can read XML, I went that way:

PS> $test = [xml](Get-GPOReport test xml)
PS> $test

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

PS> $test.GPO

xsi                 : http://www.w3.org/2001/XMLSchema-instance
xsd                 : http://www.w3.org/2001/XMLSchema
xmlns               : http://www.microsoft.com/GroupPolicy/Settings
Identifier          : Identifier
Name                : test
IncludeComments     : true
CreatedTime         : 2004-09-16T15:10:04
ModifiedTime        : 2011-01-25T21:39:45
ReadTime            : 2011-04-25T14:43:30.3150573Z
SecurityDescriptor  : SecurityDescriptor FilterDataAvailable : true
Computer            : Computer
User                : User
LinksTo             : LinksTo

PS> $test.GPO.LinksTo

SOMName                       SOMPath                 Enabled                       NoOverride
-------                       -------                 -------                       ----------
TestLab                       mydomain.com/SomeOU/... true                          false

So, all I need to do is find the GPOs where the XML doesn’t have a GPO.LinksTo property, or said property is blank.

Import-Module grouppolicy
$allGPOs = Get-GPO -All | sort DisplayName
ForEach ($gpo in $allGPOs) {
	$xml = [xml](Get-GPOReport $gpo.Id xml)
	If (!$xml.GPO.LinksTo) {
		$gpo.DisplayName
		}
	}

Bam.

Edit 4/25/2011 3:22 PM:
I realized I can also search for the GPOs which are linked, but have no settings at all:

$allGPOs = Get-GPO -All | sort DisplayName
ForEach ($gpo in $allGPOs) {
    $xml = [xml](Get-GPOReport $gpo.Id xml)
    If ($xml.GPO.LinksTo) {
        If (!$xml.GPO.Computer.ExtensionData -and !$xml.GPO.User.ExtensionData) {
            $gpo.DisplayName
            }
        }
    }

Theme: Silver is the New Black. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.