Background
Essentially, there are two ways by which Azure AD application roles can be retrieved - either using HTTP REST calls (Graph API) or using Azure AD SDK. This article covers the details of the second approach to query and retrieve all users in an application role using Azure AD-managed providers.
Since we are interested in getting the users from an Azure AD application role, we would need the application role ID against which we will be querying. So, we will first retrieve all the role IDs from an Azure AD application.
Note. This article assumes you have a basic understanding of Azure AD and application roles. If not, then it is highly recommended for you to go through this article before moving ahead.
Code
public static Dictionary<string, string> GetAllAppRoles()
{
get
{
if (appRolesDictionary == null || appRolesDictionary.Count == 0)
{
appRolesDictionary = new Dictionary<string, string>();
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid Jump ").Value;
Uri servicePointUri = new Uri(("https://graph.windows.net Jump ");
Uri serviceRoot = new Uri(servicePointUri, tenantID);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await UserHelper.GetTokenForApplication());
IPagedCollection<IApplication> retrievedApps = activeDirectoryClient.Applications.Where(w => w.AppId.Equals(ConfigHelper.ClientId)).ExecuteAsync().Result;
if (retrievedApps != null)
{
Application adApp = (Application)retrievedApps.CurrentPage.FirstOrDefault();
if (adApp != null)
{
adApp.AppRoles.ToList().ForEach(e =>
{
appRolesDictionary.Add(e.DisplayName, e.Id.ToString());
});
}
}
}
return appRolesDictionary;
}
}
The snippet above builds a dictionary with the key as the application role name and value as its ID. The ConfigHelper.Client is nothing but the Azure AD application Id which is registered and to which application roles are associated.
Now, the next part! We will now be creating a method that accepts two arguments, i.e., application role ID and service principle ID. For those who do not know what service principle ID is, without going into technicalities, we can just say in simple terms that it is the object ID present along with your application ID in Azure AD app registration details and can be retrieved easily using Azure portal (From the enterprise app sections in Azure AD).
Now, let's take a look at the core method.
public static List<IUser> GetAllUsersInAppRole(string servicePrincipalObjectId, string appRoleId)
{
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid Jump ").Value;
Uri servicePointUri = new Uri("https://graph.windows.net Jump ");
Uri serviceRoot = new Uri(servicePointUri, tenantID);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await GetTokenForApplication());
List<IUser> users = new List<IUser>();
var guidAppRoleId = Guid.Parse(appRoleId);
var appRoleAssignmentsPaged = activeDirectoryClient.ServicePrincipals.GetByObjectId(servicePrincipalObjectId).AppRoleAssignedTo.ExecuteAsync().Result;
var appRoleAssignments = EnumerateAllAsync(appRoleAssignmentsPaged);
var userObjectIds = appRoleAssignments.Where(a => a.Id == guidAppRoleId && a.PrincipalType == "User").Select(a => a.PrincipalId.ToString()).ToList();
foreach (var userObjectId in userObjectIds)
{
users.Add(activeDirectoryClient.Users.Where(w => w.ObjectId.Equals(userObjectId)).ExecuteAsync().Result.CurrentPage.FirstOrDefault());
}
return users;
}
There is an additional extension method we have used to enumerate all the paged collections.
public static IEnumerable<T> EnumerateAllAsync<T>(this IPagedCollection<T> pagedCollection)
{
return EnumerateAllAsync(pagedCollection, Enumerable.Empty<T>());
}
private static IEnumerable<T> EnumerateAllAsync<T>(this IPagedCollection<T> pagedCollection, IEnumerable<T> previousItems)
{
var newPreviousItems = previousItems.Concat(pagedCollection.CurrentPage);
if (pagedCollection.MorePagesAvailable == false)
{
return newPreviousItems;
}
var newPagedCollection = pagedCollection.GetNextPageAsync().Result;
return EnumerateAllAsync(newPagedCollection, newPreviousItems);
}
And, that's it. Now, the method call can be made quite conveniently as.
Dictionary<string, string> allAppRoles = GetAllAppRoles();
allAppRoles.TryGetValue(roleName, out string roleId);
List<IUser> appRoleUsers = GetAllUsersInAppRole("app_object_Id", roleId);
Hope this helps someone! Happy coding!