Active Directoryユーザーとコンピューターツールの[ユーザー、連絡先、グループの検索]と同様に、C#からADを検索する方法を理解しようとしています。グループ名またはユーザー名のいずれかを含む文字列があります(通常、firstname middleinitial [存在する場合] lastnameの形式ですが、常にではありません)。グループとユーザーに対して別々のクエリを実行しても、ほとんどのユーザーアカウントをキャプチャする検索方法を思い付くことができません。ユーザー、連絡先、およびグループの検索ツールは、ほぼ毎回それらを戻します。誰か提案がありますか?
DirectorySearcherクラスの使用方法はすでに知っていますが、問題は、必要な処理を実行するクエリが見つからないことです。 cnもsamaccountnameも、この中のユーザーの名前とは何の関係もないので、それらを検索することはできません。物事を分割してsnとgivenNameを検索しても、そのツールほどではありません。
.NET 3.5を使用していますか?もしそうなら-ADは.NET3.5で素晴らしい新機能を持っています-この記事をチェックしてください 。NET 3.5でのディレクトリセキュリティプリンシパルの管理 EthanWilanskiとJoeKaplanによる。
大きな新機能の1つは、ADでのユーザーやグループの検索を大幅に簡素化する「PrincipalSearcher」クラスです。
.NET 3.5を使用できない場合、作業を楽にする可能性のあるものの1つは「あいまいな名前の解決」と呼ばれ、名前に関連するほぼすべての属性を一度に検索する、あまり知られていない特別な検索フィルターです。
LDAP検索クエリを次のように指定します。
searcher.Filter = string.Format("(&(objectCategory=person)(anr={0}))", yourSearchTerm)
また、ADではデフォルトで単一値でインデックスが作成されるため、「objectCategory」属性でフィルタリングすることをお勧めします。これは、「objectClass」を使用するよりもはるかに高速です。
マーク
System.DirectoryServicesには、DirectoryEntryとDirectorySearcherの2つの名前空間があります。
DirectorySearcherの詳細については、こちらをご覧ください。
http://msdn.Microsoft.com/en-us/library/system.directoryservices.directorysearcher.aspx
次に、Filterプロパティを使用して、グループ、ユーザーなどでフィルタリングできます。
したがって、アカウント名でフィルタリングする場合は、.Filterを次のように設定します。
"(&(sAMAccountName=bsmith))"
filterAllメソッドを実行します。これにより、ユーザーに関する情報をループしてプルできるSearchResultCollectionが返されます。
public DirectoryEntry Search(string searchTerm, string propertyName)
{
DirectoryEntry directoryObject = new DirectoryEntry(<pathToAD>);
foreach (DirectoryEntry user in directoryObject.Children)
{
if (user.Properties[propertyName].Value != null)
if (user.Properties[propertyName].Value.ToString() == searchTerm)
return user;
}
return null;
}
ユーザーの検索方法に基づいて検索文字列を作成する必要があります。
using (var adFolderObject = new DirectoryEntry())
{
using(var adSearcherObject = new DirectorySearcher(adFolderObject))
{
adSearcherObject.SearchScope = SearchScope.Subtree;
adSearcherObject.Filter = "(&(objectClass=person)(" + userType + "=" + userName + "))";
return adSearcherObject.FindOne();
}
}
userTypeは、ユーザー名のフォーマット方法に応じて、sAMAccountNameまたはCNのいずれかになります。
例:
firstname.lastname(またはflastname)は通常、sAMAccountNameになります
FirstNameLastNameは通常CNになります
JoeKaplanとEthanWilansky の記事からこれを取得しました(System.DirectoryServices.AccountManagement dllの参照から)これを使用します:
using System.DirectoryServices.AccountManagement;
private bool CheckUserinAD(string domain, string username)
{
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domain);
UserPrincipal user = new UserPrincipal(domainContext);
user.Name = username;
PrincipalSearcher pS = new PrincipalSearcher();
pS.QueryFilter = user;
PrincipalSearchResult<Principal> results = pS.FindAll();
if (results != null && results.Count() > 0)
return true;
return false;
}
他の回答は十分に説明されておらず、それらを実装する方法も説明されておらず、ほとんどが間違ったフィルタープロパティを提供していました。 _.Filter
_を使用する必要はありません。プロパティ(姓= _.Surname
_、名= _.GivenName
_)をUserPrincipal
オブジェクトに割り当てて、そのオブジェクトを検索するだけです。検索をトリガーするイベントでPrincipalSearcher
を使用する:
_string firstName = txtFirstName.Text;
string lastName = txtLastName.Text;
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal up = new UserPrincipal(ctx);
if (!String.IsNullOrEmpty(firstName))
up.GivenName = firstName;
if (!String.IsNullOrEmpty(lastName))
up.Surname = lastName;
PrincipalSearcher srch = new PrincipalSearcher(up);
srch.QueryFilter = up;
_
姓名のテキストボックスがあり、ID /名前がtxtFirstName
とtxtLastName
であると想定しています。探しているプロパティに値がない場合は、それをUserPrincipal
に追加しないでください。そうしないと、例外が発生します。これが、上記のチェックの理由です。
次に、srch
に対して_.FindAll
_を実行して、検索結果をPrincipalSearchResult
オブジェクトのPrincipal
コレクションに取得します。
_using (PrincipalSearchResult<Principal> results = srch.FindAll())
{
if (results != null)
{
int resultCount = results.Count();
if (resultCount > 0) // we have results
{
foreach (Principal found in results)
{
string username = found.SamAccountName; // Note, this is not the full user ID! It does not include the domain.
}
}
}
}
_
.Count()
が_0
_であっても、結果がnullにならないこと、および両方のチェックが存在する理由に注意してください。
そのforeach
を繰り返し使用して必要なプロパティを取得します。これにより、C#を使用してADでユーザーを見つける方法の質問に答えますが、Principal
オブジェクトを使用して取得できるプロパティはごくわずかであり、この質問に到達した場合はグーグル(私がしたように)、私は非常にがっかりするでしょう。必要なのはそれだけだとわかったら、これで完了です。しかし、残りを得る(そして私自身の良心を休める)ためには、あなたは飛び込む必要があります、そして私はそれをする方法を説明します。
上記のusername
だけを使用することはできませんが、_DOMAIN\doej
_の種類の名前全体を取得する必要があります。これがあなたのやり方です。代わりに、これを上記のforeach
ループに入れます。
_string userId = GetUserIdFromPrincipal(found);
_
この関数を使用します。
_private static string GetUserIdFromPrincipal(Principal prin)
{
string upn = prin.UserPrincipalName;
string domain = upn.Split('@')[1];
domain = domain.Substring(0, domain.IndexOf(".YOURDOMAIN"));
// "domain" will be the subdomain the user belongs to.
// This may require edits depending on the organization.
return domain + @"\" + prin.SamAccountName;
}
_
それができたら、次の関数を呼び出すことができます。
_ public static string[] GetUserProperties(string strUserName)
{
UserPrincipal up = GetUser(strUserName);
if (up != null)
{
string firstName = up.GivenName;
string lastName = up.Surname;
string middleInit = String.IsNullOrEmpty(up.MiddleName) ? "" : up.MiddleName.Substring(0, 1);
string email = up.EmailAddress;
string location = String.Empty;
string phone = String.Empty;
string office = String.Empty;
string dept = String.Empty;
DirectoryEntry de = (DirectoryEntry)up.GetUnderlyingObject();
DirectorySearcher ds = new DirectorySearcher(de);
ds.PropertiesToLoad.Add("l"); // city field, a.k.a location
ds.PropertiesToLoad.Add("telephonenumber");
ds.PropertiesToLoad.Add("department");
ds.PropertiesToLoad.Add("physicalDeliveryOfficeName");
SearchResultCollection results = ds.FindAll();
if (results != null && results.Count > 0)
{
ResultPropertyCollection rpc = results[0].Properties;
foreach (string rp in rpc.PropertyNames)
{
if (rp == "l") // this matches the "City" field in AD properties
location = rpc["l"][0].ToString();
if (rp == "telephonenumber")
phone = FormatPhoneNumber(rpc["telephonenumber"][0].ToString());
if (rp == "physicalDeliveryOfficeName")
office = rpc["physicalDeliveryOfficeName"][0].ToString();
if (rp == "department")
dept = rpc["department"][0].ToString();
}
}
string[] userProps = new string[10];
userProps[0] = strUserName;
userProps[1] = firstName;
userProps[2] = lastName;
userProps[3] = up.MiddleName;
userProps[4] = middleInit;
userProps[5] = email;
userProps[6] = location;
userProps[7] = phone;
userProps[8] = office;
userProps[9] = dept;
return userProps;
}
else
return null;
}
/// <summary>
/// Returns a UserPrincipal (AD) user object based on string userID being supplied
/// </summary>
/// <param name="strUserName">String form of User ID: domain\username</param>
/// <returns>UserPrincipal object</returns>
public static UserPrincipal GetUser(string strUserName)
{
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain);
try
{
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext, strUserName);
return oUserPrincipal;
}
catch (Exception ex) { return null; }
}
public static string FormatPhoneNumber(string strPhoneNumber)
{
if (strPhoneNumber.Length > 0)
// return String.Format("{0:###-###-####}", strPhoneNumber); // formating does not work because strPhoneNumber is a string and not a number
return Regex.Replace(strPhoneNumber, @"(\d{3})(\d{3})(\d{4})", "$1-$2-$3");
else
return strPhoneNumber;
}
_
FormatPhoneNumber
関数は北米の数値用であることに注意してください。見つかった番号(_##########
_)を取り、それを_###-###-####
_に分割します。
次に、次のようなプロパティをforeach
ループに戻します。
_string[] userProps = GetUserProperties(userId);
string office = userProps[8];
_
ただし、全体的な解決策としてこれらの結果をDataRow
列に追加し、それをDataTable
の一部として返し、それをListView
またはGridView
にバインドすることもできます。これが私がそれをした方法であり、必要なプロパティで満たされた_List<string>
_を送信しました:
_ /// <summary>
/// Gets matches based on First and Last Names.
/// This function takes a list of acceptable properties:
/// USERNAME
/// MIDDLE_NAME
/// MIDDLE_INITIAL
/// EMAIL
/// LOCATION
/// POST
/// PHONE
/// OFFICE
/// DEPARTMENT
///
/// The DataTable returned will have columns with these names, and firstName and lastName will be added to a column called "NAME"
/// as the first column, automatically.
/// </summary>
/// <param name="firstName"></param>
/// <param name="lastName"></param>
/// <param name="props"></param>
/// <returns>DataTable of columns from "props" based on first and last name results</returns>
public static DataTable GetUsersFromName(string firstName, string lastName, List<string> props)
{
string userId = String.Empty;
int resultCount = 0;
DataTable dt = new DataTable();
DataRow dr;
DataColumn dc;
// Always set the first column to the Name we pass in
dc = new DataColumn();
dc.DataType = System.Type.GetType("System.String");
dc.ColumnName = "NAME";
dt.Columns.Add(dc);
// Establish our property list as columns in our DataTable
if (props != null && props.Count > 0)
{
foreach (string s in props)
{
dc = new DataColumn();
dc.DataType = System.Type.GetType("System.String");
if (!String.IsNullOrEmpty(s))
{
dc.ColumnName = s;
dt.Columns.Add(dc);
}
}
}
// Start our search
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal up = new UserPrincipal(ctx);
if (!String.IsNullOrEmpty(firstName))
up.GivenName = firstName;
if (!String.IsNullOrEmpty(lastName))
up.Surname = lastName;
PrincipalSearcher srch = new PrincipalSearcher(up);
srch.QueryFilter = up;
using (PrincipalSearchResult<Principal> results = srch.FindAll())
{
if (results != null)
{
resultCount = results.Count();
if (resultCount > 0) // we have results
{
foreach (Principal found in results)
{
// Iterate results, set into DataRow, add to DataTable
dr = dt.NewRow();
dr["NAME"] = found.DisplayName;
if (props != null && props.Count > 0)
{
userId = GetUserIdFromPrincipal(found);
// Get other properties
string[] userProps = GetUserProperties(userId);
foreach (string s in props)
{
if (s == "USERNAME")
dr["USERNAME"] = userId;
if (s == "MIDDLE_NAME")
dr["MIDDLE_NAME"] = userProps[3];
if (s == "MIDDLE_INITIAL")
dr["MIDDLE_INITIAL"] = userProps[4];
if (s == "EMAIL")
dr["EMAIL"] = userProps[5];
if (s == "LOCATION")
dr["LOCATION"] = userProps[6];
if (s == "PHONE")
dr["PHONE"] = userProps[7];
if (s == "OFFICE")
dr["OFFICE"] = userProps[8];
if (s == "DEPARTMENT")
dr["DEPARTMENT"] = userProps[9];
}
}
dt.Rows.Add(dr);
}
}
}
}
return dt;
}
_
この関数は次のように呼び出します。
_string firstName = txtFirstName.Text;
string lastName = txtLastName.Text;
List<string> props = new List<string>();
props.Add("OFFICE");
props.Add("DEPARTMENT");
props.Add("LOCATION");
props.Add("USERNAME");
DataTable dt = GetUsersFromName(firstName, lastName, props);
_
DataTable
にはこれらの列が入力され、最初の列としてNAME
列が入力されます。この列には、ADからのユーザーの実際の_.DisplayName
_が含まれます。
注:これらすべてを使用するには、_System.DirectoryServices
_および_System.DirectoryServices.AccountManagement
_、_System.Text.RegularExpressions
_、_System.Data
_を参照する必要があります。
HTH!
宮城の答えに付け加えると…。
DirectorySearcherに適用するフィルター/クエリは次のとおりです
DirectorySearcher ds = new DirectorySearcher();
ds.Filter = "samaccountname=" + userName;
SearchResult result = ds.FindOne();
この投稿で私が探していたコードは次のとおりです。
string uid = Properties.Settings.Default.uid;
string pwd = Properties.Settings.Default.pwd;
using (var context = new PrincipalContext(ContextType.Domain, "YOURDOMAIN", uid, pwd))
{
using (UserPrincipal user = new UserPrincipal(context))
{
user.GivenName = "*adolf*";
using (var searcher = new PrincipalSearcher(user))
{
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
Console.WriteLine("First Name: " + de.Properties["givenName"].Value);
Console.WriteLine("Last Name : " + de.Properties["sn"].Value);
Console.WriteLine("SAM account name : " + de.Properties["samAccountName"].Value);
Console.WriteLine("User principal name: " + de.Properties["userPrincipalName"].Value);
Console.WriteLine("Mail: " + de.Properties["mail"].Value);
PrincipalSearchResult<Principal> groups = result.GetGroups();
foreach (Principal item in groups)
{
Console.WriteLine("Groups: {0}: {1}", item.DisplayName, item.Name);
}
Console.WriteLine();
}
}
}
}
Console.WriteLine("End");
Console.ReadLine();
どの文字のワイルドカードもアスタリスク(*)のようです。それが理由です:
user.GivenName = "*firstname*";
続きを読む Microsoftドキュメント