Graph API: getting users Active Directory group names and ids with the C# SDK
The Graph API is a great way to get information about users in Azure Active Directory. I recently needed to get the names and ids of the Active Directory groups that a user was a member of. Here's how to do it with the CSDK.
I'm writing this post as, whilst it ends up being a relatively small amount of code and configuration required, if you don't know what that is, you can end up somewhat stuck. This should hopefully unstick you.
Related posts
- Azure AD Claims with Static Web Apps and Azure Functions
- Microsoft Graph client: how to filter by endswith
Azure AD app registration API permissions
To query the Graph API, we'll need:
- an Azure AD app registration
- a client id and client secret for that app registration
- the tenant id for the Azure AD tenant that the app registration is in
- GroupMember.Read.All and User.Read.All API application permissions
Of the above, it's the API permissions that I want to draw your attention to. Ultimately, you'll need to get your Azure AD admin to grant consent to these permissions for your app registration so you have the necessary permissions to query the Graph API:
How did I know that I needed these permissions? Great question! I found the answer in the "directoryObject: getMemberGroups / group memberships for a user" documentation.
The Application User.Read.All and GroupMember.Read.All permissions are the least privileged permissions that allow you to query the Graph API for the information we need. If you're interested in the other permissions available, check out the Microsoft Graph permissions reference.
Querying the Graph API
Now we've configured our app registration, we can query the Graph API. I'm going to show you how to do this with the CSDK. If you're using a different language, you'll need to find the equivalent SDK for your language.
Install the SDK
The first thing we need to do is install the SDK. I'm using the Microsoft.Graph package. At the time of writing, the latest version is 5.35.0.
Once you have added it, you'll have an entry in your .csproj along these lines:
If you're interested in the underlying project, you can find it as msgraph-sdk-dotnet on GitHub.
Create a GraphServiceClient
Next, we need to create a GraphServiceClient. This is the object that we'll use to query the Graph API. For that we'll need a credential which we'll construct using the tenantId, clientId and clientSecret that we got from our app registration:
The above code is based on the "client credentials provider / using a client secret" documentation. It's possible that you might want to use a different authentication provider. If so, check out the "choose authentication providers" documentation. Because we're querying for application permissions, we need to use the client credentials provider. We could, if we wanted to, use the client certificate approach instead. I'm not going to cover that here.
Query the Graph API
Now we come to the fun part. We're going to query the Graph API for the groups that a user is a member of. We'll need to pass in the usernameOrId of the user that we're interested in. I'm using the user's email address as the usernameOrId in my application. You might want to use the user's id instead. It's up to you.
Let's unpack the code above. It does the following:
- Creates an empty list of GroupIdDisplayName objects. This is the type that I'm using to store the group ids and display names. You can use whatever type you want.
- It indexes into the Users collection on the GraphServiceClient using the usernameOrId that we passed in. From there we make use of MemberOf.GraphGroup and call GetAsync on that, passing in a request configuration method. This is where we specify the Select and Top query parameters. We're using Select to specify only the properties that we want to return - by default lots more data would come back than we require. We're using Top to specify the maximum number of results that we want to return. I'm using 100 as that's the maximum that the Graph API will allow. We'll need to use a PageIterator to get all of the results. More on that in a moment.
- Because we want to get all of the results, we need to use a PageIterator. We create one using the CreatePageIterator method on the PageIterator class. We pass in the GraphServiceClient that we created earlier, the response that we got from the GetAsync call and a delegate that will be called for each result. In our delegate, we add the GroupIdDisplayName to our list and return true to indicate that we want to continue iterating. If we wanted to stop iterating, we'd return false. Finally we call IterateAsync on the PageIterator to get all of the results.
The PageIterator is somewhat confusing, in my opinion. I'm used to paging through results, but this way of doing it did puzzle me. If you're interested in learning more about the PageIterator, check out the "paging" documentation. I also found this documentation helpful.
Initially the PageIterator was throwing an exception when I used it. I found the answer to that on StackOverflow. It turns out that the types you specify for the PageIterator need to be correct for the request above; and if not you may be impaced by type mismatches at runtime. The entries that are returned from the API may be wider or simply different to what you require. I've the correct types in my code now - but I mention it just in case.
Putting it all together
Ultimately I wrote a GroupService that I could use to get the groups for a user. That service looks like this:
It is constructed in Program.cs:
Then I can use it in my controller:
When you browse to /api/groups, you'll get a JSON response with the group ids and display names:
And if you're anything like me, you'll discover that you are in way more groups than you thought you were.
Conclusion
This post has demonstrated both the configuration and the code required to query the Graph API for the groups that a user is a member of. Hopefully it also provides something of a reference for acquiring different types of Graph API permissions and using the CSDK to query the Graph API.
Discussion in the ATmosphere