C# 以编程方式将Windows计算机加入AD域

C# 以编程方式将Windows计算机加入AD域,c#,dns,wmi,amazon-ec2,C#,Dns,Wmi,Amazon Ec2,这类似于,但不是重复,-但是,在那里,它寻求有关手动将服务器加入域的信息(并且被正确重定向)。我正在寻找一些代码的帮助,这些代码以编程方式将机器加入域 该场景是,我们有一个启动器服务,它实例化AmazonEC2Server2008R1虚拟机,可以选择通过用户数据流传入一个机器名。在我们的映像中烘焙了一个进程,该进程在启动时检查用户数据中的名称-如果不存在名称,则VM将保持在我们的云域之外,但如果存在名称,则机器将按照指定重命名并自动加入域 问题是——如果我在实例启动后的任何时候手动运行这个过程,

这类似于,但不是重复,-但是,在那里,它寻求有关手动将服务器加入域的信息(并且被正确重定向)。我正在寻找一些代码的帮助,这些代码以编程方式将机器加入域

该场景是,我们有一个启动器服务,它实例化AmazonEC2Server2008R1虚拟机,可以选择通过用户数据流传入一个机器名。在我们的映像中烘焙了一个进程,该进程在启动时检查用户数据中的名称-如果不存在名称,则VM将保持在我们的云域之外,但如果存在名称,则机器将按照指定重命名并自动加入域

问题是——如果我在实例启动后的任何时候手动运行这个过程,它的工作原理与描述的完全相同;机器名被更改,VM被加入到域中(我们强制重启以实现这一点)

但是,当作为计划任务运行(启动时触发)时,机器重命名会按预期进行,但随后对
JoinDomainOrWorkgroup
(参见下文)的调用会选择EC2给VM的旧随机机器名,而不是刚刚分配的新名称

这将导致WMI返回代码8525,我们在AD存储库中获得一个断开连接的错误命名条目(该随机名称),并且机器未加入域。然后VM重新启动,第二次通过启动过程(由于用户数据中有内容,但机器尚未在域中,因此异常触发)执行所有相同的步骤并成功

看起来机器名是在第一个过程中设置的,但没有“最终确定”,并且
JoinDomainOrWorkgroup
仍然可以看到原始名称。在第二个过程中,机器名已经正确设置,因此
JoinDomainOrWorkgroup
按预期工作。我认为问题的关键在于,为什么进程在启动时会以这种方式运行,但在已经启动的VM上手动运行时却能完美工作

我曾尝试在重命名和连接步骤之间插入延迟,以防调用
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;
    }
  }
}
//
///设置机器名
/// 
公共静态bool SetMachineName(字符串newName)
{
_Log(LogHandler.LogType.Debug,string.Format(“将机器名设置为“{0}”…”,newName));
//调用WMI以填充计算机名称
使用(ManagementObject WMIOObject=new ManagementObject(新的管理路径(“Win32_ComputerSystem.Name=”“+System.Environment.MachineName+”))
{
ManagementBaseObject inputArgs=wmioObject.GetMethodParameters(“重命名”);
inputArgs[“Name”]=新名称;
//设定名称
ManagementBaseObject输出参数=wmiObject.InvokeMethod(“重命名”,inputArgs,null);
//奇怪的WMI shennanigans获取返回码(没有更好的方法吗??)
uint ret=(uint)(outParams.Properties[“ReturnValue”].Value);
如果(ret==0)
{
//它起作用了
返回true;
}
其他的
{
//它不起作用
_Log(LogHandler.LogType.Fatal,string.Format(“无法将计算机名称从{0}更改为{1}',System.Environment.MachineName,newName));
返回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;
    }
  }
}
//
///设置域成员资格
/// 
公共静态bool SetDomainMembership()
{
_Log(LogHandler.LogType.Debug,string.Format(“将{0}'的域成员身份设置为{1}'…”,System.Environment.MachineName,_targetDomain));
//调用WMI以加入域
使用(ManagementObject WMIOObject=new ManagementObject(新的管理路径(“Win32_ComputerSystem.Name=”“+System.Environment.MachineName+”))
{
尝试
{
//获取方法的参数
ManagementBaseObject inParams=WMIOObject.GetMethodParameters(“JoinDomainOrWorkgroup”);
inParams[“Name”]=“*******”;
inParams[“Password”]=“*******”;
输入参数[“用户名”]=“*******”;
inParams[“FJoinOptions”]=3;//幻数:3=加入域并创建计算机帐户
//执行该方法并获取返回值。
ManagementBaseObject输出参数=WMIOObject.InvokeMethod(“JoinDomainOrWorkgroup”,输入参数,null);
_Log(LogHandler.LogType.Debug,string.Format(“JoinDomainOrWorkgroup返回代码:'{0}',outParams[“ReturnValue”]);
//工作正常吗?**已禁用,因此即使失败,我们也会稍后重新启动
//uint ret=(uint)(outParams.Properties[“ReturnValue”].Value);
//如果(ret!=0)
//{
////没有
//_lh.Log(LogHandler.LogType.Fatal,string.Format(“JoinDomainOrWorkgroup失败,返回代码:“{0}”,输出参数[“ReturnValue”]);
//返回false;
//}
返回true;
}
捕获(e)
{
//它不起作用
_Log(LogHandler.LogType.Fatal,string.Format(“无法加入域“{0}”,_targetDomain),e);
返回false;
}
}
}
如果这段代码看起来愚蠢得让人发麻,我表示歉意——我是WMI新手,这主要是从我在互联网站上找到的示例中抄袭而来的;如果有更聪明/更整洁的方法,那么请务必演示。如果您能同时解决问题,将获得额外积分

好的,给你

首先,系统属性中字段的顺序有点误导-您可以看到机器名fi
/// <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;
  }
}
 public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    [DllImport("netapi32.dll", CharSet = CharSet.Unicode)]
    static extern uint NetJoinDomain(
          string lpServer,
  string lpDomain,
  string lpAccountOU,
  string lpAccount,
  string lpPassword,
      JoinOptions NameType);

    [Flags]
    enum JoinOptions
    {
        NETSETUP_JOIN_DOMAIN = 0x00000001,
        NETSETUP_ACCT_CREATE = 0x00000002,
        NETSETUP_ACCT_DELETE = 0x00000004,
        NETSETUP_WIN9X_UPGRADE = 0x00000010,
        NETSETUP_DOMAIN_JOIN_IF_JOINED = 0x00000020,
        NETSETUP_JOIN_UNSECURE = 0x00000040,
        NETSETUP_MACHINE_PWD_PASSED = 0x00000080,
        NETSETUP_DEFER_SPN_SET = 0x10000000
    }

    public static uint domainjoin(string server, string domain, string OU, string account, string password)
    {
        try
        {
            uint value1 = NetJoinDomain(server, domain, OU, account, password, (JoinOptions.NETSETUP_JOIN_DOMAIN | JoinOptions.NETSETUP_DOMAIN_JOIN_IF_JOINED | JoinOptions.NETSETUP_ACCT_CREATE));
            return value1;
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message);
            return 11;
        }
    }


    private void Button_Click(object sender, RoutedEventArgs e)
    {

        var succes = domainjoin(null, "mydomain.local", null, "administrator", "UltraSecretPasword");
        MessageBox.Show(succes.ToString());

    }
}