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!

Archive for the ‘Replication’ Category

(2014-02-16) Testing Active Directory Replication Latency/Convergence Through PowerShell (Update 3)

Posted by Jorge on 2014-02-16


I have updated the PowerShell script for testing/determining Active Directory 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 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 AD Replication Latency/Convergence # Re-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): Added STOP option, added few extra columns to output extra info of DCs, better detection of unavailable DCs/GCs, 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 # # -----> !!! 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 AD 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... Clear-Host Write-Host " *******************************************************" -ForeGroundColor Magenta Write-Host " * *" -ForeGroundColor Magenta Write-Host " * --> Test AD Replication Latency/Convergence <-- *" -ForeGroundColor Magenta Write-Host " * *" -ForeGroundColor Magenta Write-Host " * Re-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 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 "" ########## # 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 Directory Servers In AD Forest $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 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,"Site Name","IP Address","OS Version" $TableOfGCsInADForestObj.Name = $GC.Name If ($GC.Domain -ne $null -And $GC.Domain -ne "") { $TableOfGCsInADForestObj.Domain = $GC.Domain } Else { $TableOfGCsInADForestObj.Domain = $($GC.Name).Substring($($GC.Name).IndexOf(".") + 1) } If ($GC.SiteName -ne $null -And $GC.SiteName -ne "") { $TableOfGCsInADForestObj."Site Name" = $GC.SiteName } Else { If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($GC.Name).Substring(0,$($GC.Name).IndexOf(".")))} | Measure-Object).Count -eq 1) { $TableOfGCsInADForestObj."Site Name" = ($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($GC.Name).Substring(0,$($GC.Name).IndexOf(".")))})."Site Name" } If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($GC.Name).Substring(0,$($GC.Name).IndexOf(".")))} | Measure-Object).Count -eq 0) { $TableOfGCsInADForestObj."Site Name" = "<Fail>" } If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($GC.Name).Substring(0,$($GC.Name).IndexOf(".")))} | Measure-Object).Count -gt 1) { $TableOfGCsInADForestObj."Site Name" = "<Fail>" } } If ($GC.IPAddress -ne $null -And $GC.IPAddress -ne "") { $TableOfGCsInADForestObj."IP Address" = $GC.IPAddress } Else { $TableOfGCsInADForestObj."IP Address" = "<Fail>" } If ($GC.OSVersion -ne $null -And $GC.OSVersion -ne "") { $TableOfGCsInADForestObj."OS Version" = $GC.OSVersion } Else { $TableOfGCsInADForestObj."OS Version" = "<Fail>" } $TableOfGCsInADForest += $TableOfGCsInADForestObj } $TableOfGCsInADForest | FT -AutoSize Write-Host " --> Found [$($ListOfGCsInADForest.count)] GC(s) In AD Forest..." -ForeGroundColor Cyan 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) $ListOfRWDCsInADDomain = $ListOfDCsInADDomain | ?{$_.InboundConnections -ne $null -and !($_.InboundConnections -match "RODC Connection")} $ListOfRODCsInADDomain = $ListOfDCsInADDomain | ?{$_.InboundConnections -match "RODC Connection"} #$ListOfUnknownDCsInADDomain = $ListOfDCsInADDomain | ?{!($_.InboundConnections -ne $null)} $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,"Site Name","DS Type","IP Address","OS Version" $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 = "....." } 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 $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 = $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 $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" -And $SourceRWDCInADDomain -ne "STOP") { $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 } # 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 $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,"Site Name",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."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,Domain,GC,"Site Name",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."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,Domain,GC,"Site Name",Reachable $TableOfDSServersAObj.Name = $DC.Name If ($DC.Domain -ne $null -And $DC.Domain -ne "") { $TableOfDSServersAObj.Domain = $DC.Domain } Else { $TableOfDSServersAObj.Domain = $($DC.Name).Substring($($DC.Name).IndexOf(".") + 1) } $TableOfDSServersAObj.GC = "FALSE" ForEach ($GC in $ListOfGCsInADForest) { If ($DC.Name -like $GC.Name) { $TableOfDSServersAObj.GC = "TRUE" } } 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>" } } $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,"Site Name",Reachable $TableOfDSServersAObj.Name = $GC.Name If ($GC.Domain -ne $null -And $GC.Domain -ne "") { $TableOfDSServersAObj.Domain = $GC.Domain } Else { $TableOfDSServersAObj.Domain = $($GC.Name).Substring($($GC.Name).IndexOf(".") + 1) } $TableOfDSServersAObj.GC = "TRUE" If ($GC.SiteName -ne $null -And $GC.SiteName -ne "") { $TableOfDSServersAObj."Site Name" = $GC.SiteName } Else { If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($GC.Name).Substring(0,$($GC.Name).IndexOf(".")))} | Measure-Object).Count -eq 1) { $TableOfDSServersAObj."Site Name" = ($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($GC.Name).Substring(0,$($GC.Name).IndexOf(".")))})."Site Name" } If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($GC.Name).Substring(0,$($GC.Name).IndexOf(".")))} | Measure-Object).Count -eq 0) { $TableOfDSServersAObj."Site Name" = "<Fail>" } If (($TableOfDCsInADForest | ?{$_."DS Name" -eq $($($GC.Name).Substring(0,$($GC.Name).IndexOf(".")))} | Measure-Object).Count -gt 1) { $TableOfDSServersAObj."Site Name" = "<Fail>" } } $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 At Least Accessible Through LDAP Over TCP (389)" -ForeGroundColor Red Write-Host " REMARK: Each GC In The List Below Must Be At Least 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) -And $DSsrv.Domain -ne $null -And $DSsrv.Domain -ne "" -And $DSsrv.Domain -ne "<Fail>") { 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 Directory Server Is Not Available If ($DSsrv.Domain -eq $null -Or $DSsrv.Domain -eq "" -Or $DSsrv.Domain -eq "<Fail>") { Write-Host "" Write-Host " * Contacting DC/GC In AD Forest ...[$($DSsrv.Name.ToUpper())]..." -ForeGroundColor Yellow $connectionResult = $null 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 ($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,"Site Name",Time $TableOfDSServersBobj.Name = $DSsrv.Name $TableOfDSServersBObj.Domain = $DSsrv.Domain $TableOfDSServersBObj.GC = $DSsrv.GC $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 [$tempObjectDN] Does NOT Exist Yet In The Database" -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,Domain,GC,"Site Name",Time $TableOfDSServersBobj.Name = $DSsrv.Name $TableOfDSServersBObj.Domain = $DSsrv.Domain $TableOfDSServersBObj.GC = $DSsrv.GC $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 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, 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

I just pressed ENTER to use the current AD domain.

image_thumb28

Figure 1: 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_thumb31

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_thumb34

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

image_thumb37

Figure 4: Still 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 W2K12R2 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 “R2FSRWDC1.ADDMZ.LAN’ inbound replicated the temporary AD object from RWDC ‘R2FSRWDC1.ADDMZ.LAN’;
  • Again 3 seconds later, RWDC ‘R2FSRWDC1.ADDMZ.LAN’ notified RODC ‘R2FSRODC6.ADDMZ.LAN’ and RODC ‘R2FSRODC6.ADDMZ.LAN’ inbound replicated the temporary AD object from RWDC ‘R1FSRWDC1.ADDMZ.LAN’;

image_thumb40

Figure 5: 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_thumb43

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

I chose for the script to use the PDC as the target RWDC to write the temp object to.

image_thumb46

Figure 7: 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_thumb49

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

image_thumb52

Figure 9: Still 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 W2K12R2 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 ‘C1FSRWDC1.CHILD.ADCORP.LAB’ and RWDC “C1FSRWDC1.CHILD.ADCORP.LAB’ inbound replicated the temporary AD object from RWDC ‘R1FSRWDC1.ADCORP.LAB’;
  • After about 15 seconds of RWDC ‘C1FSRWDC1.CHILD.ADCORP.LAB’ receiving the object, it notified RWDC ‘C1FSRWDC2.CHILD.ADCORP.LAB’ and RWDC ‘C1FSRWDC2.CHILD.ADCORP.LAB’ inbound replicated the temporary AD object from RWDC ‘C1FSRWDC1.CHILD.ADCORP.LAB’;

image_thumb55

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

For more information on AD replication see:

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

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

Advertisements

Posted in Active Directory Domain Services (ADDS), PowerShell, Replication, Tooling/Scripting | 6 Comments »

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

Posted by Jorge on 2014-02-02


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

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) DC 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 # # 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 +++" $uiConfig.ForegroundColor = "Yellow" $uiConfigBufferSize = $uiConfig.BufferSize $uiConfigBufferSize.Width = 120 $uiConfigBufferSize.Height = 9999 $uiConfigScreenSizeMax = $uiConfig.MaxPhysicalWindowSize $uiConfigScreenSizeMaxWidth = $uiConfigScreenSizeMax.Width $uiConfigScreenSizeMaxHeight = $uiConfigScreenSizeMax.Height $uiConfigScreenSize = $uiConfig.WindowSize If ($uiConfigScreenSizeMaxWidth -lt 120) { $uiConfigScreenSize.Width = $uiConfigScreenSizeMaxWidth } Else { $uiConfigScreenSize.Width = 120 } 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 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 "" 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 { $TableOfDCsInADDomainObj."Site Name" = "<Fail>" } $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."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 = [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 $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 { $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 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

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

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

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

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

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

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

Posted in Active Directory Domain Services (ADDS), PowerShell, Replication, SYSVOL, Tooling/Scripting | 4 Comments »

(2014-02-01) Testing Active Directory Replication Latency/Convergence Through PowerShell (Update 2)

Posted by Jorge on 2014-02-01


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

I have updated the PowerShell script for testing/determining Active Directory 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) DC and W2K12(R2) DCs

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 AD Replication Latency/Convergence # Re-Written by: Jorge de Almeida Pinto [MVP-DS] # Blog: https://jorgequestforknowledge.wordpress.com/ # # 2013-03-02: (v0.1): Initial version of the script (v0.1) # 2014-02-01: (v0.2): Added STOP option, added few extra columns to output extra info of DCs, better detection of unavailable DCs/GCs, and screen adjustment section added (v0.2) # # 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 AD REPLICATION LATENCY +++" $uiConfig.ForegroundColor = "Yellow" $uiConfigBufferSize = $uiConfig.BufferSize $uiConfigBufferSize.Width = 120 $uiConfigBufferSize.Height = 9999 $uiConfigScreenSizeMax = $uiConfig.MaxPhysicalWindowSize $uiConfigScreenSizeMaxWidth = $uiConfigScreenSizeMax.Width $uiConfigScreenSizeMaxHeight = $uiConfigScreenSizeMax.Height $uiConfigScreenSize = $uiConfig.WindowSize If ($uiConfigScreenSizeMaxWidth -lt 120) { $uiConfigScreenSize.Width = $uiConfigScreenSizeMaxWidth } Else { $uiConfigScreenSize.Width = 120 } If ($uiConfigScreenSizeMaxHeight -lt 75) { $uiConfigScreenSize.Height = $uiConfigScreenSizeMaxHeight - 5 } Else { $uiConfigScreenSize.Height = 75 } $uiConfig.BufferSize = $uiConfigBufferSize $uiConfig.WindowSize = $uiConfigScreenSize # Start... Clear-Host Write-Host " *******************************************************" -ForeGroundColor Magenta Write-Host " * *" -ForeGroundColor Magenta Write-Host " * --> Test AD Replication Latency/Convergence <-- *" -ForeGroundColor Magenta Write-Host " * *" -ForeGroundColor Magenta Write-Host " * Re-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 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,"Site Name","IP Address","OS Version" $TableOfGCsInADForestObj.Name = $GC.Name If ($GC.Domain -ne $null -And $GC.Domain -ne "") { $TableOfGCsInADForestObj.Domain = $GC.Domain } Else { $TableOfGCsInADForestObj.Domain = "<Fail>" } If ($GC.SiteName -ne $null -And $GC.SiteName -ne "") { $TableOfGCsInADForestObj."Site Name" = $GC.SiteName } Else { $TableOfGCsInADForestObj."Site Name" = "<Fail>" } If ($GC.IPAddress -ne $null -And $GC.IPAddress -ne "") { $TableOfGCsInADForestObj."IP Address" = $GC.IPAddress } Else { $TableOfGCsInADForestObj."IP Address" = "<Fail>" } If ($GC.OSVersion -ne $null -And $GC.OSVersion -ne "") { $TableOfGCsInADForestObj."OS Version" = $GC.OSVersion } Else { $TableOfGCsInADForestObj."OS Version" = "<Fail>" } $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,"Site Name","DS type","IP Address","OS Version" $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 = "....." } If ($DC.SiteName -ne $null -And $DC.SiteName -ne "") { $TableOfDCsInADDomainObj."Site Name" = $DC.SiteName } Else { $TableOfDCsInADDomainObj."Site Name" = "<Fail>" } $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."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 $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" -And $SourceRWDCInADDomain -ne "STOP") { $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 } # 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 $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,"Site Name",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."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,Domain,GC,"Site Name",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."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,Domain,GC,"Site Name",Reachable $TableOfDSServersAObj.Name = $DC.Name If ($DC.Domain -ne $null -And $DC.Domain -ne "") { $TableOfDSServersAObj.Domain = $DC.Domain } Else { $TableOfDSServersAObj.Domain = "<Fail>" } $TableOfDSServersAObj.GC = "FALSE" ForEach ($GC in $ListOfGCsInADForest) { If ($DC.Name -like $GC.Name) { $TableOfDSServersAObj.GC = "TRUE" } } If ($DC.SiteName -ne $null -And $DC.SiteName -ne "") { $TableOfDSServersAObj."Site Name" = $DC.SiteName } Else { $TableOfDSServersAObj."Site Name" = "<Fail>" } $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,"Site Name",Reachable $TableOfDSServersAObj.Name = $GC.Name If ($GC.Domain -ne $null -And $GC.Domain -ne "") { $TableOfDSServersAObj.Domain = $GC.Domain } Else { $TableOfDSServersAObj.Domain = "<Fail>" } $TableOfDSServersAObj.GC = "TRUE" If ($GC.SiteName -ne $null -And $GC.SiteName -ne "") { $TableOfDSServersAObj."Site Name" = $GC.SiteName } Else { $TableOfDSServersAObj."Site Name" = "<Fail>" } $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) -And $DSsrv.Domain -ne $null -And $DSsrv.Domain -ne "" -And $DSsrv.Domain -ne "<Fail>") { 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 Directory Server Is Not Available If ($DSsrv.Domain -eq $null -Or $DSsrv.Domain -eq "" -Or $DSsrv.Domain -eq "<Fail>") { Write-Host "" Write-Host " * Contacting DC/GC In AD Forest ...[$($DSsrv.Name.ToUpper())]..." -ForeGroundColor Yellow $connectionResult = $null 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 ($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,"Site Name",Time $TableOfDSServersBobj.Name = $DSsrv.Name $TableOfDSServersBObj.Domain = $DSsrv.Domain $TableOfDSServersBObj.GC = $DSsrv.GC $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 [$tempObjectDN] Does NOT Exist Yet In The Database" -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,Domain,GC,"Site Name",Time $TableOfDSServersBobj.Name = $DSsrv.Name $TableOfDSServersBObj.Domain = $DSsrv.Domain $TableOfDSServersBObj.GC = $DSsrv.GC $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 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, except for one RODC, are reachable without any problems! That RODC basically only exists as a pre-created RODC account

I just pressed ENTER to use the current AD domain.

image

Figure 1: 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 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

image

Figure 4: Still 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 W2K12R2 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 “R2FSRWDC1.ADDMZ.LAN’ inbound replicated the temporary AD object from RWDC ‘R2FSRWDC1.ADDMZ.LAN’;
  • Again 3 seconds later, RWDC ‘R2FSRWDC1.ADDMZ.LAN’ notified RODC ‘R2FSRODC6.ADDMZ.LAN’ and RODC ‘R2FSRODC6.ADDMZ.LAN’ inbound replicated the temporary AD object from RWDC ‘R1FSRWDC1.ADDMZ.LAN’;

image

Figure 5: 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 6: Using The Current AD Domain As The Target AD Domain

I chose for the script to use the PDC as the target RWDC to write the temp object to.

image

Figure 7: 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 8: Creating The Temp Object In AD And Enumerating Through Each Directory Server To Determine The Existence Of The Temp Object

image

Figure 9: Still 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 W2K12R2 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 ‘C1FSRWDC1.CHILD.ADCORP.LAB’ and RWDC “C1FSRWDC1.CHILD.ADCORP.LAB’ inbound replicated the temporary AD object from RWDC ‘R1FSRWDC1.ADCORP.LAB’;
  • After about 15 seconds of RWDC ‘C1FSRWDC1.CHILD.ADCORP.LAB’ receiving the object, it notified RWDC ‘C1FSRWDC2.CHILD.ADCORP.LAB’ and RWDC ‘C1FSRWDC2.CHILD.ADCORP.LAB’ inbound replicated the temporary AD object from RWDC ‘C1FSRWDC1.CHILD.ADCORP.LAB’;

image

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

For more information on AD replication see:

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

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

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

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

Posted in Active Directory Domain Services (ADDS), PowerShell, Replication, Tooling/Scripting | 5 Comments »

(2013-03-03) Testing SYSVOL Replication Latency/Convergence Through PowerShell

Posted by Jorge on 2013-03-03


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

A few days ago a Microsoft PFE, that probable is using the script above, contacted me asking if I also had a version that could be used to test the latency/convergence of the SYSVOL replication. It was not that difficult to change the AD version in a way to check the SYSVOL replication. You might think I now have one script that does both AD and SYSVOL, but au contraire, there are two scripts. The one above does AD, and the one below does SYSVOL. The behavior/logic for the SYSVOL version of the script is very similar to the AD version of the script.

To use the script just copy the contents of the table below and put that in a script that could be called "Check-SYSVOL-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 text file in the NETLOGON folder 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 SMB (port 445). 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). 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!

REMARK: This script just takes the SYSVOL into account, which is the default domain DFS namespace. However, the script could be adjusted to support ANY domain DFS namespace! The script supports both NTFRS and DFSR as the replication mechanism.

After the script is started, it will give you information about the DCs in the same AD domain as the server where the script is being executed on. As the default AD domain, it will use the AD domain of the server the script is being executed on.

After that, you need to specify on which RWDC from the previous AD domain, the temporary text file 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 SYSVOL Replication Latency/Convergence # 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 SYSVOL Replication Latency/Convergence <-- *" -ForeGroundColor Yellow Write-Host "* *" -ForeGroundColor Yellow Write-Host "* 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 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 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 "" Write-Host "-------------------------------------------------------------------" -ForeGroundColor Cyan Write-Host "LIST OF DCs IN THE AD DOMAIN '$ADDomainToWriteTo'..." -ForeGroundColor Cyan ForEach ($DC in $ListOfDCsInADDomain) { $TableOfDCsInADDomainObj = "" | Select Name,PDC,SiteName,DStype $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 } $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 $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 $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") { $contextRWDCToWriteTo = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("DirectoryServer",$SourceRWDCInADDomain) $SourceRWDCInADDomainObject = [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($contextRWDCToWriteTo) $SourceRWDCInADDomainFQDN = $SourceRWDCInADDomainObject.Name $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 $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 $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.Path -ne $null) { Write-Host "SYSVOL Replication Mechanism Being Used...: NTFRS" # Get The Local Root Path For The SYSVOL $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.Path -ne $null) { Write-Host "SYSVOL Replication Mechanism Being Used...: DFS-R" -ForeGroundColor Yellow Write-Host "" # Get The Local Root Path For The SYSVOL $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,SiteName,Reachable $TableOfDSServersAObj.Name = ("$SourceRWDCInADDomainFQDN [SOURCE RWDC]").ToUpper() $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,SiteName,Time $TableOfDSServersBObj.Name = ("$SourceRWDCInADDomainFQDN [SOURCE RWDC]").ToUpper() $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,SiteName,Reachable $TableOfDSServersAObj.Name = $DC.Name $TableOfDSServersAObj.SiteName = $DC.SiteName $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 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 Successfull 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,SiteName,Time $TableOfDSServersBobj.Name = $DSsrv.Name $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 [$tempObjectName] Does NOT Exist Yet In The NetLogon Share" -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,SiteName,Time $TableOfDSServersBobj.Name = $DSsrv.Name $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 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:

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

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

image

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

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.

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

image

Figure 2: 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

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 in the table.

The script uses the AD domain the server is a part of where the script is executed. 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 3: Using The Current AD Domain As The Target AD Domain And Letting The Script To Locate An RWDC As The Target RWDC To Write The Temp Text File To

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.

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

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

For more information on SYSVOL replication see:

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

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

Posted in Active Directory Domain Services (ADDS), PowerShell, Replication, SYSVOL, Tooling/Scripting | 11 Comments »

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

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

Posted in Active Directory Domain Services (ADDS), PowerShell, Replication, Tooling/Scripting | 11 Comments »

(2012-07-19) Active Directory Replication Status Tool

Posted by Jorge on 2012-07-19


The Active Directory Replication Status Tool (ADREPLSTATUS) analyzes the replication status for domain controllers in an Active Directory domain or forest.

The screen dumps below are from my test/demo environment running with W2K12 RWDCs. The first two screen dumps show you the healthy environment and all other screen dumps show you issues within that same environment after turning off some DCs. This AD forest has two AD domains (ADCORP.LAB and CHLD.ADCORP.LAB) and each AD domain has two RWDCs.

image

Figure 1: Active Directory Replication Status Tool – Healthy Environment

image

Figure 2: Active Directory Replication Status Tool – Healthy Environment

image

Figure 3: Active Directory Replication Status Tool – Environment With Issues

image

Figure 4: Active Directory Replication Status Tool – Environment With Issues

image

Figure 5: Active Directory Replication Status Tool – Environment With Issues

image

Figure 6: Active Directory Replication Status Tool – Environment With Issues

If you click on an error code it will take you to another page showing more detailed information about that specific error (see figure 9 and 10 as an example)

image

Figure 7: Active Directory Replication Status Tool – Environment With Issues

If you click on an error code it will take you to another page showing more detailed information about that specific error (see figure 9 and 10 as an example)

image

Figure 8: Active Directory Replication Status Tool – Errors Summary

image

Figure 9: Active Directory Replication Status Tool – Detailed Information About An Error

image

Figure 10: Active Directory Replication Status Tool – Detailed Information About An Error

image

Figure 11: Active Directory Replication Status Tool – Technical Information About AD Replication

image

Figure 12: Active Directory Replication Status Tool – Technical Information About AD Replication

Overview

The Active Directory Replication Status Tool (ADREPLSTATUS) analyzes the replication status for domain controllers in an Active Directory domain or forest. ADREPLSTATUS displays data in a format that is similar to REPADMIN /SHOWREPL * /CSV imported into Excel but with significant enhancements. Specific capabilities for this tool include:

  • Expose Active Directory replication errors occurring in a domain or forest
  • Prioritize errors that need to be resolved in order to avoid the creation of lingering objects in Active Directory forests
  • Help administrators and support professionals resolve replication errors by linking to Active Directory replication troubleshooting content on Microsoft TechNet
  • Allow replication data to be exported to source or destination domain administrators or support professionals for offline analysis

System requirements

  • Supported operating systems:
    • Windows 7, Windows 8, Windows Server 2003, Windows Server 2008, Windows Server 2008 R2, Windows Server 2012, Windows Vista, Windows XP
  • ADREPLSTATUS does not install on server core installs of Windows
  • Windows 2000 not supported due to lack of support for .NET Framework 4.0
  • Domain membership requirements:
    • Must be joined to the Active Directory domain or forest you intend to monitor
  • .NET Framework requirements:
    • .NET Framework 4.0 (you may be prompted to install .NET Framework 3.5.1 first on Windows Server 2008)
  • Required User Credentials:
    • Target forest/domain user account
  • Supported DC OS versions that can be monitored by ADREPLSTATUS:
    • Windows Server 2003 (R2)
    • Windows Server 2008 (R2) 
    • Windows Server 2012 RTM
    • Windows 2000 not tested and is therefore not supported.
  • Other Requirements:
    • ADREPLSTATUS will not work when the following security setting is enabled on the operating system:
      • System cryptography: Use FIPS 140 compliant cryptographic algorithms, including encryption, hashing and signing algorithms

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

Posted in Active Directory Domain Services (ADDS), Replication, Tooling/Scripting | 12 Comments »

(2011-12-13) Active Directory Replication Over Firewalls

Posted by Jorge on 2011-12-13


I found the following information on a MSFT Wiki page. Interesting read!

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

Posted in Active Directory Domain Services (ADDS), Networking, Replication | Leave a Comment »

(2011-10-12) Password Replication Between RWDCs And Between An RWDC And An RODC

Posted by Jorge on 2011-10-12


With this blog post I want to explain the difference in replication of regular data in AD and the replication of password, also when RODCs are included in the mix. The focus will be on replication of passwords.

One thing you can be sure of is that passwords do not replicate the same way and other data in the directory, especially between an RWDC and an RODC. Between RWDCs, passwords replicate like any other piece of data in the directory. In addition to that, the following ALSO applies:

  • When a password is changed/reset on some RWDC (the initial RWDC), that RWDC will automatically forward the password to the RWDC that hosts the PDC FSMO role. This occurs over the NetLogon secure channel the initial RWDC has with the RWDC hosting the PDC FSMO role. This is default behavior of RWDCs. The exception to this behavior (in other words, the password will not be forwarded) is when the “AvoidPdcOnWan” registry option has been configured on the initial RWDC (see: New Password Change and Conflict Resolution Functionality in Windows
  • When a user tries to authenticate against a specific RWDC (the initial RWDC) and the password provided does not match the password on that initial RWDC, then that RWDC will automatically retry authentication of that same user against the RWDC hosting the PDC FSMO role. If authentication against the RWDC hosting the PDC FSMO role succeeds, then the user can log on and the forwarding RWDC (the initial RWDC against which authentication was tried) will instantly inbound replicate the (new) password. This the default behavior. The exception to this behavior (in other words, authentication will not be forwarded) is when the “AvoidPdcOnWan” registry option has been configured on the initial RWDC (see: New Password Change and Conflict Resolution Functionality in Windows. If authentication against the RWDC with the PDC FSMO role fails, the user is presented with an error stating the username or password is incorrect.
  • The new password on both the initial RWDC and RWDC with the PDC FSMO role will replicate that new password in the normal way using AD replication to other RWDCs in the same AD domain, with or without change notification depending on the location of the RWDC (from an AD site perspective) and whether or not change notification has been enabled on one or more AD site links.

Between an RWDC and an RODC, the replication of passwords is a different story. A password NEVER replicates automatically from an RWDC to any RODC, no matter what happens! Remember though that this does not apply to password related metadata, such as for example the “pwdLastSet” attribute. The password related metadata does replicate automatically from an RWDC to an RODC. So, how does a password replicate from an RWDC to an RODC? One thing is for sure and that is that it will always occur on demand/request. Two on demand/request scenarios exist, being:

  • [1] the user authenticates against the RODC while the (new) password is not cached yet. In this case the RODC forwards authentication, because it does not have the password, to the RWDC with which the RODC has setup a secure channel with. After authentication the RODC uses the “replicate single object” method to get the latest password of that user account. The RWDC will only allow on demand/request replication of a password to the RODC when that account is explicitly listed in the “Allowed To Cache List” of that RODC and also not explicitly listed in the “Denied To Cache List” of that RODC. This of course will only work when the RODC can reach any RWDC. If the RODC cannot reach the RWDC, the authentication forwarding will not work and the inbound replication of the (new) password will also not work.
  • [2] the admin pre-populates the password of the user/computer account in question on the RODC using ADUC, scripting (e.g. PowerShell) or REPADMIN. Remember that, whatever the case, an RODC will ALWAYS try to inbound replicate the password using the “Replicate Single Object” method, whether or not the user/computer is listed in the “Allowed To Cache List”. It is the responsibility of the RWDC to enforce the password replication policy configuration such as the “Allowed To Cache List” and the “Denied To Cache List”. That’s WHY an RODC can only replicate the default domain NC from a W2K8 or higher RWDC and not from a W2K/W2K3 RWDC. The W2K/W2K3 RWDCs do not understand the password replication policy configuration of an RODC and will therefore not enforce it.
    When a password is changed/reset on an RWDC, the password related metadata (e.g. “pwdLastSet” attribute) is replicated to the RODC. Based upon that information the RODC knows the locally stored password (if any) is not valid anymore in the AD domain and will therefore invalidate it. After that it will try to inbound replicate the (new) password using the “replicate single object” method.


Also see the following posts:
(2005-11-24) How To Determine On What DC A Password Was Changed For A User?

(2011-02-13) When Will The Password Expire For An AD User Account And What Happens Then?

Understanding “Read Only Domain Controller” authentication

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

Posted in Active Directory Domain Services (ADDS), Read-Only Domain Controller, Replication | 1 Comment »

(2010-10-24) Testing Active Directory Replication Latency/Convergence Through PowerShell

Posted by Jorge on 2010-10-24


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

A few days ago I needed you test AD replication latency/convergence in a single AD domain AD forest environment. I had found Brandon Shell’s powershell script that was able to do this. His script only works in a single AD domain AD forest environment, so for me that was not a problem. However, a lot of people may have multiple AD domains in their AD forest, so I decided to adjust the script to also work in such an environment. This script does not leverage the Configuration partition to create an temporary object, but rather uses a certain domain partition. The data is then replicated to all DCs in that AD domain and the GCs in other AD domains, if any, in the same AD forest.

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 should have the correct permissions to create (and delete afterwards) the temporary object in the specific container in AD (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 servers, you might need to figure out how to use the scripts. 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. I f 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!

The script has two operational modes:

  • Normal Mode –> Check-AD-Replication-Latency-Convergence.ps1
  • Verbose Mode –> Check-AD-Replication-Latency-Convergence.ps1 -verbose

REMARK: ‘Verbose Mode’ will give you additional information about AD domains, DCs and GCs

After the script is started, 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

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

REMARK: In all cases the RWDC will be checked if it exists in the AD domain. If it exists, the script continues and if it does not exist, 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.

# This script is based upon the script created by Brandon Shell [MVP-PoSH] # Source Of Original Script: http://bsonposh.com/archives/276 # # This script was adjusted by Jorge de Almeida Pinto [MVP-DS] # Source Of Adjusted Script: https://jorgequestforknowledge.wordpress.com/2010/10/24/testing-active-directory-replication-latency-convergence-through-powershell/ # # Some parameters/constants Param([switch]$verbose) # 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 $cleanupCanaryObject = $true # Get list of AD domains in AD forest $ThisADForest = [DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() $ListOfADDomainsInADForest = $ThisADForest.Domains If ($verbose) { $TableOfADDomainsInADForest = @() Write-Host "" Write-Host "-------------------------------------------------------------------" -ForeGroundColor Cyan Write-Host "LIST OF DOMAINs IN THE AD FOREST..." -ForeGroundColor Cyan Write-Host "" Write-Host "ForestMode: "$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 } # Get list of GCs in AD forest $ListOfGCsInADForest = $ThisADForest.GlobalCatalogs If ($verbose) { $TableOfGCsInADForest = @() Write-Host "" 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 "" Write-Host "-------------------------------------------------------------------" -ForeGroundColor Blue # Determine which AD domain to use for the canary object. This does assume the correct AD permissions to create the object! Write-Host "In which AD Domain Should The Canary Object Be Created?" -ForeGroundColor Blue 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 = $Env:USERDNSDOMAIN } # If a 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 Blue 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 Write-Host "" } 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 } } # Get list of DCs in AD domain $contextADDomainToWriteTo = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$ADDomainToWriteTo) $ListOfDCsInADDomain = [System.DirectoryServices.ActiveDirectory.DomainController]::findall($contextADDomainToWriteTo) If ($verbose) { $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,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) { 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 "" Write-Host "Which DC In The AD Domain '$ADDomainToWriteTo' Should Be Used To Create The Object?" -ForeGroundColor Blue Write-Host "Possible Options Are:" -ForeGroundColor Blue Write-Host "* Specify PDC To Use The DC With The PDC FSMO Role" -ForeGroundColor Blue Write-Host "* Just Press Enter To Locate An RWDC" -ForeGroundColor Blue Write-Host "* Specify A Specific FQDN Of A RWDC" -ForeGroundColor Blue 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 = ($ListOfADDomainsInADForest | %{$_.PdcRoleOwner} | ?{$_.Domain -like $ADDomainToWriteTo}).Name $SourceRWDCInADDomainDOMAIN = ($ListOfADDomainsInADForest | %{$_.PdcRoleOwner} | ?{$_.Domain -like $ADDomainToWriteTo}).Domain $SourceRWDCInADDomainSITE = ($ListOfADDomainsInADForest | %{$_.PdcRoleOwner} | ?{$_.Domain -like $ADDomainToWriteTo}).SiteName } # If nothing was specified locate an RWDC to use If ($SourceRWDCInADDomain -eq "") { # Locate just ONE DC (this could be a 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 a 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 locate the RWDC with the PDC FSMO and use that one If ($UseRWDC -eq $False) { $SourceRWDCInADDomainFQDN = ($ListOfADDomainsInADForest | %{$_.PdcRoleOwner} | ?{$_.Domain -like $ADDomainToWriteTo}).Name $SourceRWDCInADDomainDOMAIN = ($ListOfADDomainsInADForest | %{$_.PdcRoleOwner} | ?{$_.Domain -like $ADDomainToWriteTo}).Domain $SourceRWDCInADDomainSITE = ($ListOfADDomainsInADForest | %{$_.PdcRoleOwner} | ?{$_.Domain -like $ADDomainToWriteTo}).SiteName } } # 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 RWDC specified actually exists in the AD domain $RWDCvalidity = $False ForEach ($DC in $ListOfDCsInADDomain) { If ($DC.Name -like $SourceRWDCInADDomainFQDN) { $RWDCvalidity = $True } } Write-Host "" Write-Host "Checking existence of the specified RWDC '$SourceRWDCInADDomainFQDN' in the AD domain '$SourceRWDCInADDomainDOMAIN'..." -ForeGroundColor Blue If ($RWDCvalidity -eq $True) { Write-Host "" Write-Host "The specified RWDC '$SourceRWDCInADDomainFQDN' exists in the AD domain '$SourceRWDCInADDomainDOMAIN'!" -ForeGroundColor