FamilyTechYmas

viernes, 27 de agosto de 2021

Backup all GPO and links and restore via PowerShell - Ready for automation

 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.