diff --git a/.github/workflows/live-protection.yml b/.github/workflows/live-protection.yml index 0523cb06853ac..1aa5631ba4f6a 100644 --- a/.github/workflows/live-protection.yml +++ b/.github/workflows/live-protection.yml @@ -6,7 +6,7 @@ permissions: jobs: live_protection_job: - name: Create comment + name: Check base branch runs-on: ubuntu-latest steps: diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 89d7f8db95759..b92c62a2c2829 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -71,6 +71,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 + uses: github/codeql-action/upload-sarif@17a820bf2e43b47be2c72b39cc905417bc1ab6d0 # v3.28.6 with: sarif_file: results.sarif diff --git a/.openpublishing.redirection.azure.json b/.openpublishing.redirection.azure.json index d5f59ae803c4f..83f2317823183 100644 --- a/.openpublishing.redirection.azure.json +++ b/.openpublishing.redirection.azure.json @@ -123,6 +123,10 @@ { "source_path_from_root": "/docs/azure/sdk/azure-sdk-configure-proxy.md", "redirect_url": "/dotnet/azure/sdk/configure-proxy" + }, + { + "source_path_from_root": "/docs/azure/sdk/authentication/authentication-best-practices.md", + "redirect_url": "/dotnet/azure/sdk/authentication/best-practices" } ] } diff --git a/.openpublishing.redirection.core.json b/.openpublishing.redirection.core.json index e9cb0f7e1e7bc..42874b7f59ae3 100644 --- a/.openpublishing.redirection.core.json +++ b/.openpublishing.redirection.core.json @@ -338,6 +338,10 @@ "redirect_url": "/dotnet/core/compatibility/sdk/5.0/sdk-and-target-framework-change", "redirect_document_id": true }, + { + "source_path_from_root": "/docs/core/compatibility/windows-forms/8.0/anchor-layout.md", + "redirect_url": "/dotnet/core/compatibility/8.0" + }, { "source_path_from_root": "/docs/core/deploying/applications.md", "redirect_url": "/dotnet/core/deploying/index" diff --git a/docfx.json b/docfx.json index 6af826adfd294..2ab4a468a1a6b 100644 --- a/docfx.json +++ b/docfx.json @@ -475,7 +475,6 @@ "docs/standard/collections/**/**.md": "dapine", "docs/standard/commandline/**/**.md": "tdykstra", "docs/standard/data/**/**.md": "gewarren", - "docs/standard/data/sqlite/**/**.md": "avickers", "docs/standard/datetime/**/**.md": "adegeo", "docs/standard/design-guidelines/**/**.md": "kcwalina", "docs/standard/events/**/**.md": "dapine", diff --git a/docs/ai/how-to/app-service-aoai-auth.md b/docs/ai/how-to/app-service-aoai-auth.md index 6e968d54509e1..27cb0e91bd3d4 100644 --- a/docs/ai/how-to/app-service-aoai-auth.md +++ b/docs/ai/how-to/app-service-aoai-auth.md @@ -1,34 +1,30 @@ --- -title: "Authenticate and Authorize App Service to Azure OpenAI using Microsoft Entra and the Semantic Kernel SDK" -description: "Learn how to authenticate and authorize your app service application to an Azure OpenAI resource by using Microsoft Entra managed identities and the Semantic Kernel SDK for .NET." +title: "Authenticate an Azure hosted .NET app to Azure OpenAI using Microsoft Entra ID" +description: "Learn how to authenticate your Azure hosted .NET app to an Azure OpenAI resource using Microsoft Entra ID." author: haywoodsloan ms.topic: how-to ms.custom: devx-track-azurecli -ms.date: 11/24/2024 +ms.date: 01/29/2025 zone_pivot_groups: azure-interface #customer intent: As a .NET developer, I want authenticate and authorize my App Service to Azure OpenAI by using Microsoft Entra so that I can securely use AI in my .NET application. --- -# Authenticate an AI app hosted on Azure App Service to Azure OpenAI using Microsoft Entra ID +# Authenticate to Azure OpenAI from an Azure hosted app using Microsoft Entra ID -This article demonstrates how to use [Microsoft Entra ID managed identities](/azure/app-service/overview-managed-identity) to authenticate and authorize an App Service application to an Azure OpenAI resource. +This article demonstrates how to use [Microsoft Entra ID managed identities](/azure/app-service/overview-managed-identity) and the [Microsoft.Extensions.AI library](/dotnet/ai/ai-extensions) to authenticate an Azure hosted app to an Azure OpenAI resource. -This article also demonstrates how to use the [Semantic Kernel SDK](/semantic-kernel/overview) to easily implement Microsoft Entra authentication in your .NET application. - -By using a managed identity from Microsoft Entra, your App Service application can easily access protected Azure OpenAI resources without having to manually provision or rotate any secrets. +A managed identity from Microsoft Entra ID allows your app to easily access other Microsoft Entra protected resources such as Azure OpenAI. The identity is managed by the Azure platform and doesn't require you to provision, manage, or rotate any secrets. ## Prerequisites * An Azure account that has an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). * [.NET SDK](https://dotnet.microsoft.com/download/visual-studio-sdks) -* [`Microsoft.SemanticKernel` NuGet package](https://www.nuget.org/packages/Microsoft.SemanticKernel) -* [`Azure.Identity` NuGet package](https://www.nuget.org/packages/Azure.Identity) * [Create and deploy an Azure OpenAI Service resource](/azure/ai-services/openai/how-to/create-resource) * [Create and deploy a .NET application to App Service](/azure/app-service/quickstart-dotnetcore) ## Add a managed identity to App Service -Your application can be granted two types of identities: +Managed identities provide an automatically managed identity in Microsoft Entra ID for applications to use when connecting to resources that support Microsoft Entra authentication. Applications can use managed identities to obtain Microsoft Entra tokens without having to manage any credentials. Your application can be assigned two types of identities: * A **system-assigned identity** is tied to your application and is deleted if your app is deleted. An app can have only 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. @@ -41,6 +37,11 @@ Your application can be granted two types of identities: 1. Select **Identity**. 1. On the **System assigned** tab, toggle *Status* to **On**, and then select **Save**. + :::image type="content" source="../media/azure-hosted-apps/system-assigned-managed-identity-in-azure-portal.png" alt-text="A screenshot showing how to add a system assigned managed identity to an app."::: + + > [!NOTE] + > The preceding screenshot demonstrates this process on an Azure App Service, but the steps are similar on other hosts such as Azure Container Apps. + ## [User-assigned](#tab/user-assigned) To add a user-assigned identity to your app, create the identity, and then add its resource identifier to your app config. @@ -54,6 +55,11 @@ To add a user-assigned identity to your app, create the identity, and then add i > [!IMPORTANT] > After you select **Add**, the app restarts. + :::image type="content" source="../media/azure-hosted-apps/user-assigned-managed-identity-in-azure-portal.png" alt-text="A screenshot showing how to add a system assigned managed identity to an app."::: + + > [!NOTE] + > The preceding screenshot demonstrates this process on an Azure App Service, but the steps are similar on other hosts such as Azure Container Apps. + --- :::zone-end @@ -86,13 +92,16 @@ az webapp identity assign --name --resource-group :::zone-end -## Add an Azure OpenAI user role to your managed identity +## Add an Azure OpenAI user role to the identity :::zone target="docs" pivot="azure-portal" 1. In the [Azure Portal](https://aka.ms/azureportal), navigate to the scope that you want to grant **Azure OpenAI** access to. The scope can be a **Management group**, **Subscription**, **Resource group**, or a specific **Azure OpenAI** resource. 1. In the left navigation pane, select **Access control (IAM)**. 1. Select **Add**, then select **Add role assignment**. + + :::image type="content" source="../media/azure-hosted-apps/add-entra-role.png" alt-text="A screenshot showing how to add an RBAC role."::: + 1. On the **Role** tab, select the **Cognitive Services OpenAI User** role. 1. On the **Members** tab, select the managed identity. 1. On the **Review + assign** tab, select **Review + assign** to assign the role. @@ -101,7 +110,9 @@ az webapp identity assign --name --resource-group :::zone target="docs" pivot="azure-cli" -**Resource scope** +You can use the Azure CLI to assign the Cognitive Services OpenAI User role to your managed identity at varying scopes. + +# [Resource](#tab/resource) ```azurecli az role assignment create --assignee "" \ @@ -109,7 +120,7 @@ az role assignment create --assignee "" \ --scope "/subscriptions//resourcegroups//providers////" ``` -**Resource group scope** +# [Resource group](#tab/resource-group) ```azurecli az role assignment create --assignee "" \ @@ -117,7 +128,7 @@ az role assignment create --assignee "" \ --scope "/subscriptions//resourcegroups/" ``` -**Subscription scope** +# [Subscription](#tab/subscription) ```azurecli az role assignment create --assignee "" \ @@ -125,7 +136,7 @@ az role assignment create --assignee "" \ --scope "/subscriptions/" ``` -**Management group scope** +# [Management group](#tab/management-group) ```azurecli az role assignment create --assignee "" \ @@ -133,21 +144,44 @@ az role assignment create --assignee "" \ --scope "/providers/Microsoft.Management/managementGroups/" ``` +--- + :::zone-end -## Implement token-based authentication using Semantic Kernel SDK +## Implement identity authentication in your app code + +1. Add the following NuGet packages to your app: + + ```dotnetcli + dotnet add package Azure.Identity + dotnet add package Azure.AI.OpenAI + dotnet add package Microsoft.Extensions.Azure + dotnet add package Microsoft.Extensions.AI + dotnet add package Microsoft.Extensions.AI.OpenAI + ``` + + The preceding packages each handle the following concerns for this scenario: + + - **[Azure.Identity](https://www.nuget.org/packages/Azure.Identity)**: Provides core functionality to work with Microsoft Entra ID + - **[Azure.AI.OpenAI](https://www.nuget.org/packages/Azure.AI.OpenAI)**: Enables your app to interface with the Azure OpenAI service + - **[Microsoft.Extensions.Azure](https://www.nuget.org/packages/Microsoft.Extensions.Azure)**: Provides helper extensions to register services for dependency injection + - **[Microsoft.Extensions.AI](https://www.nuget.org/packages/Microsoft.Extensions.AI)**: Provides AI abstractions for common AI tasks + - **[Microsoft.Extensions.AI.OpenAI](https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI)**: Enables you to use OpenAI service types as AI abstractions provided by **Microsoft.Extensions.AI** + +1. In the `Program.cs` file of your app, create a `DefaultAzureCredential` object to discover and configure available credentials: -1. Initialize a `DefaultAzureCredential` object to assume your app's managed identity: + :::code language="csharp" source="./snippets/hosted-app-auth/program.cs" range="13-22"::: - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="tokenCredential"::: +1. Create an AI service and register it with the service collection: -1. Build a `Kernel` object that includes the Azure OpenAI Chat Completion Service, and use the previously created credentials: + :::code language="csharp" source="./snippets/hosted-app-auth/program.cs" range="24-30"::: - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="kernelBuild"::: +1. Inject the registered service for use in your endpoints: -1. Use the `Kernel` object to invoke prompt completion through Azure OpenAI: + :::code language="csharp" source="./snippets/hosted-app-auth/program.cs" range="41-46"::: - :::code language="csharp" source="./snippets/semantic-kernel/IdentityExamples.cs" id="invokePrompt"::: + > [!TIP] + > Learn more about ASP.NET Core dependency injection and how to register other AI services types in the Azure SDK for .NET [dependency injection](/dotnet/azure/sdk/dependency-injection) documentation. ## Related content diff --git a/docs/ai/how-to/snippets/hosted-app-auth/Program.cs b/docs/ai/how-to/snippets/hosted-app-auth/Program.cs new file mode 100644 index 0000000000000..deb2850199b8e --- /dev/null +++ b/docs/ai/how-to/snippets/hosted-app-auth/Program.cs @@ -0,0 +1,47 @@ +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.Azure; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); + +// DefaultAzureCredential attempts several auth flows in order until one is available +// For example, will discover Visual Studio or Azure CLI credentials +// in local environments and managed identity credentials in production deployments +var credential = new DefaultAzureCredential( + new DefaultAzureCredentialOptions + { + // If necessary, specify the tenant ID, + // user-assigned identity client or resource ID, or other options + } +); + +// Retrieve the Azure OpenAI endpoint and deployment name +string endpoint = builder.Configuration["AZURE_OPENAI_ENDPOINT"]; +string deployment = builder.Configuration["AZURE_OPENAI_GPT_NAME"]; + +builder.Services.AddChatClient( + new AzureOpenAIClient(new Uri(endpoint), credential) + .AsChatClient(deployment)); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); +} + +app.UseHttpsRedirection(); + +app.MapGet("/test-prompt", async (IChatClient chatClient) => +{ + return await chatClient.CompleteAsync("Test prompt", new ChatOptions()); +}) +.WithName("Test prompt"); + +app.Run(); diff --git a/docs/ai/how-to/snippets/hosted-app-auth/Properties/launchSettings.json b/docs/ai/how-to/snippets/hosted-app-auth/Properties/launchSettings.json new file mode 100644 index 0000000000000..ca4a4a5e90fc3 --- /dev/null +++ b/docs/ai/how-to/snippets/hosted-app-auth/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5140", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7137;http://localhost:5140", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/docs/ai/how-to/snippets/hosted-app-auth/appsettings.Development.json b/docs/ai/how-to/snippets/hosted-app-auth/appsettings.Development.json new file mode 100644 index 0000000000000..1af9f1b2ba45a --- /dev/null +++ b/docs/ai/how-to/snippets/hosted-app-auth/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AZURE_OPENAI_ENDPOINT": "your-azure-openai-endpoint", + "AZURE_OPENAI_GPT_NAME": "your-azure-openai-deployment-name" +} diff --git a/docs/azure/sdk/snippets/authentication/best-practices/appsettings.json b/docs/ai/how-to/snippets/hosted-app-auth/appsettings.json similarity index 100% rename from docs/azure/sdk/snippets/authentication/best-practices/appsettings.json rename to docs/ai/how-to/snippets/hosted-app-auth/appsettings.json diff --git a/docs/ai/how-to/snippets/hosted-app-auth/hosted-app-auth.csproj b/docs/ai/how-to/snippets/hosted-app-auth/hosted-app-auth.csproj new file mode 100644 index 0000000000000..d709d70b6dbe9 --- /dev/null +++ b/docs/ai/how-to/snippets/hosted-app-auth/hosted-app-auth.csproj @@ -0,0 +1,19 @@ + + + + net9.0 + enable + enable + hosted_app_auth + + + + + + + + + + + + diff --git a/docs/ai/how-to/snippets/hosted-app-auth/hosted-app-auth.http b/docs/ai/how-to/snippets/hosted-app-auth/hosted-app-auth.http new file mode 100644 index 0000000000000..2d59beb1465a8 --- /dev/null +++ b/docs/ai/how-to/snippets/hosted-app-auth/hosted-app-auth.http @@ -0,0 +1,6 @@ +@hosted_app_auth_HostAddress = http://localhost:5140 + +GET {{hosted_app_auth_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/docs/ai/how-to/snippets/semantic-kernel/semantic-kernel.csproj b/docs/ai/how-to/snippets/semantic-kernel/semantic-kernel.csproj index d18bde0dded4c..9ecac6f44ee80 100644 --- a/docs/ai/how-to/snippets/semantic-kernel/semantic-kernel.csproj +++ b/docs/ai/how-to/snippets/semantic-kernel/semantic-kernel.csproj @@ -15,7 +15,7 @@ - + diff --git a/docs/ai/media/assistants/generated-sales-graph.png b/docs/ai/media/assistants/generated-sales-graph.png new file mode 100644 index 0000000000000..b2711fa67e2cd Binary files /dev/null and b/docs/ai/media/assistants/generated-sales-graph.png differ diff --git a/docs/ai/media/azure-hosted-apps/add-entra-role.png b/docs/ai/media/azure-hosted-apps/add-entra-role.png new file mode 100644 index 0000000000000..1bafa64cc1c01 Binary files /dev/null and b/docs/ai/media/azure-hosted-apps/add-entra-role.png differ diff --git a/docs/ai/media/azure-hosted-apps/system-assigned-managed-identity-in-azure-portal.png b/docs/ai/media/azure-hosted-apps/system-assigned-managed-identity-in-azure-portal.png new file mode 100644 index 0000000000000..39d40fe153df7 Binary files /dev/null and b/docs/ai/media/azure-hosted-apps/system-assigned-managed-identity-in-azure-portal.png differ diff --git a/docs/ai/media/azure-hosted-apps/user-assigned-managed-identity-in-azure-portal.png b/docs/ai/media/azure-hosted-apps/user-assigned-managed-identity-in-azure-portal.png new file mode 100644 index 0000000000000..f71cb6c1965d0 Binary files /dev/null and b/docs/ai/media/azure-hosted-apps/user-assigned-managed-identity-in-azure-portal.png differ diff --git a/docs/ai/quickstarts/quickstart-ai-chat-with-data.md b/docs/ai/quickstarts/quickstart-ai-chat-with-data.md index 65c1348db5c28..b5e065554cd82 100644 --- a/docs/ai/quickstarts/quickstart-ai-chat-with-data.md +++ b/docs/ai/quickstarts/quickstart-ai-chat-with-data.md @@ -136,6 +136,9 @@ Complete the following steps to create a .NET console app that can accomplish th dotnet user-secrets set ModelName ``` + > [!NOTE] + > For the `ModelName` value, you need to specify an OpenAI text embedding model such as `text-embedding-3-small` or `text-embedding-3-large` to generate embeddings for vector search in the sections that follow. + :::zone-end ## Add the app code diff --git a/docs/ai/quickstarts/quickstart-assistants.md b/docs/ai/quickstarts/quickstart-assistants.md new file mode 100644 index 0000000000000..c2fa6918f82bd --- /dev/null +++ b/docs/ai/quickstarts/quickstart-assistants.md @@ -0,0 +1,137 @@ +--- +title: Quickstart - Create a minimal AI assistant using .NET +description: Learn to create a minimal AI assistant with tooling capabilities using .NET and the Azure OpenAI SDK libraries +ms.date: 01/25/2025 +ms.topic: quickstart +ms.custom: devx-track-dotnet, devx-track-dotnet-ai +author: alexwolfmsft +ms.author: alexwolf +zone_pivot_groups: openai-library +--- + +# Create a minimal AI assistant using .NET + +In this quickstart, you'll learn how to create a minimal AI assistant using the OpenAI or Azure OpenAI SDK libraries. AI assistants provide agentic functionality to help users complete tasks using AI tools and models. In the sections ahead, you'll learn the following: + +- Core components and concepts of AI assistants +- How to create an assistant using the Azure OpenAI SDK +- How to enhance and customize the capabilities of an assistant + +## Prerequisites + +::: zone pivot="openai" + +* [Install .NET 8.0](https://dotnet.microsoft.com/download) or higher +* [Visual Studio Code](https://code.visualstudio.com/) (optional) +* [Visual Studio](https://visualstudio.com/) (optional) +* An access key for an OpenAI model + +:::zone-end + +::: zone pivot="azure-openai" + +* [Install .NET 8.0](https://dotnet.microsoft.com/download) or higher +* [Visual Studio Code](https://code.visualstudio.com/) (optional) +* [Visual Studio](https://visualstudio.com/) (optional) +* Access to an Azure OpenAI instance via Azure Identity or an access key + +:::zone-end + +## Core components of AI assistants + +AI assistants are based around conversational threads with a user. The user sends prompts to the assistant on a conversation thread, which directs the assistant to complete tasks using the tools it has available. Assistants can process and analyze data, make decisions, and interact with users or other systems to achieve specific goals. Most assistants include the following components: + +| **Component** | **Description** | +|---|---| +| **Assistant** | The core AI client and logic that uses Azure OpenAI models, manages conversation threads, and utilizes configured tools. | +| **Thread** | A conversation session between an assistant and a user. Threads store messages and automatically handle truncation to fit content into a model's context. | +| **Message** | A message created by an assistant or a user. Messages can include text, images, and other files. Messages are stored as a list on the thread. | +| **Run** | Activation of an assistant to begin running based on the contents of the thread. The assistant uses its configuration and the thread's messages to perform tasks by calling models and tools. As part of a run, the assistant appends messages to the thread. | +| **Run steps** | A detailed list of steps the assistant took as part of a run. An assistant can call tools or create messages during its run. Examining run steps allows you to understand how the assistant is getting to its final results. | + +Assistants can also be configured to use multiple tools in parallel to complete tasks, including the following: + +- **Code interpreter tool**: Writes and runs code in a sandboxed execution environment. +- **Function calling**: Runs local custom functions you define in your code. +- **File search capabilities**: Augments the assistant with knowledge from outside its model. + +By understanding these core components and how they interact, you can build and customize powerful AI assistants to meet your specific needs. + +## Create the .NET app + +Complete the following steps to create a .NET console app and add the package needed to work with assistants: + +::: zone pivot="openai" + +1. In a terminal window, navigate to an empty directory on your device and create a new app with the `dotnet new` command: + + ```dotnetcli + dotnet new console -o AIAssistant + ``` + +1. Add the [OpenAI](https://www.nuget.org/packages/OpenAI) package to your app: + + ```dotnetcli + dotnet add package OpenAI --prerelease + ``` + +1. Open the new app in your editor of choice, such as Visual Studio Code. + + ```dotnetcli + code . + ``` + +::: zone-end + +::: zone pivot="azure-openai" + +1. In a terminal window, navigate to an empty directory on your device and create a new app with the `dotnet new` command: + + ```dotnetcli + dotnet new console -o AIAssistant + ``` + +1. Add the [Azure.AI.OpenAI](https://www.nuget.org/packages/Azure.AI.OpenAI) package to your app: + + ```dotnetcli + dotnet add package Azure.AI.OpenAI --prerelease + ``` + +1. Open the new app in your editor of choice, such as Visual Studio Code. + + ```dotnetcli + code . + ``` + +::: zone-end + +## Create the AI assistant client + +1. Open the _Program.cs_ file and replace the contents of the file with the following code to create the required clients: + + :::code language="csharp" source="snippets/assistants/program.cs" range="0-17" ::: + +1. Create an in-memory sample document and upload it to the `OpenAIFileClient`: + + :::code language="csharp" source="snippets/assistants/program.cs" range="19-53" ::: + +1. Enable file search and code interpreter tooling capabilities via the `AssistantCreationOptions`: + + :::code language="csharp" source="snippets/assistants/program.cs" range="55-78" ::: + +1. Create the `Assistant` and a thread to manage interactions between the user and the assistant: + + :::code language="csharp" source="snippets/assistants/program.cs" range="80-105" ::: + +1. Print the messages and save the generated image from the conversation with the assistant: + + :::code language="csharp" source="snippets/assistants/program.cs" range="107-147" ::: + + Locate and open the saved image in the app *bin* directory, which should resemble the following: + + :::image type="content" source="../media/assistants/generated-sales-graph.png" alt-text="A graph showing the visualization generated by the AI model."::: + +## Next steps + +- [Quickstart - Get insight about your data from an .NET Azure AI chat app](../how-to/work-with-local-models.md) +- [Generate text and conversations with .NET and Azure OpenAI Completions](/training/modules/open-ai-dotnet-text-completions/) diff --git a/docs/ai/quickstarts/snippets/assistants/AIAssistants.csproj b/docs/ai/quickstarts/snippets/assistants/AIAssistants.csproj new file mode 100644 index 0000000000000..fc0f58f7521c5 --- /dev/null +++ b/docs/ai/quickstarts/snippets/assistants/AIAssistants.csproj @@ -0,0 +1,16 @@ + + + + Exe + net9.0 + enable + enable + + + + + + + + + diff --git a/docs/ai/quickstarts/snippets/assistants/Program.cs b/docs/ai/quickstarts/snippets/assistants/Program.cs new file mode 100644 index 0000000000000..a07a79a4ff21f --- /dev/null +++ b/docs/ai/quickstarts/snippets/assistants/Program.cs @@ -0,0 +1,147 @@ +using OpenAI; +using OpenAI.Assistants; +using OpenAI.Files; +using Azure.AI.OpenAI; +using Azure.Identity; + +// Create the OpenAI client +OpenAIClient openAIClient = new("your-apy-key"); + +// For Azure OpenAI, use the following client instead: +AzureOpenAIClient azureAIClient = new( + new Uri("your-azure-openai-endpoint"), + new DefaultAzureCredential()); + +#pragma warning disable OPENAI001 +AssistantClient assistantClient = openAIClient.GetAssistantClient(); +OpenAIFileClient fileClient = openAIClient.GetOpenAIFileClient(); + +// Create an in-memory document to upload to the file client +using Stream document = BinaryData.FromBytes(""" + { + "description": "This document contains the sale history data for Contoso products.", + "sales": [ + { + "month": "January", + "by_product": { + "113043": 15, + "113045": 12, + "113049": 2 + } + }, + { + "month": "February", + "by_product": { + "113045": 22 + } + }, + { + "month": "March", + "by_product": { + "113045": 16, + "113055": 5 + } + } + ] + } + """u8.ToArray()).ToStream(); + +// Upload the document to the file client +OpenAIFile salesFile = fileClient.UploadFile( + document, + "monthly_sales.json", + FileUploadPurpose.Assistants); + +// Configure the assistant options +AssistantCreationOptions assistantOptions = new() +{ + Name = "Example: Contoso sales RAG", + Instructions = + "You are an assistant that looks up sales data and helps visualize the information based" + + " on user queries. When asked to generate a graph, chart, or other visualization, use" + + " the code interpreter tool to do so.", + Tools = + { + new FileSearchToolDefinition(), // Enable the assistant to search and access files + new CodeInterpreterToolDefinition(), // Enable the assistant to run code for data analysis + }, + ToolResources = new() + { + FileSearch = new() + { + NewVectorStores = + { + new VectorStoreCreationHelper([salesFile.Id]), + } + } + }, +}; + +// Create the assistant +Assistant assistant = assistantClient.CreateAssistant("gpt-4o", assistantOptions); + +// Configure and create the conversation thread +ThreadCreationOptions threadOptions = new() +{ + InitialMessages = { "How well did product 113045 sell in February? Graph its trend over time." } +}; + +ThreadRun threadRun = assistantClient.CreateThreadAndRun(assistant.Id, threadOptions); + +// Sent the prompt and monitor progress until the thread run is complete +do +{ + Thread.Sleep(TimeSpan.FromSeconds(1)); + threadRun = assistantClient.GetRun(threadRun.ThreadId, threadRun.Id); +} +while (!threadRun.Status.IsTerminal); + +// Get the messages from the thread run +var messages = assistantClient.GetMessagesAsync( + threadRun.ThreadId, + new MessageCollectionOptions() + { + Order = MessageCollectionOrder.Ascending + }); + +await foreach (ThreadMessage message in messages) +{ + // Print out the messages from the assistant + Console.Write($"[{message.Role.ToString().ToUpper()}]: "); + foreach (MessageContent contentItem in message.Content) + { + if (!string.IsNullOrEmpty(contentItem.Text)) + { + Console.WriteLine($"{contentItem.Text}"); + + if (contentItem.TextAnnotations.Count > 0) + { + Console.WriteLine(); + } + + // Include annotations, if any + foreach (TextAnnotation annotation in contentItem.TextAnnotations) + { + if (!string.IsNullOrEmpty(annotation.InputFileId)) + { + Console.WriteLine($"* File citation, file ID: {annotation.InputFileId}"); + } + if (!string.IsNullOrEmpty(annotation.OutputFileId)) + { + Console.WriteLine($"* File output, new file ID: {annotation.OutputFileId}"); + } + } + } + // Save the generated image file + if (!string.IsNullOrEmpty(contentItem.ImageFileId)) + { + OpenAIFile imageInfo = fileClient.GetFile(contentItem.ImageFileId); + BinaryData imageBytes = fileClient.DownloadFile(contentItem.ImageFileId); + using FileStream stream = File.OpenWrite($"{imageInfo.Filename}.png"); + imageBytes.ToStream().CopyTo(stream); + + Console.WriteLine($""); + } + } + Console.WriteLine(); +} \ No newline at end of file diff --git a/docs/ai/quickstarts/snippets/chat-with-data/azure-openai/VectorDataAI.csproj b/docs/ai/quickstarts/snippets/chat-with-data/azure-openai/VectorDataAI.csproj index dd0092836ff35..ef8477b39f211 100644 --- a/docs/ai/quickstarts/snippets/chat-with-data/azure-openai/VectorDataAI.csproj +++ b/docs/ai/quickstarts/snippets/chat-with-data/azure-openai/VectorDataAI.csproj @@ -11,7 +11,7 @@ - + diff --git a/docs/ai/quickstarts/snippets/chat-with-data/openai/VectorDataAI.csproj b/docs/ai/quickstarts/snippets/chat-with-data/openai/VectorDataAI.csproj index 67945c8566316..50950a4544b8a 100644 --- a/docs/ai/quickstarts/snippets/chat-with-data/openai/VectorDataAI.csproj +++ b/docs/ai/quickstarts/snippets/chat-with-data/openai/VectorDataAI.csproj @@ -9,7 +9,7 @@ - + diff --git a/docs/ai/toc.yml b/docs/ai/toc.yml index 0cf04d0f41f2d..103022e2b89fb 100644 --- a/docs/ai/toc.yml +++ b/docs/ai/toc.yml @@ -27,6 +27,8 @@ items: href: quickstarts/quickstart-openai-generate-images.md - name: Chat with a local AI model href: quickstarts/quickstart-local-ai.md + - name: Build a minimal AI assistant + href: quickstarts/quickstart-assistants.md - name: Concepts items: - name: How generative AI and LLMs work @@ -51,7 +53,7 @@ items: href: conceptual/understanding-openai-functions.md - name: How-to articles items: - - name: Authenticate App Service to Azure OpenAI + - name: Authenticate to Azure OpenAI from an Azure hosted app href: how-to/app-service-aoai-auth.md - name: Authenticate App Service to a vector database href: how-to/app-service-db-auth.md diff --git a/docs/ai/tutorials/snippets/llm-eval/llm-eval.csproj b/docs/ai/tutorials/snippets/llm-eval/llm-eval.csproj index f3009eff29ab1..65e78083fc992 100644 --- a/docs/ai/tutorials/snippets/llm-eval/llm-eval.csproj +++ b/docs/ai/tutorials/snippets/llm-eval/llm-eval.csproj @@ -12,7 +12,7 @@ - + diff --git a/docs/architecture/microservices/microservice-ddd-cqrs-patterns/nosql-database-persistence-infrastructure.md b/docs/architecture/microservices/microservice-ddd-cqrs-patterns/nosql-database-persistence-infrastructure.md index 6d7361aa888cb..78d429e1014de 100644 --- a/docs/architecture/microservices/microservice-ddd-cqrs-patterns/nosql-database-persistence-infrastructure.md +++ b/docs/architecture/microservices/microservice-ddd-cqrs-patterns/nosql-database-persistence-infrastructure.md @@ -150,13 +150,14 @@ For further comparison between simply using MongoDB versus Cosmos DB in the clou ### Analyze your approach for production applications: MongoDB API vs. Cosmos DB API -In eShopOnContainers, we're using MongoDB API because our priority was fundamentally to have a consistent dev/test environment using a NoSQL database that could also work with Azure Cosmos DB. +eShopOnContainers uses MongoDB API because the priority was fundamentally to have a consistent dev/test environment using a NoSQL database that could also work with Azure Cosmos DB. -However, if you are planning to use MongoDB API to access Azure Cosmos DB in Azure for production applications, you should analyze the differences in capabilities and performance when using MongoDB API to access Azure Cosmos DB databases compared to using the native Azure Cosmos DB API. If it is similar you can use MongoDB API and you get the benefit of supporting two NoSQL database engines at the same time. +However, if you're planning to use MongoDB API to access Azure Cosmos DB in Azure for production applications, you should analyze the differences in capabilities and performance when using MongoDB API to access Azure Cosmos DB databases compared to using the native Azure Cosmos DB API. If it's similar, you can use MongoDB API and get the benefit of supporting two NoSQL database engines at the same time. -You could also use MongoDB clusters as the production database in Azure's cloud, too, with [MongoDB Azure Service](https://www.mongodb.com/scale/mongodb-azure-service). But that is not a PaaS service provided by Microsoft. In this case, Azure is just hosting that solution coming from MongoDB. +You could also use MongoDB clusters as the production database in Azure's cloud, too, with [MongoDB Atlas on +Microsoft Azure](https://www.mongodb.com/products/platform/atlas-cloud-providers/azure). But that is not a PaaS service provided by Microsoft. In this case, Azure is just hosting that solution coming from MongoDB. -Basically, this is just a disclaimer stating that you shouldn't always use MongoDB API against Azure Cosmos DB, as we did in eShopOnContainers because it was a convenient choice for Linux containers. The decision should be based on the specific needs and tests you need to do for your production application. +Basically, this is just a disclaimer to say that you shouldn't always use MongoDB API against Azure Cosmos DB. eShopOnContainers uses it because it was a convenient choice for Linux containers. The decision should be based on the specific needs and tests you need to do for your production application. ### The code: Use MongoDB API in .NET applications diff --git a/docs/azure/TOC.yml b/docs/azure/TOC.yml index 402d6fad001a1..8d7bff74f9f9f 100644 --- a/docs/azure/TOC.yml +++ b/docs/azure/TOC.yml @@ -76,7 +76,7 @@ - name: Credential chains href: ./sdk/authentication/credential-chains.md - name: Best practices - href: ./sdk/authentication/authentication-best-practices.md + href: ./sdk/authentication/best-practices.md - name: ASP.NET Core guidance href: ./sdk/aspnetcore-guidance.md - name: Resource management diff --git a/docs/azure/includes/dotnet-all.md b/docs/azure/includes/dotnet-all.md index 23db0258e6ddc..d52915dd3b97b 100644 --- a/docs/azure/includes/dotnet-all.md +++ b/docs/azure/includes/dotnet-all.md @@ -371,8 +371,8 @@ | Unknown Display Name | NuGet [1.0.0](https://www.nuget.org/packages/Azure.AI.ContentSafety.Extension.Embedded.Text/1.0.0)
NuGet [1.0.1-beta.4](https://www.nuget.org/packages/Azure.AI.ContentSafety.Extension.Embedded.Text/1.0.1-beta.4) | | | | Unknown Display Name | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.Maps.TimeZones/1.0.0-beta.1) | | | | Unknown Display Name | NuGet [1.0.0-beta.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.FaultInjection/1.0.0-beta.0) | | | -| Unknown Display Name | NuGet [1.0.6-preview](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.MySql/1.0.6-preview) | | | -| Unknown Display Name | NuGet [1.0.6-preview](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.MySql/1.0.6-preview) | | | +| Unknown Display Name | NuGet [1.0.14-preview](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.MySql/1.0.14-preview) | | | +| Unknown Display Name | NuGet [1.0.14-preview](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.MySql/1.0.14-preview) | | | | Unknown Display Name | NuGet [1.42.0](https://www.nuget.org/packages/Microsoft.CognitiveServices.Speech.CLI/1.42.0) | | | | Unknown Display Name | NuGet [1.42.0](https://www.nuget.org/packages/Microsoft.CognitiveServices.Speech.Extension.Embedded.SR/1.42.0) | | | | Unknown Display Name | NuGet [1.42.0](https://www.nuget.org/packages/Microsoft.CognitiveServices.Speech.Extension.Embedded.TTS/1.42.0) | | | @@ -400,7 +400,7 @@ | Common | NuGet [2.2.1](https://www.nuget.org/packages/Microsoft.Azure.Common/2.2.1) | | | | Common - Dependencies | NuGet [1.0.0](https://www.nuget.org/packages/Microsoft.Azure.Common.Dependencies/1.0.0) | | | | Computer Vision | NuGet [7.0.1](https://www.nuget.org/packages/Microsoft.Azure.CognitiveServices.Vision.ComputerVision/7.0.1) | | GitHub [7.0.1](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.CognitiveServices.Vision.ComputerVision_6.0.0-preview.1/sdk/cognitiveservices/Vision.ComputerVision) | -| Cosmos DB | NuGet [3.42.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.42.0)
NuGet [3.47.0-preview.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.47.0-preview.0) | [docs](https://learn.microsoft.com/dotnet/api/overview/azure/cosmosdb) | GitHub [3.42.0](https://github.com/Azure/azure-cosmos-dotnet-v3/tree/3.12.0/Microsoft.Azure.Cosmos) | +| Cosmos DB | NuGet [3.42.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.42.0)
NuGet [3.47.0-preview.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.47.0-preview.1) | [docs](https://learn.microsoft.com/dotnet/api/overview/azure/cosmosdb) | GitHub [3.42.0](https://github.com/Azure/azure-cosmos-dotnet-v3/tree/3.12.0/Microsoft.Azure.Cosmos) | | Custom Image Search | NuGet [2.1.0-preview.1](https://www.nuget.org/packages/Microsoft.Azure.CognitiveServices.Search.BingCustomImageSearch/2.1.0-preview.1) | | GitHub [2.1.0-preview.1](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.CognitiveServices.Search.BingCustomImageSearch_2.1.0-preview.1/sdk/cognitiveservices/Search.BingCustomImageSearch) | | Custom Image Search | NuGet [2.0.0](https://www.nuget.org/packages/Microsoft.Azure.CognitiveServices.Search.CustomImageSearch/2.0.0) | | | | Custom Search | NuGet [2.1.0-preview.1](https://www.nuget.org/packages/Microsoft.Azure.CognitiveServices.Search.BingCustomSearch/2.1.0-preview.1) | | GitHub [2.1.0-preview.1](https://github.com/Azure/azure-sdk-for-net/tree/Microsoft.Azure.CognitiveServices.Search.BingCustomSearch_2.1.0-preview.1/sdk/cognitiveservices/Search.BingCustomSearch) | @@ -478,7 +478,7 @@ | App Service - API Apps Service | NuGet [0.9.64](https://www.nuget.org/packages/Microsoft.Azure.AppService.ApiApps.Service/0.9.64) | | | | Code Analyzers for Durable Functions | NuGet [0.5.0](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.DurableTask.Analyzers/0.5.0) | | GitHub [0.5.0](https://github.com/Azure/azure-functions-durable-extension/tree/Analyzer-v0.3.0/src/WebJobs.Extensions.DurableTask.Analyzers) | | Cosmos DB - BulkExecutor | NuGet [2.5.1-preview](https://www.nuget.org/packages/Microsoft.Azure.CosmosDB.BulkExecutor/2.5.1-preview) | | GitHub [2.5.1-preview](https://github.com/Azure/azure-cosmosdb-bulkexecutor-dotnet-getting-started) | -| Cosmos DB - Direct | NuGet [3.37.7](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Direct/3.37.7) | | GitHub [3.37.7](https://github.com/Azure/azure-cosmos-dotnet-v3) | +| Cosmos DB - Direct | NuGet [3.37.9](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Direct/3.37.9) | | GitHub [3.37.9](https://github.com/Azure/azure-cosmos-dotnet-v3) | | Cosmos DB - Encryption | NuGet [2.0.3](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Encryption/2.0.3)
NuGet [2.1.0-preview4](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Encryption/2.1.0-preview4) | | GitHub [2.0.3](https://github.com/Azure/azure-cosmos-dotnet-v3/tree/releases/encryption/1.0.0-preview4/Microsoft.Azure.Cosmos.Encryption) | | Cosmos DB - Encryption | NuGet [1.0.0-preview07](https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Encryption.Custom/1.0.0-preview07) | | | | Extensions - Caching Cosmos | NuGet [1.7.0](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Cosmos/1.7.0) | | GitHub [1.7.0](https://github.com/Azure/Microsoft.Extensions.Caching.Cosmos/tree/v1.0.0-preview4) | @@ -486,7 +486,7 @@ | Functions extension for Application Insights | NuGet [1.0.0-preview4](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.ApplicationInsights/1.0.0-preview4) | | | | Functions extension for Azure Mobile Apps | NuGet [3.0.0-beta8](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.MobileApps/3.0.0-beta8) | | GitHub [3.0.0-beta8](https://github.com/Azure/azure-webjobs-sdk-extensions/tree/v3.0.0-beta8/src/WebJobs.Extensions.MobileApps) | | Functions extension for Azure SQL and SQL Server | NuGet [3.1.284](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.Sql/3.1.284) | | | -| Functions extension for Cosmos DB | NuGet [4.8.1](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.CosmosDB/4.8.1) | | GitHub [4.8.1](https://github.com/Azure/azure-webjobs-sdk-extensions/tree/cosmos-v3.0.7/src/WebJobs.Extensions.CosmosDB) | +| Functions extension for Cosmos DB | NuGet [4.9.0](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.CosmosDB/4.9.0) | | GitHub [4.9.0](https://github.com/Azure/azure-webjobs-sdk-extensions/tree/cosmos-v3.0.7/src/WebJobs.Extensions.CosmosDB) | | Functions extension for DocumentDB | NuGet [1.3.0](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.DocumentDB/1.3.0) | | GitHub [1.3.0](https://github.com/Azure/azure-webjobs-sdk-extensions) | | Functions extension for Durable Task Framework | NuGet [3.0.2](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.DurableTask/3.0.2) | [docs](https://learn.microsoft.com/dotnet/api/overview/azure/functions) | GitHub [3.0.2](https://github.com/Azure/azure-functions-durable-extension/tree/v2.2.2/src/WebJobs.Extensions.DurableTask) | | Functions extension for Durable Task Framework - isolated worker | NuGet [1.2.2](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.DurableTask/1.2.2) | | | @@ -505,7 +505,7 @@ | Functions Extensions - Cosmos DB Mongo | NuGet [1.0.2](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.CosmosDb.Mongo/1.0.2) | | | | Functions Extensions - Dapr | NuGet [1.0.1](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.Dapr/1.0.1) | | | | Functions Extensions - Dapr Core | NuGet [1.0.1](https://www.nuget.org/packages/Microsoft.Azure.Functions.Extensions.Dapr.Core/1.0.1) | | | -| Functions Extensions - HTTP AspNet Core | NuGet [2.0.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore/2.0.0) | | | +| Functions Extensions - HTTP AspNet Core | NuGet [2.0.1](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore/2.0.1) | | | | Functions Extensions - Redis | NuGet [1.0.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Redis/1.0.0) | | | | Functions Extensions - Redis | NuGet [1.0.0](https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.Redis/1.0.0) | | | | Functions Extensions - Worker Extentions | NuGet [1.0.1](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Dapr/1.0.1) | | | @@ -525,17 +525,17 @@ | Microsoft.Azure.Functions.Worker.Core | NuGet [2.0.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Core/2.0.0) | | | | Microsoft.Azure.Functions.Worker.Extensions.Abstractions | NuGet [1.3.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Abstractions/1.3.0) | | | | Microsoft.Azure.Functions.Worker.Extensions.ApplicationInsights | NuGet [1.0.0-preview4](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.ApplicationInsights/1.0.0-preview4) | | | -| Microsoft.Azure.Functions.Worker.Extensions.CosmosDB | NuGet [4.11.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.CosmosDB/4.11.0) | | | +| Microsoft.Azure.Functions.Worker.Extensions.CosmosDB | NuGet [4.12.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.CosmosDB/4.12.0) | | | | Microsoft.Azure.Functions.Worker.Extensions.EventGrid | NuGet [3.4.2](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.EventGrid/3.4.2) | | | | Microsoft.Azure.Functions.Worker.Extensions.EventHubs | NuGet [6.3.6](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.EventHubs/6.3.6) | | | -| 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.Http | NuGet [3.3.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Http/3.3.0) | | | | Microsoft.Azure.Functions.Worker.Extensions.Kafka | NuGet [4.0.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Kafka/4.0.0) | | | | Microsoft.Azure.Functions.Worker.Extensions.Kusto | NuGet [1.0.11-Preview](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Kusto/1.0.11-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)
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.1](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Rpc/1.0.1) | | | | Microsoft.Azure.Functions.Worker.Extensions.SendGrid | NuGet [3.0.3](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.SendGrid/3.0.3) | | | -| Microsoft.Azure.Functions.Worker.Extensions.ServiceBus | NuGet [5.22.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.ServiceBus/5.22.0) | | | +| Microsoft.Azure.Functions.Worker.Extensions.ServiceBus | NuGet [5.22.1](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.ServiceBus/5.22.1) | | | | Microsoft.Azure.Functions.Worker.Extensions.SignalRService | NuGet [1.14.1](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.SignalRService/1.14.1) | | | | Microsoft.Azure.Functions.Worker.Extensions.Sql | NuGet [3.1.284](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Sql/3.1.284) | | | | Microsoft.Azure.Functions.Worker.Extensions.Storage | NuGet [6.6.0](https://www.nuget.org/packages/Microsoft.Azure.Functions.Worker.Extensions.Storage/6.6.0) | | | diff --git a/docs/azure/sdk/authentication/authentication-best-practices.md b/docs/azure/sdk/authentication/best-practices.md similarity index 87% rename from docs/azure/sdk/authentication/authentication-best-practices.md rename to docs/azure/sdk/authentication/best-practices.md index 9130f6278c570..6a327317dbe34 100644 --- a/docs/azure/sdk/authentication/authentication-best-practices.md +++ b/docs/azure/sdk/authentication/best-practices.md @@ -2,7 +2,7 @@ title: Authentication best practices with the Azure Identity library for .NET description: This article describes authentication best practices to follow when using the Azure Identity library for .NET. ms.topic: conceptual -ms.date: 01/24/2025 +ms.date: 01/29/2025 --- # Authentication best practices with the Azure Identity library for .NET @@ -48,15 +48,17 @@ The recommended credential reuse strategy differs by .NET application type. # [ASP.NET Core](#tab/aspdotnet) -Implement credential reuse through the `UseCredential` method of `Microsoft.Extensions.Azure`: +Implement credential reuse through the method of `Microsoft.Extensions.Azure`. For example, imagine an ASP.NET Core app hosted on Azure App Service, with a `UserAssignedClientId` environment variable set. The .NET configuration provider determines the environment variable exists, and `ManagedIdentityCredential` will be used to authenticate the Key Vault Secrets and Blob Storage clients. Otherwise, a chained sequence of development-time credentials is used. -:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_Dac" highlight="6" ::: +:::code language="csharp" source="../snippets/authentication/best-practices/CCA/Program.cs" id="snippet_credential_reuse_AspNetCore" highlight="16"::: For information on this approach, see [Authenticate using Microsoft Entra ID](/dotnet/azure/sdk/aspnetcore-guidance?tabs=api#authenticate-using-microsoft-entra-id). # [Other](#tab/other) -:::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_credential_reuse_noDac" highlight="8, 12" ::: +In non-ASP.NET Core apps, credential reuse is accomplished by passing the same credential instance to each client constructor. For example, imagine a WPF app using the authentication broker on Windows. + +:::code language="csharp" source="../snippets/authentication/best-practices/PCA/MainWindow.xaml.cs" id="snippet_credential_reuse_nonAspNetCore" highlight="12, 16"::: --- @@ -77,6 +79,6 @@ The Azure Identity library for .NET allows you to authenticate via managed ident - The time interval between retries starts at 0.8 seconds, and a maximum of five retries are attempted, by default. This option is optimized for resilience but introduces potentially unwanted delays in the development inner loop. - To change any of the default retry settings, use the `Retry` property on `ManagedIdentityCredentialOptions`. For example, retry a maximum of three times, with a starting interval of 0.5 seconds: - :::code language="csharp" source="../snippets/authentication/best-practices/Program.cs" id="snippet_retries" highlight="5-9" ::: + :::code language="csharp" source="../snippets/authentication/best-practices/CCA/Program.cs" id="snippet_retries" highlight="5-9"::: For more information on customizing retry policies, see [Setting a custom retry policy](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/samples/Configuration.md#setting-a-custom-retry-policy). diff --git a/docs/azure/sdk/snippets/authentication/Directory.Packages.props b/docs/azure/sdk/snippets/authentication/Directory.Packages.props index 6a7584c99a6ae..64bda2aecce28 100644 --- a/docs/azure/sdk/snippets/authentication/Directory.Packages.props +++ b/docs/azure/sdk/snippets/authentication/Directory.Packages.props @@ -5,8 +5,8 @@ - - + + diff --git a/docs/azure/sdk/snippets/authentication/best-practices/BestPractices.sln b/docs/azure/sdk/snippets/authentication/best-practices/BestPractices.sln new file mode 100644 index 0000000000000..74b689c3614e1 --- /dev/null +++ b/docs/azure/sdk/snippets/authentication/best-practices/BestPractices.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35716.79 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfidentialClientApp", "CCA\ConfidentialClientApp.csproj", "{6FB4D659-156E-C79B-1602-D99A3B0BC1B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PublicClientApp", "PCA\PublicClientApp.csproj", "{0C902D2D-2B8D-479A-C29F-90EF265B1666}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + ..\..\Directory.Packages.props = ..\..\Directory.Packages.props + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FB4D659-156E-C79B-1602-D99A3B0BC1B8}.Release|Any CPU.Build.0 = Release|Any CPU + {0C902D2D-2B8D-479A-C29F-90EF265B1666}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C902D2D-2B8D-479A-C29F-90EF265B1666}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C902D2D-2B8D-479A-C29F-90EF265B1666}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C902D2D-2B8D-479A-C29F-90EF265B1666}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8002C7E1-9D56-4063-BFEF-6CDB73707080} + EndGlobalSection +EndGlobal diff --git a/docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj b/docs/azure/sdk/snippets/authentication/best-practices/CCA/ConfidentialClientApp.csproj similarity index 81% rename from docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj rename to docs/azure/sdk/snippets/authentication/best-practices/CCA/ConfidentialClientApp.csproj index 5e7b705f2e39c..00599c753d402 100644 --- a/docs/azure/sdk/snippets/authentication/best-practices/AuthBestPractices.csproj +++ b/docs/azure/sdk/snippets/authentication/best-practices/CCA/ConfidentialClientApp.csproj @@ -1,9 +1,10 @@ - + net9.0 enable enable + ASP.NET Core diff --git a/docs/azure/sdk/snippets/authentication/best-practices/Program.cs b/docs/azure/sdk/snippets/authentication/best-practices/CCA/Program.cs similarity index 63% rename from docs/azure/sdk/snippets/authentication/best-practices/Program.cs rename to docs/azure/sdk/snippets/authentication/best-practices/CCA/Program.cs index 44c8df93465b6..33186d5fe9f09 100644 --- a/docs/azure/sdk/snippets/authentication/best-practices/Program.cs +++ b/docs/azure/sdk/snippets/authentication/best-practices/CCA/Program.cs @@ -1,39 +1,33 @@ -using Azure.Identity; -using Azure.Security.KeyVault.Secrets; -using Azure.Storage.Blobs; +using Azure.Core; +using Azure.Identity; using Microsoft.Extensions.Azure; -var userAssignedClientId = ""; +var clientId = ""; var builder = WebApplication.CreateBuilder(args); -#region snippet_credential_reuse_Dac +#region snippet_credential_reuse_AspNetCore builder.Services.AddAzureClients(clientBuilder => { clientBuilder.AddSecretClient(new Uri("")); clientBuilder.AddBlobServiceClient(new Uri("")); - clientBuilder.UseCredential(new DefaultAzureCredential()); -}); -#endregion snippet_credential_reuse_Dac - -#region snippet_credential_reuse_noDac -ChainedTokenCredential credentialChain = new( - new ManagedIdentityCredential( - ManagedIdentityId.FromUserAssignedClientId(userAssignedClientId)), - new VisualStudioCredential()); + string? clientId = builder.Configuration["UserAssignedClientId"]; -BlobServiceClient blobServiceClient = new( - new Uri(""), - credentialChain); + TokenCredential credential = clientId is not null + ? new ManagedIdentityCredential( + ManagedIdentityId.FromUserAssignedClientId(clientId)) + : new ChainedTokenCredential( + new VisualStudioCredential(), + new AzureCliCredential(), + new AzurePowerShellCredential()); -SecretClient secretClient = new( - new Uri(""), - credentialChain); -#endregion snippet_credential_reuse_noDac + clientBuilder.UseCredential(credential); +}); +#endregion snippet_credential_reuse_AspNetCore #region snippet_retries ManagedIdentityCredentialOptions miCredentialOptions = new( - ManagedIdentityId.FromUserAssignedClientId(userAssignedClientId) + ManagedIdentityId.FromUserAssignedClientId(clientId) ) { Retry = diff --git a/docs/azure/sdk/snippets/authentication/best-practices/Properties/launchSettings.json b/docs/azure/sdk/snippets/authentication/best-practices/CCA/Properties/launchSettings.json similarity index 100% rename from docs/azure/sdk/snippets/authentication/best-practices/Properties/launchSettings.json rename to docs/azure/sdk/snippets/authentication/best-practices/CCA/Properties/launchSettings.json diff --git a/docs/azure/sdk/snippets/authentication/best-practices/appsettings.Development.json b/docs/azure/sdk/snippets/authentication/best-practices/CCA/appsettings.Development.json similarity index 100% rename from docs/azure/sdk/snippets/authentication/best-practices/appsettings.Development.json rename to docs/azure/sdk/snippets/authentication/best-practices/CCA/appsettings.Development.json diff --git a/docs/azure/sdk/snippets/authentication/best-practices/CCA/appsettings.json b/docs/azure/sdk/snippets/authentication/best-practices/CCA/appsettings.json new file mode 100644 index 0000000000000..10f68b8c8b4f7 --- /dev/null +++ b/docs/azure/sdk/snippets/authentication/best-practices/CCA/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml b/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml new file mode 100644 index 0000000000000..b45e34c0aaecb --- /dev/null +++ b/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml.cs b/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml.cs new file mode 100644 index 0000000000000..203ee1ec8bcd0 --- /dev/null +++ b/docs/azure/sdk/snippets/authentication/best-practices/PCA/App.xaml.cs @@ -0,0 +1,13 @@ +using System.Configuration; +using System.Data; +using System.Windows; + +namespace PublicClientAppAuthBestPractices; + +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application +{ +} + diff --git a/docs/azure/sdk/snippets/authentication/best-practices/PCA/AssemblyInfo.cs b/docs/azure/sdk/snippets/authentication/best-practices/PCA/AssemblyInfo.cs new file mode 100644 index 0000000000000..cc29e7f74114a --- /dev/null +++ b/docs/azure/sdk/snippets/authentication/best-practices/PCA/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly:ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/docs/azure/sdk/snippets/authentication/best-practices/PCA/MainWindow.xaml b/docs/azure/sdk/snippets/authentication/best-practices/PCA/MainWindow.xaml new file mode 100644 index 0000000000000..3e0af8a51c730 --- /dev/null +++ b/docs/azure/sdk/snippets/authentication/best-practices/PCA/MainWindow.xaml @@ -0,0 +1,12 @@ + + +