Getting started with Microsoft Graph in C#
When starting your automation journey in Microsoft 365 you probably turn first to Power Automate. And Power Automate is great for many reasons and for a variety of projects. Same is true for Azure Logic Apps. But the more you dive into it and the more complicated the projects become, the more inconvenient these technologies become.
If you are at this point and want to make your way to code but don’t know how to start, you are in the right place.
In this article I want to show you how to authorize your C# application with the Microsoft Graph API and make your first request using HTTP as well as the SDK.
Contents
Create app registration in Azure Active Directory
Authorize your application with the Microsoft Graph API
Make a call to the Microsoft Graph API
Create app registration in Azure Active Directory
In this tutorial we are using a service principal to authenticate with the Microsoft Graph API. There is also the option to use a logged in user, but we won’t cover that here to keep it simple.
First we need to create an app registration in the new Entra Admin Center. Navigate to Applications/App registrations and create a new registration. Choose a name that resembles the workload, use case or project.
If you are using a web app or an azure function, use the name of the resource
Now that the app registration is created we need to specify the permissions the registration has on the Graph API. What exact permissions are needed are documented for each endpoint in the official Microsoft Graph API Reference. For our tutorial we are using the permission scopes listed next to “Application”.
Keep permissions to a minimum depending on your requirements
To add the permission scopes open up the registration you just created and navigate to API permissions.
To honour the previous hint, delete the default delegated permission User.Read
On this screen “Add a permission” and select in the new pop-up “Microsoft Graph”, “Application permissions” and then search for all necessary scopes. Confirm with “Add permission”.
Most scopes (especially application permission scopes) need consent from an administrator. Ask someone with “Global Administrator” role to consent for you if you don’t have that role yourself.
Lastly we need to create a client secret that we will use to authenticate. Navigate to “Certificates & secrets”, “Client secrets” and select “New client secret”. Choose a description that resembles the location where this secret will be used.
Again you can use the azure resource name or something like “local-development-myname”
For later use note down the generated Client secret and navigate back to the Overview to also not down the Client Id and your Tenant Id.
Authorize your application with the Microsoft Graph API
Now that the prerequisites are met, we can dive into code. First I provide you with the package versions this is based on. Add these to you .csproj file.
<PackageReference Include="Azure.Identity" Version="1.9.0" /> <PackageReference Include="Microsoft.Graph" Version="5.12.0" />
Afterwards create a new class GraphConnector.cs or rename it however you like. Paste in the code below.
public class GraphConnector { private string clientId; private string tenantId; private string clientSecret; public const string GraphScope = "https://graph.microsoft.com/.default"; public GraphServiceClient GraphServiceClient { get; private set; } public IConfidentialClientApplication application { get; private set; } public GraphConnector(string clientId, string tenantId, string clientSecret) { this.clientId = clientId; this.tenantId = tenantId; this.clientSecret = clientSecret; // Build a client application. this.application = ConfidentialClientApplicationBuilder.Create(clientId) .WithAuthority(AzureCloudInstance.AzurePublic, tenantId) .WithClientSecret(clientSecret) .Build(); // Create an authentication provider by passing in a client application. ClientSecretCredential credential = new ClientSecretCredential(tenantId, clientId, clientSecret); // Create a new instance of GraphServiceClient with the authentication provider. this.GraphServiceClient = new GraphServiceClient(credential); } public async Task<HttpClient> CreateHttpClient(string scope) { HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await GetAccessToken(scope)); return client; } /// <summary> /// Creates an access token using the aad client /// </summary> /// <returns>returns a string containing an access token</returns> public async Task<string> GetAccessToken(string scope) { AuthenticationResult authenticationResult = await this.application.AcquireTokenForClient(new string[1] { scope }) .WithAuthority(AzureCloudInstance.AzurePublic, this.tenantId) .WithForceRefresh(true) .ExecuteAsync(); return authenticationResult.AccessToken; } public async Task<string> GetAccessTokenHttP(string scope) { HttpClient client = new HttpClient(); HttpRequestMessage authMessage = new HttpRequestMessage(HttpMethod.Get, $"https://login.microsoftonline.com/{this.tenantId}/oauth2/v2.0/token"); authMessage.Content = new StringContent( $"client_id={this.clientId}" + $"&scope={UrlEncodeScope(scope)}" + $"&client_secret={this.clientSecret}" + $"&grant_type=client_credentials" , Encoding.UTF8, "application/x-www-form-urlencoded"); HttpResponseMessage response = await client.SendAsync(authMessage); string accessToken = JObject.Parse(await response.Content.ReadAsStringAsync())["access_token"]?.ToString(); return accessToken; } private string UrlEncodeScope(string scope) { string result = scope .Replace(":", "%3A") .Replace("/", "%2F"); return result; } }
This class contains functions to authorize your application for the SDK as well as for HTTP. For some calls it might be necessary to use HTTP calls, as they are not supported by the SDK. One example of this would be attaching large files to an email.
Make a call to the Microsoft Graph API
Now we have everything set up to make our first call. Let`s get alle Users from the Microsoft Graph API using the SDK. Below you can find a method that does exactly that.
public async Task<List<User>> GetAllUsers(GraphServiceClient graphClient) { List<User> allUsers = new List<User>(); var users = await graphClient.Users .Request() .Select(u => new { u.Id, u.DisplayName, u.GivenName, u.Surname, u.Department }) .GetAsync(); var pageIterator = PageIterator<User, UserCollectionResponse> .CreatePageIterator(graphClient, users, (u) => { allUsers.Add(u); return true; }); await pageIterator.IterateAsync(); return allUsers; }
Now we can just use this method to get all our users:
GraphConnector graphConnector = new GraphConnecter("yourClientId", "yourTenantId", "yourClientSecret"); List<User> allUsers = await GetAllUsers(graphConnector.GraphServiceClient);
Lastly we will do the same with using HTTP Request:
GraphConnector graphConnector = new GraphConnecter("yourClientId", "yourTenantId", "yourClientSecret"); HttpClient httpClient = await graphConnector.CreateHttpClient(GraphConnector.GraphScope); string url = $"https://graph.microsoft.com/v1.0/users?$select=id,displayName,surname,givenname,department"; bool exportedLastPage = false; List<User> allUsers = new List<Users>(); while(!exportedLastPage){ HttpRequestMessage getUsersRequest = new HttpRequestMessage(HttpMethod.Get, url); HttpResponseMessage getUsersResponse = await httpClient.SendAsync(getUsersRequest); string responseBody = await getExistingLocosResponse.Content.ReadAsStringAsync(); JObject jObject = JObject.Parse(responseBody); allUsers.AddRange(JsonConvert.DeserializeObject<List<User>>(jObject["value"]); try{ url = jObject["@odata.nextLink"].ToString(); } catch(Exception e){ exportedLastPage = true; } }