这类似于,但不是欺骗,
this question
-然而,当它寻求有关手动将服务器加入域的信息(并正确地被重定向)时,我正在寻找一些代码的帮助,这些代码以编程方式将计算机加入域。
场景是我们有一个启动服务,它实例化amazonec2server2008r1vms,可以选择通过用户数据流传入一个机器名。一个进程被烘焙到我们的映像中,在启动时检查用户数据中的名称-如果不存在任何名称,则虚拟机将保留在我们的云域之外,但如果该名称存在,则机器将按指定重命名并自动加入域。
问题在于-如果我在实例启动后的任何时候手动运行此进程,它的工作方式与所描述的完全相同;计算机名称已更改,并且虚拟机已加入域(我们强制重新启动以实现此目的)。
但是,当作为计划任务运行(启动时触发)时,计算机重命名按预期进行,但随后调用
JoinDomainOrWorkgroup
(见下文)选择EC2给VM的旧随机机器名,而不是刚刚分配的新名称。
这将导致WMI返回代码为
8525个
,我们会在广告存储库中得到一个断开连接的错误名称条目(该随机名称),并且计算机未加入域。然后,VM重新启动,第二次通过启动过程(由于用户数据中有内容,但计算机还不在域中,因此异常触发)执行所有相同的步骤并成功。
看起来机器名是在第一次通过时设置的,但不是“最终确定的”,并且
加入域或工作组
仍然看到原来的名字。在第二遍中,机器名已经正确设置,因此
加入域或工作组
按预期工作。我认为问题的关键在于,进程在启动时会以这种方式运行,但在已经启动的VM上手动运行时运行得很好。
我尝试在重命名和连接步骤之间插入一个延迟,以防调用
加入域或工作组
在幕后完成重命名之前就已经发生了,但这并没有起到任何作用——我真的没想到会这样,因为手动运行时整个过程工作得很好。所以这可能是在启动期间机器状态的细微差别和代码中的一些愚蠢的东西的结合。
也许是用
System.Environment.MachineName
在
SetDomainMembership
方法不可取?但它仍然失败,即使我把新名字作为字符串传入
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新手,而且这大部分都是从我在interwebs上找到的示例中抄袭而来;如果有更聪明/更整洁的方法来实现这一点,那么请务必演示。如果你能同时解决问题,加分!