You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多域Active Directory跨所有域及子域检索全部用户的技术问询

多域AD环境下全局编录检索用户的问题

我们尝试获取多域场景下Active Directory中所有域及子域的全部用户列表,使用了下方代码。原本以为配置3268端口(全局编录)后能检索所有域用户,但实际仅返回单个域用户。现提出以下问题:

  1. 为何全局编录未包含所有域的用户?
  2. 有资料显示需指定容器搜索特定域,若如此,全局编录的价值何在?直接连接其他域不即可?
  3. 是否存在通用方法跨所有域检索全部用户?
namespace TestApplication
{
    public static class StringMethods
    {
        public static string ValueOrNull(this string source)
        {
            return string.IsNullOrWhiteSpace(source) ? null : source;
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            string server = null;    // 域控制器机器名、域名,或 domain:3268(全局编录LDAP)、domain:3269(全局编录LDAPS)
            string container = null; // 搜索根
            string username = null;  // 连接AD的用户名,默认用当前账号则设为null
            string password = null;  // 对应用户名的密码,默认用当前账号则设为null

            // 要获取的属性
            string[] attributes = new string[] { "userPrincipalName", "msds-PrincipalName", "displayName", "distinguishedName", "mail" };

            // 修改为你的域名
            server = "domain:3268";  // 使用全局编录
            
            try
            {
                PrincipalContext principalContext = new PrincipalContext(
                    ContextType.Domain,
                    server.ValueOrNull(),
                    container.ValueOrNull(),
                    username.ValueOrNull(),
                    password.ValueOrNull());

                using (principalContext)
                {
                    using (UserPrincipal userPrincipal = new UserPrincipal(principalContext))
                    {
                        using (PrincipalSearcher principalSearcher = new PrincipalSearcher(userPrincipal))
                        {
                            DirectorySearcher underlyingSearcher = (DirectorySearcher)principalSearcher.GetUnderlyingSearcher();
                            underlyingSearcher.PropertiesToLoad.Clear();
                            underlyingSearcher.PropertiesToLoad.AddRange(attributes);
                            underlyingSearcher.SizeLimit = 0;
                            underlyingSearcher.Sort = new SortOption("displayName", SortDirection.Descending);

                            using (SearchResultCollection searchResultCollections = underlyingSearcher.FindAll())
                            {
                                if (searchResultCollections.Count != 0)
                                {
                                    foreach (SearchResult searchResult in searchResultCollections)
                                    {
                                        Console.WriteLine("------------------------------------------------");
                                        // 遍历每个用户返回的属性
                                        foreach (string propertyName in searchResult.Properties.PropertyNames)
                                        {
                                            ResultPropertyValueCollection item = searchResult.Properties[propertyName];
                                            if (item.Count < 1)
                                            {
                                                Console.WriteLine(propertyName + " : {null}");
                                            }
                                            else if (item.Count == 1)
                                            {
                                                Console.WriteLine(propertyName + " : " + item[0]);
                                            }
                                            else
                                            {
                                                Console.WriteLine(propertyName + ": ");
                                                foreach (var propertyValue in item)
                                                {
                                                    Console.WriteLine(propertyValue.ToString().PadLeft((propertyName + " :").Length));
                                                }
                                            };
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.ReadLine();
        }
    }
}

问题解答

1. 全局编录未返回所有域用户的原因

  • 默认搜索范围限制:用PrincipalContext连接全局编录但未指定container时,搜索范围默认限定在当前连接域的命名上下文(即该域的根),而非整个AD森林的根。
  • 属性复制规则:全局编录仅存储对象的核心属性(称为全局编录属性),若搜索依赖未被复制到全局编录的属性,可能导致部分对象不返回。不过你指定的属性大多是全局编录默认包含的,所以核心问题是搜索范围。

2. 全局编录的价值与直接连接域的区别

全局编录的核心价值体现在:

  • 跨域查询高效性:无需逐个连接每个域控制器,一次连接即可查询森林内所有域的对象,大幅减少网络连接开销和代码复杂度。
  • 森林级对象解析:用于快速查找森林内任意对象(比如用户UPN、SID),比如登录时的身份验证解析,这是单个域控制器无法实现的。
  • 统一核心属性查询:即使不需要完整属性,全局编录能提供跨域的核心属性集合,无需在每个域重复查询。

直接连接其他域虽能获取该域的完整属性,但需逐个处理每个域的连接、权限,域数量多的时候代码维护成本极高,且无法实现森林级的统一快速查询。

3. 跨所有域检索全部用户的通用方法

方法一:修正全局编录搜索范围

将搜索根指定为全局编录的森林根,修改代码中DirectorySearcher的配置:

// 获取underlyingSearcher后添加以下代码
underlyingSearcher.SearchRoot = new DirectoryEntry("GC://" + server.Split(':')[0]);

这样搜索范围就覆盖整个森林的全局编录数据。

方法二:遍历森林内所有域

先获取森林中的所有域,再逐个连接每个域执行查询:

// 获取当前森林的所有域
Forest currentForest = Forest.GetCurrentForest();
foreach (Domain domain in currentForest.Domains)
{
    using (PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domain.Name))
    {
        // 执行用户查询逻辑
        using (UserPrincipal userPrincipal = new UserPrincipal(domainContext))
        using (PrincipalSearcher searcher = new PrincipalSearcher(userPrincipal))
        {
            foreach (UserPrincipal user in searcher.FindAll())
            {
                // 处理用户数据
                Console.WriteLine(user.DisplayName + " | " + user.UserPrincipalName);
            }
        }
    }
}

这种方法能获取每个域的完整用户属性,但要求执行账号拥有所有域的读取权限。

注意事项

  • 权限:确保执行代码的账号在所有域都拥有读取用户对象的权限。
  • 性能:若森林内域数量较多,遍历每个域的方法比全局编录查询慢,全局编录更适合仅需核心属性的场景。

内容的提问来源于stack exchange,提问作者scd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 05:05:17