Skip to content

Repository files navigation

Azure Container Apps for Azure AI Search

This code sample builds a web site on Azure to search through a catalog of books. Searchable content is indexed and queried on Azure AI Search, and the app runs on Azure Container Apps.

This sample includes a C# bulk-insert app, a C# Azure Functions API, and a React/Node.js web client.

Component Description
bulk-insert app Creates and loads the "good-books" index on Azure AI Search. It demonstrates index creation and batch mode indexing. Sample data is loaded from the azure-search-sample-data repository.
client app Provides the client code. The web front-end includes a search page with faceted navigation, a search bar for free form search and suggested queries, and tabbed page results. It's written in JavaScript, uses Node.js for the runtime, and uses React libraries for user interaction.
api Provides the Azure Functions app used by the client to send queries to the search index.

This README is a shortened version of the full tutorial and provides just the steps for running the sample. For more information and screenshots, see the tutorial.

Prerequisites

Because deployment assigns Azure roles automatically, the deploying user needs permission to create role assignments on the target resource group — the Owner or User Access Administrator role (which grants Microsoft.Authorization/roleAssignments/write). Without it, azd up fails when it provisions the role assignments.

You don't need to hold any Azure AI Search data-plane roles in advance. During provisioning, main.bicep assigns Search Index Data Contributor and Search Service Contributor to both the app's managed identity (for keyless query access) and the deploying user (so the postprovision bulk-insert hook can create and populate the index).

For local development of the API or client:

Test the sample

The repository includes deterministic UI tests and live Azure AI Search integration tests. Maintainers can find the prerequisites, suite classifications, expected failures, visual review guidance, and protected workflow instructions in TESTING.md.

Download sample repository

  1. In a terminal, use git to clone this repository to your local computer:

    git clone https://github.com/Azure-Samples/azure-search-static-web-app
  2. Open that local directory in Visual Studio Code.

Deploy with the Azure Developer CLI (azd)

azd auth login
azd up

This provisions all Azure resources (Azure AI Search, Container Apps, Container Registry) and deploys both containers. The default uses keyless (managed identity) authentication — no code changes required.

The postprovision hook automatically runs bulk-insert to create and populate the good-books search index after infrastructure is provisioned.

Key environment variables (set automatically by azd from infra/main.bicepparam):

Variable Description
SEARCH_SERVICE_NAME Name of the Azure AI Search service (bicep output, used by seed hook)
SEARCH_INDEX_NAME Search index name (default: good-books)

Authentication

By default, the search service is configured with keyless (managed identity) authentication — disableLocalAuth: true is set on the search service, and the user-assigned managed identity receives Search data-plane role assignments. The server container app receives AZURE_CLIENT_ID and authenticates via DefaultAzureCredential. No code changes required.

Optional API key auth can be enabled by setting USE_KEYLESS_AUTH to false:

azd env set USE_KEYLESS_AUTH false
azd up

When USE_KEYLESS_AUTH=false, the infra provisions the admin key as a container secret and sets SEARCH_USE_KEY_AUTH=true on the server container, which causes api/SearchClientFactory.cs to use AzureKeyCredential instead of DefaultAzureCredential.

Keyless Authentication Configuration (Two Environment Variables)

The keyless authentication setup uses two distinct environment variables for different purposes and times:

  1. Infrastructure Provisioning Time: useKeylessAuth (Bicep parameter)

    • When: azd up executes the Bicep template (infra/main.bicep)
    • Purpose: Determines whether the Azure AI Search service disables local auth and enables role-based access control (RBAC)
    • Values: true (default) or false
    • Effect:
      • true: Sets disableLocalAuth: true on the search service, requiring keyless auth; assigns Search RBAC roles to the managed identity
      • false: Allows API key auth; provisions and injects the admin key as a container secret
    • Set via: azure.yaml hooks or azd env set / azd config during provisioning
  2. Runtime: SEARCH_USE_KEY_AUTH (Application Environment Variable)

    • When: Application containers start (server and bulk-insert)
    • Purpose: Switches the search credential strategy at runtime (keyless vs API key)
    • Values: true (use API key) or unset/false (use keyless/DefaultAzureCredential)
    • Effect:
      • SearchClientFactory.cs reads this variable and selects the credential type
      • When true: Uses AzureKeyCredential with the injected key
      • When false/unset: Uses DefaultAzureCredential with the managed identity identified by AZURE_CLIENT_ID
    • Set via: Bicep container app environment config
  3. Managed Identity Identifier: AZURE_CLIENT_ID (Infrastructure Provisioning Time)

    • When: azd up sets this on the server container app (only when useKeylessAuth=true)
    • Purpose: Identifies which managed identity DefaultAzureCredential should use when multiple identities exist on the host
    • Value: The user-assigned managed identity's client ID
    • Usage: api/SearchClientFactory.cs passes this to DefaultAzureCredential.CreateAsync() via new ManagedIdentityClientId(clientId)

Azure AI Search Keys: Admin vs Query Keys

Azure AI Search provides two types of keys for different security contexts:

Key Type Purpose Permissions Scope When to Use
Admin Key Index and service management Full: create/delete indexes, manage synonyms, configure analyzers, add/update/delete documents All operations (data plane + admin) Index creation, schema changes, bulk data loading (administrative)
Query Key Data queries only Read-only: execute search queries, retrieve documents Data-plane queries only Client-facing search API, public search interfaces, query operations

Best Practice: Use separate keys for their intended purposes:

  • Bulk-insert (index creation): Use the admin key (required for CreateOrUpdateIndexAsync)
  • Search API (queries): Use a query key (principle of least privilege)

Current Implementation: This solution uses the admin key for both bulk-insert and search queries when key auth is enabled. This is a security anti-pattern — the query-facing API has more permissions than necessary. To improve security:

  • Fetch the query key instead of the admin key in the azure.yaml postprovision hook (use az search query-key list and select the first key)
  • Pass it to the server container as SEARCH_QUERY_KEY instead of SEARCH_API_KEY
  • Update api/SearchClientFactory.cs to use SEARCH_QUERY_KEY for search operations

How the credential is selected: The search API (api/SearchClientFactory.cs) reads the SEARCH_USE_KEY_AUTH environment variable at runtime. When it is true, AzureKeyCredential is used with the injected key; otherwise DefaultAzureCredential is used (keyless default). The same logic applies to the bulk-insert seed hook. No code changes needed to switch modes.

Note

Why these deployment files

  • Client runtime config (docker-entrypoint.sh + /config.js + url-fetch.js): Vite bakes VITE_* env at BUILD time, but the backend's Container Apps FQDN isn't known until provisioning; the client reads the backend URL at container start via an injected /config.js (window.__APP_CONFIG__). Deployment wiring, not app logic.
  • bulk-insert azd seed hook: the sample needs a populated search index to function; wiring bulk-insert as an azd postprovision hook (reading connection info from the provisioned resources via env) auto-seeds the index on azd up instead of manually editing placeholder constants.

To redeploy after code changes: azd deploy

Browse the deployed app

After azd up completes, the CLI prints the URL for the client Container App. Open that URL in your browser, enter a search query such as code, and review the results.

About

Code sample demonstrates how to create an Azure Static Web app for searching on Azure AI Search.

Resources

Code of conduct

Contributing

Stars

13 stars

Watchers

11 watching

Forks

Releases

Packages

Used by

Contributors

Languages