The PowerShell script I discuss in this post creates a Group Policy backup in a different way than with the Backup-GPO cmdlet of the Group Policy module. With my function, it will be much easier to identify the correct Group Policy Object (GPO) in case you have to restore Group Policy settings.

Let's take a quick peek at the GPO-specific cmdlets included in the GroupPolicy module from Microsoft.

PS C:\> get-command -noun gpo

CommandType     Name                     Version    Source
-----------     ----                     -------    ------
Cmdlet          Backup-GPO               1.0.0.0    GroupPolicy
Cmdlet          Copy-GPO                 1.0.0.0    GroupPolicy
Cmdlet          Get-GPO                  1.0.0.0    GroupPolicy
Cmdlet          Get-GPOReport            1.0.0.0    GroupPolicy
Cmdlet          Import-GPO               1.0.0.0    GroupPolicy
Cmdlet          New-GPO                  1.0.0.0    GroupPolicy
Cmdlet          Remove-GPO               1.0.0.0    GroupPolicy
Cmdlet          Rename-GPO               1.0.0.0    GroupPolicy
Cmdlet          Restore-GPO              1.0.0.0    GroupPolicy

Performing a backup using Backup-GPO ^

Let's a take a backup of a single GPO and see what the output looks like.

Backup-GPO -name "Default Domain Policy" - path c:\temp\gpobackup
Backup GPO output

Backup GPO output

I asked the cmdlet to back up the GPO named "Default Domain Policy." The cmdlet created a folder that contains all the relevant parts of the GPO. However, the folder's name is not "Default Domain Policy." Instead it is named after the BackupID created for the GPO.

The BackupID is a 32-bit identifier that is unique for each backup. Its name matches the name of the folder containing the backup copy of the GPO. Microsoft does this so you can take multiple backups of the same GPO, save them in a common location, and separate them for later retrieval.

BackupID is unwieldy to work with in my opinion, especially if you are backing up more than one GPO. In fact, I would bet the default behavior most admins do is to backup GPOs in bulk on a regular schedule. Imagine saving a few copies of your GPOs to the same folder by BackupID and then trying to figure out which GPO is which. It is impossibly hard to manage. In this scenario, you end up with just a giant list of folders with unintelligible GUIDs.

Backup GPO output of many GPOS

Backup GPO output of many GPOS

My solution ^

I got frustrated with this approach and set out to make a better backup that lists my backups by GPO name instead of BackupID. However, Microsoft still requires the BackupID for restoring the GPO at a later date, so I decided to append the GPO Name to the folder created by Microsoft's Backup-GPO cmdlet. This gives me the best of both worlds: the GPO name and the BackupID.

Since I am still using the Microsoft cmdlets to do the work, I will need to read the GPO info, backup the GPO, and finally append the GPO name with a delimiter to the newly created folder after the backup of each GPO occurs.

Finally, I want all the GPOs saved in a folder named after the backup date. For example, if I was backing up my GPOs to a network share named Group-Policy, I want a folder created called 2019-01-05 and then a subfolder for each GPO. Here's a screenshot of how it would look:

Desired Folder Layout

Desired folder layout

For me to back up all GPOs, I would have to run the cmdlet Backup-GPO -all. The -all parameter requires a backup path and the domain name that the Group Policies are a part of. You can also specify the server, but it is not a requirement. I'll create a function called Backup-GroupPolicy to back up all GPOs. The function will have parameters for the backup path and for the server to back up the GPOs from.

I also read the domain info and save it to a variable. I'll name these variables $Path, $Server, and $Domain. $Server is not the greatest name. Since we really need the name of a domain controller (DC) for the cmdlet to work, $DC would be better. But Microsoft used ‑Server instead of -DC or -DomainController in their cmdlet, so I will follow suit as well in my function to be consistent and avoid confusion.

The first thing I need to do is read the current GPOs and store the info so I can manipulate it later on:

$GPOInfo = Get-GPO -All -Domain $domain -Server $Server

Then I will need to create a folder to save the GPO backups to. Remember, I have the $path variable that contains the backup path.

#Create a date-based folder to save Group Policy backups
$Date = Get-Date -UFormat "%Y-%m-%d"
$UpdatedPath = "$path\$date"
New-item $UpdatedPath -ItemType directory | Out-Null

I am using Out-Null after I create the folder to hide the output normally shown after the creation of a new folder. At this point, I have my GPO info stored in a variable and a date-based folder to save my backups to. Now I need to perform the backup and manipulate the default GPO backup settings.

The process I'll be following is pretty simple. A loop will perform a backup of each GPO one at a time. The backup will write the data to the location specified in the $UpdatedPath variable and simultaneously save the backup metadata to some temp variables. After the backup of a GPO finishes, the process will then append the name of the GPO to the folder just created. That process will continue for each GPO in my domain.

ForEach ($GPO in $GPOInfo) {

            #Assign temp variables for various parts of GPO data
            Write-Host "Backing up GPO named: " $GPO.Displayname
            $BackupInfo = Backup-GPO -Name $GPO.DisplayName -Domain $Domain  path $UpdatedPath -Server $Server
            $GpoBackupID = $BackupInfo.ID.Guid
            $GpoGuid = $BackupInfo.GPOID.Guid
            $GpoName = $BackupInfo.DisplayName
            $CurrentFolderName = $UpdatedPath + "\" + "{"+ $GpoBackupID + "}"
            $NewFolderName = $UpdatedPath + "\" + $GPOName

            #Rename the newly created GPO backup subfolder from its GPO ID to the GPO Displayname + GUID
            rename-item $CurrentFolderName -newname $NewFolderName

The backup script ^

Now's let's put all of these bits together into a complete script:

function Backup-GroupPolicy {
<#
    .SYNOPSIS
    Backs up all existing Group Policies to a folder

    .DESCRIPTION
    Backs up all Group Policies to a folder named after the current date. Each group policy is saved in
    its own sub folder. The folder name will be the name of the Group Policy.

    Folder Name Example:
    --------------------
    C:\GPBackup\2018-12-21\Default Domain Policy


    .PARAMETER Path
    Specifies the path where the backups will be saved. The path can be a local folder or a network based folder.
    This is a required parameter. Do not end the path with a trailing slash. A slash at the end will cause an error!

    Correct format:
    c:\backups or \\server\share

    Incorrect Format:
    c:\Backups\ or \\server\share\

    .PARAMETER Domain
    Specifies the domain to look for Group Policies. This is auto populated with the domain info from the PC running
    the cmdlet.

    .PARAMETER Server
    Specifies the Domain Controller to query for group Policies to backup

    .EXAMPLE
    Backup-GroupPolicy -path C:\Backup

    Description:
    This example creates a backup of the GPO's and saves them to the c:\backup folder.
    Since no server was specified, the code will search for the nearest Domain Controller.

    .EXAMPLE
    Backup-GroupPolicy -path C:\Backup -Domain nwtraders.local -Server DC01

    Description:
    This example creates a backup of GPO's in the nwtraders.local domain to the C:\Backup folder.
    The backups will be pulled from the DC named DC01.


    .NOTES
    Name       : Backup-GroupPolicy.ps1
    Author     : Mike Kanakos
    Version    : 1.1.0
    DateCreated: 2018-12-19
    DateUpdated: 2019-01-25

    .LINK
    https://github.com/compwiz32/PowerShell
#>

[CmdletBinding(SupportsShouldProcess=$true)]
param (
    [Parameter(Mandatory=$True,Position=0)]
    [string]
    $Path,

    [Parameter()]
    [string]
    $Domain = (Get-WmiObject Win32_ComputerSystem).Domain,

    [Parameter()]
    [string]
    $Server
    )

    begin {

        # Get current GPO information
        $GPOInfo = Get-GPO -All -Domain $domain -Server $Server


        #Create a date-based folder to save backup of group policies
        $Date = Get-Date -UFormat "%Y-%m-%d"
        $UpdatedPath = "$path\$date"

        New-item $UpdatedPath -ItemType directory | Out-Null

        Write-Host "GPO's will be backed up to $UpdatedPath" -backgroundcolor white -foregroundColor red
    } #end of begin block

    process {

        ForEach ($GPO in $GPOInfo) {

            Write-Host "Backing up GPO named: " -foregroundColor Green -nonewline
            Write-Host $GPO.Displayname -foregroundColor White

            #Assign temp variables for various parts of GPO data
            $BackupInfo = Backup-GPO -Name $GPO.DisplayName -Domain $Domain -path $UpdatedPath -Server $Server
            $GpoBackupID = $BackupInfo.ID.Guid
            $GpoGuid = $BackupInfo.GPOID.Guid
            $GpoName = $BackupInfo.DisplayName
            $CurrentFolderName = $UpdatedPath + "\" + "{"+ $GpoBackupID + "}"
            $NewFolderName = $UpdatedPath + "\" + $GPOName + "___" + "{"+ $GpoBackupID + "}"
            $ConsoleOutput = $GPOName + "___" + "{"+ $GpoBackupID + "}"

            #Rename the newly created GPO backup subfolder from its GPO ID to the GPO Displayname + GUID
            rename-item $CurrentFolderName -newname $NewFolderName


        } #End ForEach loop

    } #End of process block

    end {
    } #End of End block
} #End of function

You can run this function as needed or schedule it to run regularly via a scheduled task or a scheduled job. Once scheduled, it's one less thing you have to perform manually each month. If you work at a company that uses GPOs heavily, this function may take a good bit of time to run. I have approximately 150 GPOs in my employer's domain, and the script takes just a few short minutes to complete. The script will show the name of each GPO it's backing up when running it, so you can get an idea of how much work it has completed.

Script output in progress

Script output in progress

Conclusion ^

I built this function because I was not happy with the default output from the built-in Microsoft tools. That's what makes PowerShell so great: if something doesn't work as expected, you can build your own tool! My hope is that this tool will become a useful addition in your sysadmin arsenal. Be on the lookout for a follow-up article that walks you through the restore process using a script I wrote that was designed to do restores specifically from the backups created by this tool.

Subscribe to 4sysops newsletter!

Last but not least, all my scripts are constantly under revision as I find new ways to improve them. You can find the latest version of all my scripts at my Github PowerShell repo. Let me know what you think in the comment section below. Thanks for reading!

avataravataravatar