首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >以编程方式将Windows机器连接到AD域

以编程方式将Windows机器连接到AD域
EN

Stack Overflow用户
提问于 2010-11-15 11:15:08
回答 2查看 14.6K关注 0票数 7

这与this question类似,但不是一种欺骗--然而,在这里,它寻求关于手动将服务器连接到域(并且正确地被重定向)的信息--我正在寻找一些代码的帮助,这些代码可以编程地将机器连接到域。

场景是,我们有一个启动程序服务,可以实例化亚马逊EC2 Server2008R1 VM,可以通过用户数据流传递机器名称。我们的映像中有一个进程,用于在启动时检查用户数据中的名称--如果没有,那么VM将保留在云域之外,但是如果存在该名称,则机器将被重命名为指定的,并自动连接到域。

问题是,如果我在实例启动后的任何时候手动运行此过程,它将完全按照描述的方式工作;机器名称被更改,VM被连接到域(我们强制重新启动才能实现这一点)。

但是,当以计划任务的形式运行时(启动时触发),机器重命名将按预期进行,但随后对JoinDomainOrWorkgroup的调用(请参见下文)将获取由EC2赋予VM的旧随机机器名称,而不是刚刚分配的新名称。

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

看起来机器名称是在第一次传递时设置的,而不是“最终确定的”,而JoinDomainOrWorkgroup仍然可以看到原来的名称。在第二次测试中,机器名称已经被正确设置,因此JoinDomainOrWorkgroup按预期工作。我认为问题的症结在于,为什么进程在启动时会以这种方式运行,但在已经启动的VM上手动运行时却能很好地工作。

我尝试在重命名和连接步骤之间插入一个延迟,以防重命名在幕后完成之前调用JoinDomainOrWorkgroup,但这并没有帮助--而且我也没有真正期望它,因为整个过程在手动运行时完全可以工作。因此,这可能是启动过程中机器状态的微妙差异和代码中一些愚蠢的东西的组合。

也许在System.Environment.MachineName方法中使用SetDomainMembership是不可取的吗?但是,即使我将新名称作为字符串传入,它仍然失败,就像对SetMachineName一样。所以我很困惑。

以下是重命名机器的WMI代码:

代码语言:javascript
复制
/// <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代码:

代码语言:javascript
复制
/// <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并不熟悉,这在很大程度上是从我在网络上找到的例子中提炼出来的;如果有一种更聪明/更整洁的方法来做到这一点,那么一定要演示一下。如果你能同时解决问题,加分!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-11-17 12:10:56

好的,给你。

首先,中字段的顺序有点误导--您可以先看到机器名称,下面是域/工作组。这在潜意识中影响了我的思维,并意味着我的代码复制了顺序,尝试先设置名称,然后将机器加入到域。虽然这在某些情况下确实有效,但它不一致或不可靠。所以这里最大的教训是..。

首先加入域-然后更改机器名。

是的,实际上就是这样。经过多次的测试迭代之后,我终于意识到,如果我尝试这种方法,它可能会更好。我第一次被更改名称时绊倒了,但很快意识到它仍然在使用本地系统凭据,但是现在机器已经加入到域,它需要与加入域本身相同的域凭据。稍后进行快速的代码调整,我们现在有了一个持续可靠的WMI例程,它加入域,然后更改名称。

它可能不是最简洁的实现(请随意评论改进),但它有效。好好享受吧。

代码语言:javascript
复制
/// <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;
  }
}
票数 8
EN

Stack Overflow用户

发布于 2019-11-26 23:40:50

好吧,这么多年以后,如果有人需要的话,就更新一下吧。

WMI不再只包含JoinDomain工作组(WIN 10,Build 1909)。您可以使用netapi32.dll

更多信息在这里:

https://learn.microsoft.com/en-us/windows/win32/api/lmjoin/nf-lmjoin-netjoindomain

快速的小例子:

代码语言:javascript
复制
 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());

    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4183759

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档