VB.NET – Get Group Name From PrimaryGroupID Attribute in Active Directory

September 2, 2010 — 1 Comment

If you have ever tried to enumerate all groups that a user is a member of in Active Directory then you have probably found that the standard way of doing this (looking at the user’s MemberOf attribute) does not get the user’s primary group. You probably then found out that the user is ‘joined’ to their primary group by simply having the Relative ID (RID) of the group in their PrimaryGroupID attribute. That’s great and all… but how do we translate that RID into the name of the group? Well I’m sure there are a few ways to do it but here’s the code I came up with:

Please note that this function assumes that the group and user you are working with are on the same domain that your user account is a member of. Detailed explanation of the code can be found below.

Private Function GetPrimaryGroupName(ByVal SearcherObject As DirectorySearcher, ByVal User As SearchResult) As String
    Try
        Using Domain As DirectoryEntry = ActiveDirectory.Domain.GetCurrentDomain.GetDirectoryEntry
            Dim GroupSID As New Security.Principal.SecurityIdentifier(New Security.Principal.SecurityIdentifier(DirectCast(Domain.Properties("objectSid").Value, Byte()), 0).ToString & "-" & CStr(User.Properties("primaryGroupID")(0)))
            Dim GroupSIDString As New System.Text.StringBuilder
            Dim GroupSIDBytes(GroupSID.BinaryLength – 1) As Byte
            GroupSID.GetBinaryForm(GroupSIDBytes, 0)
            For i As Integer = 0 To GroupSIDBytes.Length – 1
                GroupSIDString.Append("\" & Hex(GroupSIDBytes(i)).PadLeft(2, "0"c))
            Next
            SearcherObject.Filter = "(objectSid=" & GroupSIDString.ToString & ")"
            Dim GroupSearchResult As SearchResult = SearcherObject.FindOne
            If Not GroupSearchResult Is Nothing Then
                Return GetGroupNameFromPath(GroupSearchResult.Path)
            Else
                Throw New ApplicationException("Failed to locate primary group – no results returned for the LDAP query " & SearcherObject.Filter)
            End If
        End Using
    Catch ex As Exception
        Throw New ApplicationException("Error getting primary group: " & ex.Message.Trim)
    End Try
End Function

Private Function GetGroupNameFromPath(ByVal Path As String) As String
    Dim GroupName As String = Path.Replace("LDAP://", String.Empty).Remove(0, 3)
    Dim SeparatorIndex As Integer = 0
    For i As Integer = 0 To GroupName.Length – 1
        If GroupName(i) = ","c AndAlso Not GroupName(i – 1) = "\"c Then
            SeparatorIndex = i
            Exit For
        End If
    Next
    GroupName = GroupName.Remove(SeparatorIndex)
    Return GroupName.Replace("\,", ",").Replace("\\", "\").Replace("\+", "+").Replace("\""", """").Replace("\<", "<").Replace("\>", ">").Replace("\;", ";")
End Function

As you can see, the GetPrimaryGroupName function takes 2 parameters – a DirectorySearcher object and a SearchResult object. The reason for this is that I wrote this function as part of a program that enumerates all groups a user is a member of, so I already had a DirectorySearcher instance setup and had already used it to retrieve a SearchResult for the user account. So it made sense to reuse the same DirectorySearcher instance by just passing it in as a parameter rather than creating a new one. Obviously that might not fit your needs precisely so you will probably have to modify it slightly but hopefully the actual core parts of the function are still a good example of how to get the name of the user’s primary group from the ID in their PrimaryGroupID attribute.

So what exactly is this function doing and why?

Well, firstly we bind to the current user’s domain and then we get the SID of the domain so that we can add the group’s RID (the value we get from the user’s PrimaryGroupID) to the end of it to get the group’s full SID. So now we have the full SID of the group, we need to find an object with that SID in AD – now I believe in 2003 domains you can just pass the SSDL form of the SID (that’s the form we are used to seeing, e.g S-5-21-X-X-X-X-XX-XX-X-XX) straight in to an LDAP query but in Windows 2000 domains you have to pass the hex version of the SID, so I chose to use the Hex version for compatibility’s sake. So the loop you see in the code is basically just looping through the binary form of the SID and converting it to a hex string that the LDAP filter will understand – this basically just means sticking a \ character in front of each byte once it has been converted to its hex representation (which we do with the handy built in Hex function).

So we just run the search by using the FindAll method of the DirectorySearcher and this should then bring back our group 🙂 So then its simply a case of getting the group name, which we do by using our GetGroupNameFromPath function. This function takes the full group path (e.g LDAP://CN=GroupA,OU=Groups,DC=MyDomain,DC=local) and does the following:

  1. Removes the LDAP:// from the start of the string and then removes the first 3 characters from the resulting string (which will be “CN=”)
  2. Loops through the string from start to end and looks for a comma that is not preceded by a \ character. This is because we want to remove everything after the comma that will be at the end of the group name but if the group name contains a comma then we would end up removing any part of the group name that comes after that comma. If a group name has a comma (or any other special character) in it then it will actually be stored in AD with a \ before it. So that is the reason for making sure there isnt a \ before the comma that we find.
  3. Now that we have the location of the first comma that is not actually part of the group name, we remove everything after that comma so that we are left with just the group name
  4. We return the string and use several calls to Replace to get rid of the \ character that will precede any special characters

Job done!

One response to VB.NET – Get Group Name From PrimaryGroupID Attribute in Active Directory

  1. 

    Thanks so much for this ! Exactly what I needed without having to use ADO.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s