This shows you the differences between two versions of the page.
userspace:active_directory_-_join_leave_relocate_ou [2015/09/30 18:37] larsg |
userspace:active_directory_-_join_leave_relocate_ou [2021/08/23 08:37] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Active Directory client management, join/ | ||
- | |||
- | * created by LarsG [[lars.gruenheid@civitec.de]] 2015/09/30 | ||
- | |||
- | * tested under windows 7 | ||
- | * tested under win2012 active directory environment | ||
- | * tested under winst 4.11.5.14 | ||
- | |||
- | 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, | ||
- | 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. | ||
- | |||
- | <code winst> | ||
- | [Actions] | ||
- | 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 /sysnative | ||
- | 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 | ||
- | </ | ||
- | |||
- | |||
- | |||
- | ==== 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 arror '' | ||
- | |||
- | 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] | ||
- | " | ||
- | " | ||
- | " | ||
- | " | ||
- | "", | ||
- | " | ||
- | ] | ||
- | </ | ||