This time, I bring this script that will help you backup all the GPO's and links in the environment.
To use it properly, you must use this code (which is built over codes from other people.):
<#
Disclaimer:
This sample script is not supported under any Microsoft standard support program or service.
The sample script is provided AS IS without warranty of any kind. Microsoft further disclaims
all implied warranties including, without limitation, any implied warranties of merchantability
or of fitness for a particular purpose. The entire risk arising out of the use or performance of
the sample scripts and documentation remains with you. In no event shall Microsoft, its authors,
or anyone else involved in the creation, production, or delivery of the scripts be liable for any
damages whatsoever (including, without limitation, damages for loss of business profits, business
interruption, loss of business information, or other pecuniary loss) arising out of the use of or
inability to use the sample scripts or documentation, even if Microsoft has been advised of the
possibility of such damages
.DESCRIPTION
This script creates a backup of all the GPO's and their links in the domain and stores them
into a folder you specify
This script is not meant to be used by running scriptname.ps1 but loading the functions and
executing the one you want at the moment
.AUTHOR
Cristian Marius Precub
.NOTES
.DATE
24-MAY-2021
.EXAMPLE
To backup all GPOs and Links: backup-allgpo
To restore all GPOs and links: restore-gpoandlinks
#>
Import-Module ActiveDirectory
Import-Module GroupPolicy
if (-not(test-path "C:\temp")) {
try{
#create temp folder
new-item "C:\temp" -Type Directory -ErrorAction Stop
} catch {"Error creating folder: $($PSItem.ToString())" >> $log }
}
$log = "C:\temp\GPO_Activity.log"
function Get-Gplink {
<#
.SYNOPSIS
return and decode content of gplink attribut
.DESCRIPTION
This function purpose is to list the gplink attribut of an OU, Site or DomainDNS.
It will return the following information for each linked GPOs:
- Target: DN of the targeted object
- GPOID: GUID of the GPO
- GPOName: Friendly Name of the GPO
- GPODomain: Originating domain of the GPO
- Enforced: <Yes|No>
- Enabled: <Yes|No>
- Order: Link order of the GPO on the OU,Site,DomainDNS (does not report inherited order)
.PARAMETER Path: Give de Distinguished Name of object you want to list the gplink
.INPUTS
DN of the object with GLINK attribut
.OUTPUTS
Target: DC=fourthcoffee,DC=com
GPOID: 31B2F340-016D-11D2-945F-00C04FB984F9
GPOName: Default Domain Policy
GPODomain: fourthcoffee.com
Enforced: <YES - NO>
Enabled: <YES - NO>
Order: 1
.EXAMPLE
get-gplink -path "dc=fourthcoffee,dc=com"
This command will list the GPOs that are linked to the DomainDNS object "dc=fourthcoffee,dc=com"
.EXAMPLE
get-gplink -path "dc=child,dc=fourthcoffee,dc=com" -server childdc.child.fourthcoffee.com
This command will list the GPOs that are linked to the DomainDNS object "dc=child,dc=fourthcoffee,dc=com". You need to specify a
target DC of the domain child.fourthcoffee.com in order for the command to work.
.EXAMPLE
Get-Gplink -site "CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=fourthcoffee,DC=com"
This command will list the GPOs that are linked to site "Default-First-Site-Name"
.EXAMPLE
get-gplink -path "dc=fourthcoffee,dc=com" | export-csv "gplink.csv"
This command will list the GPOs that are linked to the DomainDNS object "dc=fourthcoffee,dc=com" and export them to a csv file.
The csv file can be used as an input to the cmdlet new-gplink
.EXAMPLE
get-adobject -filter {(objectclass -eq "DomainDNS") -or (objectclass -eq "OrganizationalUnit")} | foreach {get-gplink -path $_.distinguishedname} | export-csv "gplinksall.csv"
This command will list all objects of type "DomainDNS" and "OrganizationalUnit" that have GPOs linked and will list those GPOs, their status and link order.
#>
[cmdletBinding()]
param ([string]$path,[string]$server,[string]$site)
#Import AD and GPO modules
Import-Module activedirectory
Import-Module grouppolicy
# get the DN to te configuration partition
$configpart=(Get-ADRootDSE).configurationNamingContext
#get content of attribut gplink on site object or OU
if ($site)
{
$gplink=Get-ADObject -Filter {distinguishedname -eq $site} -searchbase $configpart -Properties gplink
$target=$site
}
elseif ($path)
{
switch ($server)
{
"" {$gplink=Get-ADObject -Filter {distinguishedname -eq $path} -Properties gplink}
default {$gplink=Get-ADObject -Filter {distinguishedname -eq $path} -Properties gplink -server $server
}
}
$target=$path
}
#if DN is not valid return" Invalide DN" error
if ($gplink -eq $null)
{
write-host "Either Invalide DN in the current domain, specify a DC of the target DN domain or no GPOlinked to this DN"
}
# test if glink is not null or only containes white space before continuing.
if (!((($gplink.gplink) -like "") -or (($gplink.gplink) -like " ")))
{
#set variale $o to define link order
$o=0
#we split the gplink string in order to seperate the diffent GPO linked
$split=$gplink.gplink.split("]")
#we need to do a reverse for to get the proper link order
for ($s=$split.count-1;$s -gt -1;$s--)
{
#since the last character in the gplink string is a "]" the last split is empty we need to ignore it
if ($split[$s].length -gt 0)
{
$o++
$order=$o
$gpoguid=$split[$s].substring(12,36)
$gpodomainDN=($split[$s].substring(72)).split(";")
$domain=($gpodomaindn[0].substring(3)).replace(",DC=",".")
$checkdc=(get-addomaincontroller -domainname $domain -discover).name
#we test if the $gpoguid is a valid GUID in the domain if not we return a "Oprhaned GpLink or External GPO" in the $gponname
$mygpo=get-gpo -guid $gpoguid -domain $domain -server "$($checkdc).$($domain)" 2> $null
if ($mygpo -ne $null )
{
$gponame=$MyGPO.displayname
$gpodomain=$domain
}
else
{
$gponame="Orphaned GPLink"
$gpodomain=$domain
}
#we test the last 2 charaters of the split do determine the status of the GPO link
if (($split[$s].endswith(";0")))
{
$enforced= "No"
$enabled= "Yes"
}
elseif (($split[$s].endswith(";1")))
{
$enabled= "No"
$enforced="No"
}
elseif (($split[$s].endswith(";2")))
{
$enabled="Yes"
$enforced="Yes"
}
elseif (($split[$s].endswith(";3")))
{
$enabled="No"
$enforced="Yes"
}
#we create an object representing each GPOs, its links status and link order
$return = New-Object psobject
$return | Add-Member -membertype NoteProperty -Name "Target" -Value $target
$return | Add-Member -membertype NoteProperty -Name "GPOID" -Value $gpoguid
$return | Add-Member -membertype NoteProperty -Name "DisplayName" -Value $gponame
$return | Add-Member -membertype NoteProperty -Name "Domain" -Value $gpodomain
$return | Add-Member -membertype NoteProperty -Name "Enforced" -Value $enforced
$return | Add-Member -membertype NoteProperty -Name "Enabled" -Value $enabled
$return | Add-Member -membertype NoteProperty -Name "Order" -Value $order
$return
}
}
}
}
<#
----------------------------------------
File: backup-gplink.ps1
Version: 1.1
Author: Thomas Bouchereau
-----------------------------------------
Disclaimer:
This sample script is not supported under any Microsoft standard support program or service.
The sample script is provided AS IS without warranty of any kind. Microsoft further disclaims
all implied warranties including, without limitation, any implied warranties of merchantability
or of fitness for a particular purpose. The entire risk arising out of the use or performance of
the sample scripts and documentation remains with you. In no event shall Microsoft, its authors,
or anyone else involved in the creation, production, or delivery of the scripts be liable for any
damages whatsoever (including, without limitation, damages for loss of business profits, business
interruption, loss of business information, or other pecuniary loss) arising out of the use of or
inability to use the sample scripts or documentation, even if Microsoft has been advised of the
possibility of such damages
#>
function backup-gplink
{
<#
.SYNOPSIS
backup gplink of a specified GPO to a CSV file
.DESCRIPTION
This function purpose is to backup gplinks of a specified GPO to a CSV file
.INPUTS
Name of the GPO you want to backup the links
.OUTPUTS
a CSV file name <gponam>_gplinksbackup.csv
.EXAMPLE
backup-gpo -name "test1" -path $pwd\bck
This command will backup the gplinks for the GPO Test1 on the folder bck under the present at the same level as the prompt.
#>
[cmdletBinding()]
param
($gponame,
[string]$path)
$domaindn=(get-addomain).distinguishedname
[xml]$xmlreport=Get-GPOReport -Name $gponame -ReportType xml
foreach ($som in ($xmlreport.gpo.linksto.SOMPath))
{
$target=$null
$splits=$som.split("/")
for ($s=$splits.count-1;$s -gt 0;$s--)
{
$target+="OU="+$splits[$s]+","
}
$target+=$domaindn
#$path
Get-Gplink -path "$($target)" | ?{$_.displayname -eq $gponame} |Export-Csv -UseCulture -NoTypeInformation -Path "$($path)\$($gponame)_gplinksbackup.csv" -Append
}
}
function restore-gplink
{
<#
.SYNOPSIS
restore gplink of a specified GPO using the output of the backup-gplink function
.DESCRIPTION
This function purpose is to restore the gplinks of a specified GPO using the configuration that was saved by the backup-gplink.
If the GPlink is still present, it will reset it to the value present in the backup.
.INPUTS
Name of the CSV file you want to use to restore the gplink
.OUTPUTS
None
.EXAMPLE
restore-gpo -path "test1_gplinksbackup.cvs"
This command will restore the gplinks for the GPO Test1 to locations present in the backup file.
Depeding on the links found on the SOM the restore link might be in disable to prevent negative impact on the production environment.
#>
[cmdletBinding()]
param(
[string]$path,
[boolean]$force=$false)
$targets=import-csv -Path $path -UseCulture
foreach($t in $targets)
{
Write-host "Linking GPO $($t.displayname) to $($t.target)" -ForegroundColor Green
[array]$gplinks=get-gplink -path $t.target
if ($gplinks.count -eq 0)
{
new-GPLink -Target $t.target -Name $t.displayname -LinkEnabled "no" -Enforced $t.enforced
Write-host "Link for GPO $($t.displayname) on SOM $($t.target) has been restored based on your backup file but in a DISABLED state. Check it and if valide re-enable the link" -ForegroundColor Yellow
}
else
{
if ($t.order -ge $gplinks.count)
{
try
{
new-GPLink -Target $t.target -Name $t.displayname -LinkEnabled $t.enabled -Order $($gplinks.count + 1) -Enforced $t.enforced -ErrorAction stop -ErrorVariable linkerr
Write-host "Link for GPO $($t.displayname) on SOM $($t.target) has been restored based on your backup file in it's original state." -ForegroundColor Yellow
}
catch
{
Write-host "GPO already linked at $($t.target)" -ForegroundColor yellow
}
if ($linkerr -and $force)
{
Write-host "Forcing GPO link as in backup file" -ForegroundColor Yellow
set-GPLink -Target $t.target -Name $t.displayname -LinkEnabled $($t.enabled) -Order $gplinks.count -Enforced $t.enforced
}
}
else
{
try
{
new-GPLink -Target $t.target -Name $t.displayname -LinkEnabled $($t.enabled) -Order $t.order -Enforced $t.enforced -ErrorAction stop -ErrorVariable linkerr
Write-host "Link for GPO $($t.displayname) on SOM $($t.target) has been restored based on your backup file in it's original state." -ForegroundColor Yellow
}
catch
{
Write-host "GPO already linked at $($t.target)" -ForegroundColor Yellow
}
if ($linkerr -and $force)
{
Write-host "Forcing GPO link as in backup file" -ForegroundColor Yellow
set-GPLink -Target $t.target -Name $t.displayname -LinkEnabled $t.enabled -Order $t.order -Enforced $t.enforced
}
}
}
"======================="
}
}
#Hacer backup de todas las GPO
function backup-allgpo {
#adjust to your scenario the next line
$Backuplocation="C:\temp\GPOBackup"
$secondpart=(get-date).ToString('dd-MMM-yyyy')
$completepath="$Backuplocation\$secondpart"
if (-not(test-path $Backuplocation\$secondpart)) {
try{
#create folder
new-item $Backuplocation\$secondpart -Type Directory -ErrorAction Stop
} catch {"Error creating folder $($PSItem.ToString())" >> $log}
}
$path=$completepath
get-gpo -all | foreach {
Backup-GPO -name $_.displayname -Path $path
backup-gplink -gponame $_.displayname -path $path
}
$Folders=Get-ChildItem -Path $path -Directory
foreach ($one in $Folders){
[xml]$manifest = Get-Content ($one.FullName+"\"+"bkupinfo.xml")
Rename-Item -Path $one.FullName -NewName $manifest.BackupInst.GPODisplayName.'#cdata-section'
}
}
#Restaurar copias de seguridad de las GPO
function restore-gpoandlinks {
$app = new-object -com Shell.Application
$folder = $app.BrowseForFolder(0, "Select folder from where backups should be imported", 1)
$GPOFolderName = $folder.Self.Path
$files=Get-ChildItem -Path $folder.self.path -File
$Folders=Get-ChildItem -Path $GPOFolderName -Directory
foreach ($one in $Folders){
[xml]$manifest = Get-Content ($one.FullName+"\"+"bkupinfo.xml")
Rename-Item -Path $one.FullName -NewName $manifest.BackupInst.id.'#cdata-section'
}
try{
$import_array = get-childitem $GPOFolderName -Directory | Select name
} catch {
(Get-Date).DateTime + " *** " + $($PSItem.ToString()) >> $log
}
foreach ($ID in $import_array) {
$XMLFile = $GPOFolderName + "\" + $ID.Name + "\gpreport.xml"
$XMLData = [XML](get-content $XMLFile)
$GPOName = $XMLData.GPO.Name
try{
import-gpo -BackupId $ID.Name -TargetName $GPOName -path $GPOFolderName -CreateIfNeeded
(Get-Date).DateTime + " *** " + "$($GPOName) successfully imported." >> $log
} catch {
(Get-Date).DateTime + " *** " + $($PSItem.ToString()) >> $log
}
}
#restore Links
if (($files.count) -gt "0") {
foreach ($f in $files)
{
try{
restore-gplink -path $GPOFolderName\$($f.Name) -ErrorAction Stop
(Get-Date).DateTime + " *** " + "$($GPOName) links successfully restored." >> $log
} catch {
(Get-Date).DateTime + " *** " + $($PSItem.ToString()) >> $log
}
}
}
}
backup-allgpo
To restore, just change the last line to "restore-gpoandlinks"
In case you want to use it for automation, you must modify the first lines of both last two functions, regarding path.