Backing up GPOs with a Powershell script

Hi everyone

One of the recommendations I often make to customers is to ensure that they are regularly backing up their GPOs using GPMC (Vbscript or Powershell). It's a very easy activity to setup a scheduled task and to backup on a daily basis each of your GPOs. Backing up your GPOs daily will mean you not only have a data source for restoring GPOs, but you also have a basic change control record of when GPOs are being changed.

The easiest way to backup GPOs is with Powershell using the following command:

Get-GPO -all | Backup-GPO -path c:\GPOBackups

This one liner is a great place to start, but wouldn't it also be nice to be able to keep the GPO backups for a particular time period, and remove old backups greater than a defined period. As the above command is dropping the backups in C:\GPOBackups, if I was to run the command over and over each day, it wouldn't take long to consume quite a bit of space on the C: drive (depending on the number of policies you have of course).

In order to solve this problem I knocked up the following backup script. The script was written to run on a Windows Server 2008 R2 server configured with GPMC installed. The script backs up GPOs to the c:\GPOBackups folder by default (which you can override with the -GPODir argument), and keeps the last 31 days worth of backups (also override able with the -DeleteAfterDays argument).

The thinking behind sharing this script was that if you have a similar need you too can download/modify and use to backup GPOs in your environment. Additionally I was hoping to demonstrate how simple setting up GPO backups can be and why it makes sense to set it up. Please feel free to use the script but please make sure that you test, test and then test again this script in a test environment before using in a production environment.

WARNING: This script does delete old GPO backup folders, and other old folders in the targeted GPO backup location. So please test this script, and set the -GPODir argument wisely

The script deletes folders with content older than 31 days from the -GPODir argument location (default c:\GPOBackups), so please be careful which folder you provide as a GPO Backup location. A location of c:\ would be a bad location for the GPO backup folder, as other important directories would be removed. Please create a folder where you would like your GPO backups (and only your GPO backups) to go and then target the script to use the folder created.

Testing

When you are testing, you can copy folders from one location to another and preserve the source time stamp using the following command. This will give you a way to confirm that only the old folders are removed as expected. The /dcopy:t tells robocopy to preserve the source timestamp (c:\scratch).

robocopy C:\scratch C:\GPOBackups\scratch /e /z /dcopy:t

 

If you would like to use a .cmd to kick off the powershell script, and schedule the .cmd task to be run using a Windows scheduled task here is a sample .CMD command line to kick off the Powershell script.

start /wait C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe c:\binBackupGPOs.ps1 -Action backupgpos -GPODir "c:\gpobackups"

Happy GPO'ing.

Steve

# +---------------------------------------------------------------------------
# | File : BackupGPOs.ps1                                         
# | Version : 1.2                                         
# | Purpose : Back up GPOs to a targeted folder, and delete backups older than a specified number of days
# | Usage : start /wait C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe c:\bin\BackupGPOs.ps1 -Action backupgpos -GPODir c:\gpobackups
# +----------------------------------------------------------------------------
# | Maintenance History                                           
# | -------------------                                           
# | Name Date Version C/R Description       
# | ----------------------------------------------------------------------------------
# | Steve Moore 2011-11-16 1.2 Backup GPOs
# +-------------------------------------------------------------------------------
# ********************************************************************************
param ([string]$Action, [string]$Version=1.2, [bool]$Debug=$False, [string]$LogFile="$env:Computername-BackupGPOs.log", [string]$LogResults=$True, [string]$GPODir="c:\gpobackups", [int]$DeleteAfterDays=-31)

# +---------------------------------------------------------------------------
# | FUNCTIONS
# |
# | description of function, parameters and what it returns (if any)                                                                                        
# +----------------------------------------------------------------------------

function DisplayHeader {
##-----------------------------------------------------------------------
## Function: DisplayHeader
## Purpose: Use to write a header for the script
##-----------------------------------------------------------------------
  "---------------------------------------------------------"
  "- Backup GPOs -"
  "- Used to automate the process of backing up GPOs -"
  "- '$Version' -"
  "---------------------------------------------------------"
 If ($LogResults) {
  OutLog "---------------------------------------------------------" 
  OutLog "- Backup GPOs -"
  OutLog "- Used to automate the process of backing up GPOs -"
  OutLog "- '$Version' -"
  OutLog "---------------------------------------------------------"
 }
}

function OutLog {
##-----------------------------------------------------------------------
## Function: OutLog
## Purpose: Used to write output to a log file
##-----------------------------------------------------------------------
 param ([string]$strOut, [bool]$blnNoDate)
 $strDateTime = get-date -uFormat "%d-%m-%Y %H:%M"
 If (-not ($blnNoDate)) {
  $strOutPut = ($strDateTime+ ": " + $strOut)
 } else {
  $strOutPut = $strOut
 }
 If ($LogFile -and $LogResults) {"$strOutPut" | out-file -filepath $LogFile -append}
}

function OutLn {
##-----------------------------------------------------------------------
## Function: OutLn
## Purpose: Used to write output to the screen
##-----------------------------------------------------------------------
 param ([string]$strOut, [string]$strType)

 If ($strOut -match "ERR:") {
      write-host $strOut -ForegroundColor RED -BackgroundColor WHITE
 } elseIf ($strOut -match "WARN:") {
      write-host $strOut -ForegroundColor CYAN -BackgroundColor WHITE
 } elseIf ($strOut -match "WARNING:") {
      write-host $strOut -ForegroundColor CYAN -BackgroundColor WHITE
 } elseIf ($strOut -match "INFO:") {
      write-host $strOut -ForegroundColor BLUE -BackgroundColor WHITE
 } elseIf ($strOut -match "DEBUG:") {
      write-host $strOut -ForegroundColor DARKGREEN -BackgroundColor WHITE
 } elseIf ($strOut -match "OTH:") {
      write-host $strOut -ForegroundColor DARKBLUE -BackgroundColor WHITE
 } Else {
      write-host $strOut
 }
 If ($LogResults) {OutLog $strOut}
}

##-----------------------------------------------------------------------
## Function: CheckModuleLoaded
## Purpose: This function is used to confirm the AD Powershell modules are loaded
## and if not load it
##-----------------------------------------------------------------------
function CheckModuleLoaded
{
 param ([string]$strModuleName)

 $blnFound = $False

 Get-Module | ForEach {
  If ($_.name -match "$strModuleName") {$blnFound = $True}
 }

 If (!$blnFound) {
  import-module $strModuleName
  If ($Debug) {OutLn "Loading module $strModuleName"}
 }
}

##-----------------------------------------------------------------------
## Function: Remove-OldFolders
## Purpose: This function is used to remove old backup folders
##-----------------------------------------------------------------------
function Remove-OldFolders
{
    param ([int]$intDays)
    Get-ChildItem $GPODir | Where {($_.psiscontainer -eq $true) -and ($_.LastWriteTime -lt ($(Get-Date).AddDays($intDays)))} | foreach {
 $strFolder = $_.fullname
 $intDaysPos = $intDays * -1
 OutLn "INFO: Removing folder $strFolder as backup content older than $intDaysPos days"
 Remove-Item $strFolder -ea silentlycontinue -recurse
    }
}

##-----------------------------------------------------------------------
## Function: BackupAllGPOs
## Purpose: This function is used to backup all GPOs to the GPODir
##-----------------------------------------------------------------------
function BackupAllGPOs
{
    $strDateTime = get-date -uFormat "%d-%m-%Y"
    $strBackupLocation = "$GPODir\$strDateTime"

    If (-not (Test-Path $strBackupLocation)) {md $strBackupLocation}
    Get-GPO -all | foreach {
 $strGPOName = $_.Displayname
 $strId = $_.Id
 OutLn "INFO: Backing up $strGPOName"
 Backup-GPO -Guid $strId -path $strBackupLocation
    }

    Remove-OldFolders $DeleteAfterDays
}

 

##-----------------------------------------------------------------------
## Function: Usage
## Purpose: This function is used to describe how to use this script
##-----------------------------------------------------------------------
function Usage
{

@"
    Usage:
       
     .\BackupGPOs.ps1 -Action backupgpos [-LogResults `$True -LogFile testing.log -GPOdir "c:\testing" -DeleteAfterDays -20]
     Runs the BackupGPOs script to backup all GPOs to the c:\testing directory
     e.g. .\BackupGPOs -Action backupgpos -gpodir "c:\testing"

     OR

     #Delete GPO folders that are older than 21 days, into the c:\testing folder
     e.g. .\BackupGPOs -Action backupgpos -gpodir "c:\testing" -DeleteAfterDays -21
     OR

     .\BackupGPOs.ps1
     Shows the usage options of BackupGPOs
       
"@
    return
}

# +---------------------------------------------------------------------------
# | MAIN ROUTINE                                                                                        
# +----------------------------------------------------------------------------
function Main()
{
 $objHost = (get-host).UI.RawUI
 $objHost.BackgroundColor = "WHITE"
 $objHost.foregroundcolor = "DARKBLUE"

 cls

 DisplayHeader
 CheckModuleLoaded "GroupPolicy"

 $strTitle = "BackupGPOs: Backup directory:$GPODir"
 $host.ui.rawui.windowtitle = $strTitle

 If (Test-Path $LogFile) {Remove-Item -path $LogFile}

 If (($Action -eq $null) -or ($Action -eq '')) {
  Usage
  return
 }

 $strAction = $Action

 switch ($strAction) {
  "backupgpos" {
   BackupAllGPOs
  }
  default {
   Usage
   return
  }
 }
}

#$erroractionpreference = "SilentlyContinue"

Main


BackupGPOs.1.2.ps1