Purpose :
In some cases (lingering objects, corruption), an Active Directory partition needs to be rehosted. This technique allows you to “re-host” a partition on an Active Directory domain controller without dumping all the other read only partitions (like you would by simply un-checking the global catalog option). It saves time, replication traffic and reduces the impact on your domain controller in cases where you believe you have invalid data hosted on a particular server (http://blogs.msdn.com/b/canberrapfe/archive/2012/04/14/un-hosting-amp-re-hosting-active-directory-partitions.aspx)
Description:
I have written this script to automate the rehosting task of an Active Directory partition in a complex forest/site infrastructure. The first step before using this script is to build a configuration file with the domain controllers that need a rehost. To be clear, I will use the following example :
 templ_ad
In that structure, all the domain controllers on the forest wide are Global Catalogs. Each domain belongs to a specific Active Directory site. I need to rehost the partition “dc=child2,dc=root,dc=com”.
The domain controllers of the domain child2.root.com have a valid partition. I will use for the rehost one of the child2.root.com domain controller as a valid source. For this example, the valid source will be :
server1.child2.root.com
This is the list of the domain controllers that need a rehosting of the partition “dc=child2,dc=root,dc=com” :
  • server1.root.com
  • server2.root.com
  • server3.root.com
  • server1.child1.root.com
  • server2.child1.root.com
  • server3.child1.root.com
  • server1.sub1.child1.root.com
  • server2.sub1.child1.root.com
  • server3.sub1.child1.root.com
  • server1.sub2.child1.root.com
  • server2.sub2.child1.root.com
  • server3.sub2.child1.root.com
  • server1.sub3.child1.root.com
  • server2.sub3.child1.root.com
  • server3.sub3.child1.root.com

To avoid an impact on the production, there is at least one Global catalog available in a domain/site. These are the configuration files and the schedule plan for the rehost :

  • Day 1 : rehost the first root.com domain controller
    • conf1-1.txt :
server1.root.com;server1.child2.root.com;dc=child2,dc=root,dc=com
    • Execute the following command :
.\script.ps1 conf1-1.txt
  • Day 1 : when the rehost, of the first domain controller of root.com is successful, launch the rehost script for the remaining root.com domain controllers
    • conf1-2.txt :
server2.root.com;server1.root.com;dc=child2,dc=root,dc=com
server3.root.com;server1.root.com;dc=child2,dc=root,dc=com
    • Execute the following command :
.\script.ps1 conf1-2.txt
  • Day 2 : rehost the first child1.root.com domain controller
    • conf2-1.txt :
server1.child1.root.com;server1.root.com;dc=child2,dc=root,dc=com
    • Execute the following command :
.\script.ps1 conf2-1.txt
  • Day 2 : when the rehost, of the first domain controller of child1.root.com is successful, launch the rehost script for the remaining child1.root.com domain controllers
    • conf2-2.txt :
server2.child1.root.com;server1.child1.root.com;dc=child2,dc=root,dc=com
server3.child1.root.com;server1.child1.root.com;dc=child2,dc=root,dc=com
    • Execute the following command :
.\script.ps1 conf2-2.txt
  • Day 3 : rehost the first domain controller of the domains sub1, sub2 and sub3
    • conf3-1.txt :
server1.sub1.child1.root.com;server1.child1.root.com;dc=child2,dc=root,dc=com
server1.sub2.child1.root.com;server1.child1.root.com;dc=child2,dc=root,dc=com
server1.sub3.child1.root.com;server1.child1.root.com;dc=child2,dc=root,dc=com
    • Execute the following command :
.\script.ps1 conf3-1.txt
  • Day 3 : when the rehost, of the first domain controllers are successful, launch the rehost script for the remaining domain controllers
    • conf3-2.txt :
server2.sub1.child1.root.com;server1.sub1.child1.root.com;dc=child2,dc=root,dc=com
server3.sub1.child1.root.com;server1.sub1.child1.root.com;dc=child2,dc=root,dc=com
server2.sub2.child1.root.com;server1.sub2.child1.root.com;dc=child2,dc=root,dc=com
server3.sub2.child1.root.com;server1.sub2.child1.root.com;dc=child2,dc=root,dc=com
server2.sub3.child1.root.com;server1.sub3.child1.root.com;dc=child2,dc=root,dc=com
server3.sub3.child1.root.com;server1.sub3.child1.root.com;dc=child2,dc=root,dc=com
    • Execute the following command :
.\script.ps1 conf3-2.txt
Every time you will execute the script, a log file will be created. The script will do the following steps :

  • test if the source and destination server are reachable on the network
  • test if the remote registry is available on the destination server
  • a registry key and value will be created to unregister the global catalog of the destination server on the DNS (gc._msdcs zone). This step will avoid errors, if a user try to reach a Global Catalog during the rehost task
  • the script will pause for 30 minutes to let the DNS synchronize on all other domain controllers
  • unhost the partition on the destination server
  • check if the unhost task is successful by checking the event id 1660 on the destination server
  • rehost the partition on the destination server using the valid one located on the source server
  • wait for the user reply : you will have to check the generated log files and validate the rehost is successful. If you need to restart the rehost, press “r” and then Enter to restart the task. Else, just press Enter
  • the registry key and value are removed to register back the server in the DNS

The rehost task is successful if you find the following message in the log file : [mardi 20 août 2013 07:46:18] : Rehosting the partition dc=child2,dc=root,dc=com on the server server2.sub1.child1.root.com using the valid source server1.sub1.child1.root.com… New DSA Options: IS_GC DISABLE_NTDSCONN_XLATE Removal of partition dc=child2,dc=root,dc=com is in progress… Replication link from source:(null) to dest:server2.sub1.child1.root.com deleted. Full sync of partition dc=child2,dc=root,dc=com is in progress. Please be patient. This step may take many hours on a large partition. You can monitor the progress of the full sync using repadmin /showreps /v in another window. One-way replication from source:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx._msdcs. to dest:server2.sub1.child1.root.com established. New DSA Options: IS_GC ==== INBOUND NEIGHBORS ====================================== dc=child2,dc=root,dc=com SUB1-SITE\SERVER1 via RPC DSA object GUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Address: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx._msdcs. DSA invocationID: yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy SYNC_ON_STARTUP DO_SCHEDULED_SYNCS USNs: 123456789/OU, 123456789/PU Last attempt @ 2013-08-20 07:49:56 was successful.

IMPORTANT : If you have slow site links you will probably receive an AD Replication error 1818. To resolve it, please follow this procedure on the destination domain controller.

Script :

# VARIABLES
$seq_file=$args[0]
$global:scriptpath=$pwd.path.ToString() + "\"
$global:file_content = gc $seq_file

#############
# FUNCTIONS #
#############

# ENABLE THE GLOBAL CATALOG
function enable_gc([string]$servername) {
	$gcrehostlog=$scriptpath + $servername + ".log"

	Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Global catalog rehosting task on the server $servername [MANUAL CHECK OK]" -Encoding Unicode
	start-Sleep -Seconds 1

	Add-Content $gcrehostlog -Value "`n//////////////////////////`n/ Enable Global Catalog /`n//////////////////////////`n" -Encoding Unicode
	Start-Sleep -Seconds 1

	Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Enabling the Global Catalog on $servername..." -Encoding Unicode

	# CONNECT TO THE SERVER REMOTE REGISTRY
	try{
		$reg=[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $servername)
		Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Connect to the $servername remote registry [SUCCESS]" -Encoding Unicode
		Start-Sleep -Seconds 1
	}
	catch{
		Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Connect to the $servername remote registry [ERROR]`n---- EXIT -----`n" -Encoding Unicode
		continue
	}

	# DELETE THE REGISTRY KEY
	$regparentkey=$reg.Opensubkey("SOFTWARE\\Policies\\Microsoft",$true)
	$regparentkey.deletesubkeytree("Netlogon")

	# TEST IF THE NEW PARAM KEY HAS BEEN DELETED SUCCESSFULLY
	$ref_param=$reg.Opensubkey("SOFTWARE\\Policies\\Microsoft\\Netlogon\\Parameters",$true)
	if (!$ref_param) {
		Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Remove the registry key HKLM\SOFTWARE\Policies\Microsoft\Netlogon and the subkeys on $servername [SUCCESS]" -Encoding Unicode
		Start-Sleep -Seconds 1
	}
	else {
		Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Remove the registry key HKLM\SOFTWARE\Policies\Microsoft\Netlogon and the subkeys on $servername [ERROR]`n---- EXIT -----`n" -Encoding Unicode
		continue
	}
	Start-Sleep -Seconds 1
	Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : ----- END OF THE SCRIPT -----`n" -Encoding Unicode
}

# RESTART REHOST FUNCTION
function restart_rehost_task($file_content) {
	$servername = ""
	$job_list = get-job -State Completed
	if ($job_list){
		foreach ($job in $job_list){
			$servername = $job.name
			$gcrehostlog = $scriptpath + $servername + ".log"
			Get-Job -Name $servername | Receive-Job
			Get-Job -Name $servername | Remove-Job
			Write-Host "`n --> Do you want to restart the Global catalog rehosting on $servername ? `n`n`tPress r to restart `n`n`tor `n`n`tany other key to cancel"
			$continue = Read-Host
			If ($continue -eq "r"){
				$fc_tmp = $file_content | select-string $servername
				$servername_valid_source  = $fc_tmp.tostring().split(";")[1]
				$partition = $fc_tmp.tostring().split(";")[2]

				# RESTART THE REHOST
				Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Restarting the GC rehost operation of the partition $partition on the server $servername using the valid source $servername_valid_source..." -Encoding Unicode
				Start-Sleep -Seconds 1
				$RESTART_SCRIPT_BLOCK = {
				$servername=$args[0]
				$servername_valid_source=$args[1]
				$partition=$args[2]
				$gcrehostlog=$args[3]
				$repadmin_rehost_output = @(repadmin /rehost $servername "$partition" $servername_valid_source)
				$repadmin_rehost_output | Out-File -FilePath $gcrehostlog -Append -Encoding Unicode
				}
				Start-Job -name $servername -ScriptBlock $RESTART_SCRIPT_BLOCK -ArgumentList $servername,$servername_valid_source,$partition,$gcrehostlog
			}
			Else {
				enable_gc($servername)
			}
		}
		Write-Host "`n"
	}
}

#####################
#PREREQUESITE CHECKS#
#####################
foreach ($line in $file_content){
	$servername = ($line).split(";")[0]
	$servername_valid_source = ($line).split(";")[1]
	$partition = ($line).split(";")[2]
	$temp=""
	$script_path = $scriptpath

		$SCRIPT_BLOCK = {
		$servername=$args[0]
		$servername_valid_source=$args[1]
		$partition=$args[2]
		$temp=$args[3]
		$script_path=$args[4]
		$gcrehostlog=$script_path + $servername + ".log"

		Add-Content $gcrehostlog -Value "*********************" -Encoding Unicode
		Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Starting the Active Directory Global Catalog Rehosting script" -Encoding Unicode
		Add-Content $gcrehostlog -Value "//////////////////////`n/ Prerequesite tests /`n//////////////////////`n" -Encoding Unicode
		Start-Sleep -Seconds 1

		# PING FUNCTION
		function pingtest([string]$MachineName){
			$PingStatus = Gwmi Win32_PingStatus -Filter "Address = '$MachineName'" | Select-Object StatusCode
			If ($PingStatus.StatusCode -eq 0){
				Start-Sleep -Seconds 1
				Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Test network connectivity (ping) > $MachineName [SUCCESS]" -Encoding Unicode
			}
			Else {
				Start-Sleep -Seconds 1
				Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Test network connectivity (ping) > $MachineName [ERROR]`n---- EXIT -----`n" -Encoding Unicode
				exit
			}
		}

		# TEST PING
		pingtest($servername_valid_source)
		pingtest($servername)

		# TEST REMOTE REGISTRY
		try{
			$reg=[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $servername)
			Start-Sleep -Seconds 1
			Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Test remote registry > $servername [SUCCESS]" -Encoding Unicode
		}
			catch{
			Start-Sleep -Seconds 1
			Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Test remote registry > $servername [ERROR]`n---- EXIT -----`n" -Encoding Unicode
			exit
		}

		# TEST REMOTE EVENT LOG
		try{
			$temp = get-eventlog -ComputerName $servername -LogName 'Directory Service' -newest 1 -ErrorAction Stop
			Start-Sleep -Seconds 1
			Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Test remote eventlog (get the last event) > $servername [SUCCESS]" -Encoding Unicode
		}
		catch{
			Start-Sleep -Seconds 1
			Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Test remote eventlog (get the last event) > $servername [ERROR]`n---- EXIT -----`n" -Encoding Unicode
			exit
		}

		Start-Sleep -Seconds 1
		Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Test result [SUCCESS]" -Encoding Unicode

		############################
		#DISABLE THE GLOBAL CATALOG#
		############################
		Add-Content $gcrehostlog -Value "`n//////////////////////////`n/ Disable Global Catalog /`n//////////////////////////`n" -Encoding Unicode
		Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Disabling the Global Catalog on $servername" -Encoding Unicode
		Start-Sleep -Seconds 1
		try{
			$reg=[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $servername)
			Start-Sleep -Seconds 1
			Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Connect to the $servername remote registry [SUCCESS]" -Encoding Unicode
		}
		catch{
			Start-Sleep -Seconds 1
			Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Connect to the $servername remote registry [ERROR]`n---- EXIT -----`n" -Encoding Unicode
			exit
		}
		$ref=$reg.Opensubkey("SOFTWARE\\Policies\\Microsoft\\Netlogon\\Parameters",$true)
		if (!$ref) {
			Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : [WARNING] The registry key HKLM\SOFTWARE\Policies\Microsoft\Netlogon\Parameters does not exist on $servername" -Encoding Unicode
			Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : [WARNING] Creating the registry key HKLM\SOFTWARE\Policies\Microsoft\Netlogon\Parameters on $servername..." -Encoding Unicode
			$regparentkey=$reg.Opensubkey("SOFTWARE\\Policies\\Microsoft\\Netlogon",$true)
			$temp=$regparentkey.createSubKey("Parameters")

			# TEST IF THE NEW PARAM KEY HAS BEEN CREATED SUCCESSFULLY
			$ref_param=$reg.Opensubkey("SOFTWARE\\Policies\\Microsoft\\Netlogon\\Parameters",$true)
			if (!$ref_param) {
				Start-Sleep -Seconds 1
				Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Create the registry key HKLM\SOFTWARE\Policies\Microsoft\Netlogon\Parameters on $servername [ERROR]`n---- EXIT -----`n" -Encoding Unicode
				exit
			}
			else {
				Start-Sleep -Seconds 1
				Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Create the registry key HKLM\SOFTWARE\Policies\Microsoft\Netlogon\Parameters on $servername [SUCCESS]" -Encoding Unicode
			}
		}
		Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Creating the registry value DnsAvoidRegisterRecords on $servername..." -Encoding Unicode
		$regchildkey=$reg.Opensubkey("SOFTWARE\\Policies\\Microsoft\\Netlogon\\Parameters",$true)
		$regchildKey.SetValue("DnsAvoidRegisterRecords","Gc GcAtSite GcIpAddress GenericGc GenericGcAStSite",[Microsoft.Win32.RegistryValueKind]::String)

		# TEST IF THE NEW REGISTRY VALUE HAS BEEN CREATED SUCCESSFULLY
		$ref_value=$reg.Opensubkey("SOFTWARE\\Policies\\Microsoft\\Netlogon\\Parameters",$true)
		$reg_ref_value = $ref_value.GetValue("DnsAvoidRegisterRecords")
		if (!$reg_ref_value) {
			Start-Sleep -Seconds 1
			Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Create the registry value HKLM\SOFTWARE\Policies\Microsoft\Netlogon\Parameters\DnsAvoidRegisterRecords on $servername [ERROR]`n---- EXIT -----`n" -Encoding Unicode
			exit
		}
		else {
			Start-Sleep -Seconds 1
			Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Create the registry value HKLM\SOFTWARE\Policies\Microsoft\Netlogon\Parameters\DnsAvoidRegisterRecords on $servername [SUCCESS]" -Encoding Unicode
		}

		##################
		#WAIT REPLICATION#
		##################
		Add-Content $gcrehostlog -Value "`n[$((Get-Date).DateTime)] : Waiting for replication..." -Encoding Unicode
		Start-Sleep -Seconds 1800
		Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : End of replication timeout" -Encoding Unicode
		Start-Sleep -Seconds 1

		###########################
		#UNHOST/QUERY EVENT/REHOST#
		###########################
		Add-Content $gcrehostlog -Value "//////////////////////////////`n/ Unhost/Rehost AD partition /`n//////////////////////////////`n" -Encoding Unicode
		Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Unhosting the partition $partition on the server $servername..." -Encoding Unicode
		Start-Sleep -Seconds 1

		# UNHOST GC
		$repadmin_unhost_output = @(repadmin /unhost $servername "$partition")
		$repadmin_unhost_output | Out-File -FilePath $gcrehostlog -Append -Encoding Unicode

		$eventfound = "false"
		$inc=1
		while($eventfound -eq "false"){
			try{
				# UNHOST EVENT CHECK (EVENT 1660 / INSTANCEID 1073743484)
				$temp = get-eventlog -ComputerName $servername -LogName 'Directory Service' -InstanceId 1073743484 -newest 1 -ErrorAction Stop -after (get-date).addminutes(-120)
				$eventfound = "true"
				Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : GC unhosting check on $servername (event 1660) [SUCCESS]" -Encoding Unicode
				Start-Sleep -Seconds 1

				# REHOST GC
				Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Rehosting the partition $partition on the server $servername using the valid source $servername_valid_source..." -Encoding Unicode
				Start-Sleep -Seconds 1
				$repadmin_rehost_output = @(repadmin /rehost $servername "$partition" $servername_valid_source)
				$repadmin_rehost_output | Out-File -FilePath $gcrehostlog -Append -Encoding Unicode
			}
			catch{
				# WAIT WHILE EVENT 1660 NOT REGISTERED
				$string = $error[0].Exception.message.Tostring()
				if (($string -eq "No matches found") -and ($inc -le 6)){
					Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : GC unhosting check on $servername (event 1660). Next retry in 10 seconds... [WARNING]" -Encoding Unicode
					Start-Sleep -Seconds 10
					$inc++
				}
				else {
					Add-Content $gcrehostlog -Value "[$((Get-Date).DateTime)] : Cannot find event 1660. GC unhosting process fails on $servername [ERROR]`n---- EXIT -----`n" -Encoding Unicode
					exit
				}
			}
		}
		Write-Host "`n-----------------------------------`n"
	}
	Start-Job -name $servername -ScriptBlock $SCRIPT_BLOCK -ArgumentList $servername,$servername_valid_source,$partition,$temp,$script_path
}

# WAIT FOR IT ALL TO COMPLETE
While (Get-Job -State "Running")
{
	Write-Host "." -NoNewline
	Start-Sleep -Seconds 5
	if (Get-Job -State Completed) {
		restart_rehost_task($file_content)
	}
}

restart_rehost_task($file_content)

Write-Host "`n"

# GETTING THE INFORMATION BACK FROM THE JOBS
Get-Job | Receive-Job
Get-Job | Remove-Job
Rehost an Active Directory partition on one or more Global Catalog servers

Leave a Reply

Your email address will not be published. Required fields are marked *