【问题标题】:Active Directory: Tune performance of function to retrieve group membersActive Directory:调整功能的性能以检索组成员
【发布时间】:2018-05-03 15:09:17
【问题描述】:

这篇文章是对以下内容的后续:

Active Directory: DirectoryEntry member list <> GroupPrincipal.GetMembers()

我有一个函数可以检索 Active Directory 中组的所有成员的 distinctName 属性。此函数用于检索所有用户和组对象的非常大的脚本(总运行时间为 7-10 分钟)。我的问题是,distinguishedName 上的下游 SSIS 查找非常慢。这并不奇怪,因为它查找的是 varchar(255) 与 UniqueIdentifier(16 字节)。我可以在源上执行 SQL Select,然后 Merge Join,这样可以加快速度。但是,我注意到提取物中存在潜在的竞争条件(请参阅上面的运行时间),其中组成员存在而没有匹配的 distinctName。如果是这种情况,那么我需要解决这个问题;但是,Merge Join 不会使加载失败,而 Lookup 可以设置为使加载失败。

所以,我需要通过 DistinguishedName 即时获取 guid。但是,当我尝试使用以下方法时,GetGroupMemberList 函数的性能会大幅下降。有没有更好/更快的方法来通过 distinctName 获取组成员 guid?

方法(用于两个循环):

listGroupMemberGuid.Add(new DirectoryEntry("LDAP://" + member, null, null, AuthenticationTypes.Secure).Guid);

listGroupMemberGuid.Add(new DirectoryEntry("LDAP://" + user, null, null, AuthenticationTypes.Secure).Guid);

功能:

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;
}

更新 1:

foreach(searchResult)中检索DN的代码:

foreach (SearchResult searchResult in searchResultCollection)
{
    string strDn = searchResult.Properties["distinguishedName"][0].ToString();
    var de = new DirectoryEntry("LDAP://" + strDn, null, null, AuthenticationTypes.Secure);
    de.RefreshCache(new[] { "objectGuid" });
    var guid = new Guid((byte[])de.Properties["objectGuid"].Value);
}

【问题讨论】:

    标签: c# ssis active-directory


    【解决方案1】:

    它总是会变慢,因为您必须为每个成员再次与 Active Directory 对话。但是,您可以最大限度地减少它的流量。

    我做了几个快速测试,同时监控网络流量。我比较了两种方法:

    1. DirectoryEntry 上调用 .Guid,就像在代码中一样。
    2. 使用此方法:
    var de = new DirectoryEntry("LDAP://" + member, null, null, AuthenticationTypes.Secure);
    de.RefreshCache(new [] {"objectGuid"});
    var guid = new Guid((byte[]) de.Properties["objectGuid"].Value);
    

    第二种方法的网络流量明显减少:第一个帐户不到 1/3,之后的每个帐户甚至更少(似乎重用了连接)。

    我知道,如果您使用 .Properties 而不先调用 .RefreshCache,它将为帐户拉出 every 属性。似乎使用 .Guid 做同样的事情。

    调用.RefreshCache(new [] {"objectGuid"}); 只会得到objectGuid 属性,没有其他任何东西,并将其保存在缓存中。那么当你使用.Properties["objectGuid"]时,它已经在缓存中具有该属性,所以它不需要再进行任何网络连接。

    更新: 对于您在搜索中获得的那些,只需询问objectGuid 属性而不是distinguishedName

    dsLookForUsers.PropertiesToLoad.Add("objectGuid");
    
    var srcUsers = dsLookForUsers.FindAll();
    
    foreach (SearchResult user in srcUsers)
    {
        members.Add(new Guid((byte[])user.Properties["objectGuid"][0]));
    }
    

    【讨论】:

    • 我已经实现了你提供的代码。在 foreach(SearchResult) 中,我收到错误 'System.Runtime.InteropServices.COMException: 'Unknown error (0x80005000)' 这是一个样板错误。我已经提供了在此问题的更新中获取 DN 的代码。唯一的区别是错误出现在被禁用的帐户上。这有关系吗?
    • 哪一行抛出异常?
    • 没关系。对于您在搜索中获得的那些,还有另一种方法。查看我的更新。
    • 我用方法2做了一些测试。在过滤器中,我包含了一个组和一个用户对象。然后,当我遍历 SearchResultCollection 中的每个对象时,我注意到 de.PropertiesCollection 已刷新并填充了两个对象的信息。根据您的解释,我的理解是 Properties.ObjectGuid 值应该是唯一的更新,因此网络流量不会减少。但是,似乎整个 PropertyCollection 都被刷新了。我的理解正确吗?
    • new DirectoryEntry 部分仅与您从 member 属性中获取的成员相关。对于您通过DirectorySearcher 获得的其余部分,请在我的答案末尾使用我更新的方法。不要为这些人创建new DirectoryEntry
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多