Skip to content

Commit

Permalink
Merge pull request #41656 from dotnet/main
Browse files Browse the repository at this point in the history
Merge main into live
  • Loading branch information
dotnet-policy-service[bot] committed Jul 4, 2024
2 parents 4ec5463 + 0e0fc83 commit 5c13fea
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 9 deletions.
130 changes: 130 additions & 0 deletions docs/ai/azure-ai-services-authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
title: Authenticate to Azure OpenAI using .NET
description: Learn about the different options to authenticate to Azure OpenAI and other services using .NET
author: alexwolfmsft
ms.topic: conceptual
ms.date: 06/27/2024
---

# Azure AI services authentication and authorization using .NET

Application requests to Azure AI Services must be authenticated. In this article, you explore the options available to authenticate to Azure OpenAI and other AI services using .NET. These concepts apply to the Semantic Kernel SDK, as well as SDKs from specific services such as Azure OpenAI. Most AI services offer two primary ways to authenticate apps and users:

- **Key-based authentication** provides access to an Azure service using secret key values. These secret values are sometimes known as API keys or access keys depending on the service.
- **Microsoft Entra ID** provides a comprehensive identity and access management solution to ensure that the correct identities have the correct level of access to different Azure resources.

The sections ahead provide conceptual overviews for these two approaches, rather than detailed implementation steps. For more detailed information about connecting to Azure services, visit the following resources:

- [Authenticate .NET apps to Azure services](/dotnet/azure/sdk/authentication/)
- [Identity fundamentals](/entra/fundamentals/identity-fundamental-concepts)
- [What is Azure RBAC?](/azure/role-based-access-control/overview)

> [!NOTE]
> The examples in this article focus primarily on connections to Azure OpenAI, but the same concepts and implementation steps directly apply to many other Azure AI services as well.
## Authentication using keys

Access keys allow apps and tools to authenticate to an Azure AI service, such as Azure OpenAI, using a secret key provided by the service. Retrieve the secret key using tools such as the Azure portal or Azure CLI and use it to configure your app code to connect to the AI service:

```csharp
builder.Services.AddAzureOpenAIChatCompletion(
"deployment-model",
"service-endpoint",
"service-key"); // Secret key
var kernel = builder.Build();
```

Using keys is a straightforward option, but this approach should be used with caution. Keys aren't the recommended authentication option because they:

- Don't follow [the principle of least privilege](/entra/identity-platform/secure-least-privileged-access)—they provide elevated permissions regardless of who uses them or for what task.
- Can accidentally be checked into source control or stored in unsafe locations.
- Can easily be shared with or sent to parties who shouldn't have access.
- Often require manual administration and rotation.

Instead, consider using [Microsoft Entra ID](/#explore-microsoft-entra-id) for authentication, which is the recommended solution for most scenarios.

## Authentication using Microsoft Entra ID

Microsoft Entra ID is a cloud-based identity and access management service that provides a vast set of features for different business and app scenarios. Microsoft Entra ID is the recommended solution to connect to Azure OpenAI and other AI services and provides the following benefits:

- Key-less authentication using [identities](/entra/fundamentals/identity-fundamental-concepts).
- Role-based-access-control (RBAC) to assign identities the minimum required permissions.
- Can use the [`Azure.Identity`](/dotnet/api/overview/azure/identity-readme) client library to detect [different credentials across environments](/dotnet/api/azure.identity.defaultazurecredential) without requiring code changes.
- Automatically handles administrative maintenance tasks such as rotating underlying keys.

The workflow to implement Microsoft Entra authentication in your app generally includes the following:

- Local development:
1. Sign-in to Azure using a local dev tool such as the Azure CLI or Visual Studio.
1. Configure your code to use the [`Azure.Identity`](/dotnet/api/overview/azure/identity-readme) client library and `DefaultAzureCredential` class.
1. Assign Azure roles to the account you signed-in with to enable access to the AI service.

- Azure-hosted app:
1. Deploy the app to Azure after configuring it to authenticate using the `Azure.Identity` client library.
1. Assign a [managed identity](/entra/identity/managed-identities-azure-resources/overview) to the Azure-hosted app.
1. Assign Azure roles to the managed identity to enable access to the AI service.

The key concepts of this workflow are explored in the following sections.

### Authenticate to Azure locally

When developing apps locally that connect to Azure AI services, authenticate to Azure using a tool such as Visual Studio or the Azure CLI. Your local credentials can be discovered by the `Azure.Identity` client library and used to authenticate your app to Azure services, as described in the [Configure the app code](/#configure-your-app-code) section.

For example, to authenticate to Azure locally using the Azure CLI, run the following command:

```azurecli
az login
```

### Configure the app code

Use the [`Azure.Identity`](/dotnet/api/overview/azure/identity-readme) client library from the Azure SDK to implement Microsoft Entra authentication in your code. The `Azure.Identity` libraries include the `DefaultAzureCredential` class, which automatically discovers available Azure credentials based on the current environment and tooling available. Visit the [Azure SDK for .NET](/dotnet/api/azure.identity.defaultazurecredential) documentation for the full set of supported environment credentials and the order in which they are searched.

For example, configure Semantic Kernel to authenticate using `DefaultAzureCredential` using the following code:

```csharp
Kernel kernel = Kernel
.CreateBuilder()
.AddAzureOpenAITextGeneration(
"your-model",
"your-endpoint",
new DefaultAzureCredential())
.Build();
```

`DefaultAzureCredential` enables apps to be promoted from local development to production without code changes. For example, during development `DefaultAzureCredential` uses your local user credentials from Visual Studio or the Azure CLI to authenticate to the AI service. When the app is deployed to Azure, `DefaultAzureCredential` uses the managed identity that is assigned to your app.

### Assign roles to your identity

[Azure role-based access control (Azure RBAC)](/azure/role-based-access-control) is a system that provides fine-grained access management of Azure resources. Assign a role to the security principal used by `DefaultAzureCredential` to connect to an Azure AI service, whether that's an individual user, group, service principal, or managed identity. Azure roles are a collection of permissions that allow the identity to perform various tasks, such as generate completions or create and delete resources.

Assign roles such as **Cognitive Services OpenAI User** (role ID: `5e0bd9bd-7b93-4f28-af87-19fc36ad61bd`) to the relevant identity using tools such as the Azure CLI, Bicep, or the Azure Portal. For example, use the `az role assignment create` command to assign a role using the Azure CLI:

```azurecli
az role assignment create \
--role "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd" \
--assignee-object-id "$PRINCIPAL_ID" \
--scope /subscriptions/"$SUBSCRIPTION_ID"/resourceGroups/"$RESOURCE_GROUP" \
--assignee-principal-type User
```

Learn more about Azure RBAC using the following resources:

- [What is Azure RBAC?](/azure/role-based-access-control/overview)
- [Grant a user access](/azure/role-based-access-control/quickstart-assign-role-user-portal)
- [RBAC best practices](/azure/role-based-access-control/best-practices)

### Assign a managed identity to your app

In most scenarios, Azure-hosted apps should use a [managed identity](/entra/identity/managed-identities-azure-resources/overview) to connect to other services such as Azure OpenAI. Managed identities provide a fully managed identity in Microsoft Entra ID for apps to use when connecting to resources that support Microsoft Entra authentication. `DefaultAzureCredential` discovers the identity associated with your app and uses it to authenticate to other Azure services.

There are two types of managed identities you can assign to your app:

- A **system-assigned identity** is tied to your application and is deleted if your app is deleted. An app can only have one system-assigned identity.
- A **user-assigned identity** is a standalone Azure resource that can be assigned to your app. An app can have multiple user-assigned identities.

Assign roles to a managed identity just like you would an individual user account, such as the **Cognitive Services OpenAI User** role. learn more about working with managed identities using the following resources:

- [Managed identities overview](/entra/identity/managed-identities-azure-resources/overview)
- [Authenticate App Service to Azure OpenAI using Microsoft Entra ID](/dotnet/ai/how-to/app-service-aoai-auth?pivots=azure-portal)
- [How to use managed identities for App Service and Azure Functions](/azure/app-service/overview-managed-identity)
2 changes: 2 additions & 0 deletions docs/ai/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ items:
href: azure-ai-for-dotnet-developers.md
- name: What is Semantic Kernel?
href: semantic-kernel-dotnet-overview.md
- name: Authenticate to Azure AI services with .NET
href: azure-ai-services-authentication.md
- name: Quickstarts
items:
- name: Summarize text
Expand Down
3 changes: 2 additions & 1 deletion docs/azure/includes/dotnet-all.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
| Text Analytics | NuGet [5.3.0](https://www.nuget.org/packages/Azure.AI.TextAnalytics/5.3.0) | [docs](/dotnet/api/overview/azure/AI.TextAnalytics-readme) | GitHub [5.3.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.TextAnalytics_5.3.0/sdk/textanalytics/Azure.AI.TextAnalytics/) |
| Text Translation | NuGet [1.0.0](https://www.nuget.org/packages/Azure.AI.Translation.Text/1.0.0) | [docs](/dotnet/api/overview/azure/AI.Translation.Text-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.Translation.Text_1.0.0/sdk/translation/Azure.AI.Translation.Text/) |
| unknown | NuGet [1.0.0-beta.3](https://www.nuget.org/packages/Azure.Communication.CallingServer/1.0.0-beta.3) | [docs](/dotnet/api/overview/azure/Communication.CallingServer-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.CallingServer_1.0.0-beta.3/sdk/communication/Azure.Communication.CallingServer/) |
| unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.Compute.Batch/1.0.0-beta.1) | | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Compute.Batch_1.0.0-beta.1/sdk/batch/Azure.Compute.Batch/) |
| unknown | NuGet [1.0.0](https://www.nuget.org/packages/Azure.Core.Expressions.DataFactory/1.0.0) | [docs](/dotnet/api/overview/azure/Core.Expressions.DataFactory-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Core.Expressions.DataFactory_1.0.0/sdk/core/Azure.Core.Expressions.DataFactory/) |
| unknown | NuGet [1.0.0-preview.6](https://www.nuget.org/packages/Azure.IoT.ModelsRepository/1.0.0-preview.6) | [docs](/dotnet/api/overview/azure/IoT.ModelsRepository-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-preview.6](https://github.com/Azure/azure-sdk-for-net/tree/Azure.IoT.ModelsRepository_1.0.0-preview.6/sdk/modelsrepository/Azure.IoT.ModelsRepository/) |
| unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.IoT.TimeSeriesInsights/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/IoT.TimeSeriesInsights-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.IoT.TimeSeriesInsights_1.0.0-beta.1/sdk/timeseriesinsights/Azure.IoT.TimeSeriesInsights/) |
Expand Down Expand Up @@ -488,7 +489,7 @@
| Microsoft.Azure.Functions.Worker.Extensions.EventHubs | NuGet [6.3.1](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.EventHubs/6.3.1) | | |
| Microsoft.Azure.Functions.Worker.Extensions.Http | NuGet [3.2.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Http/3.2.0) | | |
| Microsoft.Azure.Functions.Worker.Extensions.Kafka | NuGet [3.10.1](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Kafka/3.10.1) | | |
| Microsoft.Azure.Functions.Worker.Extensions.Kusto | NuGet [1.0.9-Preview](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Kusto/1.0.9-Preview) | | |
| Microsoft.Azure.Functions.Worker.Extensions.Kusto | NuGet [1.0.10-Preview](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Kusto/1.0.10-Preview) | | |
| Microsoft.Azure.Functions.Worker.Extensions.OpenApi | NuGet [1.4.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.OpenApi/1.4.0)<br>NuGet [2.0.0-preview2](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.OpenApi/2.0.0-preview2) | | |
| Microsoft.Azure.Functions.Worker.Extensions.RabbitMQ | NuGet [2.0.3](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.RabbitMQ/2.0.3) | | |
| Microsoft.Azure.Functions.Worker.Extensions.Rpc | NuGet [1.0.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Rpc/1.0.0) | | |
Expand Down
1 change: 1 addition & 0 deletions docs/azure/includes/dotnet-new.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
| Text Analytics | NuGet [5.3.0](https://www.nuget.org/packages/Azure.AI.TextAnalytics/5.3.0) | [docs](/dotnet/api/overview/azure/AI.TextAnalytics-readme) | GitHub [5.3.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.TextAnalytics_5.3.0/sdk/textanalytics/Azure.AI.TextAnalytics/) |
| Text Translation | NuGet [1.0.0](https://www.nuget.org/packages/Azure.AI.Translation.Text/1.0.0) | [docs](/dotnet/api/overview/azure/AI.Translation.Text-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.Translation.Text_1.0.0/sdk/translation/Azure.AI.Translation.Text/) |
| unknown | NuGet [1.0.0-beta.3](https://www.nuget.org/packages/Azure.Communication.CallingServer/1.0.0-beta.3) | [docs](/dotnet/api/overview/azure/Communication.CallingServer-readme?view=azure-dotnet-preview&amp;preserve-view=true) | GitHub [1.0.0-beta.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Communication.CallingServer_1.0.0-beta.3/sdk/communication/Azure.Communication.CallingServer/) |
| unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.Compute.Batch/1.0.0-beta.1) | | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Compute.Batch_1.0.0-beta.1/sdk/batch/Azure.Compute.Batch/) |
| unknown | NuGet [1.0.0](https://www.nuget.org/packages/Azure.Core.Expressions.DataFactory/1.0.0) | [docs](/dotnet/api/overview/azure/Core.Expressions.DataFactory-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Core.Expressions.DataFactory_1.0.0/sdk/core/Azure.Core.Expressions.DataFactory/) |
| unknown | NuGet [1.0.0-preview.6](https://www.nuget.org/packages/Azure.IoT.ModelsRepository/1.0.0-preview.6) | [docs](/dotnet/api/overview/azure/IoT.ModelsRepository-readme?view=azure-dotnet-preview&amp;preserve-view=true) | GitHub [1.0.0-preview.6](https://github.com/Azure/azure-sdk-for-net/tree/Azure.IoT.ModelsRepository_1.0.0-preview.6/sdk/modelsrepository/Azure.IoT.ModelsRepository/) |
| unknown | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.IoT.TimeSeriesInsights/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/IoT.TimeSeriesInsights-readme?view=azure-dotnet-preview&amp;preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.IoT.TimeSeriesInsights_1.0.0-beta.1/sdk/timeseriesinsights/Azure.IoT.TimeSeriesInsights/) |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.510501" />
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.532401" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.12" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Octokit" Version="12.0.0" />
<PackageReference Include="Octokit" Version="13.0.1" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Octokit" Version="12.0.0" />
<PackageReference Include="Octokit" Version="13.0.1" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<ItemGroup>
<PackageReference Include="ExpressionTreeToString" Version="3.4.71" />
<PackageReference Include="LinqKit.Core" Version="1.2.5" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.2" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.3" />
</ItemGroup>

</Project>
10 changes: 9 additions & 1 deletion docs/csharp/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,20 @@ For example, these two methods use the `return` keyword to return integers:

:::code language="csharp" source="snippets/methods/return44.cs" id="snippet44":::

To use a value returned from a method, the calling method can use the method call itself anywhere a value of the same type would be sufficient. You can also assign the return value to a variable. For example, the following two code examples accomplish the same goal:
The examples above are expression bodied members. Expression bodied members return the value returned by the expression.

You can also choose to define your methods with a statement body and a `return` statement:

:::code language="csharp" source="snippets/methods/return44.cs" id="snippet43":::

To use a value returned from a method, the calling method can use the method call itself anywhere a value of the same type would be sufficient. You can also assign the return value to a variable. For example, the following three code examples accomplish the same goal:

:::code language="csharp" source="snippets/methods/return44.cs" id="snippet45":::

:::code language="csharp" source="snippets/methods/return44.cs" id="snippet46":::

:::code language="csharp" source="snippets/methods/return44.cs" id="snippet47":::

Sometimes, you want your method to return more than a single value. You use *tuple types* and *tuple literals* to return multiple values. The tuple type defines the data types of the tuple's elements. Tuple literals provide the actual values of the returned tuple. In the following example, `(string, string, string, int)` defines the tuple type returned by the `GetPersonalInfo` method. The expression `(per.FirstName, per.MiddleName, per.LastName, per.Age)` is the tuple literal; the method returns the first, middle, and family name, along with the age, of a `PersonInfo` object.

```csharp
Expand Down
Loading

0 comments on commit 5c13fea

Please sign in to comment.