Script to Join a Computer to the Domain

by Jeremy Saunders on November 28, 2009

This script will join a computer to a domain. You can either specify the parameters as command line arguments, or look them up in an ini file.

If an OU is not specified, the value is set to NULL, which means that the computer object will be placed in the OU/Container that is set as the Domain default within Active Directory, which is typically the Computers container.

It also checks to see if the computer object already exists in AD. If it does, it will join to the object in its existing location.

IMPORTANT: Refer to the %SystemRoot%\Debug\NetSetup.LOG file to help fault find any issues with the domain joining process.

Syntax of command line, if specifying arguments:

cscript JoinDomain.vbs “[domain]” “[username]” “[password]” “[MachineAccountOU]”

Example of command line, if specifying arguments:

cscript JoinDomain.vbs “mydomain.com” “build” “password” “OU=Citrix,OU=Servers,DC=myDomain,DC=com”

Example of the contents of the JoinDomain.ini file:

[Identification]
JoinDomain = mydomain.com
DomainAdmin = build
DomainAdminPassword = “password”
MachineObjectOU = “OU=New,OU=Servers,DC=myDomain,DC=com”

Join Domain Script


' This script will join a computer to a domain. You can either specify the parameters as
' command line arguments, or look them up in an ini file.
' If an OU is not specified, the value is set to NULL, which means that the computer
' object will be placed in the OU/Container that is set as the Domain default within
' Active Directory.
' It also checks to see if the computer object already exists in AD. If it does, it will
' join to the object in its existing location.
'
' IMPORTANT: Refer to the %SystemRoot%\Debug\NetSetup.LOG file to help fault find any
'            issues with the domain joining process.
'
' Release 1.2 by Jeremy@jhouseconsulting.com on 5th March 2010.
' Written by Jeremy@jhouseconsulting.com on 13th November 2009.
'
' Syntax of command line, if specifying arguments:
' cscript JoinDomain.vbs "<domain>" "<username>" "<password>" "<MachineAccountOU>"
'
' Example of command line, if specifying arguments:
' cscript JoinDomain.vbs "mydomain.com" "build" "password" "OU=Citrix,OU=Servers,DC=myDomain,DC=com"
'
' Example of the contents of the JoinDomain.ini file:
'
' [Identification]
' JoinDomain = mydomain.com
' DomainAdmin = build
' DomainAdminPassword = "password"
' MachineObjectOU = "OU=New,OU=Servers,DC=myDomain,DC=com"
'
Option Explicit

Const JOIN_DOMAIN = 1
Const ACCT_CREATE = 2
Const ACCT_DELETE = 4
Const WIN9X_UPGRADE = 16
Const DOMAIN_JOIN_IF_JOINED = 32
Const JOIN_UNSECURE = 64
Const MACHINE_PASSWORD_PASSED = 128
Const DEFERRED_SPN_SET = 256
Const INSTALL_INVOCATION = 262144

Dim wshShell, objFSO, sScriptFullName, sScriptPath, blnUseArgs, strDomain, strUser
Dim strPassword, strOU, objNetwork, strComputer, strExistingLocation, objComputer
Dim intReturn, strSystemDrive, striniFileLocation, striniFile, strErrorDescription
Dim blnDebug

blnDebug = False

Set wshShell = CreateObject("wscript.shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
sScriptFullName = WScript.ScriptFullName
sScriptPath = Left(sScriptFullName, InStrRev(sScriptFullName, "\"))
strSystemDrive = WshShell.ExpandEnvironmentStrings("%SystemDrive%")
striniFileLocation =strSystemDrive & "\wininst\"
striniFile = striniFileLocation & "JoinDomain.ini"
If NOT objFSO.FileExists(striniFile) Then
  striniFile = sScriptPath & "JoinDomain.ini"
End If

If WScript.Arguments.Count < 3 Then
  blnUseArgs = False
Else
  blnUseArgs = True
  strDomain = WScript.Arguments(0)
  strUser = WScript.Arguments(1)
  strPassword = WScript.Arguments(2)
  If WScript.Arguments.Count = 4 Then
    strOU = WScript.Arguments(3)
  End If
End If

If NOT blnUseArgs Then
  If objFSO.FileExists(striniFile) Then
    strDomain = GetINIString("Identification", "JoinDomain", "", striniFile)
    strDomain = Replace(strDomain,"""","")
    strUser = GetINIString("Identification", "DomainAdmin", "", striniFile)
    strUser = Replace(strUser,"""","")
    strPassword = GetINIString("Identification", "DomainAdminPassword", "", striniFile)
    strPassword = Replace(strPassword,"""","")
    strOU = GetINIString("Identification", "MachineObjectOU", "", striniFile)
    strOU = Replace(strOU,"""","")
    If blnDebug Then
      wscript.echo "Domain: " & strDomain & vbcrlf & _
                   "User: " & strUser & vbcrlf & _
                   "Password: " & strPassword & vbcrlf & _
                   "OU: " & strOU
    End If
  End If
End If

If strDomain <> "" AND strUser <> "" AND strPassword <> "" Then

  If strOU = "" Then
    strOU = NULL
  End If

  Set objNetwork = CreateObject("WScript.Network")
  strComputer = objNetwork.ComputerName

  strExistingLocation = getHostOU(strComputer,strDomain,strUser,strPassword)
  If strExistingLocation <> "" Then
    strOU = strExistingLocation
  End If

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

  wscript.echo "Attempting to join the " & chr(34) & strDomain & chr(34) & " Domain."
  If NOT IsNULL(strOU) Then
    wscript.echo "The Computer Object will be placed in the following OU:" & vbcrlf & _
                 chr(34) & strOU & chr(34)
  End If

  intReturn = objComputer.JoinDomainOrWorkGroup(strDomain, strPassword, strDomain & "\" & strUser, _
              strOU, JOIN_DOMAIN + ACCT_CREATE)

  Select Case intReturn
    Case 0
      strErrorDescription = "Successfully added the computer to the " & chr(34) & strDomain & chr(34) & " Domain."
    Case 5
      strErrorDescription = "Access is denied"
    Case 87
      strErrorDescription = "The parameter is incorrect"
    Case 110
      strErrorDescription = "The system cannot open the specified object"
    Case 1323
      strErrorDescription = "Unable to update the password"
    Case 1326
      strErrorDescription = "Logon failure: unknown username or bad password"
    Case 1355
      strErrorDescription = "The specified domain either does not exist or could not be contacted"
    Case 2224
      strErrorDescription = "The account already exists"
    Case 2691
      strErrorDescription = "The machine is already joined to the domain"
    Case 2692
      strErrorDescription = "The machine is not currently joined to a domain"
  End Select
  wscript.echo strErrorDescription

End If

Set wshShell = Nothing
Set objFSO = Nothing
Set objNetwork = Nothing
Set objComputer = Nothing

wscript.quit(0)

Function GetINIString(Section, KeyName, Default, FileName)
  Dim INIContents, PosSection, PosEndSection, sContents, Value, Found
  
  'Get contents of the INI file As a string
  INIContents = GetFile(FileName)

  'Find section
  PosSection = InStr(1, INIContents, "[" & Section & "]", vbTextCompare)
  If PosSection>0 Then

    'Section exists. Find end of section
    PosEndSection = InStr(PosSection, INIContents, vbCrLf & "[")
    '?Is this last section?
    If PosEndSection = 0 Then PosEndSection = Len(INIContents)+1
    
    'Separate section contents
    sContents = Mid(INIContents, PosSection, PosEndSection - PosSection)

    If InStr(1, sContents, vbCrLf & KeyName & "=", vbTextCompare)>0 Then
      Found = True
      'Separate value of a key.
      Value = SeparateField(sContents, vbCrLf & KeyName & "=", vbCrLf)
    ElseIf InStr(1, sContents, vbCrLf & KeyName & " =", vbTextCompare)>0 Then
      Found = True
      'Separate value of a key.
      Value = SeparateField(sContents, vbCrLf & KeyName & " =", vbCrLf)
    End If
  End If

  If isempty(Found) Then Value = Default
  GetINIString = Trim(Value)
End Function

'Separates one field between sStart And sEnd
Function SeparateField(ByVal sFrom, ByVal sStart, ByVal sEnd)
  Dim PosB: PosB = InStr(1, sFrom, sStart, 1)
  If PosB > 0 Then
    PosB = PosB + Len(sStart)
    Dim PosE: PosE = InStr(PosB, sFrom, sEnd, 1)
    If PosE = 0 Then PosE = InStr(PosB, sFrom, vbCrLf, 1)
    If PosE = 0 Then PosE = Len(sFrom) + 1
    SeparateField = Mid(sFrom, PosB, PosE - PosB)
  End If
End Function

Function GetFile(ByVal FileName)
  Dim FS: Set FS = CreateObject("Scripting.FileSystemObject")
  'Go To windows folder If full path Not specified.
  If InStr(FileName, ":\") = 0 And Left (FileName,2)<>"\\" Then
    FileName = FS.GetSpecialFolder(0) & "\" & FileName
  End If
  On Error Resume Next

  GetFile = FS.OpenTextFile(FileName).ReadAll
  Set FS = Nothing
End Function

Function getHostOU(hostname,strDomain,strUser,strPassword)

  Dim arrDefaultNamingContext
  Dim item
  Dim strDefaultNamingContext
  Dim strADsPath
  Dim strBase
  Dim strFilter
  Dim strAttributes
  Dim strScope
  Dim strCommandText
  Dim objADOConnection
  Dim objADOCommand
  Dim objADORecordset
  Dim hostDN, hostOU

' ADS Authentication constants that can be used.
  Const ADS_SECURE_AUTHENTICATION = &H1
  Const ADS_USE_ENCRYPTION = &H2
  Const ADS_USE_SSL = &H2
  Const ADS_USE_SIGNING = &H40
  Const ADS_USE_SEALING = &H80
  Const ADS_USE_DELEGATION = &H100
  Const ADS_SERVER_BIND = &H200

' First, need to set the correct Default Naming Context
'  arrDefaultNamingContext = Split(strDomain,".")
'  For Each item in arrDefaultNamingContext
'    If  strDefaultNamingContext = "" Then
'      strDefaultNamingContext =  "DC=" & item
'    Else
'      strDefaultNamingContext =  strDefaultNamingContext & ",DC=" & item
'    End If
'  Next
  strDefaultNamingContext = strDomain
  strADsPath = "LDAP://" & strDefaultNamingContext
  strBase = "<" & strADsPath & ">"

' Specify the LDAP filter
  strFilter = "(&(objectClass=computer)(cn=" & UCase(hostname) & "))"
  strAttributes = "cn,distinguishedName,ADsPath"
' Specify the scope (base, onelevel, subtree)
  strScope = "subtree"

  strCommandText = strBase & ";" & strFilter & ";" & strAttributes & ";" & strScope

' Create ADO connection using the ADSI OLE DB provider
  Set objADOConnection = CreateObject("ADODB.Connection")
  Set objADOCommand = CreateObject("ADODB.Command")
  objADOConnection.Provider = "ADsDSOObject"
  objADOConnection.Properties("User ID") = strDomain & "\" & strUser
  objADOConnection.Properties("Password") = strPassword
  objADOConnection.Properties("Encrypt Password") = True
  objADOConnection.Properties("ADSI Flag") = ADS_SERVER_BIND Or ADS_SECURE_AUTHENTICATION
  objADOConnection.open "Active Directory Provider"
  objADOCommand.activeconnection = objADOConnection
  objADOCommand.CommandText = strCommandText
  objADOCommand.Properties("Page Size") = 2000
  objADOCommand.Properties("Sort On") = "cn"

  On Error Resume Next
  Set objADORecordset = objADOCommand.Execute
  If Err.Number = 0 Then
    While Not objADORecordset.EOF
      hostDN = objADORecordset.Fields("distinguishedName")
      objADORecordset.MoveNext
    Wend
    objADOConnection.Close

    If Len(hostDN) > 0 Then
      hostOU = Right(hostDN, (Len(hostDN) - InStr(hostDN, ",")))
    Else
      hostOU =""
    End If

    If Len(hostOU) > 0 Then
      getHostOU = hostOU
    Else
      getHostOU = ""
    End If
  Else
    wscript.echo "Cannot connect to the Active Directory Provider to check for the existence of the Computer Object."
    getHostOU = ""
  End If
  On Error Goto 0
  Err.Clear

  Set objADOConnection = Nothing
  Set objADOCommand = Nothing
  Set objADORecordset = Nothing
End Function
Jeremy Saunders

Jeremy Saunders

Independent Consultant | Contractor | Microsoft & Citrix Specialist | Desktop Virtualization Specialist at J House Consulting
Jeremy is a highly respected, IT Professional, with over 30 years’ experience in the industry. He is an independent IT consultant providing expertise to enterprise, corporate, higher education and government clients. His skill set, high ethical standards, integrity, morals and attention to detail, coupled with his friendly nature and exceptional design and problem solving skills, makes him one of the most highly respected and sought after Microsoft and Citrix technical resources in Australia. His alignment with industry and vendor best practices puts him amongst the leaders of his field.
Jeremy Saunders
Jeremy Saunders
Jeremy Saunders

Previous post:

Next post: