User Tools

Site Tools


userspace:active_directory_-_join_leave_relocate_ou

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
userspace:active_directory_-_join_leave_relocate_ou [2015/09/30 17:46]
larsg created
userspace:active_directory_-_join_leave_relocate_ou [2021/08/23 08:37] (current)
Line 1: Line 1:
-====== Active Directory client management, join/leave/relocate ou ======+====== Active Directory client management, join/unjoin/relocate ou ======
  
-   created by LarsG [[lars.gruenheid@civitec.de]] 2015/09/30+created by LarsG [[lars.gruenheid@civitec.de]] 2015/09/30
  
    * tested under windows 7    * tested under windows 7
Line 7: Line 7:
    * tested under winst 4.11.5.14    * tested under winst 4.11.5.14
  
-With this package, you can join or leave a domain, or change the ou-path for the client within a domain (still experimental, details below). +With this package, you can join or leave a domain, and in theory change the ou-path for the client within a domain (still experimental, details below). 
-These three functions are conveniently assigned to the action requests setup (join), uninstall (leave), update (relocate).+These three functions are conveniently assigned to the action requests setup (join), uninstall (unjoin), update (relocate)
 + 
 +This package relies on three product properties: 
 +   * **domain_ou** 
 + 
 +''domain_ou'' must follows this syntax: ''[domain.tld][/ou_1/ou_2]'', both segments are independent and optional  
 +and only taken into account when joining a domain or relocating to another ou. when a client shall leave a domain, required information are gathered  
 +from operating system. 
 + 
 +if no domain is specified, it's being extracted from host identifier. 
 +if no ou is specified, the client will be placed in the default computer ou-path for the domain. 
 +each ou needs a leading forward-slash, all have to be in the right order, beginning at the top-most level. 
 + 
 +   * **username** 
 +   * **password** 
 + 
 +''username'' must include the domain it belongs to, either like ''DOMAIN\username'' or ''username@domain.tld'', and it has to be   
 +an account with sufficient privileges to join/unjoin clients to/from the domain(s) you want to manage. 
 + 
 +''username'' and ''password'' are prunned from productproperties upon every successful execution, so that they won't remain for  
 +everyone to see in cleartext. //i hope an option for password-masking in productproperties will be available soon.// 
 + 
 +==== Setup ==== 
 +if a client currently is in a domain and shall join another, this script will try to unjoin from the current domain, 
 +and then joined to the new domain, with the same administrative account you provided. so you will need one account with sufficient privileges for 
 +both domains, f.e. a trusted management domain containing such administrative accounts. otherwise, you have to do both steps 
 +seperately - first unjoin, then join, with different accounts.
  
-* --- S --- 
 <code winst> <code winst>
-[actions+[Actions] 
-</code winst>+noUpdateScript 
 + 
 +defVar $DomainRaw$ 
 +defVar $Domain$ 
 +defVar $DomainCurrent$ 
 +defVar $OUPath$ 
 +defVar $OUPathCurrent$ 
 +defVar $Username$ 
 +defVar $Password$ 
 +defVar $ExitCode$ 
 +defVar $JoinMode$ 
 +defVar $UnJoinMode$ 
 +defVar $JOIN_DOMAIN$ 
 +defVar $ACCT_CREATE$ 
 +defVar $ACCT_DELETE$ 
 +defVar $WIN9X_UPGRADE$ 
 +defVar $DOMAIN_JOIN_IF_JOINED$ 
 +defVar $JOIN_UNSECURE$ 
 +defVar $MACHINE_PASSWORD_PASSED$ 
 +defVar $DEFERRED_SPN_SET$ 
 +defVar $INSTALL_INVOCATION$ 
 +;defVar $ACCT_NO_OPTIONS$ 
 +;defVar $ACCT_DEACTIVATE$ 
 + 
 +;calculate joinmode from possible constants 
 +set $JOIN_DOMAIN$ = "1" 
 +set $ACCT_CREATE$ = "2" 
 +set $ACCT_DELETE$ = "4" 
 +set $WIN9X_UPGRADE$ = "16" 
 +set $DOMAIN_JOIN_IF_JOINED$ = "32" 
 +set $JOIN_UNSECURE$ = "64" 
 +set $MACHINE_PASSWORD_PASSED$ = "128" 
 +set $DEFERRED_SPN_SET$ = "256" 
 +set $INSTALL_INVOCATION$ = "262144" 
 +set $JoinMode$ = calculate($JOIN_DOMAIN$+"+"+$ACCT_CREATE$) 
 + 
 +;calculate unjoinmode from possible constants 
 +;set $ACCT_NO_OPTIONS$ = "0" 
 +;set $ACCT_DEACTIVATE$ = "2" 
 +;set $UnJoinMode$ = calculate($ACCT_DEACTIVATE$) 
 + 
 +;product property domain_ou has the following syntax: [domain.tld][/ou_1/ou_2] - both segments are optional. 
 +;domain must be the fqdn for the domain to join 
 +;ou-path must consist of the hierarchical list of ou's the client should be put in, every ou must have a leading slash.  
 +set $DomainRaw$ = getProductProperty("domain_ou",""
 + 
 +;get domain from product property - if domain is not set, get domain from host identifier. 
 +set $Domain$ = takeString(0, splitString($DomainRaw$,"/")) 
 +if ($Domain$ = ""
 + set $Domain$ = composeString(getSubList(1 : ,splitString("%HostId%",".")),"."
 + set $DomainRaw$ = $Domain$ + $DomainRaw$ 
 +endif 
 + 
 +;get ou-path from product property - if ou-path is not set, join client to standard client-ou-path for the domain. 
 +;ou-path will be transformed into ldap-friendly syntax. 
 +set $OUPath$ = "" 
 +for %OU% in getSubList(1 : ,splitString($DomainRaw$,"/")) do set $OUPath$ = "OU=%OU%,"+$OUPath$ 
 +if not ($OUPath$ = ""
 + for %DC% in splitString($Domain$,".") do set $OUPath$ = $OUPath$+"DC=%DC%," 
 + set $OUPath$ = strPart($OUPath$,"1",calculate(strLength($OUPath$)+"-1")) 
 +endif 
 + 
 +;get username + password from, product properties, hide password value in log 
 +set $Username$  = getProductProperty("username",""
 +setConfidential getProductProperty("password",""
 +set $Password$  = getProductProperty("password",""
 + 
 +;check if client is in domain  
 +if ( takeString(0, getOutStreamFromSection('execwith_vbs_check_domain cscript //nologo //e:vbs')) = "0"
 + showBitmap "%ScriptPath%\domain.png" "Active Directory"  
 + message "Join domain " + $DomainRaw$ 
 + execwith_vbs_domain_join cscript //nologo //e:vbs 
 + sub_check_domain_join 
 + dosinanicon_gpupdate /WaitOnClose 
 + comment "Setting default logon domain, clear previous login user" 
 + registry_default_domain /sysnative 
 + exitwindows /Reboot 
 +else 
 + set $DomainCurrent$ = takeString(0, getOutStreamFromSection('execwith_vbs_get_domain cscript //nologo //e:vbs')) 
 + ;check if client is already in domain requested 
 + if not ( $DomainCurrent$ = $Domain$ )  
 + showBitmap "%ScriptPath%\domain.png" "Active Directory"  
 + message "Leave domain " + $DomainCurrent$ 
 + execwith_vbs_domain_unjoin cscript //nologo //e:vbs 
 + sub_check_domain_unjoin 
 + ;TODO remove client from ad 
 + ;TODO check exitcode adsi 
 + exitwindows /ImmediateReboot 
 + else 
 + comment "Client is already in requested domain" 
 + endif 
 +endif 
 + 
 +;reset username + password entries in product properties 
 +opsiservicecall_unset_username 
 +opsiservicecall_unset_password 
 + 
 +[execwith_vbs_check_domain] 
 +Set obj = GetObject("WinMgmts:\\.\root\cimv2:Win32_ComputerSystem.Name='%PCNAME%'"
 +Wscript.Echo obj.PartOfDomain 
 + 
 +[execwith_vbs_get_domain] 
 +Set obj = GetObject("WinMgmts:\\.\root\cimv2:Win32_ComputerSystem.Name='%PCNAME%'"
 +Wscript.Echo obj.Domain 
 + 
 +[execwith_vbs_domain_join] 
 +Set obj = GetObject("WinMgmts:\\.\root\cimv2:Win32_ComputerSystem.Name='%PCNAME%'"
 +res = obj.JoinDomainOrWorkGroup("$Domain$", "$Password$", "$Username$", "$OUPath$", $JoinMode$) 
 +Wscript.Quit res 
 +  
 +[execwith_vbs_domain_unjoin] 
 +Set obj = GetObject("WinMgmts:\\.\root\cimv2:Win32_ComputerSystem.Name='%PCNAME%'"
 +res = obj.UnjoinDomainOrWorkgroup("$Password$", "$Username$"
 +;res = obj.UnjoinDomainOrWorkgroup("$Password$", "$Username$", $UnJoinMode$) 
 +Wscript.Quit res 
 + 
 +[dosinanicon_gpupdate] 
 +gpupdate /force 
 +exit %ERRORLEVEL% 
 + 
 +[registry_default_domain] 
 +openkey [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] 
 +set "DefaultDomainName"    = REG_SZ:"$Domain$" 
 +set "AltDefaultDomainName" = REG_SZ:"$Domain$" 
 +set "CachePrimaryDomain"   = REG_SZ:"$Domain$" 
 +openKey [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI] 
 +set "LastLoggedOnSAMUser" = REG_SZ:"" 
 +set "LastLoggedOnUser"    = REG_SZ:"" 
 + 
 +[opsiservicecall_unset_username] 
 +"method": "setProductProperty" 
 +"params":
 + "%installingProdName%", 
 + "username", 
 + "", 
 + "%hostid%" 
 +
 + 
 +[opsiservicecall_unset_password] 
 +"method": "setProductProperty" 
 +"params":
 + "%installingProdName%", 
 + "password", 
 + "", 
 + "%hostid%" 
 +
 + 
 +[sub_check_domain_unjoin] 
 +set $ExitCode$ = getLastExitCode 
 +if $ExitCode$ = "0"  
 + comment "Success" 
 +else 
 + logError "UNKNOWN ERROR " & ReturnValue 
 + isFatalError 
 +endif 
 + 
 +[sub_check_domain_join] 
 +set $ExitCode$ = getLastExitCode 
 +if $ExitCode$ = "0"  
 + comment "Success" 
 +else 
 +if $ExitCode$ = "2"  
 + logError "Missing OU" 
 + isFatalError 
 +else 
 +if $ExitCode$ = "5" 
 + logError "Access denied" 
 + isFatalError 
 +else 
 +if $ExitCode$ = "53"  
 + logError "Network path not found" 
 + isFatalError 
 +else 
 +if $ExitCode$ = "87" 
 + logError "Parameter incorrect" 
 + isFatalError 
 +else 
 +if $ExitCode$ = "1326"  
 + logError "Logon failure, user or pass" 
 + isFatalError 
 +else 
 +if $ExitCode$ = "1355"  
 + logError "Domain can not be contacted" 
 + isFatalError 
 +else 
 +if $ExitCode$ = "1909"  
 + logError "User account locked out" 
 + isFatalError 
 +else 
 +if $ExitCode$ = "2224"  
 + logError "Computer Account allready exists" 
 + isFatalError 
 +else 
 +if $ExitCode$ = "2691"  
 + logError "Allready joined" 
 + isFatalError 
 +else 
 + logError "Unknown error " & ReturnValue 
 + isFatalError 
 +endif 
 +endif 
 +endif 
 +endif 
 +endif 
 +endif 
 +endif 
 +endif 
 +endif 
 +endif 
 +</code> 
 + 
 + 
 + 
 +==== Uninstall ==== 
 +It seems that actually deleting computer accounts from a domain upon unjoin is currently not possible, so keep in mind that you need to manually delete the account if you want it to be gone, f.e. to re-use the name for another computer. //I am planning to add this as an optional feature.// 
 + 
 +<code winst> 
 +[Actions] 
 +defVar $DomainCurrent$ 
 +defVar $Username$ 
 +defVar $Password$ 
 +defVar $ExitCode$ 
 +defVar $UnJoinMode$ 
 +;defVar $ACCT_NO_OPTION$ 
 +;defVar $ACCT_DEACTIVATE$ 
 + 
 +;calculate unjoinmode from possible constants 
 +;set $ACCT_NO_OPTION$ = "0" 
 +;set $ACCT_DEACTIVATE$ = "2" 
 +;set $UnJoinMode$ = calculate($ACCT_DEACTIVATE$) 
 + 
 +;get username + password from, product properties, hide password value in log 
 +set $Username$  = getProductProperty("username",""
 +setConfidential getProductProperty("password",""
 +set $Password$  = getProductProperty("password",""
 + 
 +;reset username + password entries in product properties 
 +;opsiservicecall_unset_username 
 +;opsiservicecall_unset_password 
 + 
 +;check if client is in domain  
 +if not ( takeString(0, getOutStreamFromSection('execwith_vbs_check_domain cscript //nologo //e:vbs')) = "0"
 + set $DomainCurrent$ = takeString(0, getOutStreamFromSection('execwith_vbs_get_domain cscript //nologo //e:vbs')) 
 + showBitmap "%ScriptPath%\domain.png" "Active Directory"  
 + message "Leave domain " + $DomainCurrent$ 
 + ;unjoin domain 
 + execwith_vbs_domain_unjoin cscript //nologo //e:vbs 
 + sub_check_domain_unjoin 
 + ;TODO remove client from ad 
 + ;TODO check exitcode adsi 
 + ;restart client, script will be re-executed upon next opsi request  
 + exitwindows /Reboot 
 +else 
 + comment "Computer is currently not part of a domain" 
 +endif 
 + 
 +;reset username + password entries in product properties 
 +opsiservicecall_unset_username 
 +opsiservicecall_unset_password 
 + 
 +[execwith_vbs_check_domain] 
 +Set obj = GetObject("WinMgmts:\\.\root\cimv2:Win32_ComputerSystem.Name='%PCNAME%'"
 +Wscript.Echo obj.PartOfDomain 
 + 
 +[execwith_vbs_get_domain] 
 +Set obj = GetObject("WinMgmts:\\.\root\cimv2:Win32_ComputerSystem.Name='%PCNAME%'"
 +Wscript.Echo obj.Domain 
 + 
 +[execwith_vbs_domain_unjoin] 
 +Set obj = GetObject("WinMgmts:\\.\root\cimv2:Win32_ComputerSystem.Name='%PCNAME%'"
 +res = obj.UnjoinDomainOrWorkgroup("$Password$", "$Username$"
 +;res = obj.UnjoinDomainOrWorkgroup("$Password$", "$Username$", $UnJoinMode$) 
 +Wscript.Quit res 
 + 
 +[opsiservicecall_unset_username] 
 +"method": "setProductProperty" 
 +"params":
 + "%installingProdName%", 
 + "username", 
 + "", 
 + "%hostid%" 
 +
 + 
 +[opsiservicecall_unset_password] 
 +"method": "setProductProperty" 
 +"params":
 + "%installingProdName%", 
 + "password", 
 + "", 
 + "%hostid%" 
 +
 + 
 +[sub_check_domain_unjoin] 
 +set $ExitCode$ = getLastExitCode 
 +if $ExitCode$ = "0" 
 + comment "Success" 
 +else 
 + logError "UNKNOWN ERROR " & ReturnValue 
 + isFatalError 
 +endif 
 +</code> 
 + 
 +==== Update ==== 
 +Relocating a client to a different ou within the same domain is still giving me some headache, 
 +i am currently stuck at the part where the ADSI movehere function actually performs the relocation, 
 +it will throw an error ''Active Directory: not implemented'' (what ever that means). 
 + 
 +If anyone can get this to work, i wouldn't mind a heads up (; 
 + 
 +<code winst> 
 +[Actions] 
 +defVar $DomainRaw$ 
 +defVar $Domain$ 
 +defVar $DomainCurrent$ 
 +defVar $OUPath$ 
 +defVar $OUPathCurrent$ 
 +defVar $Username$ 
 +defVar $Password$ 
 +defVar $ExitCode$ 
 + 
 +;product property domain_ou has the following syntax: [domain.tld][/ou_1/ou_2] - both segments are optional. 
 +;domain must be the fqdn for the domain to join 
 +;ou-path must consist of the hierarchical list of ou's the client should be put in, every ou must have a leading slash.  
 +set $DomainRaw$ = getProductProperty("domain_ou",""
 + 
 +;get domain from product property - if domain is not set, get domain from host identifier. 
 +set $Domain$ = takeString(0, splitString($DomainRaw$,"/")) 
 +if ($Domain$ = ""
 + set $Domain$ = composeString(getSubList(1 : ,splitString("%HostId%",".")),"."
 + set $DomainRaw$ = $Domain$ + $DomainRaw$ 
 +endif 
 + 
 +;get ou-path from product property - if ou-path is not set, join client to standard client-ou-path for the domain. 
 +;ou-path will be transformed into ldap-friendly syntax. 
 +set $OUPath$ = "" 
 +for %OU% in getSubList(1 : ,splitString($DomainRaw$,"/")) do set $OUPath$ = "OU=%OU%,"+$OUPath$ 
 +if not ($OUPath$ = ""
 + for %DC% in splitString($Domain$,".") do set $OUPath$ = $OUPath$+"DC=%DC%," 
 + set $OUPath$ = strPart($OUPath$,"1",calculate(strLength($OUPath$)+"-1")) 
 +endif 
 + 
 +;get username + password from, product properties, hide password value in log 
 +set $Username$  = getProductProperty("username",""
 +setConfidential getProductProperty("password",""
 +set $Password$  = getProductProperty("password",""
 + 
 +if not ( takeString(0, getOutStreamFromSection('execwith_vbs_check_domain cscript //nologo //e:vbs')) = "0"
 + set $DomainCurrent$ = takeString(0, getOutStreamFromSection('execwith_vbs_get_domain cscript //nologo //e:vbs')) 
 + ;check if client is in domain requested 
 + if ( $DomainCurrent$ = $Domain$ )  
 + set $OUPathCurrent$ = takeString(0, getOutStreamFromSection('execwith_vbs_get_oupath cscript //nologo //e:vbs')) 
 + ;check if client is already in ou-path requested 
 + if not ( $OUPathCurrent$ = $OUPath$ ) 
 + showBitmap "%ScriptPath%\domain.png" "Active Directory"  
 + message "Relocate to ou-path " + $DomainRaw$ 
 + execwith_vbs_move_oupath cscript //nologo //e:vbs 
 + ;TODO check exitcode adsi 
 + dosinanicon_gpupdate /WaitOnClose 
 + else 
 + comment "Computer is already in the given ou-path" 
 + endif 
 + else 
 + logError "Computer is not part of the domain requested, moving to another ou-path only works within the same domain" 
 + isFatalError 
 + endif 
 +else 
 + logError "Computer is currently not part of a domain" 
 + isFatalError 
 +endif 
 + 
 +[execwith_vbs_check_domain] 
 +Set obj = GetObject("WinMgmts:\\.\root\cimv2:Win32_ComputerSystem.Name='%PCNAME%'"
 +Wscript.Echo obj.PartOfDomain 
 + 
 +[execwith_vbs_get_domain] 
 +Set obj = GetObject("WinMgmts:\\.\root\cimv2:Win32_ComputerSystem.Name='%PCNAME%'"
 +Wscript.Echo obj.Domain 
 + 
 +[execwith_vbs_get_oupath] 
 +Set adoConnection = CreateObject("ADODB.Connection"
 +adoConnection.Provider = "ADsDSOObject" 
 +adoConnection.Properties("User ID") = "$Username$" 
 +adoConnection.Properties("Password") = "$Password$" 
 +adoConnection.Properties("Encrypt Password") = True 
 +Set adoCommand = CreateObject("ADODB.Command"
 +adoConnection.Properties("ADSI Flag") = &H200 Or &H1 
 +adoConnection.Open "Active Directory Provider" 
 +Set adoCommand.ActiveConnection = adoConnection 
 +adoCommand.CommandText = "Select distinguishedName from 'LDAP://$Domain$' where objectClass='computer' and cn='%PCNAME%'" 
 +adoCommand.Properties("Searchscope") = 2 
 +Set adoRecordSet = adoCommand.Execute 
 +adoRecordSet.MoveFirst 
 +OUPathCurrent = adoRecordSet.Fields("distinguishedName").Value 
 +Wscript.Echo Mid(OUPathCurrent,InStr(OUPathCurrent,",")+1) 
 + 
 +[execwith_vbs_move_oupath] 
 +Set obj = GetObject("LDAP:"
 +obj.OpenDSObject "LDAP://w8vrsag08.$Domain$/$OUPath$", "$Username$", "$Password$",
 +res = obj.MoveHere("LDAP://CN=%PCNAME%,$OUPathCurrent$", "CN=%PCNAME%"
 +Wscript.Quit res 
 + 
 +[dosinanicon_gpupdate] 
 +gpupdate /force 
 +exit %ERRORLEVEL% 
 + 
 +[opsiservicecall_unset_username] 
 +"method": "setProductProperty" 
 +"params":
 + "%installingProdName%", 
 + "username", 
 + "", 
 + "%hostid%" 
 +
 + 
 +[opsiservicecall_unset_password] 
 +"method": "setProductProperty" 
 +"params":
 + "%installingProdName%", 
 + "password", 
 + "", 
 + "%hostid%" 
 +
 +</code> 
userspace/active_directory_-_join_leave_relocate_ou.1443635160.txt.gz · Last modified: 2021/08/23 08:37 (external edit)