c# - Programmatically join Windows machine to AD domain -
this similar to, not dupe of, this question - however, sought information on manually joining server domain (and rightly redirected) looking code programmatically joins machine domain.
the scenario have launcher service instantiates amazon ec2 server2008r1 vms, optionally passing machine name in through user-data stream. process baked our images checks user-data name on bootup - if none present vm remains outside of our cloud domain, if name present machine renamed specified , auto-joined domain.
here's problem - if run process manually time after instance start, works described; machine name changed, , vm joined domain (we force restart make happen).
however, when running scheduled task (triggered on startup) machine rename happens expected, subsequent call joindomainorworkgroup
(see below) picks-up old randomised machine name given vm ec2 instead of new name has been assigned.
this results in wmi return code of 8525, disconnected misnamed entry in ad repository (of randomised name) , machine not joined domain. vm restarts, , second pass through startup process (abnormally triggered because there content in user-data machine not yet in domain) executes same steps , succeeds.
it looks machine name set in first pass not 'finalised', , joindomainorworkgroup
still sees original name. on second pass, machine name set properly, , joindomainorworkgroup
works expected. quite why process behaves way during startup, works when run manually on already-started vm, think nub of problem.
i've tried inserting delay between rename , join steps in case call joindomainorworkgroup
happening before rename finalised behind scenes, hasn't helped - , didn't expect to, since whole process works when run manually. it's combination of subtle difference in machine state during bootup , silly in code.
maybe using system.environment.machinename
in setdomainmembership
method inadvisable? stil fails if pass new name in string setmachinename
. i'm stumped.
here's wmi code renames machine:
/// <summary> /// set machine name /// </summary> public static bool setmachinename(string newname) { _lh.log(loghandler.logtype.debug, string.format("setting machine name '{0}'...", newname)); // invoke wmi populate machine name using (managementobject wmiobject = new managementobject(new managementpath("win32_computersystem.name='" + system.environment.machinename + "'"))) { managementbaseobject inputargs = wmiobject.getmethodparameters("rename"); inputargs["name"] = newname; // set name managementbaseobject outparams = wmiobject.invokemethod("rename", inputargs, null); // weird wmi shennanigans return code (is there no better way this??) uint ret = (uint)(outparams.properties["returnvalue"].value); if (ret == 0) { // worked return true; } else { // didn't work _lh.log(loghandler.logtype.fatal, string.format("unable change machine name '{0}' '{1}'", system.environment.machinename, newname)); return false; } } }
and here's wmi code joins domain:
/// <summary> /// set domain membership /// </summary> public static bool setdomainmembership() { _lh.log(loghandler.logtype.debug, string.format("setting domain membership of '{0}' '{1}'...", system.environment.machinename, _targetdomain)); // invoke wmi join domain using (managementobject wmiobject = new managementobject(new managementpath("win32_computersystem.name='" + system.environment.machinename + "'"))) { try { // obtain in-parameters method managementbaseobject inparams = wmiobject.getmethodparameters("joindomainorworkgroup"); inparams["name"] = "*****"; inparams["password"] = "*****"; inparams["username"] = "*****"; inparams["fjoinoptions"] = 3; // magic number: 3 = join domain , create computer account // execute method , obtain return values. managementbaseobject outparams = wmiobject.invokemethod("joindomainorworkgroup", inparams, null); _lh.log(loghandler.logtype.debug, string.format("joindomainorworkgroup return code: '{0}'", outparams["returnvalue"])); // did work? ** disabled restart later if fails //uint ret = (uint)(outparams.properties["returnvalue"].value); //if (ret != 0) //{ // // nope // _lh.log(loghandler.logtype.fatal, string.format("joindomainorworkgroup failed return code: '{0}'", outparams["returnvalue"])); // return false; //} return true; } catch (managementexception e) { // didn't work _lh.log(loghandler.logtype.fatal, string.format("unable join domain '{0}'", _targetdomain), e); return false; } } }
apologies if code looks mind-numbingly stupid - i'm new wmi, , largely cribbed examples i've found on interwebs; if there's smarter/neater way means demonstrate. if can cure problem @ same time, bonus points!
ok, here is.
firstly, order of fields in system properties little misleading - see machine name first, , domain/workgroup below that. subconsciously affected thinking, , meant code copied ordering trying set name first, , join machine domain. whilst work under circumstances, it's not consistent or reliable. biggest lesson learned here is...
join domain first - change machine name.
yep, that's there it. after numerous test iterations, dawned on me might work better if tried way around. tripped-up on change of name on first pass, realised still using local system credentials - machine joined domain @ point, needed same domain credentials used join domain itself. fast bit of code-tweaking later, , have consistently-reliable wmi routine joins domain , changes name.
it might not neatest implementation (feel free comment on improvements) works. enjoy.
/// <summary> /// join domain , set machine name /// </summary> public static bool joinandsetname(string newname) { _lh.log(loghandler.logtype.debug, string.format("joining domain , changing machine name '{0}' '{1}'...", environment.machinename, newname)); // wmi object machine using (managementobject wmiobject = new managementobject(new managementpath("win32_computersystem.name='" + environment.machinename + "'"))) { try { // obtain in-parameters method managementbaseobject inparams = wmiobject.getmethodparameters("joindomainorworkgroup"); inparams["name"] = "domain_name"; inparams["password"] = "domain_account_password"; inparams["username"] = "domain_account"; inparams["fjoinoptions"] = 3; // magic number: 3 = join domain , create computer account _lh.log(loghandler.logtype.debug, string.format("joining machine domain under name '{0}'...", inparams["name"])); // execute method , obtain return values. managementbaseobject joinparams = wmiobject.invokemethod("joindomainorworkgroup", inparams, null); _lh.log(loghandler.logtype.debug, string.format("joindomainorworkgroup return code: '{0}'", joinparams["returnvalue"])); // did work? if ((uint)(joinparams.properties["returnvalue"].value) != 0) { // join domain didn't work _lh.log(loghandler.logtype.fatal, string.format("joindomainorworkgroup failed return code: '{0}'", joinparams["returnvalue"])); return false; } } catch (managementexception e) { // join domain didn't work _lh.log(loghandler.logtype.fatal, string.format("unable join domain '{0}'", _targetdomain), e); return false; } // join domain worked - change name managementbaseobject inputargs = wmiobject.getmethodparameters("rename"); inputargs["name"] = newname; inputargs["password"] = "domain_account_password"; inputargs["username"] = "domain_account"; // set name managementbaseobject nameparams = wmiobject.invokemethod("rename", inputargs, null); _lh.log(loghandler.logtype.debug, string.format("machine rename return code: '{0}'", nameparams["returnvalue"])); if ((uint)(nameparams.properties["returnvalue"].value) != 0) { // name change didn't work _lh.log(loghandler.logtype.fatal, string.format("unable change machine name '{0}' '{1}'", environment.machinename, newname)); return false; } // ok return true; } }
Comments
Post a Comment