Using PowerShell to backup Group Policy Objects
blogs.technet.microsoft.com

Using PowerShell to backup Group Policy Objects

I'm sure we all do nightly backups of our domain controllers and Active Directory some restoring at is a pain if all you need is the settings for a GPO that got edited yesterday. Fortunately GPOs have their own native backup and restore system (https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc759276(v%3dws.10)). The system works well, but it's really only designed to to backup/restore 1 policy at a time. PowerShell makes it much easier to backup all of your policies with a single command Backup-GPO -All -Path "c:\Temp". It works great but I wanted a little more than just the backup.

The script below will backup your group polices, export an HTML file with all the settings for your GPO and generates a list of what policy is linked where (also part of the HTML file). It is designed so you don't need to configure anything unless you want to. The script will get domain and forest information and will use the path it is run in to store the data. I currently have this running in System Center Orchestrator nightly so we have a daily backup of our group policies.

<#
About
	Name:		Active Directory GPO Backup
	Script: 	GpoBackup.ps1
	Author: 	James Sargent
	Created: 	2018/09/27
	Modified:	2018/09/27
	Version: 	1.00.0927a

Summary
	Does the following for all GPOs in all domains in the forest
	Native backup of all GPOs
	Exports HTML reports for all GPOs
	Creates a list of GPOs and where they are linked (Includes and identifies non-linked GPOs)

Instructions	
	There is very little to configure as all information is gathered automatically with the exception
	of how long you want to keep the data, default is 7 days.  To change this simply update the 
	$intDaysToKeep value.

	When running the script in a multi-forest domain the user running the script must have sufficient access
	in all domains to Backup GPOs, Export GPOs, etc.  This will likely be a member of the Enterprise Admins
	group.
	
	The data is compressed in to 2 zip files, one for backups, and one for reports, the link information
	CSV file is in the report zip file.
	
	The zip files are stored in a folders for each domain under the Backups folder
	
Requirements
	PowerShell 4.0 or higher
	AD PowerShell Module (RSAT)
#>

# Variables                                                          
# ------------------------------------------------------------------------
	$intDaysToKeep = 7							# Number of days to keep reports
	$strPath = (Get-Location).Path + "\"		# Path to script location
	$strPathTemp = $strPath + "Temp\"			# Path to temp folder, ensure paths have a trailing \
	$strPathBackups = $strPath + "Backups\"		# Path to backups folder, ensure paths have a trailing \
	$dtStartTime = Get-Date

# Functions
# ------------------------------------------------------------------------
Function funBackup ($strDomain)
{
	$arrGPOLinks = @()

	# Set Paths
	$tmpPathBackup = $strPathTemp + "Backup\"
	$tmpPathReports = $strPathTemp + "Reports\"
	$strPathDomainBackups = $strPathBackups + $strDomain + "\"

	# If paths don't exisit create them
	If (!(Test-Path $tmpPathBackup)) {New-Item -ItemType Directory -Path $tmpPathBackup}
	If (!(Test-Path $tmpPathReports)) {New-Item -ItemType Directory -Path $tmpPathReports}
	If (!(Test-Path $strPathDomainBackups)) {New-Item -ItemType Directory -Path $strPathDomainBackups}
	
	# Get PDCEmulator
	$strDC = Get-AdDomainController -Filter {OperationMasterRoles -Like "PDCEmulator" -And Domain -eq $strDomain} | Select -ExpandProperty HostName

	# Get GPO List
	$arrGPOList = Get-GPO -All -Domain $strDomain -Server $strDC 

	# Count GPOs for Progress bar
	$numRecords = $arrGPOList.count

	foreach ($arrPolicy in $arrGPOList) 
	{
		$tmpName = $arrPolicy.DisplayName
		$tmpID = $arrPolicy.ID.Guid
		$tmpComment = "Backup of " + $tmpName + " on " + (Get-Date($dtStartTime) -Format "yyyy-MM-dd")
		$tmpGPOLinks = $Null
		<#
		Backup GPO
			GPO will be backed up to a folder that matches the GUID, this backup can be used
			to do a Restore in the GPEdit tool
		#>
		Backup-GPO -Guid $tmpID -Path $tmpPathBackup -Comment $tmpComment
	
		<#
		Export HTML Report
			An HTML file with the display name of the GPO will be created with all of the GPO Settings.
				Example: Default Domain Policy.html
		#>
		Get-GPOReport -Name $tmpName -Path ($tmpPathReports + $tmpName + ".html") -ReportType HTML
		
		<#			
		Get GPO link lnformation
		Additional a text file with the display name of the gpo will be created with information on
		all of the OU's the policy was linked.
			Example: Default Domain Policy.link
		#>	
		#$arrGPOLinks += ([xml](Get-GPOReport -Name $tmpName -ReportType XML)).GPO.LinksTo | Select @{n="Policy";e={$tmpName}},@{n="OU";e={$_.SOMName}},@{n="Path";e={$_.SOMPath}},@{n="LinkStatus";e={$_.Enabled}},@{n="Enforced";e={$_.NoOverrided}}
		$tmpGPOLinks = ([xml](Get-GPOReport -Name $tmpName -ReportType XML)).GPO.LinksTo | Select @{n="Policy";e={$tmpName}},@{n="OU";e={$_.SOMName}},@{n="Path";e={$_.SOMPath}},@{n="LinkStatus";e={$_.Enabled}},@{n="Enforced";e={$_.NoOverrided}}
		If ($tmpGPOLinks -eq $Null) 
		{
			$tmpGPOLinks = New-Object PSObject -Property @{
			Policy		= $tmpName
			OU			= "NA"
			Path		= "NA"
			LinkStatus	= "NA"
			Enforced	= "NA"}
		} 

		$arrGPOLinks += $tmpGPOLinks
	}
	
	# Write links file
	$arrGPOLinks | Export-CSV ($tmpPathReports + "GPOLinks-" + (Get-Date($dtStartTime) -f "yyyy-MM-dd") + ".csv") -NoTypeInformation
		
	# Compress Backups
	Compress-Archive -Path $tmpPathBackup -DestinationPath ($strPathDomainBackups + "GPOBackup-" +  (Get-Date($dtStartTime) -f "yyyy-MM-dd") + ".zip")
	
	# Compress Reports
	Compress-Archive -Path $tmpPathReports -DestinationPath ($strPathDomainBackups + "GPOReports-" +  (Get-Date($dtStartTime) -f "yyyy-MM-dd") + ".zip")
	
	# Cleanup Temp folder
	Remove-Item ($strPathTemp + "*") -Force -Recurse -Confirm:$False

	# Cleanup backup folder
	Get-ChildItem -Path $strPathDomainBackups | Where {$_.CreationTime -lt $dtStartTime.AddDays(-$intDaysToKeep)} | Remove-Item -Force -Confirm:$False
}

# ----------- Main script ----------- 

# Get Domain List
$arrDomains = Get-ADForest | Select -ExpandProperty Domains

# Verify the Temp path exists
If (!(Test-Path $strPathTemp)) {New-Item -ItemType Directory -Path $strPathTemp}

# Verify the Backups path exists
If (!(Test-Path $strPathBackups )) {New-Item -ItemType Directory -Path $strPathBackups}

# Backup each domain
# The account this script is running under will need access to each domains AD
ForEach ($strDomain in $arrDomains)
{
	funBackup $strDomain
}



 
  


To view or add a comment, sign in

More articles by James Sargent

Explore content categories