This is similar to, but not cheating, this question - however, when he requested information about connecting the server manually to the domain (and was correctly redirected) I am looking for help with some code that programmatically connects the machine to the domain.
The scenario is that we have a startup service that runs Amazon EC2 Server2008R1 virtual machines, optionally passing the machine name through the User-Data stream. The process is processed by our images, which check the User-Data for the name at boot time. If none is present, the VM remains outside of our cloud domain, but if the name is present, then the machine is renamed as given and automatically joins the domain.
Here's the problem - if I start this process manually at any time after starting the instance, it works exactly as described; the machine name changes, and the virtual machine connects to the domain (we force a restart to make this happen).
However, at startup, as a scheduled task (launched at startup), the machine is renamed as expected, but a subsequent call to JoinDomainOrWorkgroup (see below) selects the old randomized machine name assigned to the EC2 virtual machine instead of the new name that it has just been assigned.
This leads to the reverse code of WMI 8525 , we get a disconnected nicknamed entry in the AD repository (this randomized name), and the machine does not join the domain. Then the virtual machine restarts, and the second goes through the start-up process (it starts abnormally because there is content in the User-Data, but the machine is not yet in the domain) it performs all the same steps and successfully terminates.
It looks like the machine name is set in the first pass, but not βfinalizedβ, and JoinDomainOrWorkgroup still sees the original name. In the second pass, the machine name is already set correctly, and therefore JoinDomainOrWorkgroup works as expected. Thatβs why the process behaves this way during startup, but works great when it starts manually on an already running VM, I think the essence of the problem.
I tried to insert a delay between the steps of renaming and joining if the JoinDomainOrWorkgroup call came before the renaming was completed behind the scenes, but this did not help - and I really did not expect this since the whole process works fine when starting manually. So this is probably a combination of the subtle difference in the state of the machine at boot time and something silly in the code.
Is it possible that using System.Environment.MachineName in the SetDomainMembership method SetDomainMembership not practical? But this will not work, even if I pass the new name as a string, as for SetMachineName . Therefore, I am at a standstill.
Here is the WMI code that renames the machine:
/// <summary> /// Set Machine Name /// </summary> public static bool SetMachineName(string newName) { _lh.Log(LogHandler.LogType.Debug, string.Format("Setting Machine Name to '{0}'...", newName)); // Invoke WMI to populate the machine name using (ManagementObject wmiObject = new ManagementObject(new ManagementPath("Win32_ComputerSystem.Name='" + System.Environment.MachineName + "'"))) { ManagementBaseObject inputArgs = wmiObject.GetMethodParameters("Rename"); inputArgs["Name"] = newName; // Set the name ManagementBaseObject outParams = wmiObject.InvokeMethod("Rename", inputArgs, null); // Weird WMI shennanigans to get a return code (is there no better way to do this??) uint ret = (uint)(outParams.Properties["ReturnValue"].Value); if (ret == 0) { // It worked return true; } else { // It didn't work _lh.Log(LogHandler.LogType.Fatal, string.Format("Unable to change Machine Name from '{0}' to '{1}'", System.Environment.MachineName, newName)); return false; } } }
And here is the WMI code that connects it to the domain:
/// <summary> /// Set domain membership /// </summary> public static bool SetDomainMembership() { _lh.Log(LogHandler.LogType.Debug, string.Format("Setting domain membership of '{0}' to '{1}'...", System.Environment.MachineName, _targetDomain)); // Invoke WMI to join the domain using (ManagementObject wmiObject = new ManagementObject(new ManagementPath("Win32_ComputerSystem.Name='" + System.Environment.MachineName + "'"))) { try { // Obtain in-parameters for the method ManagementBaseObject inParams = wmiObject.GetMethodParameters("JoinDomainOrWorkgroup"); inParams["Name"] = "*****"; inParams["Password"] = "*****"; inParams["UserName"] = "*****"; inParams["FJoinOptions"] = 3; // Magic number: 3 = join to domain and create computer account // Execute the method and obtain the return values. ManagementBaseObject outParams = wmiObject.InvokeMethod("JoinDomainOrWorkgroup", inParams, null); _lh.Log(LogHandler.LogType.Debug, string.Format("JoinDomainOrWorkgroup return code: '{0}'", outParams["ReturnValue"])); // Did it work? ** disabled so we restart later even if it fails //uint ret = (uint)(outParams.Properties["ReturnValue"].Value); //if (ret != 0) //{ // // Nope // _lh.Log(LogHandler.LogType.Fatal, string.Format("JoinDomainOrWorkgroup failed with return code: '{0}'", outParams["ReturnValue"])); // return false; //} return true; } catch (ManagementException e) { // It didn't work _lh.Log(LogHandler.LogType.Fatal, string.Format("Unable to join domain '{0}'", _targetDomain), e); return false; } } }
Sorry if this code looks insanely stupid - I'm new to WMI, and this is pretty much related to the examples I found on websites; if there is a smarter / faster way to do this, then by all means demonstrate. If you can cure the problem at the same time, bonus points!