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!

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

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

22 Responses to “(2009-01-01) Domain Join through an RODC instead of an RWDC”

  1. Hello Jorge,

    wonderful article! It will save us a lot of work if I can make it work.
    But it keeps giving me error 1354:

    ERROR_INVALID_DOMAIN_ROLE1354 0x54A
    This operation is only allowed for the Primary Domain Controller of the domain.

    This is crazy because an RODC cannot be a PDC.
    Thanks for your help!

    Like

  2. Jorge said

    Is the PDC on W2K8? Just checking, not saying it MUST be…

    Like

  3. Hi,

    I get the same error as Andrea, do you have a solution to this? The RODC is a 2008R2 server, and the client are a W7 enterprise x86.

    Thank you!

    Like

  4. Jakub said

    Hi,

    there is a mistake in the server. Creating ObjComputer must be defined like this:

    Set objComputer = GetObject(“winmgmts:{impersonationLevel=Impersonate}!\\” & strComputer & “\root\cimv2:Win32_ComputerSystem.Name='” & strComputer & “‘”)

    otherwise you get an error with comunicating with WMI Provider.

    Jakub

    Like

  5. Hakan said

    I can’t also join the domain. I have a member Windows 2008 R2 with Windows 2008 R2 RODC.

    Like

  6. […] 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 […]

    Like

  7. Steven said

    not working. i always get an 1326 failure uknown username or bad passwort.
    what kind of firewall ports have to be opened between the clients and the RODC?

    Like

  8. Having an issue where when I run the script, it gives me: “C:\Temp\DomainJoinAgainstRODC.vbs(10, 1) (null): 0x8004100E – Looking for some help! 🙂

    Like

  9. Mac MacAnanny said

    Could you post fixed code in the original post…”\”s are missing in the WMI connection string and other places, spaces need to be in the domain join string between the final options and the “+”s

    I got it to work, after looking up all the calls and getting the proper formatting. We had issues with 2012 R2 servers joining a 2008 R2 RODC in a externally firewalled domain, this script works great, just needed the proper formatting.

    Here is the corrected script that worked for me: wmi string, missing “\”s added, JoinDomainOrWorkGroup string, missing ” “(spaces) added between +’s

    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 & “\root\cimv2: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)

    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 ###”)

    Like

    • Mac MacAnanny said


      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 & “\root\cimv2: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)

      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 ###”)

      Like

    • Jorge said

      you should be using the powershell version of this script!
      see: https://jorgequestforknowledge.wordpress.com/2014/04/04/domain-join-through-an-rodc-instead-of-an-rwdc-update-1/

      Like

  10. […] 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 […]

    Like

  11. Pedram said

    Hello, will this script work for adding a windows 10 system to a domain, running on 2012r2? thank you in advance.

    Like

    • Jorge said

      Hi,

      This script is for adding machines to the domain by using the Read-Only domain join method against an RODC. It should also work against RWDCs and it should also work with Win10. However, the prerequisite is that the computer account must exist with a known password. You can adjust the script as you need to perform a regular join and not require a pre-created computer account. The values for that method are also included in the script

      Best Regards,
      Jorge

      Like

  12. ced666 said

    I used the Read-Only-Domain-Join.ps1 script (adDomain COMPANY.COM -rodcFQDN RODC1.COMPANY.COM -ipAddressLocalHost 192.168.6.3 -compAccountPWD ‘MyPa $$ w0rd’) to integrate a Windows 2012 member server R2 in the active directory via RODC.
    The script runs without any difficulty.
    After joining, server authentication in Active Directory via RODC does not work. The following error message: Remote Access Server unavailable.
    For information, I created the computer object in the Active Directory and created the DNS record and cached the password of the server.

    What could block, all the prerequisites are fulfilled, the ports are open between the Windows 2012 R2 server and the RODC controller on Windows 2012 R2.

    Do you have an idea ?

    Thanks for your help.

    Like

    • Jorge said

      Hi,

      The script directs the authentication and the joining towards the RODC. So far so good. After the machine is joined, something/someone needs to tell the joined machine in which AD site it is in as it at that moment does not that.

      This works like explained below in general…
      For SRV records check: https://jorgequestforknowledge.wordpress.com/2011/09/11/service-srv-locator-records-registered-by-windows-domain-controllers/
      When a machine does not know in which AD site it is, it does know one thing and that is the domain it has been joined to. Therefore based upon the domain name of the domain the machine is joined to, it queries its DNS server (could be the RODC if DNS is running on it) for all RWDCs thsat have registered the following SRV record: _ldap._tcp.dc._msdcs.DnsDomainName. By default all RWDCs register that SRV record, but if you have a hub and spoke model, only the HUB based RWDCs should register those records.
      The machine that is joined to the AD domain contacts that RWDC using LDAP over UDP (so called LDAP ping). Based upon its IP address that RWDC will tell the machine that is joined to the AD domain in which AD site it is in. The machine that is joined to the AD domain will store the sitename in the DynamicSiteName registry setting for current/future use. The machine that is joined to the AD domain will query the DNS server for all DCs that have registered the following SRV record: _ldap._tcp.SiteName._sites.DnsDomainName. If the RODC is in that same site, it will service that machine.

      Therefore in short: if you want your machines that use an RODC to be able to dynamically determine the site they’re in, you need to allow LDAP over UDP from all DMZ machines to the HUB RWDCs. If you do not want that you need to hard code the site setting yourself in the SiteName registry setting.

      Best regards,
      jorge

      Like

  13. antonio said

    Hello I tried to run the script and I am always receiving this error: Object doesn’t support this property or method: ‘objNetwork.’ any help plz ?

    Like

Leave a comment

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