Active Directory: настройка производительности функции для извлечения членов группы - PullRequest
0 голосов
/ 03 мая 2018

Этот пост является продолжением следующего:

Active Directory: список членов DirectoryEntry <> GroupPrincipal.GetMembers ()

У меня есть функция, которая извлекает атрибут difishedName для всех членов группы в Active Directory. Эта функция используется в очень большом сценарии, который извлекает все объекты пользователя и группы (общее время выполнения составляет 7-10 минут). Моя проблема здесь заключается в том, что нисходящий поиск служб SSIS на выделенном имени чрезвычайно медленный. Это неудивительно из-за того, что он ищет varchar (255) по сравнению с UniqueIdentifier (16 байт). Я мог бы сделать SQL Select на источнике и затем Merge Join, что ускорило бы процесс. Но я заметил потенциальное состояние гонки (см. Время выполнения выше) в извлечении, где члены группы существуют без соответствующего отличительного имени. Если это так, то мне нужно решить это; однако объединение слиянием не приведет к ошибке загрузки, тогда как поиск может быть настроен на отказ загрузки.

Итак, мне нужно получить руководство на лету через отличительное имя. Однако, когда я пытаюсь использовать приведенный ниже метод, производительность функции GetGroupMemberList существенно падает. Есть ли лучший / более быстрый способ получить 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:

Код для получения DN в foreach (searchResult):

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

1 Ответ

0 голосов
/ 03 мая 2018

Это всегда будет медленнее, так как вам придется снова общаться с Active Directory для каждого участника. Но вы можете минимизировать объем трафика, который он делает.

Я провел пару быстрых тестов, отслеживая сетевой трафик. Я сравнил два метода:

  1. Вызов .Guid на DirectoryEntry, как у вас в коде.
  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, он получит каждый атрибут для учетной записи. Кажется, что использование .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]));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...