Sorting the Group Policy mess with Powershell
Group Policy

Sorting the Group Policy mess with Powershell

Group Policy. It’s almost the same in every company. it’s historical, contain a lot of Group Policy and if you ever try to touch it, half of the systems stop working.

So, this is the first post for 2019! Happy new year!
Today I want to talk about a painful topic, the Group Policy. It’s almost the same in every company. it’s historical, contain a lot of Group Policy and if you ever try to touch it, half of the systems stop working.

old dog siting in burning house meme

Last week I saw a post from my good friend Omer a Microsoft PFE, as he talked about Common Mistakes in Active Directory and Domain Services. He points out that Removing “Authenticated Users” from the GPO Object Security Filtering is a bad thing. he also provided a Powershell script to find GPO’s without “Authenticated Users” permissions.

After running his GPO script, I saw that there few GPO’s that needed to sort. It gave me the motivation to analyze my entire environment, as a result, I created a few scripts.

The Target:


Firstly, before I start, I’m writing down what I need to look for:

  • Disabled GPO’s
  • Empty GPO’s
  • Unlinked GPO’s
  • Missing permissions GPO’s (Based on Omer Script)

I wrote all tests together in one script, but later decided to divide them to functions, so I can run each test individually and in the future create a complete Group Policy Report utilizing those functions. Therefore let’s deep dive on each test.

Disabled GPO’s

In this test, we will look for GPO’s with User, Computer or both setting disabled.

the issue:

Technically, there is no issue with GPO with disabled settings. But if both of them is disabled, then it unused GPO. We probably don’t need them. If only User or Computer disabled, then its a “red flag” and we need to make sure we are not “losing” configuration. Maybe we can merge the settings with another GPO?

The Function:

This will check if there is any settings disabled and return the GPO’s

function Get-GPODisabled {
    [cmdletbinding()]
    param (
    )
    try {
        Write-Verbose -Message "Importing GroupPolicy module"
        Import-Module GroupPolicy -ErrorAction Stop
    }
    catch {
        Write-Error -Message "GroupPolicy Module not found. Make sure RSAT (Remote Server Admin Tools) is installed"
        exit
    }
    $DisabledGPO = New-Object System.Collections.ArrayList
    try {
        Write-Verbose -Message "Importing GroupPolicy Policies"  
        $GPOs = Get-GPO -All  
        Write-Verbose -Message "Found '$($GPOs.Count)' policies to check"
    }
    catch {
        Write-Error -Message "Can't Load GPO's Please make sure you have connection to the Domain Controllers"
        exit
    }
    ForEach ($gpo  in $GPOs) { 
        Write-Verbose -Message "Checking '$($gpo.DisplayName)' status"
        switch ($gpo.GpoStatus) {
            "ComputerSettingsDisabled" {$DisabledGPO += "in '$($gpo.DisplayName)' the Computer Settings Disabled"}
            "UserSettingsDisabled" {$DisabledGPO += "in '$($gpo.DisplayName)' the User Settings Disabled"}
            "AllSettingsDisabled" {$DisabledGPO += "in '$($gpo.DisplayName)' All Settings Disabled"}
        }
    }
    if (($DisabledGPO).Count -ne 0) {
        Write-Host "The Following GPO's have settings disabled:"
        return $DisabledGPO
    }
    else {
        return "No GPO's with setting disabled Found"
    }
        
}
Usage:
Powershell window with example of Disabled Group Policy function

Empty GPO’s

In this test, we will look for empty GPO’s, that means that the GPO has no setting configured on it.

The issue:

Empty GPO’s are useless and just adding more overhead. We don’t need them in our environment.

The function:

This function will check if there is GPO with the User or Computers settings empty. one common mistake people do, is they check the “User version” and “Computer version”, while it true that a new GPO start with version 0 on both of them and changed in each modification. but if you take an old GPO and remove all settings, the version won’t reset to 0, and you will miss those GPO’s!

example of empty Group Policy with computer version greater than 0
Empty GPO with computer version 4.
function Get-GPOEmpty {
    [cmdletbinding()]
    param (
    )
    try {
        Write-Verbose -Message "Importing GroupPolicy module"
        Import-Module GroupPolicy -ErrorAction Stop
    }
    catch {
        Write-Error -Message "GroupPolicy Module not found. Make sure RSAT (Remote Server Admin Tools) is installed"
        exit
    }
    $EmptyGPO = New-Object System.Collections.ArrayList
    try {
        Write-Verbose -Message "Importing GroupPolicy Policies"  
        $GPOs = Get-GPO -All  
        Write-Verbose -Message "Found '$($GPOs.Count)' policies to check"
    }
    catch {
        Write-Error -Message "Can't Load GPO's Please make sure you have connection to the Domain Controllers"
        exit
    }
    ForEach ($gpo  in $GPOs) { 
        Write-Verbose -Message "Checking '$($gpo.DisplayName)' status"
        [xml]$GPOXMLReport = $gpo | Get-GPOReport -ReportType xml
        if ($null -eq $GPOXMLReport.gpo.User.ExtensionData -and $null -eq $GPOXMLReport.gpo.Computer.ExtensionData) {
            $EmptyGPO += $gpo
        }
    }
    if (($EmptyGPO).Count -ne 0) {
        Write-Host "The Following GPO's are empty:"
        return $EmptyGPO.DisplayName
    }
    else {
        return "No Empty GPO's Found"
    }
}
Usage:
Powershell window with example of Empty Group Policy function

Unlinked GPO’s

In this test, we will have the most results, as its the most common case. Unlinked GPO’s are GPO’s that not linked to any domain, site or OU.


The issue:

Unlinked GPO’s are not an “issue” but most of the time, they are useless overhead to manage and track. In my company, there was GPO’s from 2008. I guess we won’t use it anymore 😒

The function:
function Get-GPOUnlinked {
    [cmdletbinding()]
    param ()
    try {
        Write-Verbose -Message "Importing GroupPolicy module"
        Import-Module GroupPolicy -ErrorAction Stop
    }
    catch {
        Write-Error -Message "GroupPolicy Module not found. Make sure RSAT (Remote Server Admin Tools) is installed"
        exit
    }
    $UnlinkedGPO = New-Object System.Collections.ArrayList
    try {
        Write-Verbose -Message "Importing GroupPolicy Policies"  
        $GPOs = Get-GPO -All  
        Write-Verbose -Message "Found '$($GPOs.Count)' policies to check"
    }
    catch {
        Write-Error -Message "Can't Load GPO's Please make sure you have connection to the Domain Controllers"
        exit
    }
    ForEach ($gpo  in $GPOs) { 
        Write-Verbose -Message "Checking '$($gpo.DisplayName)' link"
        [xml]$GPOXMLReport = $gpo | Get-GPOReport -ReportType xml
        if ($null -eq $GPOXMLReport.GPO.LinksTo) { 
            $UnlinkedGPO += $gpo
        }
    }
    if (($UnlinkedGPO).Count -ne 0) {
        return $UnlinkedGPO.DisplayName
    }
    else {
        return Write-Host "No Unlinked GPO found"
    }
}
Usage:
Powershell window with example of Unlinked Group Policy function

Missing Permissions

Here we will test to see if there are GPO’s with missing Read permissions to the “Authenticated Users” or “Domain Computers” groups.

The issue:

This test is based on Omer post I shared before, so I recommend you give it a read if you haven’t yet. he explains it better than me 😃

The function:
function Get-GPOMissingPermissions {
    [cmdletbinding()]
    param ()
    try {
        Write-Verbose -Message "Importing GroupPolicy module"
        Import-Module GroupPolicy -ErrorAction Stop
    }
    catch {
        Write-Error -Message "GroupPolicy Module not found. Make sure RSAT (Remote Server Admin Tools) is installed"
        exit
    }
    $MissingPermissionsGPOArray = New-Object System.Collections.ArrayList
    try {
        Write-Verbose -Message "Importing GroupPolicy Policies"  
        $GPOs = Get-GPO -All  
        Write-Verbose -Message "Found '$($GPOs.Count)' policies to check"
    }
    catch {
        Write-Error -Message "Can't Load GPO's Please make sure you have connection to the Domain Controllers"
        exit
    }
    ForEach ($gpo  in $GPOs) { 
        Write-Verbose -Message "Checking '$($gpo.DisplayName)' status"
        If ($GPO.User.Enabled) {
            $GPOPermissionForAuthUsers = Get-GPPermission -Guid $GPO.Id -All | Select-Object -ExpandProperty Trustee | Where-Object {$_.Name -eq "Authenticated Users"}
            $GPOPermissionForDomainComputers = Get-GPPermission -Guid $GPO.Id -All | Select-Object -ExpandProperty Trustee | Where-Object {$_.Name -eq "Domain Computers"}
            If (!$GPOPermissionForAuthUsers -and !$GPOPermissionForDomainComputers) {
                $MissingPermissionsGPOArray += $gpo
            }
        }
    }
    if (($MissingPermissionsGPOArray).Count -ne 0) {
        Write-Host "The following GPO's do not grant any permissions to the 'Authenticated Users' Or 'Domain Computers' Group"
        return $MissingPermissionsGPOArray.DisplayName
    }
    else {
        return [string]"No GPO's with missing permissions to the 'Authenticated Users' or 'Domain Computers' groups found "
    }
}
Usage
Powershell window with example of Missing Permissions GPO function

Doing it Safe

Before you make any changes, I highly recommend you to backup any GPO before you change or delete. If you hear people scream and shout outside, just restore the GPO.

Backup GPO’s

While you can easily backup GPO’s over the GUI, we love Powershell more. so we will do it over Powershell.

Backup-GPO

To backup all GPO’s or specific ones, just run the following cmdlet:

## Backup All GPO's
Backup-Gpo -All -Path "Location"
 
## Backup specific GPO
Backup-Gpo -Name "GPO NAME" -Path "Location" -Comment "Comment" 
Restore-GPO

If we want to restore deleted or changed GPO, we can use the following cmdlet:

## Restore All GPO's
Restore-GPO -All -Domain "Domain Name" -Path "Backups Location"
 
## Restore specific GPO
Restore-GPO -Name "GPO NAME" -Path "Backup Location

Restore-GPO only support restoring GPO’s of the same domain!

Conclusion

In conclusion, we learned what we need to search. We saw the functions that will help us find the GPO’s. And we learned to backup and restore GPO’s. It’s time to sort the group policy mess! in the future, I hope to release the full GPO environment report, so stay tuned and good luck!

You can find all the code on GitHub


Share Tweet Send
0 Comments
Loading...