UserPrincipal.GetGroups fails with unknown error

21

I am trying to get all Active Directory groups for a user, with the following code:

    private static IEnumerable<string> GetGroupNames(string userName)
    {
        using (var context = new PrincipalContext(ContextType.Domain))
        {
            using (var userPrincipal = UserPrincipal.FindByIdentity(context, userName))
            {
                var groupSearch = userPrincipal.GetGroups(context);
                var result = new List<string>();
                foreach (var principal in groupSearch)
                {
                    Log.LogDebug("User {0} is member of group {0}", userPrincipal.DisplayName, principal.DisplayName);
                    result.Add(principal.SamAccountName);
                }
                return result;
            }
        }
    }

This code correctly finds the user principal, but fails when GetGroups is called with a PrincipalOperationException: Unknown error (0x80005000).

Root exception:

   at System.DirectoryServices.AccountManagement.ADStoreCtx.GetGroupsMemberOf(Principal foreignPrincipal, StoreCtx foreignContext)
   at System.DirectoryServices.AccountManagement.Principal.GetGroupsHelper(PrincipalContext contextToQuery)
   at System.DirectoryServices.AccountManagement.Principal.GetGroups(PrincipalContext contextToQuery)
   at [line of the GetGroup call]

Inner exception (COMException):

   at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
   at System.DirectoryServices.DirectoryEntry.Bind()
   at System.DirectoryServices.DirectoryEntry.get_AdsObject()
   at System.DirectoryServices.PropertyValueCollection.PopulateList()
   at System.DirectoryServices.PropertyValueCollection..ctor(DirectoryEntry entry, String propertyName)
   at System.DirectoryServices.PropertyCollection.get_Item(String propertyName)
   at System.DirectoryServices.AccountManagement.ADUtils.RetriveWkDn(DirectoryEntry deBase, String defaultNamingContext, String serverN

Another report with this problem.

Any clues?

.net
active-directory
directoryservices
asked on Stack Overflow Dec 23, 2010 by Holstebroe • edited May 23, 2017 by Community

2 Answers

32

Adding Environment.UserDomainName as the name argument to the PrincipalContext helped:

using (var context = new PrincipalContext(ContextType.Domain, Environment.UserDomainName))

I still don't know why PrincipalContext(ContextType.Domain) only works for finding the UserPrincipal and not the groups of the user. The COM error message "unknown error" is not very helpful and the PrincipalContext constructor overload with only the ContextType is virtually undocumented at MSDN. It smells like an issue with the .NET framework as pointed out by Harvey Kwok.

answered on Stack Overflow Dec 27, 2010 by Holstebroe
2

As mentioned in the question comments, another reason this particular error can happen when calling GetGroups is due to a documented bug in .NET and .NET Core. It happens when attempting to get groups for user principals that have a slash ('/') in their AD Distinguished Name.

The workaround, at least until the bug is fixed, is to write your own GetGroups method. Here is a working example that returns just the group names (also includes distribution lists):

public static List<string> GetGroups(UserPrincipal user)
{
    List<string> groupNames = new List<string>();

    using (DirectoryEntry directoryEntryUser = (DirectoryEntry)user.GetUnderlyingObject())
    {
        object[] groups = GetAdPropertyValueObjectArray(directoryEntryUser, "memberOf");
        const int prefixLength = 3;
        const string prefix = "CN=";  // CN = Common Name

        if (groups != null)
        {
            foreach (string group in groups)
            {
                if (group.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
                {
                    int commaIndex = group.IndexOf(",", prefixLength);
                    string groupName;

                    if (commaIndex >= 0)
                        groupName = group.Substring(prefixLength, commaIndex - prefixLength);
                    else
                        groupName = group.Substring(prefixLength);

                    if (groupName.Length > 0)
                        groupNames.Add(groupName);
                }
            }
        }
    }

    return groupNames;
}

private static object[] GetAdPropertyValueObjectArray(DirectoryEntry userAccount, string adPropertyKey)
{
    object[] result = null;
    PropertyValueCollection property = userAccount.Properties[adPropertyKey];

    if (property != null)
    {
        object propertyValue = property.Value;

        if (propertyValue != null)
        {
            if (propertyValue is string)
            {
                result = new object[1];
                result[0] = propertyValue;
            }
            else
            {
                result = (object[])propertyValue;
            }
        }
    }

    return result;
}
answered on Stack Overflow Feb 21, 2019 by Tawab Wakil • edited Feb 21, 2019 by Tawab Wakil

User contributions licensed under CC BY-SA 3.0