UPDATED: Copy and Merge Group Policies (GPOs) with PowerShell


GPO Consolidation Redux

imageDo you have Group Policies gone wild? Did you realize too late that it might not be such a good idea to delegate GPO creation to half the IT department? Have you wanted to combine multiple policies into one for simplicity? This blog post is for you.

One of my most popular posts over the years has been a vintage script from 2011:  Finally! Copy and merge GPOs! PowerShell saves the day! As I have said before, it is embarrassing to go back and look at old code from when you just started learning PowerShell. I would do things so differently now; so I did.

Recently a customer contacted me for help with Group Policy consolidation. They had 35 workstation policies linked to a single OU structure, and I suggested they reduce them to 3 to 5 policies. This was the perfect opportunity to dust off the old script and rewrite it using better PowerShell practices.

Fixes and Features

Thanks to your comments on the previous script I was able to fix some issues and add enhancements. The new, improved version of this script includes the following fixes and features:

  • All functions compatible on PowerShell v2.0 for Windows Server 2008 R2.
  • Recursive infinite loop identified and fixed.
  • Specify multiple source GPOs.
  • Dynamically create the destination GPO if it does not exist.
  • Verbose logging for every setting copied.
  • Warnings for settings that get over-written, showing both old and new values.
  • Warnings for settings that fail copying (usually the Disable/Delete type).
  • Warnings for non-registry settings that need manual copy.
  • Warning if source policy is not found.
  • Helper functions for identifying linked and unlinked GPOs.
  • Progress bar.

Nice list.

What about Group Policy Preferences?

Some folks have asked how to copy preferences as well. Here again we are limited by the features in the GroupPolicy PowerShell module. There is a cmdlet to copy preference registry settings, but that is all. When you look at the scope of how many different kinds of settings there are in preferences, this is only a very small percentage of the scope. I decided it was not worth the development time to pursue this.

Free Advice: Group Policy Optimization

You can improve group policy processing performance on client machines by reducing the total number of policies applied. This eliminates multiple per-policy processing steps. However, this does not mean to simply combine all 35 policies into one single policy. Here are some guidelines to follow for policy consolidation:

  • Using the Get-GPLink report below look at which policies change the most frequently. This is indicated in the versioning statistics and modification date for each policy. Leave these frequently-updated policies as they are or consider rolling them into one frequently-updated policy.
  • Take the remaining policies that are infrequently updated and consolidate them into a single policy. Separating the policies by update frequency will keep the bulk of the settings from triggering a refresh, keeping policy processing time to a minimum.
  • For any future policy additions that will be relatively static, add them to the consolidated policy. For settings that will change frequently, consider adding them to one of the existing policies with frequent updates.

See the great article Group Policy Processing Performance Considerations for a deeper discussion of group policy performance optimization.

When creating new group policies, consider the following points to justify if a new policy object is required:

  • Will the policy be linked to multiple OUs?
  • Will the policy require unique security group filtering scenarios?
  • Will the policy require frequent updates?

If the answer is “yes” for any of these questions, then a new group policy object may be required. Otherwise add the settings to an existing group policy object.

Group Policy Consolidation Process

Use the updated script below for combining the group policies. This script will likely meet 80% or more of your requirements. Due to scripting limitations, only the group policy registry settings can be copied into a consolidated policy. Other setting that require manual migration are noted in the output from the script.

When analyzing the policies for consolidation, take into account the following factors for the suitability of each policy to be consolidated:

  • Is the policy still relevant for the latest workstation standards?
  • Is the policy linked to multiple OUs?
  • Is the policy filtered by either security groups or WMI filters?

Once policies are identified for consolidation, use the script below to do the work. The script will create a new policy containing the merged settings, and it is not linked to any OU. Create a test OU, link the new policy, move a sample of test machines into the OU, and then validate that the policy applies all setting correctly. Pay close attention to any settings where a warning was noted in the script output. Recreate these settings manually as needed.

Note that the consolidated GPO will not include any of the following features of the other GPOs:

  • WMI filter
  • Permission filtering
  • User or computer settings disabled

These settings may need to be manually applied to the new policy where appropriate.

When the time comes for production implementation, link the new consolidated GPO to the production OU. Allow two hours for replication and workstations to refresh their settings. Next disable the consolidated individual former GPOs (see script sample below). Do this activity during a non-peak time as a precaution. Allow another two hours for policy refresh on workstations. After reboot and verification of the settings on a sample of target workstations it will be safe to delete the disabled OU links (not the policies) . Finally, backup and delete the policies themselves once all other dependencies have been eliminated and verified.

Note: When consolidating multiple GPOs linked to a single OU the sample code below consolidates the policies in the order of their group policy precedence (from bottom to top of the list). This ensures that any potentially duplicated or conflicted settings will retain the values from the highest-ranking policies in the same way these settings would be applied in the formerly separate policies.

Show me some ‘Shell


This script file contains three functions:

  • Get-GPLink – Detailed link report for GPOs, including enabled/disabled, enforced, block inheritance, WMIFilter, date created, date modified, version numbers, and more. This is based off of another GPO report that I did, but I removed the PowerShell v3 cmdlets for Windows Server 2008 R2 compatibility. This report only includes GPOs that are linked in the environment.
  • Get-GPUnlinked – This is similar to the Get-GPLink report, but it includes unlinked GPOs and a simplified Linked property for reporting.
  • Copy-GPRegistryValue – This function is the heart of the script, and it is a 99% rewrite of the previous version. See the bullet list above for the features list.

After you download the script, unblock the file, open it in the PowerShell ISE, and review the code. Press F5 to run it. Notice that there is a BREAK statement following the functions, and this prevents the sample code at the bottom from executing. This will load the functions for your use. Tweak the sample code and use F8 to run selections of it in your lab before production.

Here is the included sample code for using the functions.  Read through the comments below to understand the scenarios enabled by these functions:

 # Help            
Help Get-GPLink -Full            
Help Get-GPUnlinked -Full            
Help Copy-GPRegistryValue -Full            
# Copy one GPO registry settings into another            
Copy-GPRegistryValue -Mode All -SourceGPO 'Client Settings' `
    -DestinationGPO 'New Merged GPO' -Verbose            
# Copy one GPO registry settings into another, just user settings            
Copy-GPRegistryValue -Mode User -SourceGPO 'Client Settings' `
    -DestinationGPO 'New Merged GPO' -Verbose            
# Copy one GPO registry settings into another, just computer settings            
Copy-GPRegistryValue -Mode Computer -SourceGPO 'Client Settings' `
    -DestinationGPO 'New Merged GPO' -Verbose            
# Copy multiple GPO registry settings into another            
Copy-GPRegistryValue -Mode All  -DestinationGPO "NewMergedGPO" `
    -SourceGPO "Firewall Policy", "Starter User", "Starter Computer" -Verbose            
# Copy all GPOs linked to one OU registry settings into another            
# Sort in reverse precedence order so that the highest precedence settings overwrite            
# any potential settings conflicts in lower precedence policies.            
$SourceGPOs = Get-GPLink -Path 'OU=PHB,OU=HR,DC=CohoVineyard,DC=com' |            
    Sort-Object Precedence -Descending |            
    Select-Object -ExpandProperty DisplayName            
Copy-GPRegistryValue -Mode All -SourceGPO $SourceGPOs `
    -DestinationGPO "NewMergedGPO" -Verbose            
# Log all GPO copy output (including verbose and warning)            
# Requires PowerShell v3.0+            
Copy-GPRegistryValue -Mode All -SourceGPO 'IE Test' `
    -DestinationGPO 'New Merged GPO' -Verbose *> GPOCopyLog.txt            
# Disable all GPOs linked to an OU            
Get-GPLink -Path 'OU=PHB,OU=HR,DC=CohoVineyard,DC=com' |            
    ForEach-Object {            
        Set-GPLink -Target $_.OUDN -GUID $_.GUID -LinkEnabled No -Confirm            
# Enable all GPOs linked to an OU            
Get-GPLink -Path 'OU=PHB,OU=HR,DC=CohoVineyard,DC=com' |            
    ForEach-Object {            
        Set-GPLink -Target $_.OUDN -GUID $_.GUID -LinkEnabled Yes -Confirm            
# Quick link status of all GPOs            
Get-GPUnlinked | Out-Gridview            
# Just the unlinked GPOs            
Get-GPUnlinked | Where-Object {!$_.Linked} | Out-GridView            
# Detailed GP link status for all GPOs with links            
Get-GPLink | Out-GridView            
# List of GPOs linked to a specific OU (or domain root)            
Get-GPLink -Path 'OU=PHB,OU=HR,DC=CohoVineyard,DC=com' |            
    Select-Object -ExpandProperty DisplayName            
# List of OUs (or domain root) where a specific GPO is linked            
Get-GPLink |            
    Where-Object {$_.DisplayName -eq 'Script And Delegation Test'} |            
    Select-Object -ExpandProperty OUDN            

Important Notes

ALWAYS use –Verbose when running these functions. Here are some warnings you may notice in your output while consolidating GPOs:

  • Registry path not found. This is normal. Safely ignore it. The function attempts to explore every possible registry setting root path based on the consolidation mode (user, computer, all). These will not be present in every policy.
  • Empty value, potential setting failure. Observe the detailed output and manually verify that these settings were copied into the destination policy. These are usually policies that disable a setting.
  • Source GPO contains non-registry settings for manual copy. This script can only migrate registry-based settings. Look at the warning details to see what other types of settings are included in the policy. These settings require manual copying.
  • Overwriting previous value. The destination policy already contained the setting, and it is being overwritten. Notice that the old value appears in the warning text, and the new value appears in the following verbose text.
  • Source GPO does not exist. Check your spelling of the SourceGPO parameter value(s). Use quotes around policy names that include spaces.

That may sound like a lot of warnings, but hey, it is better than the alternative (no warnings). Now you know where you might have potential issues in the copy. Manually compare and edit the destination policy as necessary to make a good copy. Then test, test, test the policy as described above.

This process should save you 80% or more manual effort on the process of consolidating GPOs. That is a lot of clickety-click avoided.

How Do I Get The Script?

Download the full script at the TechNet Script Center.

BONUS LINK: The Group Policy team blog has some great sample PowerShell for other GPO scripting tasks.