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 ‘Read-Only Domain Controller’ Category

(2017-03-16) Domain Join Through An RODC Instead Of An RWDC (Update 2)

Posted by Jorge on 2017-03-16


In the blog post (2009-01-01) Domain Join through an RODC instead of an RWDC I explained the so called read-only domain join against an RODC. In that blog post you will find a VBS script that helps you achieve that goal. Prior to the VBS script you see multiple ways of pre-creating the computer and having the password of the computer account replicate to the RODC.

In this blog post I provide an updated PowerShell script (don’t forget the execution policy on the server!) that performs the read-only domain join. You can get the PowerShell script through this link, or you can copy it from below.

### Abstract: This PoSH Script Joins A Stand Alone Server To An AD Domain Through A Targeted RODC ### Written by: Jorge de Almeida Pinto [MVP-EMS] ### BLOG: https://jorgequestforknowledge.wordpress.com/ ### ### 2013-04-12: Initial version of the script in PowerShell (v0.1) ### 2017-03-16: Added description for code 1219, added logging (same folder as script), check and clean any site related setting in registry, ### added check that script is executed with admin credentials, added check for connectivity to RODC (v0.2) ### ### WARNING: This script checks connectivity to the targeted RODC for a specific set of ports. The script is configured with default ports ### that are required, but it is also configured with a "Custom RPC Static Port For NetLogon" (40961) as that is what I have configured in ### my test/demo environment. If you are using a different port number, then make sure to change that first before running the script. ### If you use the dynamic range of RPC ports OR you do not have a firewall between your servers and RODCs, then remove that custom port number! ### <# .SYNOPSIS Joins a stand alone server to an AD domain through a targeted RODC. .DESCRIPTION Joins a stand alone server to an AD domain through a targeted RODC. .PARAMETER adDomain The FQDN of the AD domain, the server needs to be joined to. .PARAMETER rodcFQDN The FQDN of the RODC that will be targeted to join the server to the AD domain through a read-only join. .PARAMETER ipAddressLocalHost The IP address of the local server. .PARAMETER compAccountPWD The password of the computer account that was set during the pre-creation of that computer account. .EXAMPLE - Join the server SERVER1 to the AD domain COMPANY.COM through the RODC RODC1.COMPANY.COM .\Read-Only-Domain-Join.ps1 -adDomain COMPANY.COM -rodcFQDN RODC1.COMPANY.COM -ipAddressLocalHost 192.168.6.3 -compAccountPWD 'MyPa$$w0rd' .NOTES This script requires local administrator permissions. #> Param( [Parameter(Mandatory=$TRUE, ValueFromPipeline=$TRUE, ValueFromPipelineByPropertyName=$TRUE, HelpMessage='Please specify the FQDN of the AD domain to join to.')] [ValidateNotNullOrEmpty()] [string]$adDomain, [Parameter(Mandatory=$TRUE, ValueFromPipeline=$TRUE, ValueFromPipelineByPropertyName=$TRUE, HelpMessage='Please specify the FQDN of the RODC to target for the read-only domain join.')] [ValidateNotNullOrEmpty()] [string]$rodcFQDN, [Parameter(Mandatory=$TRUE, ValueFromPipeline=$TRUE, ValueFromPipelineByPropertyName=$TRUE, HelpMessage='Please specify the IP address of the local stand alone server.')] [ValidateNotNullOrEmpty()] [string]$ipAddressLocalHost, [Parameter(Mandatory=$TRUE, ValueFromPipeline=$TRUE, ValueFromPipelineByPropertyName=$TRUE, HelpMessage='Please specify the password that was set for the pre-created computer account.')] [ValidateNotNullOrEmpty()] [string]$compAccountPWD ) ### FUNCTION: Logging Data To The Log File Function Logging($dataToLog, $lineType) { $datetimeLogLine = "[" + $(Get-Date -format "yyyy-MM-dd HH:mm:ss") + "] : " Out-File -filepath "$logFileFullPath" -append -inputObject "$datetimeLogLine$dataToLog" #Write-Output($datetimeLogLine + $dataToLog) If ($lineType -eq $NULL) { Write-Host "$datetimeLogLine$dataToLog" } If ($lineType -eq "SUCCESS") { Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Green } If ($lineType -eq "ERROR") { Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Red } If ($lineType -eq "WARNING") { Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Red } If ($lineType -eq "HEADER") { Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Magenta } If ($lineType -eq "REMARK") { Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Cyan } } ### FUNCTION: Test Credentials For Admin Privileges Function Test-Admin { $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent() (New-Object Security.Principal.WindowsPrincipal $currentUser).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) } ### FUNCTION: Test The Port Connection # Source: # Based Upon http://gallery.technet.microsoft.com/scriptcenter/97119ed6-6fb2-446d-98d8-32d823867131 Function PortConnectionCheck($fqdnServer,$port,$timeOut) { $tcpPortSocket = $null $portConnect = $null $tcpPortWait = $null $tcpPortSocket = New-Object System.Net.Sockets.TcpClient $portConnect = $tcpPortSocket.BeginConnect($fqdnServer,$port,$null,$null) $tcpPortWait = $portConnect.AsyncWaitHandle.WaitOne($timeOut,$false) If(!$tcpPortWait) { $tcpPortSocket.Close() Return "ERROR" } Else { #$error.Clear() $ErrorActionPreference = "SilentlyContinue" $tcpPortSocket.EndConnect($portConnect) | Out-Null If (!$?) { Return "ERROR" } Else { Return "SUCCESS" } $tcpPortSocket.Close() $ErrorActionPreference = "Continue" } } ### FUNCTION: Determine The Network ID To Which The IP Address And Subnet Mask Belong # Written By Nathan Linley | http://myitpath.blogspot.com # Source Of Original Script: http://poshcode.org/2888 Function Get-NetworkID ([string]$ipAddress, [string]$subnetMask) { $ipOctets = $ipAddress.split(".") $subnetOctets = $subnetMask.split(".") $result = "" For ($i = 0; $i -lt 4; $i++) { $result += $ipOctets[$i] -band $subnetOctets[$i] $result += "." } $result = $result.substring(0,$result.length -1) return $result } ### FUNCTION: Determine The Subnet Mask Based Upon The Specified Mask Bits Function Get-SubnetMask-ByLength ([int]$length) { If ($length -eq $null -or $length -gt 32 -or $length -lt 0) { Write-Error "Function 'Get-SubnetMask-ByLength'...: Invalid Subnet Mask Length Provided. Please Provide A Number BETWEEN 0 And 32" Return $null } switch ($length) { "32" {return "255.255.255.255"} "31" {return "255.255.255.254"} "30" {return "255.255.255.252"} "29" {return "255.255.255.248"} "28" {return "255.255.255.240"} "27" {return "255.255.255.224"} "26" {return "255.255.255.192"} "25" {return "255.255.255.128"} "24" {return "255.255.255.0"} "23" {return "255.255.254.0"} "22" {return "255.255.252.0"} "21" {return "255.255.248.0"} "20" {return "255.255.240.0"} "19" {return "255.255.224.0"} "18" {return "255.255.192.0"} "17" {return "255.255.128.0"} "16" {return "255.255.0.0"} "15" {return "255.254.0.0"} "14" {return "255.252.0.0"} "13" {return "255.248.0.0"} "12" {return "255.240.0.0"} "11" {return "255.224.0.0"} "10" {return "255.192.0.0"} "9" {return "255.128.0.0"} "8" {return "255.0.0.0"} "7" {return "254.0.0.0"} "6" {return "252.0.0.0"} "5" {return "248.0.0.0"} "4" {return "240.0.0.0"} "3" {return "224.0.0.0"} "2" {return "192.0.0.0"} "1" {return "128.0.0.0"} "0" {return "0.0.0.0"} } } ### 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 = "+++ READ-ONLY DOMAIN JOIN THROUGH AN RODC +++" $uiConfig.ForegroundColor = "Yellow" $uiConfigBufferSize = $uiConfig.BufferSize $uiConfigBufferSize.Width = 140 $uiConfigBufferSize.Height = 9999 $uiConfigScreenSizeMax = $uiConfig.MaxPhysicalWindowSize $uiConfigScreenSizeMaxWidth = $uiConfigScreenSizeMax.Width $uiConfigScreenSizeMaxHeight = $uiConfigScreenSizeMax.Height $uiConfigScreenSize = $uiConfig.WindowSize If ($uiConfigScreenSizeMaxWidth -lt 140) { $uiConfigScreenSize.Width = $uiConfigScreenSizeMaxWidth } Else { $uiConfigScreenSize.Width = 140 } If ($uiConfigScreenSizeMaxHeight -lt 75) { $uiConfigScreenSize.Height = $uiConfigScreenSizeMaxHeight - 5 } Else { $uiConfigScreenSize.Height = 75 } $uiConfig.BufferSize = $uiConfigBufferSize $uiConfig.WindowSize = $uiConfigScreenSize ### Definition Of Some Constants $execDateTime = Get-Date $execDateTimeYEAR = $execDateTime.Year $execDateTimeMONTH = $execDateTime.Month $execDateTimeDAY = $execDateTime.Day $execDateTimeHOUR = $execDateTime.Hour $execDateTimeMINUTE = $execDateTime.Minute $execDateTimeSECOND = $execDateTime.Second $execDateTimeCustom = [STRING]$execDateTimeYEAR + "-" + $("{0:D2}" -f $execDateTimeMONTH) + "-" + $("{0:D2}" -f $execDateTimeDAY) + "_" + $("{0:D2}" -f $execDateTimeHOUR) + "." + $("{0:D2}" -f $execDateTimeMINUTE) + "." + $("{0:D2}" -f $execDateTimeSECOND) $localComputer = Get-WmiObject -Class Win32_ComputerSystem $localComputerName = $localComputer.Name $scriptFileFullPath = $MyInvocation.MyCommand.Definition $currentScriptFolderPath = Split-Path $scriptFileFullPath $logFileFullPath = Join-Path $currentScriptFolderPath $($execDateTimeCustom + "_Read-Only-Domain-Join_" + $localComputerName + ".log") $rodcNBT = $rodcFQDN.Substring(0,$rodcFQDN.IndexOf(".")) $userName = $adDomain + "\" + $localComputerName + "`$" $userPassword = $compAccountPWD $ports = 53,88,135,389,445,464,636,3268,3269,40961 # DNS, Kerberos, RPC Endpoint Mapper, LDAP, SMB, Kerberos Change/Set Password, LDAP-SSL, GC, GC-SSL, Custom RPC Static Port For NetLogon ### Definition Of Some Variables Set-Variable JOIN_DOMAIN -option Constant -value 1 # Joins a computer to a domain. If this value is not specified, the join is a computer to a workgroup Set-Variable ACCT_CREATE -option Constant -value 2 # Creates an account on a domain Set-Variable ACCT_DELETE -option Constant -value 4 # Deletes an account when a domain exists Set-Variable WIN9X_UPGRADE -option Constant -value 16 # The join operation is part of an upgrade from Windows 98 or Windows 95 to Windows 2000 or Windows NT Set-Variable DOMAIN_JOIN_IF_JOINED -option Constant -value 32 # Allows a join to a new domain, even if the computer is already joined to a domain Set-Variable JOIN_UNSECURE -option Constant -value 64 # Performs an unsecured join Set-Variable MACHINE_PASSWORD_PASSED -option Constant -value 128 # The machine, not the user, password passed. This option is only valid for unsecure joins Set-Variable DEFERRED_SPN_SET -option Constant -value 256 # Writing SPN and DnsHostName attributes on the computer object should be deferred until the rename that follows the join Set-Variable NETSETUP_JOIN_READONLY -option Constant -value 2048 # Use an RODC to perform the domain join against Set-Variable INSTALL_INVOCATION -option Constant -value 262144 # The APIs were invoked during install ### Domain Join Options To Use $domainJoinOption = $JOIN_DOMAIN + $MACHINE_PASSWORD_PASSED + $NETSETUP_JOIN_READONLY Logging "" Logging "**********************************************************" "HEADER" Logging "* *" "HEADER" Logging "* --> Read-Only Domain Join Through An RODC <-- *" "HEADER" Logging "* *" "HEADER" Logging "* Written By: Jorge de Almeida Pinto [MVP-EMS] *" "HEADER" Logging "* *" "HEADER" Logging " BLOG: 'Jorge's Quest For Knowledge' *" "HEADER" Logging " (https://jorgequestforknowledge.wordpress.com/) *" "HEADER" Logging "* *" "HEADER" Logging "**********************************************************" "HEADER" Logging "" ### Pre-Requisites Check Logging "" Logging "------------------------------------------------------------------------------------------------------------------" "HEADER" Logging "+++ PRE-REQUISITES CHECK +++" "HEADER" Logging "" Logging "ATTENTION: To Execute This Script, The Following Pre-Requisites Must Be met:" "WARNING" Logging " * Local Server Is Configured Correctly With IP Address, Subnet Mask And DNS Servers..." "WARNING" Logging " * Admin Account Must Be(Direct) Member Of Local 'Administrators' Group!..." "WARNING" Logging " * If UAC Is Used, Admin Account Must Be Running Within An Elevated Administrator Command Prompt!..." "WARNING" Logging " * Required Ports Must Be Opened Between This Server And Targeted RODC!..." "WARNING" Logging "" Logging "ATTENTION: This Script Will Fail Without The Pre-Requisites Mentioned Above!" "WARNING" Logging "" Logging "Press Any Key To Continue...(TWICE)" Logging "" $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") ### Checking For Admin Credentials And If Those Admin Credentials have Been Elevated Due To UAC If (!(Test-Admin)) { Logging "" Logging "WARNING:" "ERROR" Logging " * Your Admin Account IS NOT A (Direct) Member Of The Local 'Administrators' Group!..." "ERROR" Logging " * Your Admin Account IS NOT Running Within An Elevated Administrator Command Prompt!..." "ERROR" Logging "" Logging "Aborting Script..." "ERROR" Logging "" EXIT } Else { Logging "" Logging "SUCCESS:" "SUCCESS" Logging " * Your Admin Account IS A (Direct) Member Of The Local 'Administrators' Group!..." "SUCCESS" Logging " * Your Admin Account IS Running Within An Elevated Administrator Command Prompt!..." "SUCCESS" Logging "" Logging "Continuing Script..." "SUCCESS" Logging "" } ### Checking Connectivity (TCP Only!) Between This Server And The Target RODC $checkOK = $true $ports | %{ $port = $_ $connectionResult = $null $connectionResult = PortConnectionCheck $rodcFQDN $port 500 If ($connectionResult -eq "SUCCESS") { Logging "The RODC '$rodcFQDN' IS Accessible And Listening On Port '$port'..." "SUCCESS" } If ($connectionResult -eq "ERROR") { Logging "The RODC '$rodcFQDN' IS NOT Accessible And Listening On Port '$port'..." "ERROR" $checkOK = $false } } If (!$checkOK) { Logging "" Logging "WARNING:" "ERROR" Logging " * One Or More Of The Required Ports IS/ARE NOT Available..." "ERROR" Logging "" Logging "Aborting Script..." "ERROR" Logging "" EXIT } Else { Logging "" Logging "SUCCESS:" "SUCCESS" Logging " * All The Required Ports ARE Available..." "SUCCESS" Logging "" Logging "Continuing Script..." "SUCCESS" Logging "" } ### Checking Local Registry Settings For Site Definition $regNameExistSiteName = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name SiteName -ErrorAction SilentlyContinue If ($regNameExistSiteName) { Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name SiteName -Force Logging "" Logging "Registry Value 'SiteName' In 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' Exists..." Logging "" Logging "Registry Value 'SiteName' Has Been Deleted..." Logging "" } $regNameExistDynamicSiteName = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name DynamicSiteName -ErrorAction SilentlyContinue If ($regNameExistDynamicSiteName) { Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name DynamicSiteName -Force Logging "" Logging "Registry Value 'DynamicSiteName' In 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' Exists..." Logging "" Logging "Registry Value 'DynamicSiteName' Has Been Deleted..." Logging "" } ### Change Preferred Error Action $ErrorActionPreference = "SilentlyContinue" ### Connecting To AD On The RODC And Getting Some NCs $rootDSEldapPath = "LDAP://$rodcFQDN/rootDSE" $directoryEntryrootDSE = New-Object System.DirectoryServices.DirectoryEntry($rootDSEldapPath, $userName, $userPassword) $defaultNamingContext = $directoryEntryrootDSE.defaultNamingContext $configurationNamingContext = $directoryEntryrootDSE.configurationNamingContext ### Checking Pre-Created Computer Account Exists And The Correct Password Of The Computer Account Is Being Used $defaultNCldapPath = "LDAP://$rodcFQDN/$defaultNamingContext" $defaultNCdirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry($defaultNCldapPath, $userName, $userPassword) $SearcherSRVCompAccount = $null $SearcherSRVCompAccount = New-Object DirectoryServices.DirectorySearcher($defaultNCdirectoryEntry) $SearcherSRVCompAccount.SearchScope = "Subtree" $SearcherSRVCompAccount.Filter = "(&(objectClass=computer)(sAMAccountName=$localComputerName`$))" $SearcherSRVCompAccountResult = $null $SearcherSRVCompAccountResult = $SearcherSRVCompAccount.FindOne() $dnSRVCompAccount = $null $dnSRVCompAccount = $SearcherSRVCompAccountResult.Properties.distinguishedname If ($dnSRVCompAccount) { Logging "" Logging "SUCCESS:" "SUCCESS" Logging " * A Computer Account For This Server DOES Exist...And" "SUCCESS" Logging " * A Correct Password Is Being Used..." "SUCCESS" Logging "" Logging "Continuing Script..." "SUCCESS" Logging "" } Else { Logging "" Logging "WARNING:" "ERROR" Logging " * A Computer Account For This Server DOES NOT Exist...Or" "ERROR" Logging " * An Incorrect Password Is Being Used..." "ERROR" Logging "" Logging "Aborting Script..." "ERROR" Logging "" EXIT } ### Change Preferred Error Action To Default $ErrorActionPreference = "Continue" $regNameExistSiteName = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name SiteName -ErrorAction SilentlyContinue If ($regNameExistSiteName) { Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name SiteName -Force Logging "" Logging "Registry Value 'SiteName' In 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' Exists..." Logging "" Logging "Registry Value 'SiteName' Has Been Deleted..." Logging "" } $regNameExistDynamicSiteName = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name DynamicSiteName -ErrorAction SilentlyContinue If ($regNameExistDynamicSiteName) { Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name DynamicSiteName -Force Logging "" Logging "Registry Value 'DynamicSiteName' In 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' Exists..." Logging "" Logging "Registry Value 'DynamicSiteName' Has Been Deleted..." Logging "" } ### Initiating Read-Only Domain Join Logging "" Logging "------------------------------------------------------------------------------------------------------------------" "HEADER" Logging "+++ INITIATING READ-ONLY DOMAIN JOIN +++" "HEADER" ### Determining The AD Site Of The Specified/Targeted RODC $rodcCompAccountldapPath = "LDAP://$rodcFQDN/CN=$rodcNBT,OU=Domain Controllers,$defaultNamingContext" $rodcCompAccountdirectoryEntry = $null $rodcCompAccountdirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry($rodcCompAccountldapPath, $userName, $userPassword) $SearcherRodcCompAccount = $null $SearcherRodcCompAccount = New-Object DirectoryServices.DirectorySearcher($rodcCompAccountdirectoryEntry) $SearcherRodcCompAccount.SearchScope = "Base" $SearcherRodcCompAccount.Filter = "(&(objectClass=computer)(dNSHostName=$rodcFQDN))" $SearcherRodcCompAccount.PropertiesToLoad.Add("msDS-SiteName") | Out-Null $SearcherRodcCompAccountResult = $null $SearcherRodcCompAccountResult = $SearcherRodcCompAccount.FindOne() $rodcADSite = $null [string]$rodcADSite = $SearcherRodcCompAccountResult.Properties."msds-sitename" ### Matching The IP Address Of The Local Server Against An AD Site In The AD Forest $subnetsContainerldapPath = "LDAP://$rodcFQDN/CN=Subnets,CN=Sites,$configurationNamingContext" $subnetsContainerdirectoryEntry = $null $subnetsContainerdirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry($subnetsContainerldapPath, $userName, $userPassword) $searcherSubnets = $null $searcherSubnets = New-Object DirectoryServices.DirectorySearcher($subnetsContainerdirectoryEntry) $searcherSubnets.SearchScope = "Subtree" $searcherSubnets.PropertiesToLoad.Add("name") | Out-Null $searcherSubnets.PropertiesToLoad.Add("siteObject") | Out-Null # We Can Take Network Masks In Both Length And Full Octet Format # We Need To Use Both. LDAP Searches # Use Length, And Network ID Generation Is By Full Octet Format. $startMaskLength = 32 For ($i = $startMaskLength; $i -ge 0; $i--) { # Loop Through Netmasks From /32 To /0 Looking For A Subnet Match In AD # Go Through All Masks From Longest To Shortest $subnetMask = &Get-SubnetMask-ByLength $i $networkID = &Get-NetworkID $ipAddressLocalHost $subnetMask # LDAP Search For The Network $searcherSubnets.filter = "(&(objectClass=subnet)(objectCategory=subnet)(cn=" + $networkID + "/" + $i + "))" $subnetObjectResult = $null $subnetObjectResult = $searcherSubnets.FindOne() #$subnetObjectsList = $searcherSubnets.FindAll() #$subnetsTable = @() #$subnetObjectsList.Properties | %{ # $subnetsTableObj = "" | Select "AD Subnet","AD Site" # $subnetsTableObj."AD Subnet" = ($_.name)[0] # $subnetsTableObj."AD Site" = $(($_.siteobject)[0]).Substring(3,$(($_.siteobject)[0]).IndexOf(",")-3) # $subnetsTable += $subnetsTableObj #} #$subnetsTable | FT -Autosize If ($subnetObjectResult -ne $null) { # If A Match Is Found, Return It Since It Is The Longest Length (Closest Match) $localComputerADSubnet = $null [string]$localComputerADSubnet = $($subnetObjectResult.Properties.name) $localComputerADSite = $null [string]$localComputerADSite = $($subnetObjectResult.Properties.siteobject).Substring(3,$($subnetObjectResult.Properties.siteobject).IndexOf(",")-3) #return $localComputerADSite Break } $subnetObjectResult = $null [string]$localComputerADSubnet = $null [string]$localComputerADSite = $null } If ($localComputerADSubnet -eq $null -Or $localComputerADSite -eq $null) { [string]$localComputerADSubnet = "NO_MATCH_FOUND" [string]$localComputerADSite = "NO_MATCH_FOUND" } ### Present The Information Logging "" Logging "Trying To Join The Local Computer '$localComputerName' To The AD Domain '$adDomain' Using The RODC '$rodcFQDN'..." Logging "" Logging "FQDN AD Domain............: $adDomain" Logging "FQDN RODC.................: $rodcFQDN" Logging "AD Site RODC..............: $rodcADSite" Logging "AD Site Local Computer....: $localComputerADSite" Logging "Matching AD Subnet........: $localComputerADSubnet" Logging "Local Computer Name.......: $localComputerName ($localComputerName`$)" Logging "Distinguished Name........: $dnSRVCompAccount" Logging "Computer Account Password.: $compAccountPWD" Logging "" ### AD Sites Must Match, Otherwise Something Is Wrong If ($rodcADSite.ToUpper() -ne $localComputerADSite.ToUpper() -Or $localComputerADSite -eq "NO_MATCH_FOUND") { Logging "" Logging "WARNING:" "ERROR" Logging " * The AD Site Of The Local Computer DOES NOT Match The AD Site Of The Specified RODC..." "ERROR" Logging " * Make Sure The IP Address Of The Local Server Is Configured Correctly So That It Will Match Against The Same AD Site As The Targeteed RODC..." "ERROR" Logging " * The Cause Of The Mismatch Can Be:" "ERROR" Logging " * The Specified IP Address IS NOT Correct..." "ERROR" Logging " * The Specified RODC IS NOT Correct..." "ERROR" Logging " * The AD Subnet For The Local Computer Is Linked To The Incorrect AD Site..." "ERROR" Logging "" Logging "Aborting Script..." "ERROR" Logging "" EXIT } ### Joining The Local Computer To The AD Domain Using The Specified Domain Join Options $returnErrorCode = $localComputer.JoinDomainOrWorkGroup($adDomain + "\" + $rodcFQDN, $compAccountPWD, $null, $null, $domainJoinOption) # List of 'system error codes' (http://msdn.microsoft.com/en-us/library/ms681381.aspx) and # List of 'network management error codes' (http://msdn.microsoft.com/en-us/library/aa370674(VS.85).aspx) $returnErrorDescription = switch ($($returnErrorCode.ReturnValue)) { 0 {"SUCCESS: The Operation Completed Successfully."} 5 {"FAILURE: Access Is Denied."} 53 {"FAILURE: The Network Path Was Not Found."} 64 {"FAILURE: The Specified Network Name Is No Longer Available."} 87 {"FAILURE: The Parameter Is Incorrect."} 1219 {"FAILURE: Logon Failure: Multiple Credentials In Use For Target Server."} 1326 {"FAILURE: Logon Failure: Unknown Username Or Bad Password."} 1355 {"FAILURE: The Specified Domain Either Does Not Exist Or Could Not Be Contacted."} 2691 {"FAILURE: The Machine Is Already Joined To The Domain."} default {"FAILURE: Unknown Error!"} } If ($($returnErrorCode.ReturnValue) -eq "0") { Logging "Domain Join Result Code...: $($returnErrorCode.ReturnValue)" "SUCCESS" Logging "Domain Join Result Text...: $returnErrorDescription" "SUCCESS" } Else { Logging "Domain Join Result Code...: $($returnErrorCode.ReturnValue)" "ERROR" Logging "Domain Join Result Text...: $returnErrorDescription" "ERROR" } If ($($returnErrorCode.ReturnValue) -eq "0") { Logging "" Logging "REMARK:" "REMARK" Logging " * The Computer Account Password Will Be Changed Shortly After The Domain Join!" "REMARK" Logging "" Logging "!!! THE COMPUTER WILL REBOOT AUTOMATICALLY IN 2 MINUTES !!!" "REMARK" Logging "" Logging "!!! TO STOP THE REBOOT USE THE COMMAND: SHUTDOWN /A !!!" "REMARK" SHUTDOWN /R /T 120 } Logging "" Logging "+++ FINISHED +++" "HEADER" Logging "------------------------------------------------------------------------------------------------------------------" "HEADER"

Have fun!

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

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

Advertisement

Posted in Active Directory Domain Services (ADDS), Domain Join, PowerShell, Read-Only Domain Controller, Tooling/Scripting | 9 Comments »

(2014-12-03) Incorrect Statement When Demoting An RODC While Retaining Metadata

Posted by Jorge on 2014-12-03


When demoting an RODC, you have two options for an end result:

  1. The RODC is demoted and became a domain joined member server
  2. The RODC is demoted and became a stand-alone server (non-domain joined) and its metadata is kept in AD

Let’s go through the process and demote an RODC.

image

Figure 1: The Active Directory Domain Services Configuration Wizard – Credentials For Demotion

image

Figure 2: The Active Directory Domain Services Configuration Wizard – Explicitly Confirming The Demotion

image

Figure 3: The Active Directory Domain Services Configuration Wizard – Choosing Whether Or Not To Retain The Metadata Of The RODC

image

Figure 4: The Active Directory Domain Services Configuration Wizard – Password For The Local Administrator

image

Figure 5: The Active Directory Domain Services Configuration Wizard – Summary Of The End Result

The PowerShell script:

#
# Windows PowerShell script for AD DS Deployment
#

Import-Module ADDSDeployment
Uninstall-ADDSDomainController `
-DemoteOperationMasterRole:$true `
-RetainDCMetadata:$true `
-Force:$true

The statement in figure 5 is incorrect. Because I selected "Retain Metadata" (see figure 3), the statement should be: "When the process is complete, this server will be a stand-alone server (non-domain joined)". When you retain the metadata of an RODC, it becomes "unoccupied" which allows you to attach to when promoting a new RODC with the same name.

If you did not select "Retain Metadata", the statement in figure 5 is correct.

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), Promotion/Demotion, Read-Only Domain Controller | Leave a Comment »

(2014-04-04) Domain Join through an RODC instead of an RWDC (Update 1)

Posted by Jorge on 2014-04-04


UPDATE 2017-03-16: There is an updated version which you can find here

In the blog post (2009-01-01) Domain Join through an RODC instead of an RWDC I explained the so called read-only domain join against an RODC. In that blog post you will find a VBS script that helps you achieve that goal. Prior to the VBS script you see multiple ways of pre-creating the computer and having the password of the computer account replicate to the RODC.

In this blog post I provide a PowerShell script (don’t forget the execution policy on the server!) that performs the read-only domain join. You can get the PowerShell script through this link, or you can copy it from below.

Param( [string]$fqdnADdomain, # The FQDN Of the AD domain [string]$fqdnRODC, # The FQDN of the RODC to use [string]$computerAccountPWD # The password for the computer account ) Clear-Host Write-Host "*******************************************************************" -ForeGroundColor Yellow Write-Host "* --> Performing Read-Only Domain Join Against RODC <-- *" -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 # Checking If All Parameters Are Available And Correct If (!($fqdnADdomain)) { Write-Host "" Write-Host "No FQDN Of An AD Domain Has Been Specified" -ForeGroundColor Red Write-Host "The FQDN Of An AD Domain Is Required!" -ForeGroundColor Red Write-Host "Aborting Script..." -ForeGroundColor Red Write-Host "" BREAK } If (!($fqdnRODC)) { Write-Host "" Write-Host "No FQDN Of An RODC Has Been Specified" -ForeGroundColor Red Write-Host "The FQDN Of An RODC Is Required!" -ForeGroundColor Red Write-Host "Aborting Script..." -ForeGroundColor Red Write-Host "" BREAK } If (!($computerAccountPWD)) { Write-Host "" Write-Host "No Computer Account Password Has Been Specified" -ForeGroundColor Red Write-Host "The Computer Account Password Specified During Pre-Creation Is Required!" -ForeGroundColor Red Write-Host "Aborting Script..." -ForeGroundColor Red Write-Host "" BREAK } # Defining Required Constants Set-Variable JOIN_DOMAIN -option Constant -value 1 # Joins a computer to a domain. If this value is not specified, the join is a computer to a workgroup Set-Variable MACHINE_PASSWORD_PASSED -option Constant -value 128 # The machine, not the user, password passed. This option is only valid for unsecure joins Set-Variable NETSETUP_JOIN_READONLY -option Constant -value 2048 # Use an RODC to perform the domain join against # Cumulative Value To Use $readOnlyDomainJoinOption = $JOIN_DOMAIN + $MACHINE_PASSWORD_PASSED + $NETSETUP_JOIN_READONLY # Getting Info From The Local Computer $localComputerSystem = Get-WMIObject Win32_ComputerSystem $computerName = $localComputerSystem.Name # Present The Gathered Information Write-Host "" Write-Host "" Write-Host "Trying To Perform A Read-Only Domain Join Using The Following Information..." -ForeGroundColor Yellow Write-Host "" Write-Host "FQDN AD Domain............: "$fqdnADdomain -ForeGroundColor Yellow Write-Host "FQDN RODC.................: "$fqdnRODC -ForeGroundColor Yellow Write-Host "Computer Name.............: "$computerName -ForeGroundColor Yellow Write-Host "Computer Account Password.: "$computerAccountPWD -ForeGroundColor Yellow Write-Host "" # Performing The Read-Only Domain Join $errorCode = $localComputerSystem.JoinDomainOrWorkGroup($fqdnADdomain+"\"+$fqdnRODC,$computerAccountPWD,$null,$null,$readOnlyDomainJoinOption) # Error Handling # List of 'system error codes' (http://msdn.microsoft.com/en-us/library/ms681381.aspx) and # List of 'network management error codes' (http://msdn.microsoft.com/en-us/library/aa370674(VS.85).aspx) $errorDescription = switch ($($errorCode.ReturnValue)) { 0 {"SUCCESS: The Operation Completed Successfully."} 5 {"FAILURE: Access Is Denied."} 53 {"FAILURE: The Network Path Was Not Found."} 64 {"FAILURE: The Specified Network Name Is No Longer Available."} 87 {"FAILURE: The Parameter Is Incorrect."} 1326 {"FAILURE: Logon failure: Unknown Username Or Bad Password."} 1355 {"FAILURE: The Specified Domain Either Does Not Exist Or Could Not Be Contacted."} 2691 {"FAILURE: The Machine Is Already Joined To The Domain."} default {"FAILURE: Unknown Error!"} } If ($($errorCode.ReturnValue) -eq "0") { Write-Host "Domain Join Result Code...: "$($errorCode.ReturnValue) -ForeGroundColor Green Write-Host "Domain Join Result Text...: "$errorDescription -ForeGroundColor Green } Else { Write-Host "Domain Join Result Code...: "$($errorCode.ReturnValue) -ForeGroundColor Red Write-Host "Domain Join Result Text...: "$errorDescription -ForeGroundColor Red } # Finishing Up Write-Host "" Write-Host "REMARK:" -ForeGroundColor Cyan Write-Host "The Computer Account Password Will Be Reset Shortly After The Domain Join!" -ForeGroundColor Cyan Write-Host "" Write-Host "###### FINISHED ######" Write-Host "-----------------------------------------------------" If ($($errorCode.ReturnValue) -eq "0") { Write-Host "" Write-Host "!!! THE COMPUTER WILL REBOOT AUTOMATICALLY IN 2 MINUTES !!!" -ForeGroundColor Cyan Write-Host "" Write-Host "!!! TO STOP THE REBOOT USE THE COMMAND: SHUTDOWN /A !!!" -ForeGroundColor Cyan SHUTDOWN /R /T 120 }

Have fun!

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), Domain Join, PowerShell, Read-Only Domain Controller, Tooling/Scripting | 2 Comments »

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

(2009-06-12) Read-Only Domain Controller (RODC) Branch Office Guide Is Available

Posted by Jorge on 2009-06-12


The Read-Only Domain Controller (RODC) Branch Office Guide is now live on TechNet and the Download Center!

The guide covers the major considerations around deploying RODC’s in Branch Office scenarios. Specifically, the topics include:

  • Branch Office Environment Characteristics
  • Deciding Which Type of Domain Controller Meets the Needs of a Branch Office Location
  • Updates to Windows Server 2003 Branch Office Guide Recommendations
  • Planning/Deploying/Administering RODCs in Branch Offices
  • Monitoring Your Branch Office Environment

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), Design Guides, Read-Only Domain Controller | Leave a Comment »

(2009-01-01) Domain Join through an RODC instead of an RWDC

Posted by Jorge on 2009-01-01


Windows Server 2008 introduces one of the coolest features in AD, being IMHO the Read-Only Domain Controller (RODC). The main goal of the RODC is to improve the AD security and to mitigate risks. It is therefore also preferably deployed at the perimeter of the network. Based upon that, three different scenarios/deployments exist:

  • RODC in a Branch Office (= primary focus!)
  • RODC in the DMZ (under investigation by MS)
  • RODC on the internet (under investigation by MS)

With regards to the RODC, Microsoft created a planning and deployment guide. This guide can be found here. Additionally Microsoft also released a RODC Compatibility Pack which can be found here.

The RODC needs to be able to "talk" to a writable W2K8 DC (W2K8 RWDC) for replication, authentication forwarding, etc. In all three scenarios it is very feasible to place a firewall between RWDCs and RODCs, whereas only one or more RODCs can talk to one or more RWDCs. Clients and servers can make LDAP writes, which refer a client to a RWDC, or special writes which are forwarded by the RODC to an RWDC. For more info about this see the presentations HERE and HERE.

Joining clients to an AD domain is such an operation for which you normally require to contact an RWDC. However, in those scenarios where the Branch Office has been separated from the Datacenter by a firewall so that only the RODC can contact an RWDC, you cannot join clients in the normal way against an RODC. The reason? Well, only the RODC can contact an RWDC and the clients/servers cannot. Another way is to join the computer in the datacenter first and then ship it to the Branch Office. However, that may not be feasible. Another way is to join the computer against the RODC with a workaround. The steps for that are explained below.

On some RWDC perform the following steps:

(1) Pre-create a computer account and set a custom password on that same computer account:

  • From a command line: net computer \<NetBIOS Name Computer> /add & net user <NetBIOS Name Computer>$ <Password>
    (additional attributes must be set afterwards such as "dNSHostName", "servicePrincipalName"!) (Be aware that this way the "userAccountControl" attribute contains an incorrect value. I’m saying "incorrect" because the bit "Password Not Required is also set" and that’s something I do not like!)
    OR
  • From a command line: ADMOD -replacedn XXX-DOMAIN-XXX:_default -add -b "CN=<NetBIOS Name Computer>,OU=<SOME OU>,XXX-DOMAIN-XXX" "objectClass::computer" "sAMAccountName:: <NetBIOS Name Computer>$" "userAccountControl::4096" -kerbenc "unicodePwd::<Password>" "dNSHostName::<NetBIOS Name Computer>.%USERDNSDOMAIN%" "servicePrincipalName:++:HOST/<NetBIOS Name Computer>;HOST/<NetBIOS Name Computer>.%USERDNSDOMAIN%"

(2) Allow the password of the computer account to be cached on the RODC

  • Use the AD domain wide ALLOW group: "Allowed RODC Password Replication Group" (for ALL RODCs)
    • Using ADUC: By selecting the computer account of the RODC and selecting the tab "Password Replication Policy" and choosing AD domain wide ALLOW group to add the computer account to
      OR
    • From a command line: net localgroup "Allowed RODC Password Replication Group" <NetBIOS Name Computer>$ /add
      OR
    • From a command line: ADMOD -replacedn XXX-DOMAIN-XXX:_default -b "CN=Allowed RODC Password Replication Group,CN=Users,XXX-DOMAIN-XXX" "member::CN=<NetBIOS Name Computer>,OU=<SOME OU>,XXX-DOMAIN-XXX"

    OR

  • Use the ALLOW group which is specific to the single RODC only
    • Using ADUC: By selecting the computer account of the RODC and selecting the tab "Password Replication Policy" and choosing the RODC specific ALLOW group to add the computer account to
      OR
    • From a command line: net (local)group "<ALLOW group specific to RODC>" <NetBIOS Name Computer>$ /add
      OR
    • From a command line: ADMOD -replacedn XXX-DOMAIN-XXX:_default -b "CN=<ALLOW group specific to RODC>,OU=<SOME OU>,XXX-DOMAIN-XXX" "member::CN=<NetBIOS Name Computer>,OU=<SOME OU>,XXX-DOMAIN-XXX"

(3) Force the computer account to be cached at the RODC

  • Using ADUC: By selecting the computer account of the RODC and selecting the tab "Password Replication Policy" and clicking the Advanced button and clicking the Prepopulate Passwords button to pre-cache the password on the RODC
    OR
  • From a command line: REPADMIN /RODCPWDREPL <RODC> <RWDC> "CN=<NetBIOS Name Computer>,OU=<SOME OU>,DC=<DOMAIN>,DC=<TLD>"

On the client or server to be joined to the AD domain using an RODC implement the following script (DomainJoinAgainstRODC.vbs) and execute it:

Const JOIN_DOMAIN = 1 ' Joins a computer to a domain. If this value is not specified, the join is a computer to a workgroup Const ACCT_CREATE = 2 ' Creates an account on a domain Const ACCT_DELETE = 4 ' Deletes an account when a domain exists Const WIN9X_UPGRADE = 16 ' The join operation is part of an upgrade from Windows 98 or Windows 95 to Windows 2000 or Windows NT Const DOMAIN_JOIN_IF_JOINED = 32 ' Allows a join to a new domain, even if the computer is already joined to a domain Const JOIN_UNSECURE = 64 ' Performs an unsecured join Const MACHINE_PASSWORD_PASSED = 128 ' The machine, not the user, password passed. This option is only valid for unsecure joins Const DEFERRED_SPN_SET = 256 ' Writing SPN and DnsHostName attributes on the computer object should be deferred until the rename that follows the join Const NETSETUP_JOIN_READONLY = 2048 ' Use an RODC to perform the domain join against Const INSTALL_INVOCATION = 262144 ' The APIs were invoked during install strDomain = "ADCORP.LAB" ' The FQDN of the AD domain strRODC = "RFSRODC1.ADCORP.LAB" ' The FQDN of the RODC to use strPassword = "Pa$$w0rd" ' The custom password for the computer account Set objNetwork = CreateObject("WScript.Network") strComputer = objNetwork.ComputerName ' The NetBIOS name of the local computer Set objComputer = GetObject("winmgmts:{impersonationLevel=Impersonate}!\" & strComputer & "rootcimv2:Win32_ComputerSystem.Name='" & strComputer & "'") Wscript.echo("### STARTING ###") Wscript.echo("Trying to join the local computer to the AD domain using an RODC...") Wscript.echo("") ReturnValue = objComputer.JoinDomainOrWorkGroup(strDomain & "" & strRODC, strPassword, NULL, NULL, JOIN_DOMAIN+MACHINE_PASSWORD_PASSED+NETSETUP_JOIN_READONLY) ' List of 'system error codes' (http://msdn.microsoft.com/en-us/library/ms681381.aspx) and ' List of 'network management error codes' (http://msdn.microsoft.com/en-us/library/aa370674(VS.85).aspx) Select Case ReturnValue Case 0 strErrDescr = "The operation completed successfully" Case 5 strErrDescr = "Access is denied" Case 87 strErrDescr = "The parameter is incorrect" Case 1326 strErrDescr = "Logon failure: unknown username or bad password" Case 1355 strErrDescr = "The specified domain either does not exist or could not be contacted" Case 2691 strErrDescr = "The machine is already joined to the domain" End Select Wscript.echo("FQDN AD Domain : '" & strDomain & "'") Wscript.echo("FQDN RODC : '" & strRODC & "'") Wscript.echo("Local Computer Name : '" & strComputer & "." & strDomain & "'") Wscript.echo("Domain Join Result Code : '" & ReturnValue & "'") Wscript.echo("Domain Join Result Text : '" & strErrDescr & "'") Wscript.echo("") Wscript.echo("### FINISHED ###")

Execute the VBS script: CSCRIPT DomainJoinAgainstRODC.vbs

Depending in the current condition of the client you may receive an output similar to the picture below.

image

When:

Domain Join Result Code : ‘0’

Domain Join Result Text : ‘The operation completed successfully’

Then the client is successfully joined to the AD domain!

Let’s analyze a few parts of the script….

(1) The name of the RODC is specified. Why is this required? When a client/server joins an AD domain it queries DNS for DCs that have registered the domain-wide SRV resource records. RODCs by default DO NOT register domain-wide SRV resource records, only site-wide SRV resource records! Because of that, you must specify in the script WHICH DC the client/server must contact for the domain join

(2) You must specify the custom password that has been configured on the computer account in AD

(3) You need to specify the option ‘JOIN_DOMAIN’ to make sure it joins a DOMAIN and not a WORKGROUP

(4) You need to specify the option ‘MACHINE_PASSWORD_PASSED’ to make sure you pass the password that has been configured on the AD account and NOT credentials that are allowed to make the join. The authentication/authorization of the domain join occurs by just knowing the password that has been configured on the AD computer account!

(5) You need to specify the option ‘NETSETUP_JOIN_READONLY’ to make the join is made in READ-ONLY mode

The procedure above works for: Windows Vista (and later), Windows Server 2008 (and later), Windows XP Professional SP2 (and later), Windows Server 2003 SP1 (and later). For both Windows XP Professional and Windows Server 2003 the hotfix specified in MS-KBQ944043_Description of the Windows Server 2008 read-only domain controller compatibility pack for Windows Server 2003 clients and for Windows XP clients *MUST* be installed! You will get a "Domain Join Result Code" of 87 (The parameter is incorrect) when the hotfix is NOT installed! Make sure it is installed!

More information:

Windows 7 (Windows Server 2008 R2) will provide an even cooler way of joining clients/servers to an AD domain. More of that later, so stay tuned!

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), Domain Join, Read-Only Domain Controller, Tooling/Scripting, VB Script | 22 Comments »

(2008-03-21) Switching Between A RODC And A RWDC

Posted by Jorge on 2008-03-21


A DC (for both RODCs/RWDCs) can be (un)assigned the GC role by just configuring a checkbox in Active Directory Sites And Services or using REPADMIN or some other tool that configures the feature. To switch between a RWDC and a RODC (or the other way around), you must always first demote, reboot and promote to whatever you want.

However, the RODC is untrusted. The RWDC is very trusted. From a security perspective I do not recommend to promote any server to a RWDC that was an RODC or a member server when:

  • it was previously managed by a non-domain admin
  • the server did not have any physical security
  • the server was from a network perspective located in an unsecure network segment
  • everything else that is non-trusted or insecure

Why? Because if the member server or the RODC has been compromised before the promotion, the person/thing (probably a hacker or a non-Domain Admins or malicious software) causing the compromise can become a domain/enterprise admin by using the power of the RWDC after the promotion to a RWDC.

So, if you do not trust the member server and/or the RODC that should become a RWDC, rebuild it from scratch! And after doing all this, don’t allow physical access or interactive logon access to ANY non-Domain Admin for ANY RWDC. The same is true for backups, snapshots and IFM sets from ANY RWDC!

I would not have any problems when the switch goes from a RWDC to a RODC. But, what is the world without having good friends watching over your shoulder? Thanks Dean!

I was made aware of a scenario that could occur when you want to switch from a RWDC to an RODC. "Now what" you may thing? Well, during the demotion of the RWDC to a member server or a stand alone server the writable NTDS.DIT file is removed/delete. So? Let’s say it becomes a stand alone server and the Domain Admin wants to replace the RWDC with an RODC using a staged (delegated) demotion [1]. The Domain Admin performs the first stage by pre-creating the RODC objects in AD and delegated the second stage to a NON-Domain Admin. The NON-Domain Admin logs to the stand alone server for the second stage. The NON-Domain Admin hates the other Domain Admins because he is not a Domain Admin or for whatever reason he would do anything to become one or just do some damage to the environment. Because the Domain Admin knows the stand alone server WAS an RWDC, the NTDS.DIT is still on the hard disk of the server and could be recovered using whatever UNDELETE/RECOVERY software that recovers deleted/lost files. From the recovered NTDS.DIT he could hack his way into the NTDS.DIT and extract the password hashes. Having the password hashes he could crack the password hashes to real passwords and misuse them. But why all the trouble and work? Why not just use the password hash with a "RUNAS-like" tool? That kinda software is also available on the internet!!! In the end he is able to misuse the password hash of the Domain Admin accounts and do all kinds of stuff that can turn into one heck of a nightmare!

So what can you do? After the demotion of the RWDC, do not hand-over the server to the NON-Domain Admin right away. Use the CIPHER tool (by default in the OS) to really remove data from unused disk space. The command for that is: CIPHER /W:<(Top)Folder that contained the NTDS.DIT>

[1] Regarding staged/delegated promotions for RODC see my slides from DEC 2008 and TechDays 2008 which you will find on my blog in different posts

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, Windows Server | Leave a Comment »

 
%d bloggers like this: