読者です 読者をやめる 読者になる 読者になる

からめもぶろぐ。

ワタシ SharePoint チョット デキル

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

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

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

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

    public static class Program {

        private static void Main(string[] args) {
            // 検索するルート階層を指定する
            using (var entry = new DirectoryEntry("LDAP://domain.local/CN=Users,DC=domain,DC=local"))
            using (var searcher = new DirectorySearcher(entry)) {
                // ユーザーログオン名が hoge@domain.local のユーザーを検索する
                searcher.Filter = "(userPrincipalName=hoge@domain.local)";
                // 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 クラスのオブジェクトが取得できるので、属性を取得したり、さらに別の検索をすることができます。ちなみに Active Directory の属性は単一の値を返すものと複数の値を返すものがあるので、PropertyValueCollection クラスには Value プロパティと Items プロパティの 2 種類の方法が用意されています。今回は LINQ でぶん回して複数の値を string.Join するようにしています。

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

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

    public static class Program {

        private static void Main(string[] args) {
            using (var entry = new DirectoryEntry("LDAP://domain.local/CN=Users,DC=domain,DC=local"))
            using (var searcher = new DirectorySearcher(entry)) {
                searcher.Filter = "(userPrincipalName=hoge@domain.local)";
                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 が入っていれば拡張スキーマも同じように検索できます。