2010-11-15 55 views
7

这与this question类似,但不是骗局 - 然而,它寻求的是手动将服务器加入域的信息(并且被正确地重定向),我正在寻找某些帮助以编程方式将计算机加入域的代码。以编程方式将Windows机器加入到域名

这种情况是我们有一个启动器服务来实例化Amazon EC2 Server2008R1 VM,可选地通过用户数据流传递机器名称。我们的映像中包含一个进程,用于在启动时检查User-Data上的名称 - 如果没有任何文件存在,则虚拟机将保持在我们的Cloud域之外,但如果名称存在,则该计算机将按指定进行重命名并自动加入该域名。

下面是问题 - 如果我在实例启动后的任何时候手动运行此过程,它的工作原理与上述完全相同;机器名称被更改,并且虚拟机加入到域中(我们强制重新启动以实现此目的)。

然而,当作为计划任务运行(在启动时触发)时,计算机重命名会按预期发生,但随后调用JoinDomainOrWorkgroup(请参阅下文)将拾取EC2给出的旧随机机器名称,而不是刚分配的新名称。

这导致WMI返回代码为,我们在AD存储库(该随机名称)中得到一个未连接的错误名称条目,并且该计算机未加入域。虚拟机然后重新启动,第二次通过启动过程(由于用户数据中有内容,但机器尚未在域中而异常触发)执行所有相同的步骤并成功。

看起来机器名称在第一遍中设置,但没有“完成”,并且JoinDomainOrWorkgroup仍然看到原始名称。在第二遍时,机器名称已正确设置,因此JoinDomainOrWorkgroup按预期工作。很明显,为什么这个过程在启动过程中以这种方式运行,但在已经启动的虚拟机上手动运行时完美工作,我认为是问题的症结所在。

我试着将重命名之间的延迟和连接的情况下,步骤重命名是在幕后完成之前调用JoinDomainOrWorkgroup是怎么回事,但是这并没有帮助 - 我真的不指望它,因为手动运行时整个过程完美运行。所以它可能是启动时机器状态的微妙差异和代码中的一些愚蠢的组合。

也许在SetDomainMembership方法中使用System.Environment.MachineName是不明智的?但即使我将新名称作为字符串传递,它仍然失败,与我为SetMachineName所做的一样。所以我很难过。

这里的WMI代码,重命名机:

/// <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; 
    } 
    } 
} 

这里就是它加入域的WMI代码:

/// <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; 
    } 
    } 
} 

道歉,如果这个代码看起来头脑麻木愚蠢的 - 我对于WMI来说是新的,这在很大程度上来自于我在互联网上找到的例子。如果有一个更聪明/更好的方法来做到这一点,那么通过一切手段证明。如果你能在同一时间解决问题,奖励积分!

+0

更多信息:调用'SetMachineName'工作,但名称不会立即改变 - 'ipconfig'仍显示旧名称,看着系统属性显示旧名称后面加上“(将改变到XXXXXXX重新启动后)“。如果'SetDomainMembership'得到一个到System.Environment.MachineName的管理路径,这仍旧是旧名称,并且不正确(导致破坏的AD条目和失败的连接)。如果我改为将新名称作为参数传递,则WMI调用将失败并显示'未找到'异常,这大概是因为还没有一台机器具有这个新名称。 – 2010-11-16 13:24:33

回答

5

好的,就在这里。

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

加入域名第一 - 然后更改 机器名称。

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

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

/// <summary> 
/// Join domain and set Machine Name 
/// </summary> 
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; 
    } 
} 
相关问题