How to move whole OU subtree bringing GPO and GPO link from source forest to destination forest

During a Forest and Domain consolidation project I had a challenge to migrate users e computers between Forests.

I had the need to maintain the same GPO and the same structure of OU.

I dug the net and could not find anything that I could meet, I had to assemble various scripts, but even so I would not have satisfied my need.

I wrote a powershell script that apply to this scenario.

I'm still working on it, but I decided to share and hope to get some feedback for improving it or correct some bugs.

This script  on target forest, create same source OU subtree, copy and link same source GPO on relative copied OU.

WMIFilter is not migrated in this script version.

Prerequisite:

  • Tested with Windows 2012 R2 (target) and Windows 2008 R2 (source)
  • RSAT
  • GPMC console
  • Tested with bidirectional Trust

These are the parameters:

  • BaseSourceDN: DN of source OU subtree you want move
  • BaseTargetDN: DN of targetOU where you want move in
  • SourceDomain: source domain name
  • TargetDomainController: target domain controller server name
  • prefix: (optional) prefix name for target GPO

# Sample .\move_OU1.ps1 -BaseSourceDN "OU=users,DC=contoso,DC=local" -BaseTargetDN "OU=users,OU=home,DC=adatum,DC=local" -SourceDomain "contoso.local" -TargetDomainController dc01.adatum.local -prefix "Contoso-"

Param(

[string]$BaseSourceDN,

[string]$BaseTargetDN,

[string]$SourceDomain,

[string]$TargetDomainController,

[string]$Prefix=""

)

function CopyGPO

{

param([string]$SourceDomain,

[string]$TargetDomainController,

[string]$SourceDN,

[string]$OUTargetUrl)

$oSourceOU=[adsi]"LDAP://$SourceDN"

$gPLinks=$oSourceOU.gPLink

if(($gPLinks -ne $null) -or ($gPLinks -ne ''))

{

$gPlink=$gPlinks.split('][')

for ($i=$gPlink.count-2;$i -gt 0;$i=$i-2)

{

$GPO_URI=$gPlink[$i].split(';')

$GPO_DN=$GPO_URI[0].substring(7)

switch($GPO_URI[1])

{

"1" {$EnableStatus = 'No'; $EnforceStatus = 'No'}

"2" {$EnableStatus = 'Yes'; $EnforceStatus = 'Yes'}

"3" {$EnableStatus = 'No'; $EnforceStatus = 'Yes'}

"0" {$EnableStatus = 'Yes'; $EnforceStatus = 'No'}

}

$GPO_URI_wDomain="LDAP://$SourceDomain/$GPO_DN"

$oGPO=[adsi]"$GPO_URI_wDomain"

$GPO_Name=$oGPO.DisplayName

try

{

Write-Host

Write-Host "Try to copy GPO $GPO_Name" -ForegroundColor Green

Copy-GPO -SourceName "$GPO_Name" -SourceDomain "$SourceDomain" -TargetName "$Prefix$GPO_Name" -TargetDomainController "$TargetDomainController" -ErrorAction Stop

$cGPOApply=$oGPO.psbase.objectsecurity.access |?{$_.ActivedirectoryRights -eq 'ExtendedRight'}

Set-GPPermission -Name "$Prefix$GPO_Name" -TargetName "Authenticated Users" -PermissionLevel None -TargetType Group -ErrorAction SilentlyContinue

foreach($GPOApply in $cGPOApply)

{

$SecurityFiltering=$GPOApply.IdentityReference

Set-GPPermission -Name "$Prefix$GPO_Name" -TargetName "$SecurityFiltering" -PermissionLevel GpoApply -Server "$TargetDomainController" -TargetType Group -ErrorAction SilentlyContinue

}

if ($Prefix -eq '')

{

Write-Host "$GPO_Name copied" -ForegroundColor Green

}

else

{

Write-Host "$GPO_Name copied and renamed in $Prefix$GPO_Name" -ForegroundColor Green

}

}

catch [exception]

{

write-host $_.Exception.Message -ForegroundColor Red

}

try

{

Write-Host

Write-Host "Try to create link for GPO:$Prefix$GPO_Name on OU:$OUTargetUrl" -ForegroundColor Green

New-GPLink -Name "$Prefix$GPO_Name" -Target $OUTargetUrl -Server "$TargetDomainController" -LinkEnabled $EnableStatus -Enforced $EnforceStatus -ErrorAction Stop

Write-Host "Link for GPO:$Prefix$GPO_Name on OU:$OUTargetUrl created" -ForegroundColor Green

}

catch [Exception]{

write-host $_.Exception.Message -ForegroundColor Red

}

}

return

}

}

function CreateOU

{

param ([string]$SourceDN,

[string]$TargetDN)

$oSourceOU=[adsi]"LDAP://$SourceDN"

$RDN=[string]$oSourceOU.Name

try

{

Write-Host

Write-Host "Try to create OU=$RDN,$TargetDN" -ForegroundColor Green

New-ADOrganizationalUnit -Name $RDN -Path $TargetDN

Write-Host "OU=$RDN,$TargetDN created" -ForegroundColor Green

$gPOption=$oSourceOU.gPOptions

# Check if Blocked Inheritance is true

if($gPOption -eq 1){

Write-Host

Write-Host "Try to set Block Inheritance true" -ForegroundColor Green

Set-GPInheritance -IsBlocked Yes -Target "OU=$RDN,$TargetDN" # Block Inheritance

Write-Host "Block Inheritance is true" -ForegroundColor Green

}

}

catch [Exception]{

write-host $_.Exception.ServerErrorMessage -ForegroundColor Red

}

$NewTargetDN="OU=$RDN,$TargetDN"

$NewSourceDN=$SourceDN

CopyGPO $SourceDomain $TargetDomainController $SourceDN $NewTargetDN

$cOUs=$oSourceOU.Children | Where-Object{$_.schemaclassname -eq "OrganizationalUnit"}

if (($cOUs -ne '') -and ($cOUs.count -ne 0))

{

foreach($ChildOU in $cOUs)

{

$RDN=[string]$ChildOU.name

$NewSourceDN="OU=$RDN,"+$SourceDN

CreateOU $NewSourceDN $NewTargetDN

}

}

return

}

if(($BaseSourceDN -eq '') -or (!$BaseSourceDN) -or ($BaseTargetDN -eq '') -or (!$BaseTargetDN) -or

($SourceDomain -eq '') -or (!$SourceDomain) -or ($TargetDomainController -eq '') -or (!$TargetDomainController))

{

Write-Host "Incomplete parameters" -ForegroundColor Blue

Write-Host "Use this sintax:" -ForegroundColor Blue

Write-Host ".\move-ou.ps1 -BaseSourceDN ""OU=users,DC=contoso,DC=local"" -BaseTargetDN ""OU=users,OU=home,DC=adatum,DC=local"" -SourceDomain ""contoso.local"" -TargetDomainController ""dc01.adatum.local""" -ForegroundColor Blue

}

else

{

CreateOU $BaseSourceDN $BaseTargetDN

}