Gabe's Code

Stuff I've learned along the way

Active Directory


profile for Gabriel Luci at Stack Overflow, Q&A for professional and enthusiast programmers

Active Directory: Find out if one user is a member of a group

This article will discuss figuring out if a specific user is a member of a specific group. That is, if you already know who the user is and what the group is. But before learning that, it’s helpful to know just what makes a user a member of a group. If you haven’t read that article yet, do that first:

What makes a member a member?

The easy way

Whatever type of code you’re writing, if you’re using Windows Authentication, there will usually be a way to get an object for the currently-authenticated user. For example, in an ASP.NET application that is using Windows Authentication, you can use this to determine if the currently-authenticated user is a member of a group:

var isMember = HttpContext.Current.User.IsInRole("DOMAIN\\GroupName");

Or in a desktop application, you can use this:

var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
var isMember = principal.IsInRole("DOMAIN\\GroupName");

Even if you’re not using .NET, there will usually be some equivalent.

.NET’s WindowsPrincipal.IsInRole is designed for testing authorization: whether a person should be granted permissions that are granted to the group. Thus, it does work for nested membership (if the account is a member of a group that is a member of the group in question). It is fairly quick too, since it uses the account’s authentication token to test the membership, which is already in memory. If you’re curious, under the hood it uses Windows’ built-in function called CheckTokenMembership.

The caveat is that IsInRole (or any method designed for authentication) will not work for groups that are:

The slightly harder but still easy way

While the following code is in C#, the principals used can usually be adapted to any language that can query LDAP. This method will work regardless of:

private static bool IsUserInGroup(DirectoryEntry user, DirectoryEntry group, bool recursive) {

    //fetch the attributes we're going to need
    user.RefreshCache(new [] {"distinguishedName", "objectSid"});
    group.RefreshCache(new [] {"distinguishedName", "groupType"});

    //This magic number tells AD to look for the user recursively through any nested groups
    var recursiveFilter = recursive ? ":1.2.840.113556.1.4.1941:" : "";

    var userDn = (string) user.Properties["distinguishedName"].Value;
    var groupDn = (string) group.Properties["distinguishedName"].Value;
    var filter = $"(member{recursiveFilter}={userDn})";

    if (((int) group.Properties["groupType"].Value & 8) == 0) {
        var groupDomainDn = groupDn.Substring(
            groupDn.IndexOf(",DC=", StringComparison.Ordinal));
        var userDomainDn = userDn.Substring(
            userDn.IndexOf(",DC=", StringComparison.Ordinal));
        if (groupDomainDn != userDomainDn) {
            //It's a Domain Local group, and the user and group are on
            //different domains, so the account might show up as a Foreign
            //Security Principal. So construct a list of SID's that could
            //appear in the group for this user
            var fspFilters = new StringBuilder();
            var userSid =
                new SecurityIdentifier((byte[]) user.Properties["objectSid"].Value, 0);
            if (recursive) {
                //Any of the groups the user is in could show up as an FSP,
                //so we need to check for them all
                user.RefreshCache(new [] {"tokenGroupsGlobalAndUniversal"});
                var tokenGroups = user.Properties["tokenGroupsGlobalAndUniversal"];
                foreach (byte[] token in tokenGroups) {
                    var groupSid = new SecurityIdentifier(token, 0);
            filter = $"(|{filter}{fspFilters})";

    var searcher = new DirectorySearcher {
        Filter = filter,
        SearchRoot = group,
        PageSize = 1, //we're only looking for one object
        SearchScope = SearchScope.Base

    searcher.PropertiesToLoad.Add("cn"); //just so it doesn't load every property

    return searcher.FindOne() != null;

This method works by searching for groups that have the user as a member. But since we set SearchRoot to the group itself, it is only possible for that one group to be returned. So the search has two possible outcomes:

  1. If the user is a member, the group is returned
  2. Otherwise, nothing is returned

Hence, we only need to test if something was returned.

The recursive option works by using one of a few magic numbers called Matching Rule OIDs. This specific one (1.2.840.113556.1.4.1941) is called LDAP_MATCHING_RULE_IN_CHAIN. It can only be used on attributes that accept distinguished names (like member). It tells Active Directory to follow the chain of groups to find the user; for example, if the user is a member of a group that is a member of the group in question.

More information can be found on Microsoft’s article on their LDAP Search Filter Syntax.

There is a bit of magic in here for Foreign Security Principals; that is, if the user could be on an external trusted domain from the group. So it constructs the distinguishedName of the FSP that would be there if that were the case. For a recursive search, we use tokenGroupsGlobalAndUniversal to get a recursive list of all the user’s groups from the user’s domain and see if any of those are in the group in question.

The tokenGroupsGlobalAndUniversal attribute contains only Global and Universal groups. Domain Local groups cannot be used outside of the domain anyway, so we don’t need to be concerned with those. The tokenGroups attribute is similar. It does include Domain Local groups, however it excludes Distribution groups, which is why I didn’t use it here.

Primary Group

If you read my What makes a member a member? article, then you’ll know that a user’s primary group is not governed by the member attribute of the group, so the above method won’t work if you need to test if a group is the user’s primary group. Granted, that’s a pretty rare need, so you may be able to ignore this altogether.

But if you do need to find out if a group is a user’s primary group, this is how you can do it:

private static bool IsUserPrimaryGroup(DirectoryEntry user, DirectoryEntry group) {
    user.RefreshCache(new[] {"primaryGroupID", "objectSid"});
    group.RefreshCache(new[] {"objectSid"});

    //Get the SID's as a string
    var userSid =
        new SecurityIdentifier((byte[])user.Properties["objectSid"].Value, 0).ToString();
    var groupSid =
        new SecurityIdentifier((byte[])group.Properties["objectSid"].Value, 0).ToString();

    //Replace the RID portion of the user's SID with the primaryGroupId
    //so we're left with the primary group's SID
    var primaryGroupSid =
        userSid.Remove(userSid.LastIndexOf("-", StringComparison.Ordinal) + 1)
        + user.Properties["primaryGroupId"].Value;

    return groupSid == primaryGroupSid;



Can you plz tell me how can we find a user in a container group (organizational unit) or not?


There is a simple example here. Basically, you set the OU as the SearchRoot and it will search only that OU and below. If you need more specific help, ask a question on StackOverflow. If you tag your question with active-directory, I will probably see it.

I want to use your pretty code in my Web-Page but it doesn’t work correctly.
I got an “CS1056 Unexpected character ‘’ on this code …
What am I doing wrong ? Thanks in advance for your reply.
BR Robert

Maybe it picked up a weird character while copy/pasting. It will tell you the line that it’s complaining about, so look at that line carefully.

Gabe, Sorry my first post was not complete:

function = IsUserInGroup()
first syntax, compiler problem: var filter = $”(member{recursiveFilter}={userDn})”;

I have also looked around and other users in other, but similar cases wrote that the problem could be fixed by installing a Nuget package.
Anyway, my own abilities exceed this aim :-(


Krissy Kernan on

Hi Gabe,

I’m trying to use your IsUserInGroup() code. I converted it to, but I’m not sure how to pass the DirectoryEntry user and group to the function. Can you please share that line of code?

My question is in regards to the user and group variables of type DirectoryEntry that are used in the function call.

I am currently using DirectoryEntry and DirectorySearcher to authenticate an AD user. To locate the user’s group, I was using windowsPrincipal.IsInRole. That works fine if the application is pointed to the AD domain that I am logged into, but it doesn’t work when the application is pointed to a different trusted domain. I’m trying to find another way to verify a user’s groups.

When your function refers to “DirectoryEntry user”, is that referring to the username property of DirectoryEntry? In regards to “Directory Entry group”, is that a second DirectoryEntry object? The only other way I know to get AD groups is to do a DirectorySearcher with the property of “memberOf”. The issue with that approach is that I need to search for parent and child groups which can be performance intensive. I’m trying to test this code to see if it’s a better option for me. Thanks for the assistance.

When your function refers to “DirectoryEntry user”, is that referring to the username property of DirectoryEntry? - No, it’s referring to the DirectoryEntry object itself, not a property of it.

The group parameter is a second object. This function checks if a user is a member of a specific group, so you need to tell it which group you are looking for. You create a DirectoryEntry for a group the same way you do for a user. For example, New DirectoryEntry("LDAP://CN=MyGroup,OU=Groups,DN=example,DC=com").

If you’re looking at an external, trusted domain, then you can include the domain DNS name (or even a specific domain controller) in the LDAP path, like this: New DirectoryEntry("LDAP://,OU=Groups,DN=example,DC=com")

If you don’t know the group, then this isn’t the method you should be using. Maybe you want to find all of the user’s groups.

Leave a comment

Your email address is used to display your Gravatar, if applicable, and subscribe you to replies using the Mailgun web service, which you are free to unsubscribe from when you get any emails. Your email address will not be displayed publicly or shared with anyone else.