Skip to content

Commit 6987420

Browse files
committed
progress
1 parent 695a30c commit 6987420

File tree

1 file changed

+82
-112
lines changed

1 file changed

+82
-112
lines changed
Lines changed: 82 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
11
---
22
title: Best practices for using the Azure SDK with ASP.NET Core
3-
description: Provides guidance and an overview of best practices for using the Azure SDK with ASP.NET Core
3+
description: Learn best practices and the steps to properly implement the Azure SDK for .NET in your ASP.NET Core apps.
44
ms.topic: conceptual
55
ms.custom: devx-track-dotnet
6-
ms.date: 10/08/2024
6+
ms.date: 10/22/2024
77
---
88

99
# Implement the Azure SDK for .NET in ASP.NET Core apps
1010

11-
The Azure SDK for .NET enables ASP.NET Core apps to integrate with many different Azure services. In this article, you'll learn best practices and the steps to properly implement the Azure SDK for .NET in your ASP.NET Core apps. You'll learn how to:
11+
The Azure SDK for .NET enables ASP.NET Core apps to integrate with many different Azure services. In this article, you'll learn best practices and the steps to implement the Azure SDK for .NET in your ASP.NET Core apps. You'll learn how to:
1212

1313
- Register services for dependency injection.
1414
- Implement centralized, standardized configuration.
1515
- Authenticate to Azure without using passwords or secrets.
1616
- Configure common web app concerns such as logging and retries.
17-
- Get started with additional topics such unit testing.
1817

1918
## Register service clients
2019

21-
The Azure SDK for .NET provides many service clients to connect your app to services such as Azure Blob Storage and Azure Key Vault. Register these services with the dependency container in the `Program.cs` file of your app to make them available to your app using Dependency Injection. The [Microsoft.Extensions.Azure](https://www.nuget.org/packages/Microsoft.Extensions.Azure) library provides helper methods to properly register your services and handles various concerns for you, such as setting up logging, handling service lifetimes, and assisting with authentication credential management.
20+
The Azure SDK for .NET provides service clients to connect your app to Azure services such as Azure Blob Storage and Azure Key Vault. Register these services with the dependency container in the `Program.cs` file of your app to make them available to your app using Dependency Injection. The [Microsoft.Extensions.Azure](https://www.nuget.org/packages/Microsoft.Extensions.Azure) library provides helper methods to properly register your services and handles various concerns for you, such as setting up logging, handling service lifetimes, and authentication credential management.
2221

23-
To register the services you need, complete the following steps.
22+
Complete the following steps to register the services you need:
2423

25-
1. Install the [Microsoft.Extensions.Azure](https://www.nuget.org/packages/Microsoft.Extensions.Azure) package.
24+
1. Install the [Microsoft.Extensions.Azure](https://www.nuget.org/packages/Microsoft.Extensions.Azure) package:
2625

2726
```dotnetcli
2827
dotnet add package Microsoft.Extensions.Azure
@@ -41,14 +40,16 @@ To register the services you need, complete the following steps.
4140
```csharp
4241
builder.Services.AddAzureClients(clientBuilder =>
4342
{
44-
// Register clients for each service
43+
// Register clients for each Azure service
4544
clientBuilder.AddSecretClient(new Uri("<key_vault_url>"));
4645
clientBuilder.AddBlobServiceClient(new Uri("<storage_url>"));
4746
clientBuilder.AddServiceBusClientWithNamespace(
4847
"<your_namespace>.servicebus.windows.net");
48+
49+
// Register a shared credential for Microsoft Entra ID authentication
4950
clientBuilder.UseCredential(new DefaultAzureCredential());
5051
51-
// Register a subclient for each Service Bus Queue
52+
// Register a subclient for each Azure Service Bus Queue
5253
foreach (string queue in queueNames)
5354
{
5455
clientBuilder.AddClient<ServiceBusSender, ServiceBusClientOptions>(
@@ -79,10 +80,12 @@ To register the services you need, complete the following steps.
7980
8081
<h1>Reports</h1>
8182
83+
<ul>
8284
@foreach(var report in reports)
8385
{
84-
<p>@report.Name</p>
86+
<li>@report.Name</li>
8587
}
88+
</ul>
8689
8790
@code {
8891
List<BlobItem> reports = new();
@@ -101,35 +104,80 @@ To register the services you need, complete the following steps.
101104
102105
---
103106
107+
## Authenticate using Microsoft Entra ID
108+
109+
Microsoft Entra ID is the recommended approach to authorize requests to Azure services. Use the [Azure Identity client library]() to implement secretless connections to Azure services in your code. The Azure Identity client library provides tools such as `DefaultAzureCredential` to simplify configuring secure connections. `DefaultAzureCredential` supports multiple authentication methods and determines which method should be used at runtime. This approach enables your app to use different authentication methods in different environments (local vs. production) without implementing environment-specific code.
110+
111+
Some Azure services also allow you to authorize requests using secrets keys. However, this approach should be used with caution. Developers must be diligent to never expose the access key in an unsecure location. Anyone who has the access key is able to authorize requests against the service and data.
112+
113+
Consider the following service client registrations:
114+
115+
```csharp
116+
builder.Services.AddAzureClients(clientBuilder =>
117+
{
118+
// Register clients for each Azure service
119+
clientBuilder.AddSecretClient(new Uri("<key_vault_url>"));
120+
clientBuilder.AddBlobServiceClient(new Uri("<storage_url>"));
121+
clientBuilder.AddServiceBusClientWithNamespace(
122+
"<your_namespace>.servicebus.windows.net");
123+
124+
// Register a shared credential for Microsoft Entra ID authentication
125+
clientBuilder.UseCredential(new DefaultAzureCredential());
126+
127+
// Register a subclient for each Azure Service Bus Queue
128+
foreach (string queue in queueNames)
129+
{
130+
clientBuilder.AddClient<ServiceBusSender, ServiceBusClientOptions>(
131+
(_, _, provider) => provider.GetService<ServiceBusClient>()
132+
.CreateSender(queue)).WithName(queue);
133+
}
134+
});
135+
```
136+
137+
In the preceding code, the `clientBuilder.UseCredential()` method accepts an instance of `DefaultAzureCredential` that will be reused across your registered services. `DefaultAzureCredential` discovers available credentials in the current environment and use them to connect to Azure services. The full order and locations in which `DefaultAzureCredential` looks for credentials can be found in the [`Azure Identity library overview`](/dotnet/api/overview/azure/Identity-readme#defaultazurecredential).
138+
139+
For example, when you run the app locally, `DefaultAzureCredential` discovers and uses credentials from the following developer tools:
140+
141+
- Environment variables
142+
- Visual Studio
143+
- Azure CLI
144+
- Azure PowerShell
145+
- Azure Developer CLI
146+
147+
`DefaultAzureCredential` also discovers credentials after you deploy your app from the following:
148+
149+
- Environment variables
150+
- Workload identity
151+
- Managed identity
152+
104153
## Set up service configurations
105154

106-
Azure service clients support configurations to change their default behaviors. You can define service client configurations directly in your code when you register a service. For example, in the [Register clients and subclients](#register-service-clients-and-subclients) section, you explicitly passed the Uri-typed variables to the client constructors. However, the recommended approach is to [store configurations in environment-dependent JSON files](/dotnet/core/extensions/configuration-providers#json-configuration-provider). For example, use an `appsettings.Development.json` file to store development environment settings and an `appsettings.Production.json` file to contain production environment settings. You can add any properties from the [`ClientOptions`](/dotnet/api/azure.core.clientoptions) class into the JSON file.
155+
Azure service clients support configurations to change their default behaviors. There are two ways to configure service clients:
156+
157+
- You can [store configurations in environment-dependent JSON files](/dotnet/core/extensions/configuration-providers#json-configuration-provider). Configuration files are generally the recommended approach because they simplify app deployments between environments and help eliminate hard coded values.
158+
- You can also configurations directly in your code when you register the service client. For example, in the [Register clients and subclients](#register-service-clients-and-subclients) section, you explicitly passed the Uri-typed variables to the client constructors.
159+
160+
The following example uses an `appsettings.Development.json` file to store development environment settings and an `appsettings.Production.json` file to contain production environment settings. You can add any properties from the [`ClientOptions`](/dotnet/api/azure.core.clientoptions) class into the JSON file.
107161

108162
1. Update the `appsettings.<environment>.json` file in your app to match the following structure:
109163

110164
```json
111165
{
112-
"AzureDefaults": {
113-
"Diagnostics": {
114-
"IsTelemetryDisabled": false,
115-
"IsLoggingContentEnabled": true
116-
}
117-
},
118166
"KeyVault": {
119167
"VaultUri": "https://<your-key-vault-name>.vault.azure.net"
120168
},
121-
"ServiceBus": {
122-
"Namespace": "<your_service-bus_namespace>.servicebus.windows.net"
123-
},
124169
"Storage": {
125170
"ServiceUri": "https://<your-storage-account-name>.storage.windows.net"
171+
},
172+
"ServiceBus": {
173+
"Namespace": "<your_service-bus_namespace>.servicebus.windows.net"
126174
}
127175
}
128176
```
129177

130178
In the preceding JSON sample:
131179

132-
- The top-level key names, `AzureDefaults`, `KeyVault`, `ServiceBus`, and `Storage`, are arbitrary. All other key names hold significance, and JSON serialization is performed in a case-insensitive manner.
180+
- The top-level key names, `KeyVault`, `ServiceBus`, and `Storage`, are arbitrary names used to reference the config sections from your code. All other key names hold significance, and JSON serialization is performed in a case-insensitive manner.
133181
- The `KeyVault:VaultUri`, `ServiceBus:Namespace`, and `Storage:ServiceUri` key values map to the `Uri`- and `string`-typed arguments of the <xref:Azure.Security.KeyVault.Secrets.SecretClient.%23ctor(System.Uri,Azure.Core.TokenCredential,Azure.Security.KeyVault.Secrets.SecretClientOptions)?displayProperty=fullName>, <xref:Azure.Messaging.ServiceBus.ServiceBusClient.%23ctor(System.String)?displayProperty=fullName>, and <xref:Azure.Storage.Blobs.BlobServiceClient.%23ctor(System.Uri,Azure.Core.TokenCredential,Azure.Storage.Blobs.BlobClientOptions)?displayProperty=fullName> constructor overloads, respectively. The `TokenCredential` variants of the constructors are used because a default `TokenCredential` is set via the <xref:Microsoft.Extensions.Azure.AzureClientFactoryBuilder.UseCredential(Azure.Core.TokenCredential)?displayProperty=fullName> method call.
134182

135183
1. Retrieve the settings in the JSON configuration file using `IConfiguration` and pass them into your service registrations:
@@ -147,18 +195,14 @@ Azure service clients support configurations to change their default behaviors.
147195
builder.Configuration["ServiceBus:Namespace"]);
148196

149197
clientBuilder.UseCredential(new DefaultAzureCredential());
150-
151-
// Set up any default settings
152-
clientBuilder.ConfigureDefaults(
153-
builder.Configuration.GetSection("AzureDefaults"));
154198
});
155199
```
156200

157-
### Configure retries and service defaults
201+
### Configure retries and Azure defaults
158202

159203
At some point, you may want to change default Azure client configurations globally or for a specific service client. For example, you may want different retry settings or to use a different service API version. You can set the retry settings globally or on a per-service basis.
160204

161-
Update your configuration file to set a new default retry policy, as well as a specific retry policy for Azure Key Vault:
205+
1. Update your configuration file to set default Azure settings, such as a new default retry policy and a specific retry policy for Azure Key Vault:
162206

163207
```json
164208
{
@@ -181,13 +225,7 @@ Update your configuration file to set a new default retry policy, as well as a s
181225
}
182226
```
183227

184-
## Authenticate using Microsoft Entra ID
185-
186-
Microsoft Entra ID is the recommended approach to authorize requests to Azure services. Use the [Azure Identity client library]() to implement secretless connections to Azure services in your code. The Azure Identity client library provides tools such as `DefaultAzureCredential` to simplify configuring secure connections. `DefaultAzureCredential` supports multiple authentication methods and determines which method should be used at runtime. This approach enables your app to use different authentication methods in different environments (local vs. production) without implementing environment-specific code.
187-
188-
Some Azure services also allow you to authorize requests using secrets keys. However, this approach should be used with caution. Developers must be diligent to never expose the access key in an unsecure location. Anyone who has the access key is able to authorize requests against the service and data.
189-
190-
Consider the following service client registrations:
228+
2. Add a call to the `ConfigureDefaults` extension method in your `AddAzureClients` setup:
191229

192230
```csharp
193231
builder.Services.AddAzureClients(clientBuilder =>
@@ -209,53 +247,20 @@ builder.Services.AddAzureClients(clientBuilder =>
209247
});
210248
```
211249

212-
In the preceding code, the `clientBuilder.UseCredential()` method accepts an instance of `DefaultAzureCredential` that will be reused across your registered services. `DefaultAzureCredential` discovers available credentials in the current environment and use them to connect to Azure services. The full order and locations in which `DefaultAzureCredential` looks for credentials can be found in the [`Azure Identity library overview`](/dotnet/api/overview/azure/Identity-readme#defaultazurecredential).
213-
214-
For example, when you run the app locally, `DefaultAzureCredential` discovers and uses credentials from the following developer tools:
215-
216-
- Environment variables
217-
- Visual Studio
218-
- Azure CLI
219-
- Azure PowerShell
220-
- Azure Developer CLI
221-
222-
`DefaultAzureCredential` also discovers credentials after you deploy your app from the following:
223-
224-
- Environment variables
225-
- Workload identity
226-
- Managed identity
227-
228250
## Configure logging
229251

230-
The Azure SDK for .NET client libraries include the ability to log client library operations. This logging allows you to monitor requests and responses between services clients and Azure services. When you register the Azure SDK library's client via a call to the <xref:Microsoft.Extensions.Azure.AzureClientServiceCollectionExtensions.AddAzureClients%2A> extension method, some logging configurations are handled for you.
231-
232-
```csharp
233-
```csharp
234-
builder.Services.AddAzureClients(clientBuilder =>
235-
{
236-
clientBuilder.AddSecretClient(
237-
builder.Configuration.GetSection("KeyVault"));
238-
239-
clientBuilder.AddBlobServiceClient(
240-
builder.Configuration.GetSection("Storage"));
252+
The Azure SDK for .NET client libraries can log client library operations to monitor requests and responses to Azure services. When you register an Azure SDK client using the <xref:Microsoft.Extensions.Azure.AzureClientServiceCollectionExtensions.AddAzureClients%2A> extension method, the <xref:Microsoft.Extensions.Azure.AzureEventSourceLogForwarder> is registered with the dependency injection container. This service forwards log messages from Azure SDK event sources to <xref:Microsoft.Extensions.Logging.ILoggerFactory> to enables you to use the standard ASP.NET Core logging configuration for logging.
241253

242-
clientBuilder.AddServiceBusClientWithNamespace(
243-
builder.Configuration["ServiceBus:Namespace"]);
254+
The following table depicts how the Azure SDK for .NET `EventLevel` maps to the ASP.NET Core `LogLevel`.
244255

245-
clientBuilder.UseCredential(new DefaultAzureCredential());
246-
247-
// Set up any default settings
248-
clientBuilder.ConfigureDefaults(
249-
builder.Configuration.GetSection("AzureDefaults"));
250-
});
251-
```
252-
253-
In the preceding sample, the `AddAzureClients` method:
254-
255-
- Registers the following objects with the dependency injection (DI) container:
256-
- Log forwarder service
257-
- Azure Service Bus client
258-
- Sets the default token credential to be used for all registered clients.
256+
| Azure SDK `EventLevel` | ASP.NET Core `LogLevel` |
257+
|------------------------|-------------------------|
258+
| `Critical` | `Critical` |
259+
| `Error` | `Error` |
260+
| `Informational` | `Information` |
261+
| `Warning` | `Warning` |
262+
| `Verbose` | `Debug` |
263+
| `LogAlways` | `Information` |
259264

260265
You can change default log levels and other settings using the same JSON configurations outlined in the [configure authentication](#configure-authentication) section. For example, toggle a the `ServiceBusClient` log level to `Debug` by setting the `Logging:LogLevel:Azure.Messaging.ServiceBus` key as follows:
261266

@@ -270,38 +275,3 @@ You can change default log levels and other settings using the same JSON configu
270275
}
271276
}
272277
```
273-
274-
## Unit testing considerations
275-
276-
Unit testing is an important part of a sustainable development process that can improve code quality and prevent regressions or bugs in your apps. This section provides a basic introduction to Unit Testing with the Azure SDK for .NET. Visit the [Unit testing and mocking with the Azure SDK for .NET](/dotnet/azure/sdk/unit-testing-mocking) article for a detailed exploration of these unit testing concepts.
277-
278-
Unit testing presents challenges when the code you're testing performs network calls, such as those made to Azure resources by Azure service clients. Tests that run against live services can experience issues, such as latency that slows down test execution, dependencies on code outside of the isolated test, and issues with managing service state and costs every time the test is run. Instead of testing against live Azure services, replace the service clients with mocked or in-memory implementations to avoid these issues.
279-
280-
Each of the Azure SDK clients follows [mocking guidelines](https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-mocking) that allow their behavior to be overridden:
281-
282-
* Each client offers at least one protected constructor to allow inheritance for testing.
283-
* All public client members are virtual to allow overriding.
284-
285-
To create a test service client, you can either use a mocking library or standard C# features such as inheritance. Mocking frameworks allow you to simplify the code that you must write to override member behavior. (These frameworks also have other useful features that are beyond the scope of this article.)
286-
287-
```csharp
288-
KeyVaultSecret keyVaultSecret = SecretModelFactory.KeyVaultSecret(
289-
new SecretProperties("secret"), "secretValue");
290-
291-
Mock<SecretClient> clientMock = new Mock<SecretClient>();
292-
clientMock.Setup(c => c.GetSecret(
293-
It.IsAny<string>(),
294-
It.IsAny<string>(),
295-
It.IsAny<CancellationToken>())
296-
)
297-
.Returns(Response.FromValue(keyVaultSecret, Mock.Of<Response>()));
298-
299-
clientMock.Setup(c => c.GetSecretAsync(
300-
It.IsAny<string>(),
301-
It.IsAny<string>(),
302-
It.IsAny<CancellationToken>())
303-
)
304-
.ReturnsAsync(Response.FromValue(keyVaultSecret, Mock.Of<Response>()));
305-
306-
SecretClient secretClient = clientMock.Object;
307-
```

0 commit comments

Comments
 (0)