多域Active Directory跨所有域及子域检索全部用户的技术问询
多域AD环境下全局编录检索用户的问题
我们尝试获取多域场景下Active Directory中所有域及子域的全部用户列表,使用了下方代码。原本以为配置3268端口(全局编录)后能检索所有域用户,但实际仅返回单个域用户。现提出以下问题:
- 为何全局编录未包含所有域的用户?
- 有资料显示需指定容器搜索特定域,若如此,全局编录的价值何在?直接连接其他域不即可?
- 是否存在通用方法跨所有域检索全部用户?
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
相关产品推荐
相关产品推荐

