问题描述
我有一个小组,我们称之为 GotRocks.我正在尝试获取其所有成员,但在 DirectoryEntry 和 AccountManagement 之间我得到的结果截然不同.以下是成员检索方法的计数:
I have a group, lets call it GotRocks. I am attempting to get all of its members, but I am getting wildly different results count-wise between DirectoryEntry and AccountManagement. Below are the counts by member retrieval method:
Method 1: DirectoryEntry.PropertyName.member = 350
Method 2: AccountManagement.GroupPrincipal.GetMembers(false) = 6500
Method 2: AccountManagement.GroupPrincipal.GetMembers(true) = 6500
作为健全性检查,我进入 ADUC 并从组中提取成员列表,默认情况下限制为 2,000.这里重要的是 ADUC 似乎验证了 AccountManagement 结果.我也检查了 Children 属性,但它是空白的.此外,DirectoryEntry 中列出的成员都不属于 SchemaName 组 - 他们都是用户.
As a sanity check, I went into ADUC and pulled the list of members from the group, which is limited to 2,000 by default. The important thing here is that ADUC seems to validate the AccountManagement result. I have checked the Children property as well, but it is blank. Also, none of the members listed in DirectoryEntry are of the SchemaName group - they are all users.
我不认为这是代码问题,但可能是对 DirectoryEntry 和 GetMembers 方法如何检索组成员缺乏了解.谁能解释为什么 DirectoryEntry 成员列表会产生与 GetMembers 递归函数不同的结果?我需要注意某种方法或属性吗?注意:我已经构建了一个函数,它将通过member;range={0}-{1}"查询 DirectoryEntry,其中循环获取 1,500 个块中的成员.我在这里完全不知所措.
I do not think this is a code problem, but perhaps a lack of understanding of how DirectoryEntry and the GetMembers methods retrieve group members. Can anyone explain why the DirectoryEntry member list would yield a different result from the GetMembers recursive function? Is there a certain method or property I need to be aware of? Note: I have built a function that will query DirectoryEntry by "member;range={0}-{1}" where the loop gets members in chunks of 1,500. I am at a complete and utter loss here.
DirectoryEntry 返回的结果如此之少的事实是有问题的,因为我想使用 DirectoryEntry 的一个简单事实是,走这条路线至少比 AccountManagement 快两个数量级(即秒表时间为 1,100 毫秒,而250,000 毫秒).
The fact that DirectoryEntry is returning so few results is problematic because I want to use DirectoryEntry for the simple fact that going this route is, at a minimum, two orders of magnitude faster than AccountManagement (i.e., stopwatch times of 1,100 milliseconds versus 250,000 milliseconds).
更新 1:方法:
方法一:目录入口
private List<string> GetGroupMemberList(string strPropertyValue, string strActiveDirectoryHost, int intActiveDirectoryPageSize)
{
// Variable declaration(s).
List<string> listGroupMemberDn = new List<string>();
string strPath = strActiveDirectoryHost + "/<GUID=" + strPropertyValue + ">";
string strMemberPropertyRange = null;
DirectoryEntry directoryEntryGroup = null;
DirectorySearcher directorySearcher = null;
SearchResultCollection searchResultCollection = null;
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms676302(v=vs.85).aspx
const int intIncrement = 1500;
// Load the DirectoryEntry.
try
{
directoryEntryGroup = new DirectoryEntry(strPath, null, null, AuthenticationTypes.Secure);
directoryEntryGroup.RefreshCache();
}
catch (Exception)
{ }
try
{
if (directoryEntryGroup.Properties["member"].Count > 0)
{
int intStart = 0;
while (true)
{
int intEnd = intStart + intIncrement - 1;
// Define the PropertiesToLoad attribute, which contains a range flag that LDAP uses to get a list of members in a pre-specified chunk/block of members that is defined by each loop iteration.
strMemberPropertyRange = string.Format("member;range={0}-{1}", intStart, intEnd);
directorySearcher = new DirectorySearcher(directoryEntryGroup)
{
Filter = "(|(objectCategory=person)(objectCategory=computer)(objectCategory=group))", // User, Contact, Group, Computer objects
SearchScope = SearchScope.Base,
PageSize = intActiveDirectoryPageSize,
PropertiesToLoad = { strMemberPropertyRange }
};
try
{
searchResultCollection = directorySearcher.FindAll();
foreach (SearchResult searchResult in searchResultCollection)
{
var membersProperties = searchResult.Properties;
// Find the property that starts with the PropertyName of "member;" and get all of its member values.
var membersPropertyNames = membersProperties.PropertyNames.OfType<string>().Where(n => n.StartsWith("member;"));
// For each record in the memberPropertyNames, get the PropertyName and add to the lest.
foreach (var propertyName in membersPropertyNames)
{
var members = membersProperties[propertyName];
foreach (string memberDn in members)
{
listGroupMemberDn.Add(memberDn);
}
}
}
}
catch (DirectoryServicesCOMException)
{
// When the start of the range exceeds the number of available results, an exception is thrown and we exit the loop.
break;
}
intStart += intIncrement;
}
}
return listGroupMemberDn;
}
finally
{
listGroupMemberDn = null;
strPath = null;
strMemberPropertyRange = null;
directoryEntryGroup.Close();
if(directoryEntryGroup != null) directoryEntryGroup.Dispose();
if (directorySearcher != null) directorySearcher.Dispose();
if(searchResultCollection != null) searchResultCollection.Dispose();
}
}
方法 2:AccountManagement(将 bolRecursive 切换为 true 或 false).
Method 2: AccountManagement (toggle bolRecursive as either true or false).
private List<Guid> GetGroupMemberList(string strPropertyValue, string strDomainController, bool bolRecursive)
{
// Variable declaration(s).
List<Guid> listGroupMemberGuid = null;
GroupPrincipal groupPrincipal = null;
PrincipalSearchResult<Principal> listPrincipalSearchResult = null;
List<Principal> listPrincipalNoNull = null;
PrincipalContext principalContext = null;
ContextType contextType;
IdentityType identityType;
try
{
listGroupMemberGuid = new List<Guid>();
contextType = ContextType.Domain;
principalContext = new PrincipalContext(contextType, strDomainController);
// Setup the IdentityType. Use IdentityType.Guid because GUID is unique and never changes for a given object. Make sure that is what strPropertyValue is receiving.
// This is required, otherwise you will get a MultipleMatchesException error that says "Multiple principals contain a matching Identity."
// This happens when you have two objects that AD thinks match whatever you're passing to UserPrincipal.FindByIdentity(principalContextDomain, strPropertyValue)
identityType = IdentityType.Guid;
groupPrincipal = GroupPrincipal.FindByIdentity(principalContext, identityType, strPropertyValue);
if (groupPrincipal != null)
{
// Get all members that the group contains and add it to the list.
// Note: The true flag in GetMembers() specifies a recursive search, which enables the application to search a group recursively and return only principal objects that are leaf nodes.
listPrincipalSearchResult = groupPrincipal.GetMembers(bolRecursive);
// Remove the nulls from the list, otherwise the foreach loop breaks prematurly on the first null found and misses all other object members.
listPrincipalNoNull = listPrincipalSearchResult.Where(item => item.Name != null).ToList();
foreach (Principal principal in listPrincipalNoNull)
{
listGroupMemberGuid.Add((Guid)principal.Guid);
}
}
return listGroupMemberGuid;
}
catch (MultipleMatchesException)
{
// Multiple principals contain a matching identity.
// In other words, the same property value was found on more than one record in either of the six attributes that are listed within the IdentityType enum.
throw new MultipleMatchesException(strPropertyValue);
}
finally
{
// Cleanup objects.
listGroupMemberGuid = null;
if(listPrincipalSearchResult != null) listPrincipalSearchResult.Dispose();
if(principalContext != null) principalContext.Dispose();
if(groupPrincipal != null) groupPrincipal.Dispose();
}
}
更新 2:
public static void Main()
{
Program objProgram = new Program();
// Other stuff here.
objProgram.GetAllUserSingleDc();
// Other stuff here.
}
private void GetAllUserSingleDc()
{
string strDomainController = "domain.com";
string strActiveDirectoryHost = "LDAP://" + strDomainController;
int intActiveDirectoryPageSize = 1000;
string[] strAryRequiredProperties = null;
DirectoryEntry directoryEntry = null;
DirectorySearcher directorySearcher = null;
SearchResultCollection searchResultCollection = null;
DataTypeConverter objConverter = null;
Type fieldsType = null;
fieldsType = typeof(AdUserInfoClass);
objConverter = new DataTypeConverter();
directoryEntry = new DirectoryEntry(strActiveDirectoryHost, null, null, AuthenticationTypes.Secure);
directorySearcher = new DirectorySearcher(directoryEntry)
{
//Filter = "(|(objectCategory=person)(objectCategory=computer)(objectCategory=group))", // User, Contact, Group, Computer objects
Filter = "(sAMAccountName=GotRocks)", // Group
SearchScope = SearchScope.Subtree,
PageSize = intActiveDirectoryPageSize
PropertiesToLoad = { "isDeleted","isCriticalSystemObject","objectGUID","objectSid","objectCategory","sAMAccountName","sAMAccountType","cn","employeeId",
"canonicalName","distinguishedName","userPrincipalName","displayName","givenName","sn","mail","telephoneNumber","title","department",
"description","physicalDeliveryOfficeName","manager","userAccountControl","accountExpires","lastLogon","logonCount","lockoutTime",
"primaryGroupID","pwdLastSet","uSNCreated","uSNChanged","whenCreated","whenChanged","badPasswordTime","badPwdCount","homeDirectory",
"dNSHostName" }
};
searchResultCollection = directorySearcher.FindAll();
try
{
foreach (SearchResult searchResult in searchResultCollection)
{
clsAdUserInfo.GidObjectGuid = objConverter.ConvertByteAryToGuid(searchResult, "objectGUID");
clsAdUserInfo.StrDirectoryEntryPath = strActiveDirectoryHost + "/<GUID=" + clsAdUserInfo.GidObjectGuid + ">";
clsAdUserInfo.StrSchemaClassName = new DirectoryEntry(clsAdUserInfo.StrDirectoryEntryPath, null, null, AuthenticationTypes.Secure).SchemaClassName;
if (clsAdUserInfo.StrSchemaClassName == "group")
{
// Calling the functions here.
List<string> listGroupMemberDnMethod1 = GetGroupMemberListStackOverflow(clsAdUserInfo.GidObjectGuid.ToString(), strActiveDirectoryHost, intActiveDirectoryPageSize);
List<Guid> listGroupMemberGuidMethod2 = GetGroupMemberList(clsAdUserInfo.GidObjectGuid.ToString(), strDomainController, false)
}
// More stuff here.
}
}
finally
{
// Cleanup objects.
// Class constructors.
objProgram = null;
clsAdUserInfo = null;
// Variables.
intActiveDirectoryPageSize = -1;
strActiveDirectoryHost = null;
strDomainController = null;
strAryRequiredProperties = null;
directoryEntry.Close();
if(directoryEntry !=null) directoryEntry.Dispose();
if(directorySearcher != null) directorySearcher.Dispose();
if(searchResultCollection != null) searchResultCollection.Dispose();
objConverter = null;
fieldsType = null;
}
}
更新 3:
下面是我使用
的命名空间列表.
Below is the list of namespaces that I am using
.
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.Security.Principal;
using System.Text;
using System.Linq;
using System.Collections;
更新 4:Program.cs
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.Security.Principal;
using System.Text;
using System.Linq;
namespace activeDirectoryLdapExamples
{
public class Program
{
public static void Main()
{
Program objProgram = new Program();
objProgram.GetAllUserSingleDc();
}
#region GetAllUserSingleDc
private void GetAllUserSingleDc()
{
Program objProgram = new Program();
string strDomainController = "EnterYourDomainhere";
string strActiveDirectoryHost = "LDAP://" + strDomainController;
int intActiveDirectoryPageSize = 1000;
DirectoryEntry directoryEntry = null;
DirectorySearcher directorySearcher = null;
SearchResultCollection searchResultCollection = null;
DataTypeConverter objConverter = null;
objConverter = new DataTypeConverter();
directoryEntry = new DirectoryEntry(strActiveDirectoryHost, null, null, AuthenticationTypes.Secure);
directorySearcher = new DirectorySearcher(directoryEntry)
{
Filter = "(sAMAccountName=GotRocks)", // Group
SearchScope = SearchScope.Subtree,
PageSize = intActiveDirectoryPageSize,
PropertiesToLoad = { "isDeleted","isCriticalSystemObject","objectGUID","objectSid","objectCategory","sAMAccountName","sAMAccountType","cn","employeeId",
"canonicalName","distinguishedName","userPrincipalName","displayName","givenName","sn","mail","telephoneNumber","title","department",
"description","physicalDeliveryOfficeName","manager","userAccountControl","accountExpires","lastLogon","logonCount","lockoutTime",
"primaryGroupID","pwdLastSet","uSNCreated","uSNChanged","whenCreated","whenChanged","badPasswordTime","badPwdCount","homeDirectory",
"dNSHostName" }
};
searchResultCollection = directorySearcher.FindAll();
try
{
foreach (SearchResult searchResult in searchResultCollection)
{
Guid? gidObjectGuid = objConverter.ConvertByteAryToGuid(searchResult, "objectGUID");
string StrSamAccountName = objConverter.ConvertToString(searchResult, "sAMAccountName");
// Get new DirectoryEntry and retrieve the SchemaClassName from it by binding the current objectGUID to it.
string StrDirectoryEntryPath = strActiveDirectoryHost + "/<GUID=" + gidObjectGuid + ">";
string StrSchemaClassName = new DirectoryEntry(StrDirectoryEntryPath, null, null, AuthenticationTypes.Secure).SchemaClassName;
#region GetGroupMembers
if (StrSchemaClassName == "group")
{
// FAST!
var watch = System.Diagnostics.Stopwatch.StartNew();
List<string> listGroupMemberDn = GetGroupMemberList(gidObjectGuid.ToString(), strActiveDirectoryHost, intActiveDirectoryPageSize);
watch.Stop();
var listGroupMemberDnElapsedMs = watch.ElapsedMilliseconds;
// SLOW!
watch = System.Diagnostics.Stopwatch.StartNew();
List<Guid> listGroupMemberGuidRecursiveTrue = GetGroupMemberList(gidObjectGuid.ToString(), strDomainController, true);
watch.Stop();
var listGroupMemberGuidRecursiveTrueElapsedMs = watch.ElapsedMilliseconds;
watch = System.Diagnostics.Stopwatch.StartNew();
List<Guid> listGroupMemberGuidRecursiveFalse = GetGroupMemberList(gidObjectGuid.ToString(), strDomainController, false);
watch.Stop();
var listGroupMemberGuidRecursiveFalseElapsedMs = watch.ElapsedMilliseconds;
////// Display all members of the list.
//listGroupMemberDn.ForEach(item => Console.WriteLine("Member GUID: {0}", item));
//listGroupMemberGuidRecursiveTrue.ForEach(item => Console.WriteLine("Member GUID: {0}", item));
//listGroupMemberGuidRecursiveFalse.ForEach(item => Console.WriteLine("Member GUID: {0}", item));
Console.WriteLine("objectGUID: {0}", gidObjectGuid);
Console.WriteLine("sAMAccountName: {0}", strSamAccountName);
// Result: 350
Console.WriteLine("
listGroupMemberDn Count Members: {0}", listGroupMemberDn.Count);
Console.WriteLine("Total RunTime listGroupMemberDnElapsedMs (in milliseconds): {0}", listGroupMemberDnElapsedMs);
// Result: 6500
Console.WriteLine("
listGroupMemberGuidRecursiveTrue Count Members: {0}", listGroupMemberGuidRecursiveTrue.Count);
Console.WriteLine("Total RunTime listGroupMemberGuidRecursiveTrueElapsedMs (in milliseconds): {0}", listGroupMemberGuidRecursiveTrueElapsedMs);
// Result: 6500
Console.WriteLine("
listGroupMemberGuidRecursiveFalse Count Members: {0}", listGroupMemberGuidRecursiveFalse.Count);
Console.WriteLine("Total RunTime listGroupMemberGuidRecursiveFalseElapsedMs (in milliseconds): {0}", listGroupMemberGuidRecursiveFalseElapsedMs);
Console.WriteLine("
");
}
#endregion
#region CurrentSearchResult
else
{
Console.WriteLine("ObjectGuid = {0}", gidObjectGuid);
Console.WriteLine("SamAccountName = {0}", strSamAccountName);
}
#endregion
}
Console.WriteLine("
Press any key to continue.");
Console.ReadKey();
}
finally
{
objProgram = null;
intActiveDirectoryPageSize = -1;
strActiveDirectoryHost = null;
strDomainController = null;
directoryEntry.Close();
if (directoryEntry != null) directoryEntry.Dispose();
if (directorySearcher != null) directorySearcher.Dispose();
if (searchResultCollection != null) searchResultCollection.Dispose();
objConverter = null;
}
}
#endregion
#region GetGroupMemberListGuid
private List<Guid> GetGroupMemberList(string strPropertyValue, string strDomainController, bool bolRecursive)
{
List<Guid> listGroupMemberGuid = null;
List<Principal> listPrincipalNoNull = null;
GroupPrincipal groupPrincipal = null;
PrincipalSearchResult<Principal> listPrincipalSearchResult = null;
PrincipalContext principalContext = null;
ContextType contextType;
IdentityType identityType;
try
{
listGroupMemberGuid = new List<Guid>();
contextType = ContextType.Domain;
principalContext = new PrincipalContext(contextType, strDomainController);
identityType = IdentityType.Guid;
groupPrincipal = GroupPrincipal.FindByIdentity(principalContext, identityType, strPropertyValue);
if (groupPrincipal != null)
{
listPrincipalSearchResult = groupPrincipal.GetMembers(bolRecursive);
listPrincipalNoNull = listPrincipalSearchResult.Where(item => item.Name != null).ToList();
foreach (Principal principal in listPrincipalNoNull)
{
listGroupMemberGuid.Add((Guid)principal.Guid);
}
}
return listGroupMemberGuid;
}
catch (MultipleMatchesException)
{
throw new MultipleMatchesException(strPropertyValue);
}
finally
{
// Cleanup objects.
listGroupMemberGuid = null;
listPrincipalNoNull = null;
principalContext = null;
if (groupPrincipal != null) groupPrincipal.Dispose();
if (listPrincipalSearchResult != null) listPrincipalSearchResult.Dispose();
if (principalContext != null) principalContext.Dispose();
}
}
#endregion
#region GetGroupMemberListDn
private List<string> GetGroupMemberList(string strPropertyValue, string strActiveDirectoryHost, int intActiveDirectoryPageSize)
{
List<string> listGroupMemberDn = new List<string>();
string strPath = strActiveDirectoryHost + "/<GUID=" + strPropertyValue + ">";
const int intIncrement = 1500; // https://msdn.microsoft.com/en-us/library/windows/desktop/ms676302(v=vs.85).aspx
var members = new List<string>();
// The count result returns 350.
var group = new DirectoryEntry(strPath, null, null, AuthenticationTypes.Secure);
//var group = new DirectoryEntry($"LDAP://{"EnterYourDomainHere"}/<GUID={strPropertyValue}>", null, null, AuthenticationTypes.Secure);
while (true)
{
var memberDns = group.Properties["member"];
foreach (var member in memberDns)
{
members.Add(member.ToString());
}
if (memberDns.Count < intIncrement) break;
group.RefreshCache(new[] { $"member;range={members.Count}-*" });
}
return members;
}
#endregion
#region DataTypeConvert
private class DataTypeConverter
{
public DataTypeConverter() { }
public String ConvertToString(SearchResult searchResult, string strPropertyName)
{
String bufferObjectString = null;
try
{
bufferObjectString = (String)this.GetPropertyValue(searchResult, strPropertyName);
if (string.IsNullOrEmpty(bufferObjectString))
{
return null;
}
else
{
return bufferObjectString;
}
}
finally
{
bufferObjectString = null;
}
}
public Guid? ConvertByteAryToGuid(SearchResult searchResult, string strPropertyName)
{
Guid? bufferObjectGuid = null;
try
{
bufferObjectGuid = new Guid((Byte[])(Array)this.GetPropertyValue(searchResult, strPropertyName));
if (bufferObjectGuid == null || bufferObjectGuid == Guid.Empty)
{
throw new NullReferenceException("The field " + strPropertyName + ", of type GUID, can neither be NULL nor empty.");
}
else
{
return bufferObjectGuid;
}
}
finally
{
bufferObjectGuid = null;
}
}
}
#endregion
}
}
推荐答案
最后一个代码块(更新2)就是答案!
用于读取 member
属性的代码比它需要的要复杂.它返回倾斜的结果可能是有原因的,但我并没有看得太难,因为您根本不需要使用 DirectorySearcher
.我只是重写了它.
The code you have for reading the member
attribute is more complicated than it needs to be. There may be a reason in there why it's returning skewed results, but I didn't look too hard because you don't need to be using DirectorySearcher
at all. I just rewrote it.
这是最简单的形式:
private static List<string> GetGroupMemberList(string groupGuid, string domainDns) {
var members = new List<string>();
var group = new DirectoryEntry($"LDAP://{domainDns}/<GUID={groupGuid}>", null, null, AuthenticationTypes.Secure);
while (true) {
var memberDns = group.Properties["member"];
foreach (var member in memberDns) {
members.Add(member.ToString());
}
if (memberDns.Count == 0) break;
try {
group.RefreshCache(new[] {$"member;range={members.Count}-*", "member"});
} catch (System.Runtime.InteropServices.COMException e) {
if (e.ErrorCode == unchecked((int) 0x80072020)) { //no more results
break;
}
throw;
}
}
return members;
}
这样称呼它:
var members = GetGroupMemberList("00000000-0000-0000-0000-000000000000", "domain.com");
这不是递归的.要使其递归,您必须从每个成员创建一个新的 DirectoryEntry
并测试它是否是一个组,然后获取该组的成员.
This is not recursive. To make it recursive, you will have to create a new DirectoryEntry
from each member and test if it is a group, then get the members of that group.
我打开了代码,所以这里是递归版本.它很慢,因为它必须绑定到每个成员才能查看它是否是一个组.
I have the code open, so here's the recursive version. It is slow because it has to bind to each member to see if it's a group.
这不是防弹的.在某些情况下,您可能会得到奇怪的结果(例如,如果您的用户组中有受信任的外部域上的用户).
This is not bullet-proof. There are still cases where you might get weird results (like if you have users on trusted external domains in a group).
private static List<string> GetGroupMemberList(string groupGuid, string domainDns, bool recurse = false) {
var members = new List<string>();
var group = new DirectoryEntry($"LDAP://{domainDns}/<GUID={groupGuid}>", null, null, AuthenticationTypes.Secure);
while (true) {
var memberDns = group.Properties["member"];
foreach (var member in memberDns) {
if (recurse) {
var memberDe = new DirectoryEntry($"LDAP://{member}");
if (memberDe.Properties["objectClass"].Contains("group")) {
members.AddRange(GetGroupMemberList(
new Guid((byte[]) memberDe.Properties["objectGuid"].Value).ToString(), domainDns,
true));
} else {
members.Add(member.ToString());
}
} else {
members.Add(member.ToString());
}
}
if (memberDns.Count == 0) break;
try {
group.RefreshCache(new[] {$"member;range={members.Count}-*", "member"});
} catch (System.Runtime.InteropServices.COMException e) {
if (e.ErrorCode == unchecked((int) 0x80072020)) { //no more results
break;
}
throw;
}
}
return members;
}
更新:我确实必须编辑您的 GetMembers
示例,因为它不断向我抛出异常.我注释掉了 .Where
行并更改了将成员添加到列表中的 foreach
循环:
Update:
I did have to edit your GetMembers
example, since it kept throwing exceptions on me. I commented out the .Where
line and changed the foreach
loop that adds the members to the list:
//listPrincipalNoNull = listPrincipalSearchResult.Where(item => item.Name != null).ToList();
if (groupPrincipal != null) {
foreach (Principal principal in listPrincipalSearchResult) {
listGroupMemberGuid.Add(((DirectoryEntry)principal.GetUnderlyingObject()).Guid);
}
}
这当然是在编译一个 Guid 列表而不是 DN.
This, of course, is compiling a list of Guids rather than DNs.
更新 2: 这是一个版本,它还拉取将该组作为主要组的用户(但未在 member
中列出组的属性).GetMembers
似乎可以做到这一点.将用户创建的组作为主要组会很奇怪,但在技术上是可行的.部分内容从此处的答案中复制:如何检索组中的用户,包括主要组用户
Update 2: Here is a version that also pulls users who have the group as the primary group (but not listed in the member
attribute of the group). GetMembers
seems to do this. It would be odd for a user-created group to be the primary group, but it is technically possible. Parts of this are copied from the answer here: How to retrieve Users in a Group, including primary group users
private List<string> GetGroupMemberList(string strPropertyValue, string strActiveDirectoryHost, int intActiveDirectoryPageSize)
{
// Variable declaration(s).
List<string> listGroupMemberDn = new List<string>();
string strPath = strActiveDirectoryHost + "/<GUID=" + strPropertyValue + ">";
const int intIncrement = 1500; // https://msdn.microsoft.com/en-us/library/windows/desktop/ms676302(v=vs.85).aspx
var members = new List<string>();
// The count result returns 350.
var group = new DirectoryEntry(strPath, null, null, AuthenticationTypes.Secure);
//var group = new DirectoryEntry($"LDAP://{"EnterYourDomainHere"}/<GUID={strPropertyValue}>", null, null, AuthenticationTypes.Secure);
while (true)
{
var memberDns = group.Properties["member"];
foreach (var member in memberDns)
{
members.Add(member.ToString());
}
if (memberDns.Count < intIncrement) break;
group.RefreshCache(new[] { $"member;range={members.Count}-*" });
}
//Find users that have this group as a primary group
var secId = new SecurityIdentifier(group.Properties["objectSid"][0] as byte[], 0);
/* Find The RID (sure exists a best method)
*/
var reg = new Regex(@"^S.*-(d+)$");
var match = reg.Match(secId.Value);
var rid = match.Groups[1].Value;
/* Directory Search for users that has a particular primary group
*/
var dsLookForUsers =
new DirectorySearcher {
Filter = string.Format("(primaryGroupID={0})", rid),
SearchScope = SearchScope.Subtree,
PageSize = 1000,
SearchRoot = new DirectoryEntry(strActiveDirectoryHost)
};
dsLookForUsers.PropertiesToLoad.Add("distinguishedName");
var srcUsers = dsLookForUsers.FindAll();
foreach (SearchResult user in srcUsers)
{
members.Add(user.Properties["distinguishedName"][0].ToString());
}
return members;
}
这篇关于Active Directory:DirectoryEntry 成员列表<>GroupPrincipal.GetMembers()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!