2010-11-15 7 views
7

Dies ist ähnlich, aber nicht ein Betrüger von, this question - jedoch, wo es Informationen über die manuelle Beitritt zu einem Server zu einer Domäne gesucht (und wurde zu Recht umgeleitet) Ich suche Hilfe mit einigen Code, der eine Maschine programmatisch einer Domäne hinzufügt.Programmatisch beitreten Windows-Maschine zu AD-Domäne

Das Szenario besteht darin, dass wir einen Launcher-Dienst haben, der Amazon EC2 Server2008R1-VMs instanziiert und optional einen Computernamen über den Benutzerdatenstrom weiterleitet. Ein Prozess wird in unsere Bilder eingebettet, der Benutzerdaten nach einem Namen beim Booten überprüft - Wenn keiner vorhanden ist, bleibt die VM außerhalb unserer Cloud-Domäne, aber wenn der Name vorhanden ist, wird der Computer wie angegeben umbenannt und automatisch verbunden die Domäne.

Hier ist das Problem - wenn ich diesen Prozess manuell jedes Mal nach dem Start der Instanz ausführen, funktioniert es genau wie beschrieben; Der Computername wird geändert und die VM wird mit der Domäne verbunden (wir erzwingen einen Neustart, um dies zu ermöglichen).

Beim Ausführen als geplante Task (ausgelöst beim Start) erfolgt die Maschinenumbenennung jedoch wie erwartet, aber der folgende Aufruf an JoinDomainOrWorkgroup (siehe unten) übernimmt den alten, von VM2 über VM2 angegebenen, randomisierten Maschinennamen der neue Name wurde gerade vergeben.

Dies führt zu einem WMI-Rückkehrcode , wir erhalten einen getrennten Eintrag mit falschem Namen im AD-Repository (mit diesem randomisierten Namen) und der Computer ist nicht mit der Domäne verbunden. Die VM startet dann neu, und ein zweiter Durchlauf durch den Startprozess (abnormal ausgelöst, da Inhalt in Benutzerdaten vorhanden ist, aber die Maschine noch nicht in der Domäne ist) führt alle die gleichen Schritte aus und ist erfolgreich.

Es sieht so aus, als ob der Computername im ersten Durchlauf gesetzt, aber nicht 'finalisiert' ist, und JoinDomainOrWorkgroup sieht immer noch den ursprünglichen Namen. Beim zweiten Durchlauf ist der Computername bereits richtig eingestellt, und JoinDomainOrWorkgroup funktioniert wie erwartet. Ziemlich warum der Prozess so während des Starts funktioniert, aber funktioniert perfekt, wenn manuell auf einer bereits gestarteten VM ausgeführt wird, ist ich der Kern des Problems.

Ich habe versucht, eine Verzögerung zwischen den Umbenennen und Join Schritte für den Fall, dass der Anruf an JoinDomainOrWorkgroup geschah, bevor das Umbenennen hinter den Kulissen abgeschlossen wurde, aber das hat nicht geholfen - und ich habe es nicht wirklich erwartet , da der ganze Prozess perfekt funktioniert, wenn er manuell ausgeführt wird. Es ist also wahrscheinlich eine Kombination aus einem subtilen Unterschied im Maschinenzustand während des Bootens und etwas albern im Code.

Vielleicht nicht mit System.Environment.MachineName in der SetDomainMembership Methode ist nicht ratsam? Aber es schlägt immer fehl, auch wenn ich den neuen Namen als String übergebe wie für SetMachineName. Also bin ich ratlos.

Hier ist der WMI-Code, der die Maschine umbenennt:

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

Und hier ist der WMI-Code, der es in die Domäne beitritt:

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

Entschuldigt, wenn dieser Code nervtötend dumm aussieht - ich bin neu für WMI, und das ist weitgehend von Beispielen, die ich auf den Interwebs gefunden habe; Wenn es eine klügere/bessere Möglichkeit gibt, dies zu tun, dann demonstriere es auf jeden Fall. Wenn Sie das Problem zur gleichen Zeit lösen können, Bonuspunkte!

+0

Weitere Informationen: der Aufruf von 'SetMachineName' funktioniert, aber der Name sofort nicht ändert -' ipconfig' noch den alten Namen zeigt, und im Systemeigenschaften Suche zeigt den alten Namen von „gefolgt (wird sich ändern bis XXXXXXX nach dem Neustart) ". Wenn 'SetDomainMembership' einen Verwaltungspfad zu System.Environment.MachineName erhält, ist dies immer noch der alte Name und ist falsch (was zu einem fehlerhaften AD-Eintrag und einem fehlgeschlagenen Join führt). Wenn ich stattdessen den neuen Namen als Parameter übergebe, schlägt der WMI-Aufruf mit der Ausnahme 'Nicht gefunden' fehl, vermutlich, weil noch kein Rechner mit diesem neuen Namen fest eingestellt ist. –

Antwort

5

OK, hier ist es.

Erstens ist die Reihenfolge der Felder in den Systemeigenschaften ein wenig irreführend - Sie sehen Maschinenname zuerst und Domäne/Arbeitsgruppe darunter. Das hat mein Denken unbewusst beeinflusst und bedeutet, dass mein Code diese Reihenfolge kopiert hat, indem ich zuerst versucht habe, den Namen festzulegen und dann den Computer der Domäne beizutreten. Während dies unter bestimmten Umständen funktioniert, ist es nicht konsistent oder zuverlässig. So ist die größte Lektion, die hier gelernt habe, ist ...

die Domain zuerst Mitglied werden - dann der Name Maschine ändern.

Ja, das ist eigentlich alles, was es ist. Nach zahlreichen Test-Iterationen dämmerte es mir schließlich, dass es besser funktionieren könnte, wenn ich es so herumprobierte. Ich bin bei meinem ersten Durchlauf auf die Änderung des Namens gestoßen, habe aber schnell festgestellt, dass die lokalen Systemanmeldeinformationen weiterhin verwendet werden. Da der Computer jedoch jetzt mit der Domäne verbunden ist, benötigt er die gleichen Domänenanmeldeinformationen der Domäne selbst beitreten. Ein schnelles bisschen Code-Tweaking später, und wir haben jetzt eine durchgängig zuverlässige WMI-Routine, die der Domäne beitritt und dann den Namen ändert.

Es ist vielleicht nicht die neuste Implementierung (fühlen Sie sich frei, Verbesserungen zu kommentieren), aber es funktioniert. Genießen.

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