Optimizing Azure RAG: Single vs Multi-Spoke Strategies

Think of a “Spoke” as a Security Boundary. While Azure allows you to spread services across different spokes, doing so adds complexity to your networking (DNS, Peering, and Firewall rules).

Here is the recommendation based on Azure Landing Zone (ALZ) best practices for 2026:


1. The “Single AI Spoke” Strategy (Recommended)

Most enterprises group Databricks, AI Search, and OpenAI into a single “AI & Data Spoke.”

  • Why? These services are “chatty.” Databricks needs to push data to AI Search, and OpenAI needs to pull data from AI Search. If they are in the same VNet, this traffic is faster, simpler to secure with Network Security Groups (NSGs), and avoids the “hop” through a central Hub Firewall which can add latency and cost.
  • Best for: A single business unit or a specific project team building an assistant.

2. The “Multi-Spoke” Strategy (Enterprise Scale)

You would only split them into separate spokes if you are building a Shared AI Platform for the whole company.

  • How it looks:
    • Spoke A (Shared AI): Centralized Azure OpenAI (used by 10 different teams).
    • Spoke B (Data Refinery): Your Databricks and ADLS (dedicated to your team’s private data).
    • Spoke C (App): Your Frontend Chat UI.
  • Why? This allows the “Central IT” team to manage OpenAI quotas and costs in one place, while your team manages your own data.
  • Trade-off: You will need Private DNS Zone peering across all three spokes so the services can find each other’s Private Endpoints.

3. The “Cross-Service” Security Checklist

Regardless of whether they are in one spoke or two, you must handle these three connections:

ConnectionRequirement2026 Standard
Databricks $\rightarrow$ AI SearchPrivate EndpointUse a User-Assigned Managed Identity on the Databricks cluster to push vectors to Search.
OpenAI $\rightarrow$ AI SearchShared Private LinkThis is a special “handshake” in the Azure Portal that lets OpenAI talk to Search without going over the internet.
Frontend $\rightarrow$ OpenAIVNet IntegrationYour Web App/Frontend must be “VNet Integrated” to reach the OpenAI Private Endpoint.

4. Final Recommendation

If you are using Terraform and a Databricks-heavy refinery, I recommend the Single Spoke approach for now.

  1. Create one VNet called vnet-ai-prod.
  2. Create separate subnets for each service:
    • snet-databricks-host / snet-databricks-container (for the spark nodes).
    • snet-endpoints (for Private Endpoints for OpenAI, AI Search, and ADLS).
  3. Use Private DNS Zones linked to this VNet so that my-openai.openai.azure.com resolves to a local internal IP (e.g., 10.0.1.5).

For a single department, keeping everything in a single spoke is the most efficient, cost-effective, and secure “starter” architecture for 2026. It minimizes networking latency and simplifies the DNS configuration that often trips up Terraform deployments.

Here is my specific recommendation for your single-department networking and security setup:

1. Subnet Segmentation (The “Clean” Spoke)

Don’t put all services in one big subnet. Divide your Spoke VNet into functional zones to apply specific Network Security Groups (NSGs):

  • Subnet A (Databricks Private): For the worker nodes.
  • Subnet B (Databricks Public): For the “Secure Cluster Connectivity” (No Public IP) relay.
  • Subnet C (Private Endpoints): This is the “Safe Zone” where you place the Private Endpoints for OpenAI, AI Search, and ADLS Gen2.
  • Subnet D (Integration): If you have a Frontend Web App, this is where you’ll use VNet Integration so the app can reach the services in Subnet C.

2. The 2026 Security “Golden Rules”

  • Identity over Keys: In your Terraform, disable local_auth_enabled for OpenAI and AI Search. This forces the system to use Microsoft Entra ID (RBAC). If a developer accidentally leaks an API key in a notebook, it simply won’t work.
  • Managed Identities: Use a User-Assigned Managed Identity. Assign it to both the Databricks cluster and the Frontend App. Grant it the Cognitive Services OpenAI User and Search Index Data Reader roles.
  • Data Exfiltration Protection (DEP): Since it’s a single department, you can easily enable DEP on your Databricks workspace. This prevents users from “exporting” results out to a personal storage account; they can only move data between the approved ADLS and AI Search resources.

3. Shared Private Links (The Handshake)

One of the most common mistakes is forgetting the OpenAI $\rightarrow$ AI Search path. Even if both are in your VNet, the OpenAI service itself needs a “Shared Private Link” to talk to your search index securely.

  • Action: In the Azure AI Foundry (formerly AI Studio) portal or via Terraform’s azapi provider, create a Shared Private Link from OpenAI to the AI Search resource.

4. Why a “Single Spoke” for one department?

BenefitExplanation
Lower CostYou avoid VNet Peering charges (which apply when data moves between spokes).
Simpler DNSYou only need to link your Private DNS Zones to one VNet.
Faster IterationYour Terraform script is “self-contained.” You don’t have to wait for a Central IT team to approve peering requests or firewall rules in a different subscription.

5. Final Recommendation Summary

  1. Architecture: One Spoke VNet with VNet-Injected Databricks.
  2. Access: All public access Disabled.
  3. Authentication: 100% Managed Identity (No API keys).
  4. Governance: Unity Catalog enabled for the structured data tables.

Terraform snippet for the Private Endpoint and DNS configuration

I managing both structured (SQL) and unstructured (Docs) data in a single department spoke, the Terraform setup below is designed for a “Zero-Trust” configuration. It ensures that OpenAI and AI Search services are invisible to the public internet and only accessible via your VNet.

1. Networking Strategy

This snippet covers the Private DNS Zones and Private Endpoints—the two areas where 90% of RAG networking issues occur.

Terraform

# 1. Private DNS Zones (The "Phonebook" for your VNet)
resource "azurerm_private_dns_zone" "openai_dns" {
  name                = "privatelink.openai.azure.com"
  resource_group_name = azurerm_resource_group.ai_rg.name
}

resource "azurerm_private_dns_zone" "search_dns" {
  name                = "privatelink.search.windows.net"
  resource_group_name = azurerm_resource_group.ai_rg.name
}

# 2. VNet Links (Telling the DNS zones which VNet to serve)
resource "azurerm_private_dns_zone_virtual_network_link" "openai_link" {
  name                  = "openai-link"
  resource_group_name   = azurerm_resource_group.ai_rg.name
  private_dns_zone_name = azurerm_private_dns_zone.openai_dns.name
  virtual_network_id    = azurerm_virtual_network.ai_vnet.id
}

# 3. Private Endpoint for Azure OpenAI
resource "azurerm_private_endpoint" "openai_endpoint" {
  name                = "pe-openai-department"
  location            = azurerm_resource_group.ai_rg.location
  resource_group_name = azurerm_resource_group.ai_rg.name
  subnet_id           = azurerm_subnet.endpoint_subnet.id

  private_service_connection {
    name                           = "psc-openai"
    private_connection_resource_id = azurerm_cognitive_account.openai.id
    subresource_names              = ["account"]
    is_manual_connection           = false
  }

  private_dns_zone_group {
    name                 = "openai-dns-group"
    private_dns_zone_ids = [azurerm_private_dns_zone.openai_dns.id]
  }
}


2. Key Architectural Components (The “2026 Check”)

ResourceSub-resource NameDNS Zone Name
Azure OpenAIaccountprivatelink.openai.azure.com
AI SearchsearchServiceprivatelink.search.windows.net
ADLS Gen2 (Blob)blobprivatelink.blob.core.windows.net
ADLS Gen2 (DFS)dfsprivatelink.dfs.core.windows.net

Note: For ADLS Gen2, you need both blob and dfs endpoints if you’re using Databricks, as Spark often uses the DFS endpoint for optimized file operations.


3. Recommendations for your Managed Identities

To make this work for both data types without using API keys, add this to your Terraform:

Terraform

# Grant the AI Assistant (App) permission to use OpenAI
resource "azurerm_role_assignment" "app_openai_user" {
  scope                = azurerm_cognitive_account.openai.id
  role_definition_name = "Cognitive Services OpenAI User"
  principal_id         = azurerm_user_assigned_identity.assistant_id.principal_id
}

# Grant the AI Assistant permission to read from AI Search
resource "azurerm_role_assignment" "app_search_reader" {
  scope                = azurerm_search_service.ai_search.id
  role_definition_name = "Search Index Data Reader"
  principal_id         = azurerm_user_assigned_identity.assistant_id.principal_id
}

4. Final Security Check

  1. Disable Public Access: Ensure public_network_access_enabled = false is set on the Storage Account, AI Search, and OpenAI resources.
  2. Databricks “No Public IP”: In your Databricks workspace resource, set public_network_access_enabled = false and use the no_public_ip parameter for the cluster.
  3. DNS Propagation: Remember that when you apply this Terraform, DNS can take 2–5 minutes to propagate. If your first connection fails, give it a moment to “settle.”

With this setup, your assistant will be able to query Databricks SQL (structured) and AI Search (unstructured) while keeping every single packet of data inside your department’s private network.

Leave a comment