userspace:active_directory_-_join_leave_relocate_ou
Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| userspace:active_directory_-_join_leave_relocate_ou [2015/09/30 19:46] – created larsg | userspace:active_directory_-_join_leave_relocate_ou [2021/08/23 10:37] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| 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 |
| * 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, | + | With this package, you can join or leave a domain, |
| - | 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** | ||
| + | |||
| + | '' | ||
| + | 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, | ||
| + | |||
| + | * **username** | ||
| + | * **password** | ||
| + | |||
| + | '' | ||
| + | an account with sufficient privileges to join/unjoin clients to/from the domain(s) you want to manage. | ||
| + | |||
| + | '' | ||
| + | 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$ = " | ||
| + | set $ACCT_CREATE$ = " | ||
| + | set $ACCT_DELETE$ = " | ||
| + | set $WIN9X_UPGRADE$ = " | ||
| + | set $DOMAIN_JOIN_IF_JOINED$ = " | ||
| + | set $JOIN_UNSECURE$ = " | ||
| + | set $MACHINE_PASSWORD_PASSED$ = " | ||
| + | set $DEFERRED_SPN_SET$ = " | ||
| + | set $INSTALL_INVOCATION$ = " | ||
| + | set $JoinMode$ = calculate($JOIN_DOMAIN$+" | ||
| + | |||
| + | ;calculate unjoinmode from possible constants | ||
| + | ;set $ACCT_NO_OPTIONS$ = " | ||
| + | ;set $ACCT_DEACTIVATE$ = " | ||
| + | ;set $UnJoinMode$ = calculate($ACCT_DEACTIVATE$) | ||
| + | |||
| + | ;product property domain_ou has the following syntax: [domain.tld][/ | ||
| + | ;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(" | ||
| + | |||
| + | ;get domain from product property - if domain is not set, get domain from host identifier. | ||
| + | set $Domain$ = takeString(0, | ||
| + | if ($Domain$ = "" | ||
| + | set $Domain$ = composeString(getSubList(1 : , | ||
| + | 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 : , | ||
| + | if not ($OUPath$ = "" | ||
| + | for %DC% in splitString($Domain$," | ||
| + | set $OUPath$ = strPart($OUPath$," | ||
| + | endif | ||
| + | |||
| + | ;get username + password from, product properties, hide password value in log | ||
| + | set $Username$ | ||
| + | setConfidential getProductProperty(" | ||
| + | set $Password$ | ||
| + | |||
| + | ;check if client is in domain | ||
| + | if ( takeString(0, | ||
| + | showBitmap " | ||
| + | message "Join domain " + $DomainRaw$ | ||
| + | execwith_vbs_domain_join cscript //nologo //e:vbs | ||
| + | sub_check_domain_join | ||
| + | dosinanicon_gpupdate / | ||
| + | comment " | ||
| + | registry_default_domain / | ||
| + | exitwindows /Reboot | ||
| + | else | ||
| + | set $DomainCurrent$ = takeString(0, | ||
| + | ;check if client is already in domain requested | ||
| + | if not ( $DomainCurrent$ = $Domain$ ) | ||
| + | showBitmap " | ||
| + | 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 / | ||
| + | else | ||
| + | comment " | ||
| + | endif | ||
| + | endif | ||
| + | |||
| + | ;reset username + password entries in product properties | ||
| + | opsiservicecall_unset_username | ||
| + | opsiservicecall_unset_password | ||
| + | |||
| + | [execwith_vbs_check_domain] | ||
| + | Set obj = GetObject(" | ||
| + | Wscript.Echo obj.PartOfDomain | ||
| + | |||
| + | [execwith_vbs_get_domain] | ||
| + | Set obj = GetObject(" | ||
| + | Wscript.Echo obj.Domain | ||
| + | |||
| + | [execwith_vbs_domain_join] | ||
| + | Set obj = GetObject(" | ||
| + | res = obj.JoinDomainOrWorkGroup(" | ||
| + | Wscript.Quit res | ||
| + | |||
| + | [execwith_vbs_domain_unjoin] | ||
| + | Set obj = GetObject(" | ||
| + | res = obj.UnjoinDomainOrWorkgroup(" | ||
| + | ;res = obj.UnjoinDomainOrWorkgroup(" | ||
| + | Wscript.Quit res | ||
| + | |||
| + | [dosinanicon_gpupdate] | ||
| + | gpupdate /force | ||
| + | exit %ERRORLEVEL% | ||
| + | |||
| + | [registry_default_domain] | ||
| + | openkey [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] | ||
| + | set " | ||
| + | set " | ||
| + | set " | ||
| + | openKey [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI] | ||
| + | set " | ||
| + | set " | ||
| + | |||
| + | [opsiservicecall_unset_username] | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | "", | ||
| + | " | ||
| + | ] | ||
| + | |||
| + | [opsiservicecall_unset_password] | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | "", | ||
| + | " | ||
| + | ] | ||
| + | |||
| + | [sub_check_domain_unjoin] | ||
| + | set $ExitCode$ = getLastExitCode | ||
| + | if $ExitCode$ = " | ||
| + | comment " | ||
| + | else | ||
| + | logError " | ||
| + | isFatalError | ||
| + | endif | ||
| + | |||
| + | [sub_check_domain_join] | ||
| + | set $ExitCode$ = getLastExitCode | ||
| + | if $ExitCode$ = " | ||
| + | comment " | ||
| + | else | ||
| + | if $ExitCode$ = " | ||
| + | logError " | ||
| + | isFatalError | ||
| + | else | ||
| + | if $ExitCode$ = " | ||
| + | logError " | ||
| + | isFatalError | ||
| + | else | ||
| + | if $ExitCode$ = " | ||
| + | logError " | ||
| + | isFatalError | ||
| + | else | ||
| + | if $ExitCode$ = " | ||
| + | logError " | ||
| + | isFatalError | ||
| + | else | ||
| + | if $ExitCode$ = " | ||
| + | logError "Logon failure, user or pass" | ||
| + | isFatalError | ||
| + | else | ||
| + | if $ExitCode$ = " | ||
| + | logError " | ||
| + | isFatalError | ||
| + | else | ||
| + | if $ExitCode$ = " | ||
| + | logError "User account locked out" | ||
| + | isFatalError | ||
| + | else | ||
| + | if $ExitCode$ = " | ||
| + | logError " | ||
| + | isFatalError | ||
| + | else | ||
| + | if $ExitCode$ = " | ||
| + | logError " | ||
| + | isFatalError | ||
| + | else | ||
| + | logError " | ||
| + | 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$ = " | ||
| + | ;set $ACCT_DEACTIVATE$ = " | ||
| + | ;set $UnJoinMode$ = calculate($ACCT_DEACTIVATE$) | ||
| + | |||
| + | ;get username + password from, product properties, hide password value in log | ||
| + | set $Username$ | ||
| + | setConfidential getProductProperty(" | ||
| + | set $Password$ | ||
| + | |||
| + | ;reset username + password entries in product properties | ||
| + | ; | ||
| + | ; | ||
| + | |||
| + | ;check if client is in domain | ||
| + | if not ( takeString(0, | ||
| + | set $DomainCurrent$ = takeString(0, | ||
| + | showBitmap " | ||
| + | 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 " | ||
| + | endif | ||
| + | |||
| + | ;reset username + password entries in product properties | ||
| + | opsiservicecall_unset_username | ||
| + | opsiservicecall_unset_password | ||
| + | |||
| + | [execwith_vbs_check_domain] | ||
| + | Set obj = GetObject(" | ||
| + | Wscript.Echo obj.PartOfDomain | ||
| + | |||
| + | [execwith_vbs_get_domain] | ||
| + | Set obj = GetObject(" | ||
| + | Wscript.Echo obj.Domain | ||
| + | |||
| + | [execwith_vbs_domain_unjoin] | ||
| + | Set obj = GetObject(" | ||
| + | res = obj.UnjoinDomainOrWorkgroup(" | ||
| + | ;res = obj.UnjoinDomainOrWorkgroup(" | ||
| + | Wscript.Quit res | ||
| + | |||
| + | [opsiservicecall_unset_username] | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | "", | ||
| + | " | ||
| + | ] | ||
| + | |||
| + | [opsiservicecall_unset_password] | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | "", | ||
| + | " | ||
| + | ] | ||
| + | |||
| + | [sub_check_domain_unjoin] | ||
| + | set $ExitCode$ = getLastExitCode | ||
| + | if $ExitCode$ = " | ||
| + | comment " | ||
| + | else | ||
| + | logError " | ||
| + | isFatalError | ||
| + | endif | ||
| + | </ | ||
| + | |||
| + | ==== 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 '' | ||
| + | |||
| + | If anyone can get this to work, i wouldn' | ||
| + | |||
| + | <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][/ | ||
| + | ;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(" | ||
| + | |||
| + | ;get domain from product property - if domain is not set, get domain from host identifier. | ||
| + | set $Domain$ = takeString(0, | ||
| + | if ($Domain$ = "" | ||
| + | set $Domain$ = composeString(getSubList(1 : , | ||
| + | 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 : , | ||
| + | if not ($OUPath$ = "" | ||
| + | for %DC% in splitString($Domain$," | ||
| + | set $OUPath$ = strPart($OUPath$," | ||
| + | endif | ||
| + | |||
| + | ;get username + password from, product properties, hide password value in log | ||
| + | set $Username$ | ||
| + | setConfidential getProductProperty(" | ||
| + | set $Password$ | ||
| + | |||
| + | if not ( takeString(0, | ||
| + | set $DomainCurrent$ = takeString(0, | ||
| + | ;check if client is in domain requested | ||
| + | if ( $DomainCurrent$ = $Domain$ ) | ||
| + | set $OUPathCurrent$ = takeString(0, | ||
| + | ;check if client is already in ou-path requested | ||
| + | if not ( $OUPathCurrent$ = $OUPath$ ) | ||
| + | showBitmap " | ||
| + | message " | ||
| + | execwith_vbs_move_oupath cscript //nologo //e:vbs | ||
| + | ;TODO check exitcode adsi | ||
| + | dosinanicon_gpupdate / | ||
| + | else | ||
| + | comment " | ||
| + | endif | ||
| + | else | ||
| + | logError " | ||
| + | isFatalError | ||
| + | endif | ||
| + | else | ||
| + | logError " | ||
| + | isFatalError | ||
| + | endif | ||
| + | |||
| + | [execwith_vbs_check_domain] | ||
| + | Set obj = GetObject(" | ||
| + | Wscript.Echo obj.PartOfDomain | ||
| + | |||
| + | [execwith_vbs_get_domain] | ||
| + | Set obj = GetObject(" | ||
| + | Wscript.Echo obj.Domain | ||
| + | |||
| + | [execwith_vbs_get_oupath] | ||
| + | Set adoConnection = CreateObject(" | ||
| + | adoConnection.Provider = " | ||
| + | adoConnection.Properties(" | ||
| + | adoConnection.Properties(" | ||
| + | adoConnection.Properties(" | ||
| + | Set adoCommand = CreateObject(" | ||
| + | adoConnection.Properties(" | ||
| + | adoConnection.Open " | ||
| + | Set adoCommand.ActiveConnection = adoConnection | ||
| + | adoCommand.CommandText = " | ||
| + | adoCommand.Properties(" | ||
| + | Set adoRecordSet = adoCommand.Execute | ||
| + | adoRecordSet.MoveFirst | ||
| + | OUPathCurrent = adoRecordSet.Fields(" | ||
| + | Wscript.Echo Mid(OUPathCurrent, | ||
| + | |||
| + | [execwith_vbs_move_oupath] | ||
| + | Set obj = GetObject(" | ||
| + | obj.OpenDSObject " | ||
| + | res = obj.MoveHere(" | ||
| + | Wscript.Quit res | ||
| + | |||
| + | [dosinanicon_gpupdate] | ||
| + | gpupdate /force | ||
| + | exit %ERRORLEVEL% | ||
| + | |||
| + | [opsiservicecall_unset_username] | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | "", | ||
| + | " | ||
| + | ] | ||
| + | |||
| + | [opsiservicecall_unset_password] | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | "", | ||
| + | " | ||
| + | ] | ||
| + | </ | ||
userspace/active_directory_-_join_leave_relocate_ou.1443635160.txt.gz · Last modified: (external edit)
