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!

(2014-02-17) Testing SYSVOL Replication Latency/Convergence Through PowerShell (Update 3)

Posted by Jorge on 2014-02-17


I have updated the PowerShell script for testing/determining SYSVOL Replication Latency/Convergence.

Detailed information about the script can be read here.

This script requires PowerShell v2.0 or higher

The script supports: W2K3(R2) DCs, W2K8(R2) DCs and W2K12(R2) DCs

The script supports: NTFRS or DFS-R Replication for the SYSVOL

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

!!! 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 !!!:

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

# Abstract: This PoSH Script Checks The SYSVOL Replication Latency/Convergence # Written By: Jorge de Almeida Pinto [MVP-DS] # Blog: https://jorgequestforknowledge.wordpress.com/ # # 2013-03-02: (v0.1): Initial version of the script # 2014-02-01: (v0.2): Updated to also work on W2K3, added STOP option, added few extra columns to output extra info of DCs, better detection of unavailable DCs, and screen adjustment section added # 2014-02-09: (v0.3): Solved a bug with regards to the detection/location of RWDCs and RODCs # 2014-02-11: (v0.4): Added additional logic to determine if a DC is either an RWDC or RODC when it fails using the first logic and changed the layout a little bit # # REQUIRES: PowerShell v2.0 or higher # REQUIRES: At least 2 RWDCs # SUPPORTS: W2K3(R2), W2K8(R2), W2K12(R2) DCs and most likely higher # SUPPORTS: NTFRS or DFS-R Replication for the SYSVOL # # -----> !!! DISCLAIMER/REMARKS !!! <------ # * The script is freeware, you are free to distribute it, but always refer to this website (https://jorgequestforknowledge.wordpress.com/) 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 any 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 !!! <------ # Clear The Screen Clear-Host # Configure The Appropriate Screen And Buffer Size To Make Sure Everything Fits Nicely $uiConfig = (Get-Host).UI.RawUI $uiConfig.WindowTitle = "+++ CHECKING SYSVOL REPLICATION LATENCY/CONVERGENCE +++" $uiConfig.ForegroundColor = "Yellow" $uiConfigBufferSize = $uiConfig.BufferSize $uiConfigBufferSize.Width = 150 $uiConfigBufferSize.Height = 9999 $uiConfigScreenSizeMax = $uiConfig.MaxPhysicalWindowSize $uiConfigScreenSizeMaxWidth = $uiConfigScreenSizeMax.Width $uiConfigScreenSizeMaxHeight = $uiConfigScreenSizeMax.Height $uiConfigScreenSize = $uiConfig.WindowSize If ($uiConfigScreenSizeMaxWidth -lt 150) { $uiConfigScreenSize.Width = $uiConfigScreenSizeMaxWidth } Else { $uiConfigScreenSize.Width = 150 } If ($uiConfigScreenSizeMaxHeight -lt 75) { $uiConfigScreenSize.Height = $uiConfigScreenSizeMaxHeight - 5 } Else { $uiConfigScreenSize.Height = 75 } $uiConfig.BufferSize = $uiConfigBufferSize $uiConfig.WindowSize = $uiConfigScreenSize # Start... Write-Host " *******************************************************" -ForeGroundColor Magenta Write-Host " * *" -ForeGroundColor Magenta Write-Host " * --> Test SYSVOL Replication Latency/Convergence <-- *" -ForeGroundColor Magenta Write-Host " * *" -ForeGroundColor Magenta Write-Host " * Written By: Jorge de Almeida Pinto [MVP-DS] *" -ForeGroundColor Magenta Write-Host " * (https://jorgequestforknowledge.wordpress.com/) *" -ForeGroundColor Magenta Write-Host " * *" -ForeGroundColor Magenta Write-Host " *******************************************************" -ForeGroundColor Magenta ########## # 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() $ErrorActionPreference = "SilentlyContinue" $tcpPortSocket.EndConnect($portConnect) | Out-Null If (!$?) { #Write-Host $error[0] Return "ERROR" } Else { Return "SUCCESS" } $tcpPortSocket.Close() $ErrorActionPreference = "Continue" } } ########## # Get The FQDN Of The Local AD Domain From The Server This Script Is Executed On $ADDomainToWriteTo = $(Get-WmiObject -Class Win32_ComputerSystem).Domain ########## # Get List Of Directory Servers In AD Forest $ThisADForest = [DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() $configNCDN = $ThisADForest.schema.Name.Substring(("CN=Schema,").Length) $searchRootNTDSdsa = [ADSI]"LDAP://CN=Sites,$configNCDN" $searcherNTDSdsaRW = New-Object System.DirectoryServices.DirectorySearcher($searchRootNTDSdsa) $searcherNTDSdsaRO = New-Object System.DirectoryServices.DirectorySearcher($searchRootNTDSdsa) $searcherNTDSdsaRW.Filter = "(objectCategory=NTDSDSA)" $searcherNTDSdsaRO.Filter = "(objectCategory=NTDSDSARO)" $objNTDSdsaRW = $searcherNTDSdsaRW.FindAll() $objNTDSdsaRO = $searcherNTDSdsaRO.FindAll() $TableOfRWDCsInADForest = @() $objNTDSdsaRW | %{ $ntdsDN = $_.Properties.distinguishedname $nbtRWDCName = $ntdsDN[0].Substring(("CN=NTDS Settings,CN=").Length) $nbtRWDCName = $nbtRWDCName.Substring(0,$nbtRWDCName.IndexOf(",")) $nbtRWDCSite = $ntdsDN[0].Substring(("CN=NTDS Settings,CN=$nbtRWDCName,CN=Servers,CN=").Length) $nbtRWDCSite = $nbtRWDCSite.Substring(0,$nbtRWDCSite.IndexOf(",")) $TableOfRWDCsInADForestObj = "" | Select "DS Name","Site Name" $TableOfRWDCsInADForestObj."DS Name" = $nbtRWDCName $TableOfRWDCsInADForestObj."Site Name" = $nbtRWDCSite $TableOfRWDCsInADForest += $TableOfRWDCsInADForestObj } $TableOfRODCsInADForest = @() $objNTDSdsaRO | %{ $ntdsDN = $_.Properties.distinguishedname $nbtRODCName = $ntdsDN[0].Substring(("CN=NTDS Settings,CN=").Length) $nbtRODCName = $nbtRODCName.Substring(0,$nbtRODCName.IndexOf(",")) $nbtRODCSite = $ntdsDN[0].Substring(("CN=NTDS Settings,CN=$nbtRODCName,CN=Servers,CN=").Length) $nbtRODCSite = $nbtRODCSite.Substring(0,$nbtRODCSite.IndexOf(",")) $TableOfRODCsInADForestObj = "" | Select "DS Name","Site Name" $TableOfRODCsInADForestObj."DS Name" = $nbtRODCName $TableOfRODCsInADForestObj."Site Name" = $nbtRODCSite $TableOfRODCsInADForest += $TableOfRODCsInADForestObj } $TableOfDCsInADForest = $TableOfRWDCsInADForest + $TableOfRODCsInADForest ########## # 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) $ListOfRWDCsInADDomain = $ListOfDCsInADDomain | ?{$_.InboundConnections -ne $null -and !($_.InboundConnections -match "RODC Connection")} $ListOfRODCsInADDomain = $ListOfDCsInADDomain | ?{$_.InboundConnections -match "RODC Connection"} $TableOfDCsInADDomain = @() Write-Host "" Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------" -ForeGroundColor Cyan Write-Host "LIST OF DCs IN THE AD DOMAIN '$ADDomainToWriteTo'..." -ForeGroundColor Cyan ForEach ($DC in $ListOfDCsInADDomain) { $TableOfDCsInADDomainObj = "" | Select Name,PDC,"Site Name","DS Type","IP Address","OS Version" $TableOfDCsInADDomainObj.Name = $DC.Name $TableOfDCsInADDomainObj.PDC = "FALSE" If ($DC.Roles -ne $null -And $DC.Roles -Contains "PdcRole") { $TableOfDCsInADDomainObj.PDC = "TRUE" $pdcFQDN = $DC.Name $pdcSite = $DC.SiteName } If ( $DC.SiteName -ne $null -And $DC.SiteName -ne "") { $TableOfDCsInADDomainObj."Site Name" = $DC.SiteName } Else { If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($DC.Name).Substring(0,$($DC.Name).IndexOf(".")))} | Measure-Object).Count -eq 1) { $TableOfDCsInADDomainObj."Site Name" = ($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($DC.Name).Substring(0,$($DC.Name).IndexOf(".")))})."Site Name" } If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($DC.Name).Substring(0,$($DC.Name).IndexOf(".")))} | Measure-Object).Count -eq 0) { $TableOfDCsInADDomainObj."Site Name" = "<Fail>" } If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($DC.Name).Substring(0,$($DC.Name).IndexOf(".")))} | Measure-Object).Count -gt 1) { $TableOfDCsInADDomainObj."Site Name" = "<Fail>" } } $DStype = $null If ($DStype -eq $null) { ForEach ($RWDC In $ListOfRWDCsInADDomain) { If ($RWDC.Name -like $DC.Name) { $DStype = "Read/Write" BREAK } } } If ($DStype -eq $null) { ForEach ($RODC In $ListOfRODCsInADDomain) { If ($RODC.Name -like $DC.Name) { $DStype = "Read-Only" BREAK } } } If ($DStype -eq $null) { $DStype = "<Unknown>" If (($TableOfRWDCsInADForest | ?{$_."DS Name" -eq $($($DC.Name).Substring(0,$($DC.Name).IndexOf(".")))} | Measure-Object).Count -eq 1) { $DStype = "Read/Write" } If (($TableOfRODCsInADForest | ?{$_."DS Name" -eq $($($DC.Name).Substring(0,$($DC.Name).IndexOf(".")))} | Measure-Object).Count -eq 1) { $DStype = "Read-Only" } } $TableOfDCsInADDomainObj."DS Type" = $DStype If ($DC.IPAddress -ne $null -And $DC.IPAddress -ne "") { $TableOfDCsInADDomainObj."IP Address" = $DC.IPAddress } Else { $TableOfDCsInADDomainObj."IP Address" = "<Fail>" } If ($DC.OSVersion -ne $null -And $DC.OSVersion -ne "") { $TableOfDCsInADDomainObj."OS Version" = $DC.OSVersion } Else { $TableOfDCsInADDomainObj."OS Version" = "<Fail>" } $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 "[*] Specify 'STOP' To End The Script" -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 $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 = $ListOfRWDCsInADDomain $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 $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 $SourceRWDCInADDomainSITE = $pdcSite } } # If A Specific RWDC Was Specified Then Use That One If ($SourceRWDCInADDomain -ne "" -And $SourceRWDCInADDomain -ne "PDC" -And $SourceRWDCInADDomain -ne "STOP") { $contextRWDCToWriteTo = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("DirectoryServer",$SourceRWDCInADDomain) $SourceRWDCInADDomainObject = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($contextRWDCToWriteTo) $SourceRWDCInADDomainFQDN = $SourceRWDCInADDomainObject.Name $SourceRWDCInADDomainSITE = $SourceRWDCInADDomainObject.SiteName } # If STOP Was Specified Then End The Script If ($SourceRWDCInADDomain -eq "STOP") { Write-Host "" Write-Host "'STOP' Was Specified..." -ForeGroundColor Red Write-Host "Aborting Script..." -ForeGroundColor Red Write-Host "" EXIT } # 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 $smbPort = "445" $timeOut = "500" $smbConnectionResult = $null $fqdnDC = $SourceRWDCInADDomainFQDN $smbConnectionResult = PortConnectionCheck $fqdnDC $smbPort $timeOut If ($smbConnectionResult -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 ($smbConnectionResult -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 } ########## # Determine SYSVOL Replication Mechanism And SYSVOL/NetLogon Location On Sourcing RWDC Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------" -ForeGroundColor Cyan Write-Host "SYSVOL REPLICATION MECHANISM..." -ForeGroundColor Cyan Write-Host "" # Get The Default Naming Contexr $defaultNamingContext = (([ADSI]"LDAP://$SourceRWDCInADDomainFQDN/rootDSE").defaultNamingContext) # Find The Computer Account Of The Sourcing RWDC $Searcher = New-Object DirectoryServices.DirectorySearcher $Searcher.Filter = "(&(objectClass=computer)(dNSHostName=$SourceRWDCInADDomainFQDN))" $Searcher.SearchRoot = "LDAP://" + $SourceRWDCInADDomainFQDN + "/OU=Domain Controllers," + $defaultNamingContext # The following appears NOT to work on W2K3, but it does upper-level OSes # $dcObjectPath = $Searcher.FindAll().Path # The following appears to work on all OSes $dcObjectPath = $Searcher.FindAll() | %{$_.Path} # Check If An NTFRS Subscriber Object Exists To Determine If NTFRS Is Being Used Instead Of DFS-R $SearcherNTFRS = New-Object DirectoryServices.DirectorySearcher $SearcherNTFRS.Filter = "(&(objectClass=nTFRSSubscriber)(name=Domain System Volume (SYSVOL share)))" $SearcherNTFRS.SearchRoot = $dcObjectPath $ntfrsSubscriptionObject = $SearcherNTFRS.FindAll() If ($ntfrsSubscriptionObject -ne $null) { Write-Host "SYSVOL Replication Mechanism Being Used...: NTFRS" # Get The Local Root Path For The SYSVOL # The following appears NOT to work on W2K3, but it does upper-level OSes # $sysvolRootPathOnSourcingRWDC = $ntfrsSubscriptionObject.Properties.frsrootpath # The following appears to work on all OSes $sysvolRootPathOnSourcingRWDC = $ntfrsSubscriptionObject | %{$_.Properties.frsrootpath} } # Check If An DFS-R Subscriber Object Exists To Determine If DFS-R Is Being Used Instead Of NTFRS $SearcherDFSR = New-Object DirectoryServices.DirectorySearcher $SearcherDFSR.Filter = "(&(objectClass=msDFSR-Subscription)(name=SYSVOL Subscription))" $SearcherDFSR.SearchRoot = $dcObjectPath $dfsrSubscriptionObject = $SearcherDFSR.FindAll() If ($dfsrSubscriptionObject -ne $null) { Write-Host "SYSVOL Replication Mechanism Being Used...: DFS-R" -ForeGroundColor Yellow Write-Host "" # Get The Local Root Path For The SYSVOL # The following appears NOT to work on W2K3, but it does not upper-level OSes. NOT really needed, because W2K3 does not support DFS-R for SYSVOL! # $sysvolRootPathOnSourcingRWDC = $dfsrSubscriptionObject.Properties."msdfsr-rootpath" # The following appears to work on all OSes $sysvolRootPathOnSourcingRWDC = $dfsrSubscriptionObject | %{$_.Properties."msdfsr-rootpath"} } # Determine The UNC Of The Folder To Write The Temp File To $scriptsUNCPathOnSourcingRWDC = "\\" + $SourceRWDCInADDomainFQDN + "\" + $($sysvolRootPathOnSourcingRWDC.Replace(":","$")) + "\Scripts" ########## # Get List Of DCs In AD Domain 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,"Site Name",Reachable $TableOfDSServersAObj.Name = ("$SourceRWDCInADDomainFQDN [SOURCE RWDC]").ToUpper() $TableOfDSServersAObj."Site Name" = $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,"Site Name",Time $TableOfDSServersBObj.Name = ("$SourceRWDCInADDomainFQDN [SOURCE RWDC]").ToUpper() $TableOfDSServersBObj."Site Name" = $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,"Site Name",Reachable $TableOfDSServersAObj.Name = $DC.Name If ($DC.SiteName -ne $null -And $DC.SiteName -ne "") { $TableOfDSServersAObj."Site Name" = $DC.SiteName } Else { If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($DC.Name).Substring(0,$($DC.Name).IndexOf(".")))} | Measure-Object).Count -eq 1) { $TableOfDSServersAObj."Site Name" = ($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($DC.Name).Substring(0,$($DC.Name).IndexOf(".")))})."Site Name" } If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($DC.Name).Substring(0,$($DC.Name).IndexOf(".")))} | Measure-Object).Count -eq 0) { $TableOfDSServersAObj."Site Name" = "<Fail>" } If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($DC.Name).Substring(0,$($DC.Name).IndexOf(".")))} | Measure-Object).Count -gt 1) { $TableOfDSServersAObj."Site Name" = "<Fail>" } } $smbPort = "445" $timeOut = "500" $smbConnectionResult = $null $fqdnDC = $DC.Name $smbConnectionResult = PortConnectionCheck $fqdnDC $smbPort $timeOut If ($smbConnectionResult -eq "SUCCESS") { $TableOfDSServersAObj.Reachable = "TRUE" } If ($smbConnectionResult -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 TEXT FILE IN SYSVOL/NETLOGON...:" -ForeGroundColor Cyan Write-Host "" $domainNCDN = $defaultNamingContext $tempObjectName = "sysvolReplTempObject" + (Get-Date -f yyyyMMddHHmmss) + ".txt" Write-Host " --> On RWDC.............: $SourceRWDCInADDomainFQDN" -ForeGroundColor Yellow Write-Host " --> With Full Name......: $tempObjectName" -ForeGroundColor Yellow Write-Host " --> With Contents.......: ...!!!...TEMP OBJECT TO TEST SYSVOL REPLICATION LATENCY/CONVERGENCE...!!!..." -ForeGroundColor Yellow Write-Host " --> In AD Domain........: $ADDomainToWriteTo ($domainNCDN)" -ForeGroundColor Yellow "...!!!...TEMP OBJECT TO TEST AD REPLICATION LATENCY/CONVERGENCE...!!!..." | Out-File -FilePath $($scriptsUNCPathOnSourcingRWDC + "\" + $tempObjectName) Write-Host "`n Temp Text File [$tempObjectName] Has Been Created In The NetLogon Share Of 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 At Least Accessible Through SMB Over TCP (445)" -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 [$tempObjectName] Exists In The NetLogon Share" (" "*3) -ForeGroundColor Green continue } # If The Directory Server Is A DC In The AD Domain, Then Connect Through LDAP (TCP:445) If ($DSsrv.Name -notmatch $SourceRWDCInADDomainFQDN) { 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 = "\\" + $($DSsrv.Name) + "\Netlogon\" + $tempObjectName $connectionResult = "SUCCESS" } If ($DSsrv.Reachable -eq "FALSE") { Write-Host " - DC Is NOT Reachable..." -ForeGroundColor Red $connectionResult = "FAILURE" } } # If The Connection To The DC Is Successful If ($connectionResult -eq "SUCCESS") { If (Test-Path -Path $objectPath) { # If The Temp Object Already Exists Write-Host " - Object [$tempObjectName] Now Does Exist In The NetLogon Share" (" "*3) -ForeGroundColor Green If (!($TableOfDSServersB | ?{$_.Name -match $DSsrv.Name})) { $TableOfDSServersBobj = "" | Select Name,"Site Name",Time $TableOfDSServersBobj.Name = $DSsrv.Name $TableOfDSServersBObj."Site Name" = $DSsrv."Site Name" $TableOfDSServersBObj.Time = ("{0:n2}" -f ((Get-Date)-$startDateTime).TotalSeconds) $TableOfDSServersB += $TableOfDSServersBObj } } Else { # If The Temp Object Does Not Yet Exist Write-Host " - Object [$tempObjectName] Does NOT Exist Yet In The NetLogon Share" -ForeGroundColor Red $replicated = $false } } # If The Connection To The DC Is Unsuccessful 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,"Site Name",Time $TableOfDSServersBobj.Name = $DSsrv.Name $TableOfDSServersBObj."Site Name" = $DSsrv."Site Name" $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 Text File... `n" -ForeGroundColor Yellow Remove-Item $($scriptsUNCPathOnSourcingRWDC + "\" + $tempObjectName) -Force Write-Host " Temp Text File [$tempObjectName] 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, EXCEPT ONE:

In this case ALL directory servers, except for one RODC, are reachable without any problems! That RODC basically only exists as a pre-created RODC account

The script uses the AD domain the server is a part of where the script is executed. I chose for the script to use the PDC as the target RWDC to write the temp object to

image_thumb13

Figure 1: Using The Current AD Domain As The Target AD Domain And Letting The Script Use The PDC As The Target RWDC To Write The Temp Text File To

This is a W2K12R2 AD domain that uses DFS-R as the SYSVOL replication mechanism.

image_thumb16

Figure 2: Showing The Replication Mechanism Used For The SYSVOL, Incl. A List Of Directory Servers In The AD Domain

The temporary text file has been created and the script is now checking it on all directory servers (DCs in AD domain)

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

If a DC is marked red, then the temporary text file has not yet replicated to it OR the DC has been marked as unreachable.

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

image_thumb19

Figure 3: Creating The Temp Text File In The NetLogon Share, Enumerating Through Each Directory Server To Determine The Existence Of The Temp Text File And The End Result Of The SYSVOL Replication Latency/Convergence Test

Also check out this blog post to find the script version to check latency/convergence of AD

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/ ########

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

10 Responses to “(2014-02-17) Testing SYSVOL Replication Latency/Convergence Through PowerShell (Update 3)”

  1. […] A Hotfix Rollup Package (Build 4.1.3508.0) Is Available for Forefront Identity Manager 2010 R2 Testing SYSVOL Replication Latency/Convergence Through PowerShell (Update 3) […]

    Like

  2. […] UPDATE: A newer version of this blog post and the PowerShell script can be found here. […]

    Liked by 1 person

  3. […] To check the replication latency/convergence also see: (2014-02-17) Testing SYSVOL Replication Latency/Convergence Through PowerShell (Update 3) […]

    Like

  4. […] Test sysvol file convergence: https://jorgequestforknowledge.wordpress.com/2014/02/17/testing-sysvol-replication-latencyconvergenc… […]

    Like

  5. […] Sysvol. Danach ist es notwendig, das Monitoring anzupassen. Wir nutzen eine angepasste Version von testing-sysvol-replication-latencyconvergence-through-powershell. Wer denkt, das sei alles “mal nebenbei” passiert, irrt sich. Wir benötigten sechs […]

    Like

  6. This is a great script, helped me in my migration from FRS to DFS-R. It would be nice to be able to run this script silently (via scheduled task) so we’re able to run this on a schedule and compare results.

    Like

  7. albertwt said

    Jorge,
    Is this script only perform non destructive testing in the AD domain ? what’s the minimum AD account recommended to run this script by Scheduled task monthly ?
    Thanks,

    Like

    • Jorge said

      This script creates a temporary TEXT file in the netlogon share of the local DC. That requires local admin permissions, and therefore domain admin
      In addition, to determine the convergence it connects to every other DC in the AD domain through the admin share of the drive where then SYSVOL/NETLOGON is stored. That requires local admin permissions on the box the script is connecting to, and therefore domain admin permissions.

      Like

  8. mavsx said

    Good Stuff Jorge. Thank you.

    Like

  9. […] As soon as you change anything in the Azure AD, the on-premises DCs need to consume that new configuration. Unfortunately that may take some hours. To speed up the consumption of the configuration, restart the “Azure AD Password Protection DC Agent” service on a DC in each AD domain that is able to communicate with the Azure AD Password Protection Proxy service. The Azure AD Password Protection DC Agent on that DC will fetch the new configuration from Azure AD through the Azure AD Password Protection Proxy Service and put it on the SYSVOL (“<SYSVOL Path>domainAzureADPasswordProtection”, was previously “<SYSVOL Path>domainPolicies{4A9AB66B-4365-4C2A-996C-58ED9927332D}AzureADPasswordProtection”, changed with version 1.2.65.0) of the AD domain the DC belongs to. The needs settings then need to be replicated through the SYSVOL every other DC in the same AD domain and be consumed by every individual DC. Every DC will log event ID 30006 in the “Microsoft-AzureADPasswordProtection-DCAgent/Admin” Event Log. If you need to measure the convergence of your SYSVOL, check out the following blog post: (2014-02-17) Testing SYSVOL Replication Latency/Convergence Through PowerShell (Update 3). […]

    Like

Leave a comment

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