這與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來說是新的,這在很大程度上來自於我在互聯網上找到的例子。如果有一個更聰明/更好的方法來做到這一點,那麼通過一切手段證明。如果你能在同一時間解決問題,獎勵積分!
更多信息:調用'SetMachineName'工作,但名稱不會立即改變 - 'ipconfig'仍顯示舊名稱,看着系統屬性顯示舊名稱後面加上「(將改變到XXXXXXX重新啓動後)「。如果'SetDomainMembership'得到一個到System.Environment.MachineName的管理路徑,這仍舊是舊名稱,並且不正確(導致破壞的AD條目和失敗的連接)。如果我改爲將新名稱作爲參數傳遞,則WMI調用將失敗並顯示'未找到'異常,這大概是因爲還沒有一臺機器具有這個新名稱。 – 2010-11-16 13:24:33