' JoinComputer.vbs
' VBScript program to grant permission to a group or user to join a
' computer to the domain. The group NT name and the computer
' Distinguished Name are specified in the program.
'
' ----------------------------------------------------------------------
' Copyright (c) 2003-2010 Richard L. Mueller
' Hilltop Lab web site - http://www.rlmueller.net
' Version 1.0 - July 13, 2003
' Version 1.1 - January 25, 2004 - Modify error trapping.
' Version 1.2 - November 6, 2010 - No need to set objects to Nothing.
'
' You have a royalty-free right to use, modify, reproduce, and
' distribute this script file in any way you find useful, provided that
' you agree that the copyright owner above has no warranty, obligations,
' or liability for such use.

Option Explicit

Const USER_ACCOUNT_RESTRICTIONS = _
    "{4C164200-20C0-11D0-A768-00AA006E0529}"
Const VALIDATED_SPN = "{F3A64788-5306-11D1-A9C5-0000F80367C1}"
Const VALIDATED_DNS_HOST_NAME = "{72E39547-7B18-11D1-ADEF-00C04FD8D5CD}"
Const RESET_PASSWORD_GUID = "{00299570-246D-11D0-A768-00AA006E0529}"
Const ADS_RIGHT_DS_CONTROL_ACCESS = &H100
Const ADS_RIGHT_DS_WRITE_PROP = &H20
Const ADS_RIGHT_DS_SELF = &H8
Const ADS_ACETYPE_ACCESS_ALLOWED = &H0
Const ADS_ACETYPE_ACCESS_DENIED = &H1
Const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = &H5
Const ADS_ACETYPE_ACCESS_DENIED_OBJECT = &H6
Const ADS_ACEFLAG_INHERITED_ACE = &H10
Const ADS_ACEFLAG_OBJECT_TYPE_PRESENT = &H1

Dim objSecDescriptor, objDACL, objComputer
Dim strComputerDN, strTrustee
Dim objACE1, objACE2, objACE3, objACE4

' Specify the trustee - group NT name in form "MyDomain\GroupNTName".
strTrustee = "MyDomain\Marketing"

' Bind to the computer object with the LDAP provider.
strComputerDN = "cn=Mrktg23,cn=Computers,dc=MyDomain,dc=com"
On Error Resume Next
Set objComputer = GetObject("LDAP://" & strComputerDN)
If (Err.Number <> 0) Then
    On Error GoTo 0
    Wscript.Echo "Computer not found" & vbCrLf & strComputerDN
    Wscript.Quit(1)
End If
On Error GoTo 0

' Bind to the computer security objects.
On Error Resume Next
Set objSecDescriptor = objComputer.Get("ntSecurityDescriptor")
If (Err.Number <> 0) Then
    On Error GoTo 0
    Wscript.Echo "Cannot retrieve security descriptor" & vbCrLf _
        & "ADsSecurity.dll may not be registered on this computer" _
        & vbCrLf & "Program aborted"
    Wscript.Quit
End If
On Error GoTo 0
Set objDACL = objSecDescriptor.discretionaryAcl

' Create ACE 1 - Write Account Restrictions.
Set objACE1 = CreateObject("AccessControlEntry")
objACE1.Trustee = strTrustee
objACE1.AceFlags = 0
objACE1.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
objACE1.Flags = ADS_ACEFLAG_OBJECT_TYPE_PRESENT
objACE1.objectType = USER_ACCOUNT_RESTRICTIONS
objACE1.AccessMask = ADS_RIGHT_DS_WRITE_PROP
objDACL.AddAce objACE1

' Create ACE 2 - Validated Write to Service Principal Name.
Set objACE2 = CreateObject("AccessControlEntry")
objACE2.Trustee = strTrustee
objACE2.AceFlags = 0
objACE2.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
objACE2.Flags = ADS_ACEFLAG_OBJECT_TYPE_PRESENT
objACE2.objectType = VALIDATED_SPN
objACE2.AccessMask = ADS_RIGHT_DS_SELF
objDACL.AddAce objACE2

' Create ACE 3 - Validated Write to DNS Host Name.
Set objACE3 = CreateObject("AccessControlEntry")
objACE3.Trustee = strTrustee
objACE3.AceFlags = 0
objACE3.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
objACE3.Flags = ADS_ACEFLAG_OBJECT_TYPE_PRESENT
objACE3.objectType = VALIDATED_DNS_HOST_NAME
objACE3.AccessMask = ADS_RIGHT_DS_SELF
objDACL.AddAce objACE3

' Create ACE 4 - Reset Password.
Set objACE4 = CreateObject("AccessControlEntry")
objACE4.Trustee = strTrustee
objACE4.AceFlags = 0
objACE4.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
objACE4.Flags = ADS_ACEFLAG_OBJECT_TYPE_PRESENT
objACE4.objectType = RESET_PASSWORD_GUID
objACE4.AccessMask = ADS_RIGHT_DS_CONTROL_ACCESS
objDACL.AddAce objACE4

' Reorder ACE's in DACL.
objSecDescriptor.discretionaryACL = Reorder(objDACL)
      
' Update the Computer object.
On Error Resume Next
objComputer.Put "ntSecurityDescriptor", objSecDescriptor
If (Err.Number <> 0) Then
    On Error GoTo 0
    Wscript.Echo "Security descriptor cannot be saved" _
        & vbCrLf & "Trustee may not exist"
    Wscript.Quit
End If
On Error GoTo 0
objComputer.SetInfo

Wscript.Echo "Done"

Function Reorder(objDACL)
    ' Reorder ACE's in DACL.

    Dim objNewDACL, objInheritedDACL, objAllowDACL, objDenyDACL
    Dim objAllowObjectDACL, objDenyObjectDACL, objACE

    Set objNewDACL = CreateObject("AccessControlList")
    Set objInheritedDACL = CreateObject("AccessControlList")
    Set objAllowDACL = CreateObject("AccessControlList")
    Set objDenyDACL = CreateObject("AccessControlList")
    Set objAllowObjectDACL = CreateObject("AccessControlList")
    Set objDenyObjectDACL = CreateObject("AccessControlList")

    For Each objACE In objDACL
        If ((objACE.AceFlags And ADS_ACEFLAG_INHERITED_ACE) = _
                ADS_ACEFLAG_INHERITED_ACE) Then
            objInheritedDACL.AddAce objACE
        Else
            Select Case objACE.AceType
                Case ADS_ACETYPE_ACCESS_ALLOWED
                    objAllowDACL.AddAce objACE
                Case ADS_ACETYPE_ACCESS_DENIED
                    objDenyDACL.AddAce objACE
                Case ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
                    objAllowObjectDACL.AddAce objACE
                Case ADS_ACETYPE_ACCESS_DENIED_OBJECT
                    objDenyObjectDACL.AddAce objACE
            End Select
        End If
    Next
        
    For Each objACE In objDenyDACL
        objNewDACL.AddAce objACE
    Next
      
    For Each objACE In objDenyObjectDACL
        objNewDACL.AddAce objACE
    Next
        
    For Each objACE In objAllowDACL
        objNewDACL.AddAce objACE
    Next
        
    For Each objACE In objAllowObjectDACL
        objNewDACL.AddAce objACE
    Next
        
    For Each objACE In objInheritedDACL
        objNewDACL.AddAce objACE
    Next
        
    objNewDACL.ACLRevision = objDACL.ACLRevision
    Set Reorder = objNewDACL

End Function