GroupPrincipal.GetMembers在组(或子组,如果递归)包含ForeignSecurityPrincipal时失败

对于遇到同样问题的人来说,这不是一个问题。

发生以下错误:

System.DirectoryServices.AccountManagement.PrincipalOperationException: An error (87) occurred while enumerating the groups. The group's SID could not be resolved. at System.DirectoryServices.AccountManagement.SidList.TranslateSids(String target, IntPtr[] pSids) at System.DirectoryServices.AccountManagement.SidList.ctor(List`1 sidListByteFormat, String target, NetCred credentials) at System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.TranslateForeignMembers() 

运行以下代码并且组或子组包含ForeignSecurityPrincipal时:

 private static void GetUsersFromGroup() { var groupDistinguishedName = "CN=IIS_IUSRS,CN=Builtin,DC=Domain,DC=com"; //NB: Exception thrown during iteration of members rather than call to GetMembers. using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "Domain", "Username", "Password")) { using (GroupPrincipal groupPrincipal = GroupPrincipal.FindByIdentity(ctx, IdentityType.DistinguishedName, groupDistinguishedName)) { using (var searchResults = groupPrincipal.GetMembers(true))//Occurs when false also. { foreach (UserPrincipal item in searchResults.OfType()) { Console.WriteLine("Found user: {0}", item.SamAccountName) } } } } } 

我向微软提出了一个支持电话,他们已经确认这是一个问题。 内部已经提出了一个错误,但尚未确定是否会修复此错误。

Microsoft建议使用以下解决方法代码,但由于重复调用UserPrincipal.FindByIdentity,因此对具有大量用户的组执行效果不佳。

 class Program { //"CN=IIS_IUSRS,CN=Builtin,DC=dev-sp-sandbox,DC=local"; //TODO MODIFY THIS LINE ACCORDING TO YOUR DC CONFIGURATION static void Main(string[] args) { if (args.Length != 1) { Console.WriteLine("Usage: ListGroupMembers \"group's DistinguishedName\""); Console.WriteLine("Example: ListGroupMembers \"CN=IIS_IUSRS,CN=Builtin,DC=MyDomain,DC=local\""); return; } string groupDistinguishedName = args[0]; PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "dev-sp-dc", "Administrator", "Corp123!"); List users = new List(); listGroupMembers(groupDistinguishedName, ctx, users); foreach (UserPrincipal u in users) { Console.WriteLine(u.DistinguishedName); } } //Recursively list the group's members which are not Foreign Security Principals private static void listGroupMembers(string groupDistinguishedName, PrincipalContext ctx, List users) { DirectoryEntry group = new DirectoryEntry("LDAP://" + groupDistinguishedName); foreach (string dn in group.Properties["member"]) { DirectoryEntry gpMemberEntry = new DirectoryEntry("LDAP://" + dn); System.DirectoryServices.PropertyCollection userProps = gpMemberEntry.Properties; object[] objCls = (userProps["objectClass"].Value) as object[]; if (objCls.Contains("group")) listGroupMembers(userProps["distinguishedName"].Value as string, ctx, users); if (!objCls.Contains("foreignSecurityPrincipal")) { UserPrincipal u = UserPrincipal.FindByIdentity(ctx, IdentityType.DistinguishedName, dn); if(u!=null) // u==null for any other types except users users.Add(u); } } } } 

可以修改上述代码以查找导致组中出现问题的外部安全主体。

Microsoft提供了有关外部安全主体的以下信息:

这是AD中的一类对象,它表示来自外部源的安全主体(因此另一个林/域或下面的“特殊”帐户之一)。 该课程在此处记录: http : //msdn.microsoft.com/en-us/library/cc221858(v = PO10.10).aspx此处记录了容器: http : //msdn.microsoft.com/en -us / library / cc200915(v = PROT.10).aspx FSP不是AD中的真实对象,而是位于不同的受信任域/林中的对象的占位符(指针)。 它也可以是“特殊身份”之一,它们是一群众所周知的帐户,它们也被归类为FSP,因为它们的SID与域SID不同。 例如,匿名,经过身份validation的用户,批处理以及此处记录的其他几个帐户: http : //technet.microsoft.com/en-us/library/cc779144(v = WS.10).aspx

当然这是一个老线程,但可能会帮助某人。 我用下面的代码块来解决问题。 Principal类公开一个名为StructuralObjectClass的属性,它告诉您该主体的AD类是什么。 我用它来决定对象是否是用户。 GetMembers(true)以递归方式搜索有问题的groupPrincipal中的所有嵌套成员。

希望这有助于某人。

  List members = new List(); foreach (var principal in groupPrincipal.GetMembers(true)) { var type = principal.StructuralObjectClass; if (type.Contains("user")) members.Add((UserPrincipal)principal); } 

谢谢,R

帐户管理库存在许多令人悲伤的缺陷,这只是众多中的另一个……

要使事情稍微快一点,您可以做的一件事就是调整LDAP查询,以便它同时检查组成员资格和对象类型作为查询的一部分而不是循环。 老实说,我怀疑它会有很大的不同。

查询的大部分灵感来自如何编写LDAP查询以测试用户是否是组的成员? 。

查询: (&(!objectClass=foreignSecurityPrincipal)(memberof=CN=YourGroup,OU=Users,DC=YourDomain,DC=com))

注意:这是一个未经测试的查询…

如果有一种方法可以在AccountManagement中运行LDAP查询(我的另一个抱怨),那么这将是您的麻烦的结束,因为您可以运行查询并让AccountManagement从那里获取它,但此选项不存在…

根据个人经验,如果您坚持使用AccountManagement,我看不到任何其他选择。 您可以做的是转储AccountManagement并仅使用DirectoryServices。 在引擎盖下,所有AccountManagement都会包装DirectoryEntry对象,你可以编写一些帮助类来做类似的事情。

作为替代方案,您可以使用此代码来获取成员:

 var pth = "LDAP://ex.invalid/CN=grpName,OU=Groups,OU=whatever,DC=ex,DC=invalid"; var dirEntry = new DirectoryEntry(pth); var members = dirEntry.Invoke("Members"); //COM object foreach (var member in (IEnumerable)members) { var userEntry = new DirectoryEntry(member); //member is COM object var sid = new SecurityIdentifier((byte[]) userEntry.InvokeGet("objectSid"), 0); var typ = typeof(System.Security.Principal.NTAccount); var account = (NTAccount)sid.Translate(typ); Console.WriteLine(account.Value); }