Jorge's Quest For Knowledge!

All About Identity And Security On-Premises And In The Cloud – It's Just Like An Addiction, The More You Have, The More You Want To Have!

(2013-03-02) Testing Active Directory Replication Latency/Convergence Through PowerShell

Posted by Jorge on 2013-03-02


UPDATE: The latest version of the script can be found here.

About two years ago I wrote AD replication latency/convergence test script that can be found in this blog post.

The main differences of this new script compared to the old script are:

  • Only one mode of operation. Verbose information previously only available in verbose is now available in normal mode;
  • I changed the colors of the output. The dark blue text with a black background was terrible!;
  • When the script enumerates all directory servers the object replicates to, it will test the connection to each an every directory server. For DCs in the same AD domain that will be through port TCP:389 and for GCs in other AD domain that will be through port TCP:3268. If the directory server is reachable over the required port it will mark the DC/GC as such and the same applies if the directory server is not reachable because of routing issues or firewalls. The latter is important as the script then does not keep trying to reach those DCs/GCs while they are no reachable.

To use the script just copy the contents of the table below and put that in a script that could be called "Check-AD-Replication-Latency-Convergence.ps1", but this is of course not mandatory. In the explanations and examples I will use this name. Note that this script is not signed in any way by me, so you might need to adjust the PowerShell Execution Policy of the server where you want to execute this script. It is not possible to pass credentials through the script, so the account you are using to execute the script should have the correct permissions to create (and delete afterwards) the temporary object in the specific container in some AD domain (default used is ‘CN=Users,DC=<DOMAIN>,DC=<TLD>’) or you should execute the script from a PowerShell console window that was started with credentials that do have the correct permissions. In addition, the server where this script is executed must be able to access every DC (RWDC and RODC) in the same AD domain through LDAP (port 389) and all GCs in other AD domains through LDAP-GC (port 3268). So if your network is not fully routed or you have firewalls deployed between directory servers, and those directory servers are therefore not reachable, they will be marked as not reachable. This prevents the script from trying to contact the directory servers while it is not possible. Last but not least, I just tested this script in a single AD domain AD forest with a few DCs (RWDCs and RODCs) and in a multiple AD domain AD forest, also with a few DCs. So I do not know how well this is going to perform in an environment with a lot of DCs. If you are going to use this script, the only thing I would like to ask you is to give me feedback about it and your experiences. Thanks!

After the script is started, it will give you information about AD domains, DCs and GCs. At some point you need to specify in which AD domain, the temporary object will be created:

  • Either specify the FQDN of an existing AD domain in the AD forest
    OR
  • Just press ENTER to use the current AD domain the server is joined to and where the script is being executed on

REMARK: In either case the AD domain will be checked if it exists in the AD forest. If it exists, the script continues and if it does not exist, the script aborts.

After that, you need to specify on which RWDC from the previous AD domain, the temporary object will be created:

  • Either specify the term PDC to target the RWDC that hosts the PDC FSMO role
    OR
  • Either specify the FQDN of an existing RWDC in the AD domain
    OR
  • Just press ENTER to search for an RWDC in the AD domain
    • If an RWDC is located right away, then that RWDC will be used
    • If an RODC is located right away, then that cannot be used and the RWDC that hosts the PDC FSMO role will be targeted instead

REMARK: In all cases the DC specified will be checked if it exists in the AD domain and if it is an RWDC and not an RODC. If it exists and it is an RWDC, the script continues and if it does not exist or if it is an RODC, the script aborts.

The code of the PowerShell script that does this is included below. Screen dumps are also included after the code sample.

I have also linked a file with the contents below to this blog post. Get the most up-to-date version HERE.

!!! DISCLAIMER/REMARKS !!!:

  • The script is freeware, you are free to distribute it, but always refer to this website as the location where you got it
  • This script is furnished "as is". No warranty is expressed or implied!
  • Always test first in lab environment to see if it meets your needs!
  • Use this script at your own risk!
  • I do not warrant this script to be fit for any purpose, use or environment
  • I have tried to check everything that needed to be checked, but I do not guarantee the script does not have bugs.
  • I do not guarantee the script will not damage or destroy your system(s), environment or whatever.
  • I do not accept liability in any way if you screw up, use the script wrong or in any other way where damage is caused to your environment/systems!
  • If you do not accept these terms do not use the script and delete it immediately!

!!! DISCLAIMER/REMARKS !!!:

# Abstract: This PoSH Script Checks The AD Replication Latency/Convergence # Written By: Brandon Shell # Blog: http://bsonposh.com/ # Re-Written by: Jorge de Almeida Pinto [MVP-DS] # Blog: https://jorgequestforknowledge.wordpress.com/ ########## # Start... Clear-Host Write-Host "*******************************************************" -ForeGroundColor Yellow Write-Host "* *" -ForeGroundColor Yellow Write-Host "* --> Test AD Replication Latency/Convergence <-- *" -ForeGroundColor Yellow Write-Host "* *" -ForeGroundColor Yellow Write-Host "* Written By: Brandon Shell [MVP-PoSH] *" -ForeGroundColor Yellow Write-Host "* (http://bsonposh.com/) *" -ForeGroundColor Yellow Write-Host "* *" -ForeGroundColor Yellow Write-Host "* Re-Written By: Jorge de Almeida Pinto [MVP-DS] *" -ForeGroundColor Yellow Write-Host "* (https://jorgequestforknowledge.wordpress.com/) *" -ForeGroundColor Yellow Write-Host "* *" -ForeGroundColor Yellow Write-Host "*******************************************************" -ForeGroundColor Yellow ########## # Some Constants $continue = $true $cleanupTempObject = $true ########## # The Function To Test The Port Connection Function PortConnectionCheck($fqdnDC,$port,$timeOut) { $tcpPortSocket = $null $portConnect = $null $tcpPortWait = $null $tcpPortSocket = New-Object System.Net.Sockets.TcpClient $portConnect = $tcpPortSocket.BeginConnect($fqdnDC,$port,$null,$null) $tcpPortWait = $portConnect.AsyncWaitHandle.WaitOne($timeOut,$false) If(!$tcpPortWait) { $tcpPortSocket.Close() #Write-Host "Connection Timeout" Return "ERROR" } Else { #$error.Clear() $tcpPortSocket.EndConnect($portConnect) | Out-Null If (!$?) { #Write-Host $error[0] Return "ERROR" } Else { Return "SUCCESS" } $tcpPortSocket.Close() } } ########## # Get List Of AD Domains In The AD Forest, Create And Present In A Table $ThisADForest = [DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() $ListOfADDomainsInADForest = $ThisADForest.Domains $TableOfADDomainsInADForest = @() Write-Host "" Write-Host "-----------------------------------------------------------------------------" -ForeGroundColor Cyan Write-Host "LIST OF DOMAINs IN THE AD FOREST..." -ForeGroundColor Cyan Write-Host "" Write-Host "Forest Mode/Level...: "$ThisADForest.ForestMode ForEach ($Domain in $ListOfADDomainsInADForest) { $TableOfADDomainsInADForestObj = "" | Select Name,RootDomain,DomainMode,CurrentDomain $TableOfADDomainsInADForestObj.Name = $Domain.Name $TableOfADDomainsInADForestObj.RootDomain = "FALSE" If ($ThisADForest.RootDomain -like $Domain.Name) { $TableOfADDomainsInADForestObj.RootDomain = "TRUE" } $TableOfADDomainsInADForestObj.DomainMode = $Domain.DomainMode If ($Domain.Name -like $Env:USERDNSDOMAIN) { $TableOfADDomainsInADForestObj.CurrentDomain = "TRUE" } Else { $TableOfADDomainsInADForestObj.CurrentDomain = "FALSE" } $TableOfADDomainsInADForest += $TableOfADDomainsInADForestObj } $TableOfADDomainsInADForest | FT -AutoSize Write-Host " --> Found [$($ListOfADDomainsInADForest.count)] AD Domain(s) In AD Forest..." -ForeGroundColor Cyan Write-Host "" ########## # Get List Of GCs In AD Forest, Create And Present In A Table $ListOfGCsInADForest = $ThisADForest.GlobalCatalogs $TableOfGCsInADForest = @() Write-Host "-----------------------------------------------------------------------------" -ForeGroundColor Cyan Write-Host "LIST OF GCs IN THE AD FOREST '$ThisADForest'..." -ForeGroundColor Cyan ForEach ($GC in $ListOfGCsInADForest) { $TableOfGCsInADForestObj = "" | Select Name,Domain,SiteName $TableOfGCsInADForestObj.Name = $GC.Name $TableOfGCsInADForestObj.Domain = $GC.Domain $TableOfGCsInADForestObj.SiteName = $GC.SiteName $TableOfGCsInADForest += $TableOfGCsInADForestObj } $TableOfGCsInADForest | FT -AutoSize Write-Host " --> Found [$($ListOfGCsInADForest.count)] GC(s) In AD Forest..." -ForeGroundColor Cyan Write-Host "" ########## # Determine Which AD Domain To Use For The Temp Object. This Does Assume The Correct Permissions In That AD Domain To Create/Delete The Object! Write-Host "-----------------------------------------------------------------------------" -ForeGroundColor Cyan Write-Host "In Which AD Domain Should The Temp Object Be Created?" -ForeGroundColor Cyan Write-Host "" $ADDomainToWriteTo = Read-Host "Please Provide FQDN Or Just Press ENTER For Current AD Domain" # If No FQDN Of An AD Domain Is Specified, Then Use The Local AD Domain If ($ADDomainToWriteTo -eq "") { $ADDomainToWriteTo = $(Get-WmiObject -Class Win32_ComputerSystem).Domain } # If The FQDN Of An AD Domain Is Specified, Then Check If It Exists In This AD Forest If ($ADDomainToWriteTo -ne "") { $ADdomainvalidity = $False ForEach ($Domain in $ListOfADDomainsInADForest) { If ($Domain.Name -like $ADDomainToWriteTo) { $ADdomainvalidity = $True } } Write-Host "" Write-Host "Checking Existence Of The Specified AD Domain '$ADDomainToWriteTo' In The AD Forest..." -ForeGroundColor Yellow If ($ADdomainvalidity -eq $True) { Write-Host "" Write-Host "The Specified AD Domain '$ADDomainToWriteTo' Exists In The AD Forest!" -ForeGroundColor Green Write-Host "" Write-Host "Continuing Script..." -ForeGroundColor Green } If ($ADdomainvalidity -eq $False) { Write-Host "" Write-Host "The Specified AD Domain '$ADDomainToWriteTo' Does Not Exist In The AD Forest!" -ForeGroundColor Red Write-Host "" Write-Host "Please Re-Run The Script And Provide The FQDN Of An AD Domain That Does Exist In The AD Forest" -ForeGroundColor Red Write-Host "" Write-Host "Aborting Script..." -ForeGroundColor Red Write-Host "" Break } } Write-Host "" ########## # Get List Of DCs In AD Domain, Create And Present In A Table $contextADDomainToWriteTo = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$ADDomainToWriteTo) $ListOfDCsInADDomain = [System.DirectoryServices.ActiveDirectory.DomainController]::findall($contextADDomainToWriteTo) $TableOfDCsInADDomain = @() Write-Host "-----------------------------------------------------------------------------" -ForeGroundColor Cyan Write-Host "LIST OF DCs IN THE AD DOMAIN '$ADDomainToWriteTo'..." -ForeGroundColor Cyan ForEach ($DC in $ListOfDCsInADDomain) { $TableOfDCsInADDomainObj = "" | Select Name,Domain,GC,FSMO,SiteName,DStype $TableOfDCsInADDomainObj.Name = $DC.Name $TableOfDCsInADDomainObj.Domain = $ADDomainToWriteTo $TableOfDCsInADDomainObj.GC = "FALSE" ForEach ($GC in $ListOfGCsInADForest) { If ($DC.Name -like $GC.Name) { $TableOfDCsInADDomainObj.GC = "TRUE" } } If ($DC.Roles -ne $null) { If ($DC.Roles -Contains "PdcRole") { $pdcFQDN = $DC.Name $pdcDomain = $ADDomainToWriteTo $pdcSite = $DC.SiteName } ForEach ($FSMO In $DC.Roles) { If ($FSMO -eq "SchemaRole") {$FSMO = "SCH"} If ($FSMO -eq "NamingRole") {$FSMO = "DNM"} If ($FSMO -eq "PdcRole") {$FSMO = "PDC"} If ($FSMO -eq "RidRole") {$FSMO = "RID"} If ($FSMO -eq "InfrastructureRole") {$FSMO = "INF"} $TableOfDCsInADDomainObj.FSMO += $FSMO+"/" } $TableOfDCsInADDomainObj.FSMO = ($TableOfDCsInADDomainObj.FSMO).SubString(0,$TableOfDCsInADDomainObj.FSMO.Length-1) } Else { $TableOfDCsInADDomainObj.FSMO = "....." } $TableOfDCsInADDomainObj.SiteName = $DC.SiteName $ListOfRWDCsInADDomain = [System.DirectoryServices.ActiveDirectory.DomainController]::findall($contextADDomainToWriteTo) | ?{$_.OutboundConnections -ne $null} $DStype = "Read-Only" ForEach ($RWDC In $ListOfRWDCsInADDomain) { If ($RWDC.Name -like $DC.Name) { $DStype = "Read/Write" Break } } $TableOfDCsInADDomainObj.DStype = $DStype $TableOfDCsInADDomain += $TableOfDCsInADDomainObj } $TableOfDCsInADDomain | FT -AutoSize Write-Host " --> Found [$($ListOfDCsInADDomain.count)] DC(s) In AD Domain..." -ForeGroundColor Cyan Write-Host "" ########## # Specify A RWDC From The Selected AD Domain Write-Host "-----------------------------------------------------------------------------" -ForeGroundColor Cyan Write-Host "Which RWDC In The AD Domain '$ADDomainToWriteTo' Should Be Used To Create The Object?" -ForeGroundColor Cyan Write-Host "" Write-Host "Available Options Are:" -ForeGroundColor Yellow Write-Host "[*] Specify PDC To Use The DC With The PDC FSMO Role" -ForeGroundColor Yellow Write-Host "[*] Just Press Enter To Locate An RWDC" -ForeGroundColor Yellow Write-Host "[*] Specify The FQDN Of A Specific RWDC" -ForeGroundColor Yellow Write-Host "" $SourceRWDCInADDomain = Read-Host "Please Choose An Option" # If PDC Was Specified Find The RWDC With The PDC FSMO Role And Use That If ($SourceRWDCInADDomain -eq "PDC") { $SourceRWDCInADDomainFQDN = $pdcFQDN $SourceRWDCInADDomainDOMAIN = $pdcDomain $SourceRWDCInADDomainSITE = $pdcSite } # If Nothing Was Specified Automatically Locate An RWDC To Use If ($SourceRWDCInADDomain -eq "") { # Locate Just ONE DC (This Could Be An RWDC Or RODC) $SourceRWDCInADDomainObjectONE = [System.DirectoryServices.ActiveDirectory.DomainController]::findone($contextADDomainToWriteTo) # Locate All RWDCs In The AD Domain $SourceRWDCInADDomainObjectALL = [System.DirectoryServices.ActiveDirectory.DomainController]::findall($contextADDomainToWriteTo) | ?{$_.OutboundConnections -ne $null} $UseRWDC = $False # Check If The Single DC Found Is An RWDC Or Not By Checking If It Is In The List Of RWDCs ForEach ($RWDC In $SourceRWDCInADDomainObjectALL) { If ($RWDC.Name -like $SourceRWDCInADDomainObjectONE.Name) { $UseRWDC = $True } } # If The Single DC Found Is An RWDC, Then Use That One If ($UseRWDC -eq $True) { $SourceRWDCInADDomainFQDN = $SourceRWDCInADDomainObjectONE.Name $SourceRWDCInADDomainDOMAIN = $SourceRWDCInADDomainObjectONE.Domain $SourceRWDCInADDomainSITE = $SourceRWDCInADDomainObjectONE.SiteName } # If The Single DC Found Is An RODC, Then Find The RWDC With The PDC FSMO Role And Use That If ($UseRWDC -eq $False) { $SourceRWDCInADDomainFQDN = $pdcFQDN $SourceRWDCInADDomainDOMAIN = $pdcDomain $SourceRWDCInADDomainSITE = $pdcSite } } # If A Specific RWDC Was Specified Then Use That One If ($SourceRWDCInADDomain -ne "" -And $SourceRWDCInADDomain -ne "PDC") { $contextRWDCToWriteTo = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("DirectoryServer",$SourceRWDCInADDomain) $SourceRWDCInADDomainObject = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($contextRWDCToWriteTo) $SourceRWDCInADDomainFQDN = $SourceRWDCInADDomainObject.Name $SourceRWDCInADDomainDOMAIN = $SourceRWDCInADDomainObject.Domain $SourceRWDCInADDomainSITE = $SourceRWDCInADDomainObject.SiteName } # Check If The Selected DC Actually Exists In The AD Domain And Its Is An RWDC And NOT An RODC $RWDCvalidity = $False ForEach ($DC in $ListOfRWDCsInADDomain) { If ($DC.Name -like $SourceRWDCInADDomainFQDN) { $RWDCvalidity = $True } } Write-Host "" Write-Host "Checking Existence And Connectivity Of The Specified RWDC '$SourceRWDCInADDomainFQDN' In The AD Domain '$ADDomainToWriteTo'..." -ForeGroundColor Yellow If ($RWDCvalidity -eq $True) { Write-Host "" Write-Host "The Specified DC '$SourceRWDCInADDomainFQDN' Is An RWDC And It Exists In The AD Domain '$ADDomainToWriteTo'!" -ForeGroundColor Green Write-Host "" Write-Host "Continuing Script..." -ForeGroundColor Green $ldapPort = "389" $timeOut = "500" $ldapConnectionResult = $null $fqdnDC = $SourceRWDCInADDomainFQDN $ldapConnectionResult = PortConnectionCheck $fqdnDC $ldapPort $timeOut If ($ldapConnectionResult -eq "SUCCESS") { Write-Host "" Write-Host "The Specified RWDC '$SourceRWDCInADDomainFQDN' Is Reachable!" -ForeGroundColor Green Write-Host "" Write-Host "Continuing Script..." -ForeGroundColor Green Write-Host "" } If ($ldapConnectionResult -eq "ERROR") { Write-Host "" Write-Host "The Specified RWDC '$SourceRWDCInADDomainFQDN' Is NOT Reachable!" -ForeGroundColor Red Write-Host "" Write-Host "Please Re-Run The Script And Make Sure To Use An RWDC That Is Reachable!" -ForeGroundColor Red Write-Host "" Write-Host "Aborting Script..." -ForeGroundColor Red Write-Host "" Break } } If ($RWDCvalidity -eq $False) { Write-Host "" Write-Host "The Specified DC '$SourceRWDCInADDomainFQDN' Either Does NOT Exist In The AD Domain '$ADDomainToWriteTo' Or Is NOT And RWDC!" -ForeGroundColor Red Write-Host "" Write-Host "Please Re-Run The Script And Provide The FQDN Of An RWDC Within The AD Domain '$ADDomainToWriteTo' That Does Exist" -ForeGroundColor Red Write-Host "" Write-Host "Aborting Script..." -ForeGroundColor Red Write-Host "" Break } ########## # Get List Of DCs/GCs In AD Domain/Forest To Which The Temp Object Will Replicate, Create And Present In A Table Write-Host "-----------------------------------------------------------------------------" -ForeGroundColor Cyan Write-Host "LIST OF DIRECTORY SERVERS THE TEMP OBJECT REPLICATES TO..." -ForeGroundColor Cyan # Put The Selected RWDC Already In the Table [A] Of Directory Servers To Which The Temp Object Will Replicate $TableOfDSServersA = @() $TableOfDSServersAObj = "" | Select Name,Domain,GC,SiteName,Reachable $TableOfDSServersAObj.Name = ("$SourceRWDCInADDomainFQDN [SOURCE RWDC]").ToUpper() $TableOfDSServersAObj.Domain = $ADDomainToWriteTo $TableOfDSServersAObj.GC = "FALSE" ForEach ($GC in $ListOfGCsInADForest) { If ($SourceRWDCInADDomainFQDN -like $GC.Name) { $TableOfDSServersAObj.GC = "TRUE" } } $TableOfDSServersAObj.SiteName = $SourceRWDCInADDomainSITE $TableOfDSServersAObj.Reachable = "TRUE" $TableOfDSServersA += $TableOfDSServersAObj # Put The Selected RWDC Already In the Table [B] Of Directory Servers Where The Replication Starts $TableOfDSServersB = @() $TableOfDSServersBObj = "" | Select Name,Domain,GC,SiteName,Time $TableOfDSServersBObj.Name = ("$SourceRWDCInADDomainFQDN [SOURCE RWDC]").ToUpper() $TableOfDSServersBObj.Domain = $ADDomainToWriteTo $TableOfDSServersBObj.GC = "FALSE" ForEach ($GC in $ListOfGCsInADForest) { If ($SourceRWDCInADDomainFQDN -like $GC.Name) { $TableOfDSServersBObj.GC = "TRUE" } } $TableOfDSServersBObj.SiteName = $SourceRWDCInADDomainSITE $TableOfDSServersBObj.Time = 0.00 $TableOfDSServersB += $TableOfDSServersBObj # Add All Other Remaining DCs In The Targeted AD Domain To The List Of Directory Servers [A] ForEach ($DC In $ListOfDCsInADDomain) { If(!($DC.Name -like $SourceRWDCInADDomainFQDN)) { $TableOfDSServersAObj = "" | Select Name,Domain,GC,SiteName,Reachable $TableOfDSServersAObj.Name = $DC.Name $TableOfDSServersAObj.Domain = $DC.Domain $TableOfDSServersAObj.GC = "FALSE" ForEach ($GC in $ListOfGCsInADForest) { If ($DC.Name -like $GC.Name) { $TableOfDSServersAObj.GC = "TRUE" } } $TableOfDSServersAObj.SiteName = $DC.SiteName $ldapPort = "389" $timeOut = "500" $ldapConnectionResult = $null $fqdnDC = $DC.Name $ldapConnectionResult = PortConnectionCheck $fqdnDC $ldapPort $timeOut If ($ldapConnectionResult -eq "SUCCESS") { $TableOfDSServersAObj.Reachable = "TRUE" } If ($ldapConnectionResult -eq "ERROR") { $TableOfDSServersAObj.Reachable = "FALSE" } $TableOfDSServersA += $TableOfDSServersAObj } } # Add All Other Remaining GCs In The AD Forest To The List Of Directory Servers [A] ForEach ($GC In $ListOfGCsInADForest) { $ToBeAdded = $True ForEach ($DC In $ListOfDCsInADDomain) { If($DC.Name -like $GC.Name) { $ToBeAdded = $False } } If ($ToBeAdded) { $TableOfDSServersAObj = "" | Select Name,Domain,GC,SiteName,Reachable $TableOfDSServersAObj.Name = $GC.Name $TableOfDSServersAObj.Domain = $GC.Domain $TableOfDSServersAObj.GC = "TRUE" $TableOfDSServersAObj.SiteName = $GC.SiteName $gcPort = "3268" $timeOut = "500" $gcConnectionResult = $null $fqdnGC = $GC.Name $gcConnectionResult = PortConnectionCheck $fqdnGC $gcPort $timeOut If ($gcConnectionResult -eq "SUCCESS") { $TableOfDSServersAObj.Reachable = "TRUE" } If ($gcConnectionResult -eq "ERROR") { $TableOfDSServersAObj.Reachable = "FALSE" } $TableOfDSServersA += $TableOfDSServersAObj } } $TableOfDSServersA | FT -AutoSize Write-Host " --> Found [$($TableOfDSServersA.count)] Directory Server(s)..." -ForeGroundColor Cyan Write-Host "" ########## # Create The Temp Object On The Targeted RWDC Write-Host "-----------------------------------------------------------------------------" -ForeGroundColor Cyan Write-Host "CREATING TEMP CONTACT OBJECT IN AD...:" -ForeGroundColor Cyan Write-Host "" $domainNCDN = (([ADSI]"LDAP://$SourceRWDCInADDomainFQDN/rootDSE").defaultNamingContext) $container = "CN=Users," + $domainNCDN $tempObjectName = "adReplTempObject" + (Get-Date -f yyyyMMddHHmmss) Write-Host " --> On RWDC.............: $SourceRWDCInADDomainFQDN" -ForeGroundColor Yellow Write-Host " --> With Full Name......: $tempObjectName" -ForeGroundColor Yellow Write-Host " --> With Description....: ...!!!...TEMP OBJECT TO TEST AD REPLICATION LATENCY/CONVERGENCE...!!!..." -ForeGroundColor Yellow Write-Host " --> In AD Domain........: $ADDomainToWriteTo ($domainNCDN)" -ForeGroundColor Yellow Write-Host " --> In Container........: $container" -ForeGroundColor Yellow $tempObject = ([ADSI]"LDAP://$SourceRWDCInADDomainFQDN/$container").Create("contact","CN=$tempObjectName") $tempObject.Put("Description","...!!!...TEMP OBJECT TO TEST AD REPLICATION LATENCY/CONVERGENCE...!!!...") $tempObject.SetInfo() $tempObjectDN = $tempObject.distinguishedname Write-Host "`n Temp Contact Object [$tempObjectDN] Has Been Created On RWDC [$SourceRWDCInADDomainFQDN]! `n" -ForeGroundColor Yellow ########## # Go Through The Process Of Checking Each Directory Server To See If The Temp Object Already Has Replicated To It $startDateTime = Get-Date $i = 0 Write-Host " --> Found [$($TableOfDSServersA.count)] Directory Server(s)..." -ForeGroundColor Yellow Write-Host "" While($continue) { $i++ $oldpos = $host.UI.RawUI.CursorPosition Write-Host " ====================== CHECK $i ======================" -ForeGroundColor Yellow Write-Host "" Write-Host " REMARK: Each DC In The List Below Must Be Accessible Through LDAP Over TCP (389)" -ForeGroundColor Red Write-Host " REMARK: Each GC In The List Below Must Be Accessible Through LDAP-GC Over TCP (3268)" -ForeGroundColor Red Write-Host "" Start-Sleep 1 $replicated = $true # For Each Directory Server In The List/Table [A] Perform A Number Of Steps ForEach ($DSsrv in $TableOfDSServersA) { If ($DSsrv.Name -match $SourceRWDCInADDomainFQDN) { Write-Host " * Contacting DC In AD domain ...[$($DSsrv.Name.ToUpper())]..." -ForeGroundColor Yellow Write-Host " - DC Is Reachable..." -ForeGroundColor Green Write-Host " - Object [$tempObjectDN] Exists In The Database" (" "*3) -ForeGroundColor Green continue } # If The Directory Server Is A DC In The AD Domain, Then Connect Through LDAP (TCP:389) If ($DSsrv.Domain -like $ADDomainToWriteTo) { Write-Host "" Write-Host " * Contacting DC In AD domain ...[$($DSsrv.Name.ToUpper())]..." -ForeGroundColor Yellow $connectionResult = $null If ($DSsrv.Reachable -eq "TRUE") { Write-Host " - DC Is Reachable..." -ForeGroundColor Green $objectPath = [ADSI]"LDAP://$($DSsrv.Name)/$tempObjectDN" $connectionResult = "SUCCESS" } If ($DSsrv.Reachable -eq "FALSE") { Write-Host " - DC Is NOT Reachable..." -ForeGroundColor Red $connectionResult = "FAILURE" } } # If The Directory Server Is A GC In Another AD Domain, Then Connect Through LDAP-GC (TCP:3268) If (!($DSsrv.Domain -like $ADDomainToWriteTo)) { Write-Host "" Write-Host " * Contacting GC In Other AD Domain ...[$($DSsrv.Name.ToUpper())]..." -ForeGroundColor Yellow $connectionResult = $null If ($DSsrv.Reachable -eq "TRUE") { Write-Host " - DC Is Reachable..." -ForeGroundColor Green $objectPath = [ADSI]"GC://$($DSsrv.Name)/$tempObjectDN" $connectionResult = "SUCCESS" } If ($DSsrv.Reachable -eq "FALSE") { Write-Host " - DC Is NOT Reachable..." -ForeGroundColor Red $connectionResult = "FAILURE" } } # If The Connection To The DC Is Successfull If ($connectionResult -eq "SUCCESS") { If ($objectPath.name) { # If The Temp Object Already Exists Write-Host " - Object [$tempObjectDN] Now Does Exist In The Database" (" "*3) -ForeGroundColor Green If (!($TableOfDSServersB | ?{$_.Name -match $DSsrv.Name})) { $TableOfDSServersBobj = "" | Select Name,Domain,GC,SiteName,Time $TableOfDSServersBobj.Name = $DSsrv.Name $TableOfDSServersBObj.Domain = $DSsrv.Domain $TableOfDSServersBObj.GC = $DSsrv.GC $TableOfDSServersBObj.SiteName = $DSsrv.SiteName $TableOfDSServersBObj.Time = ("{0:n2}" -f ((Get-Date)-$startDateTime).TotalSeconds) $TableOfDSServersB += $TableOfDSServersBObj } } Else { # If The Temp Object Does Not Yet Exist Write-Host " - Object [$tempObjectDN] Does NOT Exist Yet In The Database" -ForeGroundColor Red $replicated = $false } } # If The Connection To The DC Is Unsuccessfull If ($connectionResult -eq "FAILURE") { Write-Host " - Unable To Connect To DC/GC And Check For The Temp Object..." -ForeGroundColor Red If (!($TableOfDSServersB | ?{$_.Name -match $DSsrv.Name})) { $TableOfDSServersBobj = "" | Select Name,Domain,GC,SiteName,Time $TableOfDSServersBobj.Name = $DSsrv.Name $TableOfDSServersBObj.Domain = $DSsrv.Domain $TableOfDSServersBObj.GC = $DSsrv.GC $TableOfDSServersBObj.SiteName = $DSsrv.SiteName $TableOfDSServersBObj.Time = "Fail" $TableOfDSServersB += $TableOfDSServersBObj } } } If ($replicated) { $continue = $false } Else { $host.UI.RawUI.CursorPosition = $oldpos } } ########## # Show The Start Time, The End Time And The Duration Of The Replication $endDateTime = Get-Date $duration = "{0:n2}" -f ($endDateTime.Subtract($startDateTime).TotalSeconds) Write-Host "`n Start Time......: $(Get-Date $startDateTime -format "yyyy-MM-dd HH:mm:ss")" -ForeGroundColor Yellow Write-Host " End Time........: $(Get-Date $endDateTime -format "yyyy-MM-dd HH:mm:ss")" -ForeGroundColor Yellow Write-Host " Duration........: $duration Seconds" -ForeGroundColor Yellow ########## # Delete The Temp Object On The RWDC If ($cleanupTempObject) { Write-Host "" Write-Host " Deleting Temp Contact Object... `n" -ForeGroundColor Yellow ([ADSI]"LDAP://$SourceRWDCInADDomainFQDN/$container").Delete("contact","CN=$tempObjectName") Write-Host " Temp Contact Object [$tempObjectDN] Has Been Deleted On The Target RWDC! `n" -ForeGroundColor Yellow } ########## # Output The Table [B] Containing The Information Of Each Directory Server And How Long It Took To Reach That Directory Server After The Creation On The Source RWDC $TableOfDSServersB | Sort-Object Time | FT -AutoSize

SINGLE AD DOMAIN AD FOREST WHERE ALL DCs ARE REACHABLE:

In this case ALL directory servers are reachable without any problems!

I just pressed ENTER to use the current AD domain.

image

Figure 1: Using The Current AD Domain As The Target AD Domain

I specified the term PDC to target the RWDC that hosts the PDC FSMO role

image

Figure 2: Using The RWDC That Hosts The PDC FSMO Role As The Target RWDC To Write The Temp Object To

The temporary object has been created and the script is now checking it on all directory servers (DCs in AD domain and GCs in other AD domains in AD forest if any)

If a DC is marked green, then the temporary AD object has replicated to it and the script found the temporary AD object.

If a DC is marked red, then the temporary AD object has not yet replicated to it OR the DC/GC has been marked as unreachable.

image

Figure 3: Creating The Temp Object In AD And Enumerating Through Each Directory Server To Determine The Existence Of The Temp Object

When finished it shows the start time, the end time and the duration of time before the temporary AD object reached all directory servers. It also removed the temporary AD object again to keep stuff clean.

This is a W2K12 AD forest/domain that has change notification enabled on all AD site links. So after a change has occurred on a certain RWDC it will notify the very first replication partner after 15 seconds (=default) and each other replication partner with pauses of 3 seconds (=default).

SO looking at the example above the following happened:

  • RWDC ‘R2FSRWDC1.ADDMZ.LAN’ is where the creation of the temporary AD object originated;
  • After about 15 seconds, RWDC ‘R2FSRWDC1.ADDMZ.LAN’ notified RWDC ‘R2FSRWDC2.ADDMZ.LAN’ and RWDC ‘R2FSRWDC2.ADDMZ.LAN’ inbound replicated the temporary AD object from RWDC ‘R2FSRWDC1.ADDMZ.LAN’;
  • About 3 seconds later, RWDC ‘R2FSRWDC1.ADDMZ.LAN’ notified RODC ‘R2FSRODC5.ADDMZ.LAN’ and RODC ‘R2FSRODC5.ADDMZ.LAN’ inbound replicated the temporary AD object from RWDC ‘R2FSRWDC1.ADDMZ.LAN’.

image

Figure 4: The End Result Of The AD Replication Latency/Convergence Test

SINGLE AD DOMAIN AD FOREST WHERE NOT ALL DCs ARE REACHABLE:

In this case NOT ALL directory servers are reachable. The RODC is separated by a firewall from the RWDCs. The RODC can communicate with both RWDCs, The RWDCs however cannot with the RODC, except for the RPC Endpoint Mapper port and the statically configured AD replication port for change notifications. For DCs that are not reachable, the domain name and the site name of the DC might not always be specified n the table.

I just pressed ENTER to use the current AD domain.

image

Figure 5: Using The Current AD Domain As The Target AD Domain

I chose for the script to find an RWDC as the target RWDC to write the temp object to. NOTE the reachability of the RODC!

image

Figure 6: Letting The Script To Locate An RWDC As The Target RWDC To Write The Temp Object To

The temporary object has been created and the script is now checking it on all directory servers (DCs in AD domain and GCs in other AD domains in AD forest if any)

If a DC is marked green, then the temporary AD object has replicated to it and the script found the temporary AD object.

If a DC is marked red, then the temporary AD object has not yet replicated to it OR the DC/GC has been marked as unreachable.

image

Figure 7: Creating The Temp Object In AD And Enumerating Through Each Directory Server To Determine The Existence Of The Temp Object

When finished it shows the start time, the end time and the duration of time before the temporary AD object reached all directory servers. It also removed the temporary AD object again to keep stuff clean.

This is a W2K12 AD forest/domain that has change notification enabled on all AD site links. So after a change has occurred on a certain RWDC it will notify the very first replication partner after 15 seconds (=default) and each other replication partner with pauses of 3 seconds (=default).

SO looking at the example above the following happened:

  • RWDC ‘R2FSRWDC1.ADDMZ.LAN’ is where the creation of the temporary AD object originated;
  • After about 15 seconds, RWDC ‘R2FSRWDC1.ADDMZ.LAN’ notified RWDC ‘R2FSRWDC2.ADDMZ.LAN’ and RWDC ‘R2FSRWDC2.ADDMZ.LAN’ inbound replicated the temporary AD object from RWDC ‘R2FSRWDC1.ADDMZ.LAN’;
  • MOST LIKELY, about 3 seconds later, RWDC ‘R2FSRWDC1.ADDMZ.LAN’ notified RODC ‘R2FSRODC5.ADDMZ.LAN’ and RODC ‘R2FSRODC5.ADDMZ.LAN’ inbound replicated the temporary AD object from RWDC ‘R2FSRWDC1.ADDMZ.LAN’. However, the script is not able to check that because the firewall does not allow communications to the RODC! Because the RODC is not reachable, it is marked as “Fail” in the timing.

image

Figure 8: The End Result Of The AD Replication Latency/Convergence Test

MULTIPLE AD DOMAIN AD FOREST WHERE ALL DCs ARE REACHABLE:

In this case ALL directory servers are reachable without any problems!

I just pressed ENTER to use the current AD domain.

image

Figure 9: Using The Current AD Domain As The Target AD Domain

I chose for the script to find an RWDC as the target RWDC to write the temp object to.

image

Figure 10: Letting The Script To Locate An RWDC As The Target RWDC To Write The Temp Object To

The temporary object has been created and the script is now checking it on all directory servers (DCs in AD domain and GCs in other AD domains in AD forest if any)

If a DC is marked green, then the temporary AD object has replicated to it and the script found the temporary AD object.

If a DC is marked red, then the temporary AD object has not yet replicated to it OR the DC/GC has been marked as unreachable.

image

Figure 11: Creating The Temp Object In AD And Enumerating Through Each Directory Server To Determine The Existence Of The Temp Object

When finished it shows the start time, the end time and the duration of time before the temporary AD object reached all directory servers. It also removed the temporary AD object again to keep stuff clean.

This is a W2K12 AD forest/domain that has change notification enabled on all AD site links. So after a change has occurred on a certain RWDC it will notify the very first replication partner after 15 seconds (=default) and each other replication partner with pauses of 3 seconds (=default).

SO looking at the example above the following happened:

  • RWDC ‘R1FSRWDC1.ADCORP.LAB’ is where the creation of the temporary AD object originated;
  • After about 15 seconds, RWDC ‘R1FSRWDC1.ADCORP.LAB’ notified RWDC ‘R1FSRWDC2.ADCORP.LAB’ and RWDC ‘R1FSRWDC2.ADCORP.LAB’ inbound replicated the temporary AD object from RWDC ‘R1FSRWDC1.ADCORP.LAB’;
  • About 3 seconds later, RWDC ‘R1FSRWDC1.ADCORP.LAB’ notified RWDC ‘C1FSRWDC2.CHILD.ADCORP.LAB’ and RWDC “C1FSRWDC2.CHILD.ADCORP.LAB’ inbound replicated the temporary AD object from RWDC ‘R1FSRWDC1.ADCORP.LAB’;
  • After about 15 seconds of RWDC ‘C1FSRWDC2.CHILD.ADCORP.LAB’ receiving the object, it notified RWDC ‘C1FSRWDC1.CHILD.ADCORP.LAB’ and RWDC ‘C1FSRWDC1.CHILD.ADCORP.LAB’ inbound replicated the temporary AD object from RWDC ‘C1FSRWDC2.CHILD.ADCORP.LAB’;

image

Figure 12: The End Result Of The AD Replication Latency/Convergence Test

For more information on AD replication see:

As check out this blog post to find the script version to check latency/convergence of the SYSVOL

Cheers,

Jorge

———————————————————————————————

* This posting is provided "AS IS" with no warranties and confers no rights!

* Always evaluate/test yourself before using/implementing this!

* DISCLAIMER:

https://jorgequestforknowledge.wordpress.com/disclaimer/

———————————————————————————————

############### Jorge’s Quest For Knowledge #############

#########

http://JorgeQuestForKnowledge.wordpress.com/ ########

———————————————————————————————

12 Responses to “(2013-03-02) Testing Active Directory Replication Latency/Convergence Through PowerShell”

  1. […] UPDATE: the information below still applies. However, the script has been updated and is available through this post. […]

    Like

  2. […] « (2013-03-02) Testing Active Directory Replication Latency/Convergence Through PowerShell […]

    Like

  3. […] (2013-03-02) Testing Active Directory Replication Latency/Convergence Through PowerShell […]

    Like

  4. Vasilis said

    Really good job! Thank you for sharing! It works like a charm, does exactly what it says.

    Like

  5. Curtis Petree said

    I’ve been searching for quite some time for a powershell script that did this. I’ve used it on 2003 (x32) on through 2008 R2. It has worked perfectly. Wonderful job to both you and the original author.

    Like

  6. Curtis Petree said

    In a lab environment, we’re seeing all of the DCS enumerating as Read Only DCs, while the instanceType of all of them is still 0x4 (Write).

    Has anyone else seen this type of behavior, or possibly a way to correct it?

    Like

  7. […] Detailed information about the script can be read here. […]

    Like

  8. […] Detailed information about the script can be read here. […]

    Like

  9. Jon Sykes said

    Great script. How do you stop the test but still display the results in case there’s a DC that is corrupted/can’t replicate to?

    Like

    • Jorge said

      can you send me screendumps of what you are seeing/experiencing to the mail address available through the contact page of my blog?
      regards,
      Jorge

      Like

  10. fiddley said

    Thank you! Love this script, it’s a beast and a work of art too!

    I’m am going to try and modify it to add these features. 1) Add an option to place an object on all domain controllers simultaneously and watch all replications in all directions. 2) add a multiple run option so you can schedule the test to run say 5 times and get aggregate times to smooth out any delays due to transient issues. 3) Trigger an urgent replication event (e.g. account lockout) to see how long that takes & 4) Add an index to the lists where you can make a selection so you can just type a number instead of typing/copy pasting the FQDN or whatever.

    If I manage to do it without breaking your good work, I’ll post it to you.

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.