Authenticate and authorize App Service to a vector database

This article demonstrates how to manage the connection between your App Service .NET application and a vector database solution. It covers using Microsoft Entra managed identities for supported services and securely storing connection strings for others.

By adding a vector database to your application, you can enable [semantic memories or vector stores](vector stores) for your AI. The Semantic Kernel SDK for .NET enables you to easily implement memory storage and recall using your preferred vector database solution.

Prerequisites

Use Microsoft Entra managed identity for authentication

If a vector database service supports Microsoft Entra authentication, you can use a managed identity with your App Service to securely access your vector database without having to manually provision or rotate any secrets. For a list of Azure services that support Microsoft Entra authentication, see Azure services that support Microsoft Entra authentication.

Add a managed identity to App Service

Your application can be granted 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.

Add a system-assigned identity

  1. Navigate to your app's page in the Azure portal, and then scroll down to the Settings group.
  2. Select Identity.
  3. On the System assigned tab, toggle Status to On, and then select Save.

Run the az webapp identity assign command to create a system-assigned identity:

az webapp identity assign --name <appName> --resource-group <groupName>

Add a user-assigned identity

To add a user-assigned identity to your app, create the identity, and then add its resource identifier to your app config.

  1. Create a user-assigned managed identity resource by following these instructions.

  2. In the left navigation pane of your app's page, scroll down to the Settings group.

  3. Select Identity.

  4. Select User assigned > Add.

  5. Locate the identity that you created earlier, select it, and then select Add.

    Important

    After you select Add, the app restarts.

  1. Create a user-assigned identity:

    az identity create --resource-group <groupName> --name <identityName>
    
  2. Assign the identity to your app:

    az webapp identity assign --resource-group <groupName> --name <appName> --identities <identityId>
    

Add an Azure role to your managed identity

  1. In the Azure Portal, navigate to the scope that you want to grant vector database access to. The scope can be a Management group, Subscription, Resource group, or a specific Azure resource.
  2. In the left navigation pane, select Access control (IAM).
  3. Select Add, then select Add role assignment.
  4. On the Role tab, select the appropriate role that grants read access to your vector database.
  5. On the Members tab, select the managed identity.
  6. On the Review + assign tab, select Review + assign to assign the role.

Resource scope

az role assignment create --assignee "<managedIdentityObjectID>" \
--role "<myVectorDbReaderRole>" \
--scope "/subscriptions/<subscriptionId>/resourcegroups/<resourceGroupName>/providers/<providerName>/<resourceType>/<resourceSubType>/<resourceName>"

Resource group scope

az role assignment create --assignee "<managedIdentityObjectID>" \
--role "<myVectorDbReaderRole>" \
--scope "/subscriptions/<subscriptionId>/resourcegroups/<resourceGroupName>"

Subscription scope

az role assignment create --assignee "<managedIdentityObjectID>" \
--role "<myVectorDbReaderRole>" \
--scope "/subscriptions/<subscriptionId>"

Management group scope

az role assignment create --assignee "<managedIdentityObjectID>" \
--role "<myVectorDbReaderRole>" \
--scope "/providers/Microsoft.Management/managementGroups/<managementGroupName>"

Implement token-based authentication with the vector database

The following code samples require these additional libraries:

  1. Initialize a DefaultAzureCredential object to pick up your app's managed identity:

    // Initialize a DefaultAzureCredential.
    // This credential type will try several authentication flows in order until one is available.
    // Will pickup Visual Studio or Azure CLI credentials in local environments.
    // Will pickup managed identity credentials in production deployments.
    TokenCredential credentials = new DefaultAzureCredential(
        new DefaultAzureCredentialOptions
        {
            // If using a user-assigned identity specify either:
            // ManagedIdentityClientId or ManagedIdentityResourceId.
            // e.g.: ManagedIdentityClientId = "myIdentityClientId".
        }
    );
    
  2. Initialize an IMemoryStore object for your vector database, then use it to build an ISemanticTextMemory:

    // Retrieve the endpoint obtained from the Azure AI Search deployment.
    // Retrieve the endpoint and deployments obtained from the Azure OpenAI deployment.
    // Must use the deployment name not the underlying model name.
    IConfigurationRoot config = new ConfigurationBuilder().AddUserSecrets<Program>().Build();
    string searchEndpoint = config["AZURE_AISEARCH_ENDPOINT"]!;
    string openAiEndpoint = config["AZURE_OPENAI_ENDPOINT"]!;
    string embeddingModel = config["AZURE_OPENAI_EMBEDDING_NAME"]!;
    
    // The Semantic Kernel SDK provides a connector extension for Azure AI Search.
    // Initialize an AzureAISearchMemoryStore using your managed identity credentials.
    IMemoryStore memoryStore = new AzureAISearchMemoryStore(searchEndpoint, credentials);
    
    // Build a SemanticMemoryStore with Azure AI Search as the store.
    // Must also include a text embedding generation service.
    ISemanticTextMemory memory = new MemoryBuilder()
        .WithOpenAITextEmbeddingGeneration(embeddingModel, openAiEndpoint)
        .WithMemoryStore(memoryStore)
        .Build();
    
  3. Build a Kernel object, then import the ISemanticTextMemory object using the TextMemoryPlugin:

    // Build a Kernel, include a chat completion service.
    string chatModel = config["AZURE_OPENAI_GPT_NAME"]!;
    Kernel kernel = Kernel
        .CreateBuilder()
        .AddAzureOpenAIChatCompletion(chatModel, openAiEndpoint, credentials)
        .Build();
    
    // Import the semantic memory store as a TextMemoryPlugin.
    // The TextMemoryPlugin enable recall via prompt expressions.
    kernel.ImportPluginFromObject(new TextMemoryPlugin(memory));
    
  4. Use the Kernel object to invoke a prompt that includes memory recall:

    // Must configure the memory collection, number of memories to recall, and relevance score.
    // The {{...}} syntax represents an expression to Semantic Kernel.
    // For more information on this syntax see:
    // https://learn.microsoft.com/semantic-kernel/prompts/prompt-template-syntax
    string memoryCollection = config["AZURE_OPENAI_MEMORY_NAME"]!;
    string? result = await kernel.InvokePromptAsync<string>(
        "{{recall 'where did I grow up?'}}",
        new()
        {
            [TextMemoryPlugin.CollectionParam] = memoryCollection,
            [TextMemoryPlugin.LimitParam] = "2",
            [TextMemoryPlugin.RelevanceParam] = "0.79",
        }
    );
    Console.WriteLine($"Output: {result}");
    

Use Key Vault to store connection secrets

If a vector database doesn't support Microsoft Entra authentication, you can use a Key Vault to store your connection secrets and retrieve them with your App Service application. By using a Key Vault to store your connection secrets you can share them with multiple applications, and control access to individual secrets per application.

Before following these steps, retrieve a connection string for your vector database. For example, see Use Azure Cache for Redis with an ASP.NET Core web app.

Add a connection string to Key Vault

Important

Before following these steps, ensure you have created a Key Vault using the Azure Portal.

  1. Navigate to your key vault in the Azure Portal.
  2. In the Key Vault left navigation, select Objects then select Secrets.
  3. Select + Generate/Import.
  4. On the Create a secret screen choose the following values:
    • Upload options: Manual.
    • Name: Type a name for the secret. The secret name must be unique within a Key Vault.
    • Value: The connection string for your vector database.
    • Leave the other values to their defaults. Select Create.
  5. When you receive the message that the secret has been successfully created, it's ready to use in your application.

Important

Before following these steps, ensure you have created a Key Vault using the Azure CLI.

  1. Grant your user account permissions to your key vault through Role-Based Access Control (RBAC), assign a role using the Azure CLI command az role assignment create:

    az role assignment create \
    --role "Key Vault Secrets User" \
    --assignee "<yourEmailAddress>" \
    --scope "/subscriptions/<subscriptionId>/resourceGroups/<resourceGroupName>/providers/Microsoft.KeyVault/vaults/<keyVaultName>"
    
  2. Add the connection string to Key Vault using the Azure CLI command az keyvault secret set:

    az keyvault secret set \
    --vault-name "<keyVaultName>" \
    --name "<secretName>" \
    --value "<connectionString>"
    

Grant your App Service access to Key Vault

  1. Assign a managed identity to your App Service.
  2. Add the Key Vault Secrets User and Key Vault Reader roles to your managed identity.

Implement connection string retrieval from Key Vault

To use the following code samples, you need these additional libraries:

These code samples use a Redis database, but you can apply them to any vector database that supports connection strings.

  1. Initialize a DefaultAzureCredential object to pick up your app's managed identity:

    // Initialize a DefaultAzureCredential.
    // This credential type will try several authentication flows in order until one is available.
    // Will pickup Visual Studio or Azure CLI credentials in local environments.
    // Will pickup managed identity credentials in production deployments.
    TokenCredential credentials = new DefaultAzureCredential(
        new DefaultAzureCredentialOptions
        {
            // If using a user-assigned identity specify either:
            // ManagedIdentityClientId or ManagedIdentityResourceId.
            // e.g.: ManagedIdentityClientId = "myIdentityClientId".
        }
    );
    
  2. Add Key Vault when building your configuration, this will map your Key Vault secrets to the IConfigurationRoot object:

    // User secrets let you provide connection strings when testing locally
    // For more info see: https://learn.microsoft.com/aspnet/core/security/app-secrets
    IConfigurationRoot config = new ConfigurationBuilder()
        .AddUserSecrets<Program>()
        .AddAzureKeyVault(new Uri("{vaultURI}"), credentials)
        .Build();
    
    // Retrieve the Redis connection string obtained from the Key Vault.
    string redisConnectionString = config["AZURE_REDIS_CONNECT_STRING"]!;
    
  3. Use your vector database connection string from Key Vault to initialize an IMemoryStore object, and then use it to build an ISemanticTextMemory:

    // Use the connection string to connect to the database
    IDatabase database = (
        await ConnectionMultiplexer.ConnectAsync(redisConnectionString)
    ).GetDatabase();
    
    // The Semantic Kernel SDK provides a connector extension for Redis.
    // Initialize an RedisMemoryStore using your managed identity credentials.
    IMemoryStore memoryStore = new RedisMemoryStore(database);
    
    // Retrieve the endpoint and deployments obtained from the Azure OpenAI deployment.
    // Must use the deployment name not the underlying model name.
    string openAiEndpoint = config["AZURE_OPENAI_ENDPOINT"]!;
    string embeddingModel = config["AZURE_OPENAI_EMBEDDING_NAME"]!;
    
    // Build a SemanticMemoryStore with Azure AI Search as the store.
    // Must also include a text embedding generation service.
    ISemanticTextMemory memory = new MemoryBuilder()
        .WithOpenAITextEmbeddingGeneration(embeddingModel, openAiEndpoint)
        .WithMemoryStore(memoryStore)
        .Build();
    
  4. Build a Kernel object, then import the ISemanticTextMemory object using the TextMemoryPlugin:

    // Build a Kernel, include a chat completion service.
    string chatModel = config["AZURE_OPENAI_GPT_NAME"]!;
    Kernel kernel = Kernel
        .CreateBuilder()
        .AddAzureOpenAIChatCompletion(chatModel, openAiEndpoint, credentials)
        .Build();
    
    // Import the semantic memory store as a TextMemoryPlugin.
    // The TextMemoryPlugin enable recall via prompt expressions.
    kernel.ImportPluginFromObject(new TextMemoryPlugin(memory));
    
  5. Use the Kernel object to invoke a prompt that includes memory recall:

    // Must configure the memory collection, number of memories to recall, and relevance score.
    // The {{...}} syntax represents an expression to Semantic Kernel.
    // For more information on this syntax see:
    // https://learn.microsoft.com/semantic-kernel/prompts/prompt-template-syntax
    string memoryCollection = config["AZURE_OPENAI_MEMORY_NAME"]!;
    string? result = await kernel.InvokePromptAsync<string>(
        "{{recall 'where did I grow up?'}}",
        new()
        {
            [TextMemoryPlugin.CollectionParam] = memoryCollection,
            [TextMemoryPlugin.LimitParam] = "2",
            [TextMemoryPlugin.RelevanceParam] = "0.79",
        }
    );
    Console.WriteLine($"Output: {result}");
    

Use application settings to store connection secrets

If a vector database doesn't support Microsoft Entra authentication, you can use the App Service application settings to store your connection secrets. By using application settings you can store your connection secrets without provisioning any additional Azure resources.

Before following these steps, retrieve a connection string for your vector database. For example, see Use Azure Cache for Redis in .NET Framework.

Add a connection string to application settings

  1. Navigate to your app's page on the Azure Portal.
  2. In the app's left menu, select Configuration > Application settings.
    • By default, values for application settings are hidden in the portal for security.
    • To see a hidden value of an application setting, select its Value field.
  3. Select New connection setting.
  4. On the Add/Edit connection string screen choose the following values:
    • Name: Type a name for the setting. The setting name must be unique.
    • Value: The connection string for your vector database.
    • Type: The type of connection, Custom if no others apply.
    • Leave the other values to their defaults. Select OK.
  5. Select Save back in the Configuration page.

Add or edit an app setting with the Azure CLI command az webapp config connection-string set:

az webapp config connection-string set \
--name "<appName>" \
--resource-group "<groupName>" \
--connection-string-type "<connectionType>" \
--settings <connectionName>='<connectionString>'

Implement connection string retrieval from app settings

To use the following code samples, you need these additional libraries:

These code samples use a Redis database, but you can apply them to any vector database that supports connection strings.

  1. Add environment variables when building your configuration, this will map your connection strings to the IConfigurationRoot object:

    // User secrets let you provide connection strings when testing locally
    // For more info see: https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets
    IConfigurationRoot config = new ConfigurationBuilder()
        .AddUserSecrets<Program>()
        .AddEnvironmentVariables()
        .Build();
    
    // Retrieve the Redis connection string obtained from the app settings.
    // The connection string name should match the entry in application settings
    string redisConnectionString = config.GetConnectionString("AZURE_REDIS")!;
    
  2. Use your vector database connection string from app settings to initialize an IMemoryStore object, and then use it to build an ISemanticTextMemory:

    // Use the connection string to connect to the database
    IDatabase database = (
        await ConnectionMultiplexer.ConnectAsync(redisConnectionString)
    ).GetDatabase();
    
    // The Semantic Kernel SDK provides a connector extension for Redis.
    // Initialize an RedisMemoryStore using your managed identity credentials.
    IMemoryStore memoryStore = new RedisMemoryStore(database);
    
    // Retrieve the endpoint and deployments obtained from the Azure OpenAI deployment.
    // Must use the deployment name not the underlying model name.
    string openAiEndpoint = config["AZURE_OPENAI_ENDPOINT"]!;
    string embeddingModel = config["AZURE_OPENAI_EMBEDDING_NAME"]!;
    
    // Build a SemanticMemoryStore with Azure AI Search as the store.
    // Must also include a text embedding generation service.
    ISemanticTextMemory memory = new MemoryBuilder()
        .WithOpenAITextEmbeddingGeneration(embeddingModel, openAiEndpoint)
        .WithMemoryStore(memoryStore)
        .Build();
    
  3. Build a Kernel object, then import the ISemanticTextMemory object using the TextMemoryPlugin:

    // Build a Kernel, include a chat completion service.
    string chatModel = config["AZURE_OPENAI_GPT_NAME"]!;
    Kernel kernel = Kernel
        .CreateBuilder()
        .AddAzureOpenAIChatCompletion(chatModel, openAiEndpoint, credentials)
        .Build();
    
    // Import the semantic memory store as a TextMemoryPlugin.
    // The TextMemoryPlugin enable recall via prompt expressions.
    kernel.ImportPluginFromObject(new TextMemoryPlugin(memory));
    
  4. Use the Kernel object to invoke a prompt that includes memory recall:

    // Must configure the memory collection, number of memories to recall, and relevance score.
    // The {{...}} syntax represents an expression to Semantic Kernel.
    // For more information on this syntax see:
    // https://learn.microsoft.com/semantic-kernel/prompts/prompt-template-syntax
    string memoryCollection = config["AZURE_OPENAI_MEMORY_NAME"]!;
    string? result = await kernel.InvokePromptAsync<string>(
        "{{recall 'where did I grow up?'}}",
        new()
        {
            [TextMemoryPlugin.CollectionParam] = memoryCollection,
            [TextMemoryPlugin.LimitParam] = "2",
            [TextMemoryPlugin.RelevanceParam] = "0.79",
        }
    );
    Console.WriteLine($"Output: {result}");