Authenticate .NET apps to Azure services during local development using service principals
During local development, applications need to authenticate to Azure to access various Azure services. Two common approaches for local authentication are to use a developer account or a service principal. This article explains how to use an application service principal. In the sections ahead, you learn:
- How to register an application with Microsoft Entra to create a service principal
- How to use Microsoft Entra groups to efficiently manage permissions
- How to assign roles to scope permissions
- How to authenticate using a service principal from your app code
Using dedicated application service principals allows you to adhere to the principle of least privilege when accessing Azure resources. Permissions are limited to the specific requirements of the app during development, preventing accidental access to Azure resources intended for other apps or services. This approach also helps avoid issues when the app is moved to production by ensuring it isn't over-privileged in the development environment.
When the app is registered in Azure, an application service principal is created. For local development:
- Create a separate app registration for each developer working on the app to ensure each developer has their own application service principal, avoiding the need to share credentials.
- Create a separate app registration for each app to limit the app's permissions to only what is necessary.
During local development, environment variables are set with the application service principal's identity. The Azure Identity library reads these environment variables to authenticate the app to the required Azure resources.
Register the app in Azure
Application service principal objects are created through an app registration in Azure using either the Azure portal or Azure CLI.
In the Azure portal, use the search bar to navigate to the App registrations page.
On the App registrations page, select + New registration.
On the Register an application page:
- For the Name field, enter a descriptive value that includes the app name and the target environment.
- For the Supported account types, select Accounts in this organizational directory only (Microsoft Customer Led only - Single tenant), or whichever option best fits your requirements.
Select Register to register your app and create the service principal.
On the App registration page for your app, copy the Application (client) ID and Directory (tenant) ID and paste them in a temporary location for later use in your app code configurations.
Select Add a certificate or secret to set up credentials for your app.
On the Certificates & secrets page, select + New client secret.
In the Add a client secret flyout panel that opens:
- For the Description, enter a value of Current.
- For the Expires value, leave the default recommended value of 180 days.
- Select Add to add the secret.
On the Certificates & secrets page, copy the Value property of the client secret for use in a future step.
Note
The client secret value is only displayed once after the app registration is created. You can add more client secrets without invalidating this client secret, but there's no way to display this value again.
Create a Microsoft Entra group for local development
Create a Microsoft Entra group to encapsulate the roles (permissions) the app needs in local development rather than assigning the roles to individual service principal objects. This approach offers the following advantages:
- Every developer has the same roles assigned at the group level.
- If a new role is needed for the app, it only needs to be added to the group for the app.
- If a new developer joins the team, a new application service principal is created for the developer and added to the group, ensuring the developer has the right permissions to work on the app.
Navigate to the Microsoft Entra ID overview page in the Azure portal.
Select All groups from the left-hand menu.
On the Groups page, select New group.
On the New group page, fill out the following form fields:
- Group type: Select Security.
- Group name: Enter a name for the group that includes a reference to the app or environment name.
- Group description: Enter a description that explains the purpose of the group.
Select the No members selected link under Members to add members to the group.
In the flyout panel that opens, search for the service principal you created earlier and select it from the filtered results. Choose the Select button at the bottom of the panel to confirm your selection.
Select Create at the bottom of the New group page to create the group and return to the All groups page. If you don't see the new group listed, wait a moment and refresh the page.
Assign roles to the group
Next, determine what roles (permissions) your app needs on what resources and assign those roles to the Microsoft Entra group you created. Groups can be assigned a role at the resource, resource group, or subscription scope. This example shows how to assign roles at the resource group scope, since most apps group all their Azure resources into a single resource group.
In the Azure portal, navigate to the Overview page of the resource group that contains your app.
Select Access control (IAM) from the left navigation.
On the Access control (IAM) page, select + Add and then choose Add role assignment from the drop-down menu. The Add role assignment page provides several tabs to configure and assign roles.
On the Role tab, use the search box to locate the role you want to assign. Select the role, and then choose Next.
On the Members tab:
- For the Assign access to value, select User, group, or service principal .
- For the Members value, choose + Select members to open the Select members flyout panel.
- Search for the Microsoft Entra group you created earlier and select it from the filtered results. Choose Select to select the group and close the flyout panel.
- Select Review + assign at the bottom of the Members tab.
On the Review + assign tab, select Review + assign at the bottom of the page.
Set the app environment variables
At runtime, certain credentials from the Azure Identity library, such as DefaultAzureCredential
, EnvironmentCredential
, and ClientSecretCredential
, search for service principal information by convention in the environment variables. There are multiple ways to configure environment variables when working with .NET, depending on your tooling and environment.
Regardless of the approach you choose, configure the following environment variables for a service principal:
AZURE_CLIENT_ID
: Used to identify the registered app in Azure.AZURE_TENANT_ID
: The ID of the Microsoft Entra tenant.AZURE_CLIENT_SECRET
: The secret credential that was generated for the app.
In Visual Studio, environment variables can be set in the launchsettings.json
file in the Properties
folder of your project. These values are pulled in automatically when the app starts. However, these configurations don't travel with your app during deployment, so you need to set up environment variables on your target hosting environment.
"profiles": {
"SampleProject": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7177;http://localhost:5177",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"AZURE_CLIENT_ID": "<your-client-id>",
"AZURE_TENANT_ID":"<your-tenant-id>",
"AZURE_CLIENT_SECRET": "<your-client-secret>"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"AZURE_CLIENT_ID": "<your-client-id>",
"AZURE_TENANT_ID":"<your-tenant-id>",
"AZURE_CLIENT_SECRET": "<your-client-secret>"
}
}
}
Authenticate to Azure services from your app
The Azure Identity library provides various credentials—implementations of TokenCredential
adapted to supporting different scenarios and Microsoft Entra authentication flows. The steps ahead demonstrate how to use ClientSecretCredential when working with service principals locally and in production.
Implement the code
Add the Azure.Identity package. In an ASP.NET Core project, also install the Microsoft.Extensions.Azure package:
In a terminal of your choice, navigate to the application project directory and run the following commands:
dotnet add package Azure.Identity
dotnet add package Microsoft.Extensions.Azure
Azure services are accessed using specialized client classes from the various Azure SDK client libraries. These classes and your own custom services should be registered for dependency injection so they can be used throughout your app. In Program.cs
, complete the following steps to configure a client class for dependency injection and token-based authentication:
- Include the
Azure.Identity
andMicrosoft.Extensions.Azure
namespaces viausing
directives. - Register the Azure service client using the corresponding
Add
-prefixed extension method. - Configure
ClientSecretCredential
with thetenantId
,clientId
, andclientSecret
. - Pass the
ClientSecretCredential
instance to theUseCredential
method.
builder.Services.AddAzureClients(clientBuilder =>
{
var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");
clientBuilder.AddBlobServiceClient(
new Uri("https://<account-name>.blob.core.windows.net"));
clientBuilder.UseCredential(new ClientSecretCredential(tenantId, clientId, clientSecret));
});
An alternative to the UseCredential
method is to provide the credential to the service client directly:
var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");
builder.Services.AddSingleton<BlobServiceClient>(_ =>
new BlobServiceClient(
new Uri("https://<account-name>.blob.core.windows.net"),
new ClientSecretCredential(tenantId, clientId, clientSecret)));