💻

System.DirectoryServices 名前空間を使って LDAP 検索をする

2022/01/01に公開

はじめに

System.DirectoryServices.dll を参照設定すると LDAP (Lightweight Directory Access Protocol) による Active Directory 検索ができるようになります。やってみたらそれほど難しくはありませんでした。

ユーザーの検索をしてみる

example.com ドメインに対してユーザー ログオン名 (userPrincipalName) で検索してみます。DirectoryEntry クラスは Active Directory のオブジェクトをカプセル化します。DirectorySearcher クラスでクエリ式を作成します。

public static class Program
{

    private static void Main(string[] args)
    {
        // 検索するルート階層を指定する
        using (var entry = new DirectoryEntry("LDAP://example.com/CN=Users,DC=example,DC=com"))
        using (var searcher = new DirectorySearcher(entry))
        {
            // ユーザーログオン名が hoge@example.com のユーザーを検索する
            searcher.Filter = "(userPrincipalName=hoge@example.com)";
            // 1 件だけ抽出するときは FindOne メソッドで
            // 結果が複数件戻ってくるときは FindAll メソッドを使う
            var result = searcher.FindOne();
            foreach (var value in result.GetDirectoryEntry().Properties
                .Cast<PropertyValueCollection>()
                .OrderBy(x => x.PropertyName))
            {
                // 属性と値をすべて表示する
                Console.WriteLine("{0}={1}", 
                    value.PropertyName, 
                    string.Join(";", value.Cast<object>().Select(x => x.ToString())));
            }
        }
        Console.ReadKey();
    }

}

LDAP クエリは書き方が特殊で、and 条件は (&(A=b)(B=b)) のように書き、or 条件は (|(A=a)(B=b)) のように書きます。慣れてしまえばそれほどは難しくありません。ワイルドカードも使えますので、ある程度自由の利いた検索ができるようになっています。

FindOne メソッドの戻り値は SearchResult クラスです。GetDirectoryEntry メソッドを呼び出せば DirectoryEntry クラスのオブジェクトが取得できます。DirectoryEntry のオブジェクトからは属性を取得したり、オブジェクトを起点として別の検索ができます。Active Directory の属性は単一の値を返すものと複数の値を返すものがあります。そのため PropertyValueCollection クラスには Value プロパティと Items プロパティの 2 種類の方法が用意されています。今回は LINQ を使って複数の値を string.Join するようにしています。

ユーザーが属するグループの検索をしてみる

ちょっとだけ応用編。検索したユーザーが所属するグループを検索します。グループの member 属性には識別名が入っているのでユーザーの distinguishedName 属性の値を渡してあげます。今度は結果が複数あるので FindAll メソッドを使っています。LINQ には対応していないので CastSelect が必要です。

public static class Program
{

    private static void Main(string[] args)
    {
        using (var entry = new DirectoryEntry("LDAP://example.com/CN=Users,DC=domain,DC=local"))
        using (var searcher = new DirectorySearcher(entry))
        {
            searcher.Filter = "(userPrincipalName=hoge@example.com)";
            var result = searcher.FindOne();
            var user = result.GetDirectoryEntry();
            // 識別名を取得する
            var distinguishedName = user.Properties["distinguishedName"].Value;
            // メンバーにユーザーが含まれるグループを検索する
            searcher.Filter = string.Format("(&(objectClass=group)(member={0}))", distinguishedName);
            var groups = searcher.FindAll();
            foreach (var group in groups.Cast<SearchResult>().Select(x => x.GetDirectoryEntry()))
            {
                Console.WriteLine(group.Properties["cn"].Value);
            }
        }
        Console.ReadKey();
    }

}

おわりに

もちろん Exchange が入っていれば拡張スキーマも同じように検索できます。

Discussion