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.
![Its ok burning meme](./images/GPOFeatureImage.jpg)
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 its unused GPO. We probably don’t need them. If only User or Computer disabled, then it’s 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 are 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"
}
}
![Result of the Get-GPODisabled Result of the Get-GPODisabled](/posts/sorting-group-policy/images/image-1_huddbaae1ef1317d7dec4208e95e6fd6de_7024_640x0_resize_q75_bgffffff_box_3.jpg)
PowerShell Terminal with the result of running the Get-GPODisabled
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!
![Empty GPO with computer version 4. Empty GPO with computer version 4.](/posts/sorting-group-policy/images/GPO_hu76271612db5202da32f0b261438d73c5_14964_640x0_resize_q75_bgffffff_box_3.jpg)
Example of GPO settings
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"
}
}
![Result of the Get-GPOEmpty Result of the Get-GPOEmpty](/posts/sorting-group-policy/images/image-2_hu32e822b58bbc720133826e2147b6978a_3281_640x0_resize_q75_bgffffff_box_3.jpg)
PowerShell Terminal with the result of running the Get-GPOEmpty
Unlinked GPO’s
In this test, we will have the most results, as it’s 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"
}
}
![Result of the Get-GPOUnlinked Result of the Get-GPOUnlinked](/posts/sorting-group-policy/images/image-3_hu88be08f63fa15a1683d67221d3800204_6770_640x0_resize_q75_bgffffff_box_3.jpg)
PowerShell Terminal with the result of running the Get-GPOUnlinked
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 "
}
}
![Result of the Get-GPOMissingPermissions Result of the Get-GPOMissingPermissions](/posts/sorting-group-policy/images/image-4_hu452d2a02941eade7194701ea1061e0e0_4399_640x0_resize_q75_bgffffff_box_3.jpg)
PowerShell Terminal with the result of running the Get-GPOMissingPermissions
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 with PowerShell.
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