2010-11-15 15 views
7

Esto es similar, pero no es una estafa de, this question - sin embargo, donde buscaba información sobre cómo unir manualmente un servidor a un dominio (y se redirigía correctamente) Estoy buscando ayuda con algunos código que une programáticamente una máquina a un dominio.Unirme mediante una máquina Windows al dominio AD

El caso es que tenemos un servicio de inicio de sesión que crea instancias de las máquinas virtuales de Amazon EC2 Server2008R1, opcionalmente pasando un nombre de equipo a través de la secuencia de datos de usuario. Se procesa un proceso en nuestras imágenes que verifica los nombres de usuario para un nombre en el arranque: si no hay ninguno, la máquina virtual permanece fuera de nuestro dominio de la nube, pero si el nombre está presente, la máquina se renombra como especificada y se une automáticamente a el dominio.

Aquí está el problema: si ejecuto este proceso manualmente en cualquier momento después del inicio de la instancia, funciona exactamente como se describe; el nombre de la máquina se cambia y la VM se une al dominio (forzamos un reinicio para que esto suceda).

Sin embargo, cuando se ejecuta como Tarea programada (activada al inicio) el cambio de nombre de la máquina ocurre como se esperaba, pero la siguiente llamada al JoinDomainOrWorkgroup (ver más abajo) recoge el antiguo nombre de la máquina el nuevo nombre que acaba de ser asignado.

Esto da como resultado un código de retorno WMI de , obtenemos una entrada errónea desconectada en el repositorio AD (de ese nombre aleatorio) y la máquina no está unida al dominio. A continuación, la máquina virtual se reinicia y una segunda etapa pasa por el proceso de inicio (anormalmente activada porque hay contenido en User-Data pero la máquina todavía no está en el dominio) ejecuta todos los mismos pasos y tiene éxito.

Parece que el nombre de la máquina está configurado en la primera pasada pero no está 'finalizado', y JoinDomainOrWorkgroup aún ve el nombre original. En la segunda pasada, el nombre de la máquina ya está configurado correctamente, por lo que JoinDomainOrWorkgroup funciona como se esperaba. El motivo por el que el proceso se comporta de esta manera durante el inicio, pero funciona perfectamente cuando se ejecuta manualmente en una máquina virtual ya iniciada, creo que es el meollo del problema.

He intentado insertar un retraso entre los pasos de cambio de nombre y unión en caso de que la llamada a JoinDomainOrWorkgroup ocurriera antes de que el cambio de nombre se finalizara entre bastidores, pero esto no ha ayudado, y realmente no esperaba , ya que todo el proceso funciona perfectamente cuando se ejecuta manualmente. Por lo tanto, es probable que sea una combinación de una diferencia sutil en el estado de la máquina durante el arranque y algo tonto en el código.

¿Quizás usar System.Environment.MachineName en el método SetDomainMembership es desaconsejable? Pero aún falla, incluso si paso el nuevo nombre como una cadena como lo hago para SetMachineName. Así que estoy perplejo.

Aquí está el código de WMI que cambia el nombre de la máquina:

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

Y aquí está el código WMI que lo une al dominio:

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

Disculpas si este código es abrumadoramente estúpida - I Soy nuevo en WMI, y esto está en gran parte reducido a los ejemplos que he encontrado en las interwebs; si hay una manera más inteligente/más ordenada de hacerlo, demuéstralo. Si puedes curar el problema al mismo tiempo, ¡puntos de bonificación!

+0

Más información: la llamada a 'funciona SetMachineName', pero el nombre no cambia de forma instantánea -' ipconfig' sigue mostrando el nombre antiguo, y mirando a Propiedades del sistema muestra el antiguo nombre seguido de "(cambiará a XXXXXXX después del reinicio) ". Si 'SetDomainMembership' obtiene una ruta de administración a System.Environment.MachineName, este sigue siendo el nombre anterior y es incorrecto (lo que lleva a una entrada de AD rota y una unión fallida). Si, en cambio, paso el nuevo nombre como parámetro, la llamada WMI falla con una excepción 'No encontrado', presumiblemente porque aún no hay una máquina con este nuevo nombre firmemente establecido. –

Respuesta

5

OK, aquí está.

En primer lugar, el orden de los campos en Propiedades del sistema es un poco engañoso: primero se ve el nombre del equipo y el dominio/grupo de trabajo debajo. Esto afectó inconscientemente mi pensamiento, y significó que mi código copió ese orden intentando primero establecer el nombre, y luego unir la máquina al dominio. Si bien esto funciona en algunas circunstancias, no es consistente ni confiable. Entonces la mayor lección aprendida aquí es ...

Únase primero al dominio - luego cambie el nombre de la máquina.

Sí, eso es todo lo que hay que hacer. Después de numerosas iteraciones de prueba, finalmente caí en la cuenta de que podría funcionar mejor si lo intentara de esta manera. Me tropecé con el cambio de nombre en mi primer pase, pero rápidamente me di cuenta de que todavía estaba usando las credenciales del sistema local, pero ahora que la máquina estaba unida al dominio en este momento, necesitaba las mismas credenciales de dominio que las que usaba. para unir el dominio mismo. Un poco de retoque de código más tarde, y ahora tenemos una rutina WMI consistentemente confiable que une el dominio y luego cambia el nombre.

Puede que no sea la mejor implementación (no dude en comentar las mejoras) pero funciona. Disfrutar.

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