Authenticate NET apps to Azure services during local development using service principals
مقالة
When creating cloud applications, developers need to debug and test applications on their local workstation. When an application is run on a developer's workstation during local development, it still must authenticate to any Azure services used by the app. This article covers how to set up dedicated application service principal objects to be used during local development.
Dedicated application service principals for local development allow you to follow the principle of least privilege during app development. Since permissions are scoped to exactly what is needed for the app during development, app code is prevented from accidentally accessing an Azure resource intended for use by a different app. This also prevents bugs from occurring when the app is moved to production because the app was overprivileged in the dev environment.
An application service principal is set up for the app when the app is registered in Azure. When registering apps for local development, it's recommended to:
Create separate app registrations for each developer working on the app. This will create separate application service principals for each developer to use during local development and avoid the need for developers to share credentials for a single application service principal.
Create separate app registrations per app. This scopes the app's permissions to only what is needed by the app.
During local development, environment variables are set with the application service principal's identity. The Azure SDK for NET reads these environment variables and uses this information to authenticate the app to the Azure resources it needs.
1 - Register the application in Azure
Application service principal objects are created with an app registration in Azure. This can be done using either the Azure portal or Azure CLI.
Sign in to the Azure portal and follow these steps.
Instructions
Screenshot
In the Azure portal:
Enter app registrations in the search bar at the top of the Azure portal.
Select the item labeled App registrations under the Services heading on the menu that appears below the search bar.
On the App registrations page, select + New registration.
On the Register an application page, fill out the form as follows.
Name → Enter a name for the app registration in Azure. It is recommended this name include the app name, the user the app registration is for, and an identifier like 'dev' to indicate this app registration is for use in local development.
Supported account types → Accounts in this organizational directory only.
Select Register to register your app and create the application service principal.
On the App registration page for your app:
Application (client) ID → This is the app id the app will use to access Azure during local development. Copy this value to a temporary location in a text editor as you will need it in a future step.
Directory (tenant) id → This value will also be needed by your app when it authenticates to Azure. Copy this value to a temporary location in a text editor it will also be needed it in a future step.
Client credentials → You must set the client credentials for the app before your app can authenticate to Azure and use Azure services. Select Add a certificate or secret to add credentials for your app.
On the Certificates & secrets page, select + New client secret.
The Add a client secret dialog will pop out from the right-hand side of the page. In this dialog:
Description → Enter a value of Current.
Expires → Select a value of 24 months.
Select Add to add the secret.
On the Certificates & secrets page, you will be shown the value of the client secret.
Copy this value to a temporary location in a text editor as you will need it in a future step.
IMPORTANT: This is the only time you will see this value. Once you leave or refresh this page, you will not be able to see this value again. You may add an additional client secret without invalidating this client secret, but you will not see this value again.
First, use the az ad sp create-for-rbac command to create a new service principal for the app. This will also create the app registration for the app at the same time.
az ad sp create-for-rbac --name {service-principal-name}
The output of this command will look like the following. It's recommended to copy this output into a temporary file in a text editor as you'll need these values in a future step as this is the only place you ever see the client secret (password) for the service principal. You can, however, add a new password later without invalidating the service principal or existing passwords if need be.
2 - Create an Azure AD security group for local development
Since there typically multiple developers who work on an application, it's recommended to create an Azure AD group to encapsulate the roles (permissions) the app needs in local development rather than assigning the roles to individual service principal objects. This offers the following advantages.
Every developer is assured to have the same roles assigned since roles are assigned at the group level.
If a new role is needed for the app, it only needs to be added to the Azure AD 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, assuring the developer has the right permissions to work on the app.
Navigate to the Azure Active Directory page in the Azure portal by typing Azure Active Directory into the search box at the top of the page and then selecting Azure Active Directory from under services.
On the Azure Active Directory page, select Groups from the left-hand menu.
On the All groups page, select New group.
On the New Group page:
Group type → Security
Group name → A name for the security group, typically created from the application name. It is also helpful to include a string like local-dev in the name of the group to indicate the purpose of the group.
Group description → A description of the purpose of the group.
Select the No members selected link under Members to add members to the group.
On the Add members dialog box:
Use the search box to filter the list of principal names in the list.
Select the application service principals for local development for this app. As objects are selected, they will be greyed out and move to the Selected items list at the bottom of the dialog.
When finished, select the Select button.
Back on the New group page, select Create to create the group.
The group will be created and you will be taken back to the All groups page. It may take up to 30 seconds for the group to appear and you may need to refresh the page due to caching in the Azure portal.
The az ad group create command is used to create groups in Azure Active Directory. The --display-name and --main-nickname parameters are required. The name given to the group should be based on the name of the application. It's also useful to include a phrase like 'local-dev' in the name of the group to indicate the purpose of the group.
az ad group create \
--display-name MyDisplay \
--mail-nickname MyDisplay \
--description \<group-description>
To add members to the group, you'll need the object ID of the application service principal, which is different that the application ID. Use the az ad sp list to list the available service principals. The --filter parameter command accepts OData style filters and can be used to filter the list as shown. The --query parameter limits to columns to only those of interest.
az ad sp list \
--filter "startswith(displayName, 'msdocs')" \
--query "[].{objectId:objectId, displayName:displayName}" \
--output table
az ad group member add \
--group \<group-name> \
--member-id \<object-id> \
3 - Assign roles to the application
Next, you need to determine what roles (permissions) your app needs on what resources and assign those roles to your app. In this example, the roles will be assigned to the Azure Active Directory group created in step 2. Roles can be assigned a role at a resource, resource group, or subscription scope. This example will show how to assign roles at the resource group scope since most applications group all their Azure resources into a single resource group.
Locate the resource group for your application by searching for the resource group name using the search box at the top of the Azure portal.
Navigate to your resource group by selecting the resource group name under the Resource Groups heading in the dialog box.
On the page for the resource group, select Access control (IAM) from the left-hand menu.
On the Access control (IAM) page:
Select the Role assignments tab.
Select + Add from the top menu and then Add role assignment from the resulting drop-down menu.
The Add role assignment page lists all of the roles that can be assigned for the resource group.
Use the search box to filter the list to a more manageable size. This example shows how to filter for Storage Blob roles.
Select the role that you want to assign.
Select Next to go to the next screen.
The next Add role assignment page allows you to specify what user to assign the role to.
Select User, group, or service principal under Assign access to.
Select + Select members under Members
A dialog box will open on the right-hand side of the Azure portal.
In the Select members dialog:
The Select text box can be used to filter the list of users and groups in your subscription. If needed, type the first few characters of the local development Azure AD group you created for the app.
Select the local development Azure AD group associated with your application.
Select Select at the bottom of the dialog to continue.
The Azure AD group will now show as selected on the Add role assignment screen.
Select Review + assign to go to the final page and then Review + assign again to complete the process.
An application service principal is assigned a role in Azure using the az role assignment create command.
az role assignment create --assignee "{appId}" \
--role "{roleName}" \
--resource-group "{resourceGroupName}"
To get the role names that a service principal can be assigned to, use the az role definition list command.
az role definition list \
--query "sort_by([].{roleName:roleName, description:description}, &roleName)" \
--output table
For example, to allow the application service principal with the appId of 00000000-0000-0000-0000-000000000000 read, write, and delete access to Azure Storage blob containers and data to all storage accounts in the msdocs-dotnet-sdk-auth-example resource group, you would assign the application service principal to the Storage Blob Data Contributor role using the following command.
az role assignment create --assignee "00000000-0000-0000-0000-000000000000" \
--role "Storage Blob Data Contributor" \
--resource-group "msdocs-dotnet-sdk-auth-example"
For information on assigning permissions at the resource or subscription level using the Azure CLI, see the article Assign Azure roles using the Azure CLI.
4 - Set application environment variables
The DefaultAzureCredential object will look for the service principal information in a set of environment variables at runtime. There are multiple ways to configure environment variables when working with .NET depending on your tooling and environment.
Regardless of which approach you choose, you'll need to configure the following environment variables when working with a service principal.
AZURE_CLIENT_ID → The app ID value.
AZURE_TENANT_ID → The tenant ID value.
AZURE_CLIENT_SECRET → The password/credential generated for the app.
When working locally with Visual Studio, environment variables can be set in the launchsettings.json file in the Properties folder of your project. When the app starts up, these values will be pulled in automatically. Keep in mind these configurations do not travel with your application when it gets deployed, so you'll still need to set up environment variables on your target hosting environment.
When working locally with Visual Studio Code, environment variables can be set in the launch.json file of your project. When the app starts up, these values will be pulled in automatically. Keep in mind these configurations do not travel with your application when it gets deployed, so you'll still need to set up environment variables on your target hosting environment.
You can set environment variables for Windows from the command line. However, when using this approach the values are accessible to all applications running on that operating system and may cause conflicts if you aren't careful. Environment variables can be set at the user or system level.
# Set user environment variables
setx ASPNETCORE_ENVIRONMENT "Development"
setx AZURE_CLIENT_ID "00000000-0000-0000-0000-000000000000"
setx AZURE_TENANT_ID "11111111-1111-1111-1111-111111111111"
setx AZURE_CLIENT_SECRET "=abcdefghijklmnopqrstuvwxyz"
# Set system environment variables - requires running as admin
setx ASPNETCORE_ENVIRONMENT "Development"
setx AZURE_CLIENT_ID "00000000-0000-0000-0000-000000000000" /m
setx AZURE_TENANT_ID "11111111-1111-1111-1111-111111111111" /m
setx AZURE_CLIENT_SECRET "=abcdefghijklmnopqrstuvwxyz" /m
PowerShell can also be used to set environment variables at the user or machine level.
# Set user environment variables
[Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development", "User")
[Environment]::SetEnvironmentVariable("AZURE_CLIENT_ID", "00000000-0000-0000-0000-000000000000", "User")
[Environment]::SetEnvironmentVariable("AZURE_TENANT_ID", "11111111-1111-1111-1111-111111111111", "User")
[Environment]::SetEnvironmentVariable("AZURE_CLIENT_SECRET", "=abcdefghijklmnopqrstuvwxyz", "User")
# Set system environment variables - requires running as admin
[Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development", "Machine")
[Environment]::SetEnvironmentVariable("AZURE_CLIENT_ID", "00000000-0000-0000-0000-000000000000", "Machine")
[Environment]::SetEnvironmentVariable("AZURE_TENANT_ID", "11111111-1111-1111-1111-111111111111", "Machine")
[Environment]::SetEnvironmentVariable("AZURE_CLIENT_SECRET", "=abcdefghijklmnopqrstuvwxyz", "Machine")
5 - Implement DefaultAzureCredential in your application
DefaultAzureCredential supports multiple authentication methods and determines the authentication method being used at runtime. In this way, your app can use different authentication methods in different environments without implementing environment specific code.
The order and locations in which DefaultAzureCredential looks for credentials is found at DefaultAzureCredential.
To implement DefaultAzureCredential, first add the Azure.Identity and optionally the Microsoft.Extensions.Azure packages to your application. You can do this using either the command line or the NuGet Package Manager.
Right click on your project node in Visual Studio and select Manage NuGet Packages. Search for Azure.Identity in the search field, and install the matching package. Repeat this process for the Microsoft.Extensions.Azure package as well.
Azure services are generally accessed using corresponding client classes from the SDK. These classes and your own custom services should be registered in the Program.cs file so they can be accessed via dependency injection throughout your app. Inside of Program.cs, follow the steps below to correctly setup your service and DefaultAzureCredential.
Include the Azure.Identity and Microsoft.Extensions.Azure namespaces with a using directive.
Register the Azure service using relevant helper methods.
Pass an instance of the DefaultAzureCredential object to the UseCredential method.
An example of this is shown in the following code segment.
using Microsoft.Extensions.Azure;
using Azure.Identity;
// Inside of Program.cs
builder.Services.AddAzureClients(x =>
{
x.AddBlobServiceClient(new Uri("https://<account-name>.blob.core.windows.net"));
x.UseCredential(new DefaultAzureCredential());
});
Alternatively, you can also utilize DefaultAzureCredential in your services more directly without the help of additional Azure registration methods, as seen below.
using Azure.Identity;
// Inside of Program.cs
builder.Services.AddSingleton<BlobServiceClient>(x =>
new BlobServiceClient(
new Uri("https://<account-name>.blob.core.windows.net"),
new DefaultAzureCredential()));
When the above code is run on your local workstation during local development, it will look in the environment variables for an application service principal or at Visual Studio, VS Code, the Azure CLI, or Azure PowerShell for a set of developer credentials, either of which can be used to authenticate the app to Azure resources during local development.
When deployed to Azure this same code can also authenticate your app to other Azure resources. DefaultAzureCredential can retrieve environment settings and managed identity configurations to authenticate to other services automatically.
التعاون معنا على GitHub
يمكن العثور على مصدر هذا المحتوى على GitHub حيث يمكنك أيضاً إضافة مشاكل وطلبات سحب ومراجعتها. للحصول على معلومات إضافية، اطلع على دليل المساهم لدينا.
قريبًا: خلال عام 2024، سنتخلص تدريجيًا من GitHub Issues بوصفها آلية إرسال ملاحظات للمحتوى ونستبدلها بنظام ملاحظات جديد. لمزيد من المعلومات، راجع https://aka.ms/ContentUserFeedback.