以编程方式将Windows计算机加入AD域

这与此问题类似,但不是欺骗 – 但是,它寻求有关手动将服务器加入域(并且被正确重定向)的信息,我正在寻找有关以编程方式将计算机连接到域的某些代码的帮助。

方案是我们有一个启动器服务来实例化Amazon EC2 Server2008R1 VM,可选择通过User-Data流传递Machine Name。 我们的映像中会出现一个流程,用于在启动时检查用户数据的名称 – 如果不存在,则VM仍在我们的云域之外,但如果存在该名称,则将计算机重命名为指定并自动加入域名。

这是问题所在 – 如果我在实例启动后的任何时候手动运行此过程,它的工作方式完全如上所述; 机器名称已更改,并且VM已加入域(我们强制重新启动以实现此目的)。

但是,当作为计划任务(在启动时触发)运行时,机器重命名按预期发生,但随后对JoinDomainOrWorkgroup调用(见下文)通过EC2而不是新名称获取给VM的旧随机机器名称刚被分配。

这导致WMI返回代码为8525 ,我们在AD存储库(该随机名称)中获得了一个断开连接的错误名称条目,并且该机器未加入该域。 然后VM重新启动,第二次通过启动过程(exception触发,因为User-Data中有内容,但机器尚未在域中)执行所有相同的步骤并成功。

看起来机器名称在第一次传递中设置但未“最终确定”,并且JoinDomainOrWorkgroup仍然看到原始名称。 在第二次传递时,计算机名已正确设置,因此JoinDomainOrWorkgroup按预期工作。 很明显为什么这个过程在启动过程中会以这种方式运行,但是在已经启动的VM上手动运行时效果很好,我认为是问题的核心。

我已经尝试在重命名和连接步骤之间插入一个延迟,以防在重命名在幕后完成之前调用JoinDomainOrWorkgroup ,但这没有帮助 – 我并没有真正期望它,因为整个手动运行时,过程完美无缺。 因此,它可能是启动期间机器状态的细微差别和代码中的愚蠢差异的组合。

也许在SetDomainMembership方法中使用System.Environment.MachineName是不可取的? 但即使我将新名称作为字符串传递给SetMachineName ,它SetMachineName 。 所以我很难过。

这是重命名机器的WMI代码:

 ///  /// Set Machine Name ///  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; } } } 

以下是将其加入域的WMI代码:

 ///  /// Set domain membership ///  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; } } } 

抱歉,如果这段代码看起来很麻烦 – 我是WMI的新手,这在很大程度上来自我在互联网上发现的例子; 如果有更智能/更简洁的方式来做到这一点,那么一定要certificate。 如果你可以同时解决问题,奖励积分!

好的,这是。

首先,系统属性中字段的顺序有点误导 – 您首先看到机器名,然后是下面的域/工作组。 这下意识地影响了我的想法,并且意味着我的代码通过尝试首先设置名称然后将机器加入域来复制该排序。 虽然这在某些情况下确实有效,但它不一致或不可靠。 所以这里学到的最大教训是……

首先加入域 – 然后更改计算机名称。

是的,这实际上就是它的全部。 在经过多次测试迭代之后,我终于明白,如​​果我以这种方式尝试它可能会更好。 我在第一次通过时更改了名称,但很快意识到它仍在使用本地系统凭据 – 但是现在机器已加入域,它需要与使用的域凭据相同的域名加入域名本身。 稍后会进行一些快速的代码调整,现在我们有一个一致可靠的WMI例程,它连接域然后更改名称。

它可能不是最好的实现(随意评论改进),但它的工作原理。 请享用。

 ///  /// Join domain and set Machine Name ///  public static bool JoinAndSetName(string newName) { _lh.Log(LogHandler.LogType.Debug, string.Format("Joining domain and changing Machine Name from '{0}' to '{1}'...", Environment.MachineName, newName)); // Get WMI object for this machine using (ManagementObject wmiObject = new ManagementObject(new ManagementPath("Win32_ComputerSystem.Name='" + Environment.MachineName + "'"))) { try { // Obtain in-parameters for the 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 to domain and create computer account _lh.Log(LogHandler.LogType.Debug, string.Format("Joining machine to domain under name '{0}'...", inParams["Name"])); // Execute the method and obtain the return values. ManagementBaseObject joinParams = wmiObject.InvokeMethod("JoinDomainOrWorkgroup", inParams, null); _lh.Log(LogHandler.LogType.Debug, string.Format("JoinDomainOrWorkgroup return code: '{0}'", joinParams["ReturnValue"])); // Did it work? if ((uint)(joinParams.Properties["ReturnValue"].Value) != 0) { // Join to domain didn't work _lh.Log(LogHandler.LogType.Fatal, string.Format("JoinDomainOrWorkgroup failed with return code: '{0}'", joinParams["ReturnValue"])); return false; } } catch (ManagementException e) { // Join to domain didn't work _lh.Log(LogHandler.LogType.Fatal, string.Format("Unable to join domain '{0}'", _targetDomain), e); return false; } // Join to domain worked - now change name ManagementBaseObject inputArgs = wmiObject.GetMethodParameters("Rename"); inputArgs["Name"] = newName; inputArgs["Password"] = "domain_account_password"; inputArgs["UserName"] = "domain_account"; // Set the 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 to change Machine Name from '{0}' to '{1}'", Environment.MachineName, newName)); return false; } // All ok return true; } }