2010-11-15 53 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; 
    } 
} 
相關問題