2010-11-15 1 views
7

Это похоже на, но не обман, this question - однако, когда он искал информацию о ручном подключении сервера к домену (и был правильно перенаправлен), я ищу помощь с некоторыми код, который программно соединяет машину с доменом.Программно подключиться к Windows-машине к домену AD

Сценарий заключается в том, что у нас есть служба запуска, которая запускает виртуальные машины Amazon EC2 Server2008R1, опционально передавая имя машины через поток User-Data. Процесс обрабатывается нашими изображениями, которые проверяют User-Data для имени при загрузке. Если ни один не присутствует, VM остается за пределами нашего облачного домена, но если имя присутствует, то машина переименовывается как заданная и автоматически присоединяется к домена.

В этом случае проблема - если я запускаю этот процесс вручную в любое время после запуска экземпляра, он работает точно так, как описано; имя машины изменяется, а виртуальная машина присоединена к домену (мы вынуждаем перезапуск, чтобы это произошло).

Однако при запуске в качестве запланированной задачи (запускается при запуске) переименование машины происходит так, как ожидалось, но последующий вызов JoinDomainOrWorkgroup (см. Ниже) подбирает старое рандомизированное имя машины, присвоенное VM EC2 вместо новое имя, которое оно только что назначено.

Это приводит к обратному коду WMI . Мы получаем несвязанную запись с неправильным названием в репозитории AD (этого рандомизированного имени), и машина не присоединяется к домену. Затем VM перезагружается, а второй проходит через процесс запуска (ненормально запущен, потому что есть контент в User-Data, но машина еще не находится в домене) выполняет все те же шаги и преуспевает.

Похоже, что имя машины задано в первом проходе, но не «финализировано», а JoinDomainOrWorkgroup все еще видит исходное имя. На втором проходе имя машины уже установлено правильно, и поэтому JoinDomainOrWorkgroup работает должным образом. Весьма почему процесс ведет себя таким образом во время запуска, но отлично работает при запуске вручную на уже запущенной VM, я думаю, что суть проблемы.

Я попытался вставить задержку между шагами переименования и соединения в случае, когда был вызван вызов JoinDomainOrWorkgroup, прежде чем переименование было завершено за кулисами, но это не помогло - и я действительно не ожидал, что он , так как весь процесс отлично работает при запуске вручную. Таким образом, это, вероятно, сочетание тонкой разницы в состоянии машины во время загрузки и что-то глупое в коде.

Возможно использование 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; 
    } 
    } 
} 

Извинения, если этот код выглядит ум numbingly глупо - я «Новое для WMI», и это в значительной степени связано с примерами, которые я нашел на веб-сайтах; если есть более умный/более быстрый способ сделать это, то непременно продемонстрируйте. Если вы можете вылечить проблему в одно и то же время, бонусные баллы!

+0

Дополнительная информация: вызов 'SetMachineName' работает, но имя не меняется мгновенно -' ipconfig' все еще показывает старое название, и, глядя на свойствах системы показывает старое название, за которой следует «(изменится до XXXXXXX после перезапуска) ". Если 'SetDomainMembership' получает путь управления к System.Environment.MachineName, это по-прежнему старое имя и неверно (что приводит к сломанной записи AD и неудачному соединению). Если я вместо этого передаю новое имя в качестве параметра, вызов WMI завершится неудачей с исключением «Not Found», по-видимому, потому, что еще не установлена ​​машина с этим новым именем. –

ответ

5

OK, здесь.

Во-первых, порядок полей в Свойствах системы немного вводит в заблуждение - сначала вы видите имя машины, а ниже - Domain/Workgroup. Это подсознательно повлияло на мое мышление и означало, что мой код скопировал это упорядочение, поставив сначала имя, а затем присоединив машину к домену. Хотя это работает при некоторых обстоятельствах, оно не является последовательным или надежным. Поэтому самый большой урок здесь ...

Сначала присоединитесь к домену, затем измените имя .

Да, это на самом деле все, что есть. После многочисленных итераций тестов, наконец, я понял, что это может сработать лучше, если я попробую это так. Я сработал с изменением имени на своем первом проходе, но быстро понял, что он все еще использует учетные данные локальной системы, - но теперь, когда машина была присоединена к домену в этот момент, ему нужны те же учетные данные домена, которые были использованы присоединиться к самому домену. Быстрый бит настройки кода позже, и теперь у нас есть последовательно надежная процедура 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; 
    } 
} 
Смежные вопросы