A transparent .NET 8 reverse proxy that sits between Azure AI Foundry and Cosmos DB.
It intercepts document writes to encrypt the content field with AES-256-GCM before
it reaches the database, and decrypts it on reads so callers see the original data.
Azure AI Foundry workloads write documents to Cosmos DB.
Some documents contain a "content" field that must be encrypted at rest before
it reaches the database. Rather than modifying every client, this transparent reverse
proxy intercepts document-write operations and encrypts the sensitive field in-flight.
The proxy is intentionally minimal:
- Default behavior is pass-through. Reads, queries, metadata, change feed, patch, and batch requests flow to Cosmos DB untouched.
- Transparent encryption/decryption. Document writes get
contentencrypted; reads get it decrypted. Callers never see ciphertext. - Removable. Point the AI Foundry connection target back to the real Cosmos endpoint and the system works without the proxy (documents already stored will remain encrypted).
flowchart LR
A[AI Foundry Agent] -->|HTTPS<br/>CosmosDB connection target| C[Web App - Proxy]
C -->|HTTPS<br/>public endpoint| D[Cosmos DB]
style C fill:#f9f,stroke:#333
sequenceDiagram
participant Client as AI Foundry Agent
participant Proxy as Web App (Proxy)
participant Cosmos as Cosmos DB
Client->>Proxy: HTTPS request (via connection target)
Proxy->>Cosmos: Forward to public endpoint
Cosmos-->>Proxy: Response
Proxy-->>Client: Response (decrypted if needed)
- The AI Foundry project has a CosmosDB connection whose
targetpoints to the proxy Web App URL (e.g.https://proxy-xxx.azurewebsites.net). - The capability host's
threadStorageConnectionsreferences this connection so agents route Cosmos DB traffic to the proxy. - The proxy forwards all requests to the real Cosmos DB public endpoint (configured via the
COSMOS_ENDPOINTenv var).
flowchart TD
A[Incoming Request] --> B{POST or PUT<br/>and path contains /docs?}
B -->|No| C[Pass through unchanged]
B -->|Yes| D{Body has 'query' property?}
D -->|Yes| E[Cosmos SQL query — pass through]
D -->|No| F[Encrypt 'content' field<br/>AES-256-GCM]
F --> G[Add cdbProxyMetadata]
G --> H[Forward to Cosmos DB]
C --> H
E --> H
H --> I{Response has JSON body?}
I -->|No| J[Stream response as-is]
I -->|Yes| K[Decrypt 'content' field<br/>Strip cdbProxyMetadata]
K --> L[Return to caller]
J --> L
| Condition | Action |
|---|---|
POST/PUT and path contains /docs and body has no query property |
Encrypt content field (AES-256-GCM), add cdbProxyMetadata, forward |
POST to /docs with query property |
Cosmos SQL query — pass through unchanged |
| JSON response from Cosmos | Decrypt content field, strip cdbProxyMetadata |
| Everything else | Pass through unchanged (headers, body, auth tokens) |
- Algorithm: AES-256-GCM
- Key: 32-byte key provided via
ENCRYPTION_KEYenvironment variable (base64-encoded) - Envelope format:
ENC+ type byte (0x01 = base64 string, 0x02 = JSON), then the plaintext - Stored as:
base64( nonce[12] + tag[16] + ciphertext ) - Decryption handles both the envelope format and a legacy fallback
graph TD
P[Program.cs<br/>Entry point & DI setup] --> H[CosmosProxyHandler.cs<br/>Proxy logic, encrypt/decrypt]
P --> PS[AiFoundryPingService.cs<br/>Background health-check]
PS --> TC[ThreadContextCache.cs<br/>Thread → Tenant/User mapping]
H --> TC
The Web App runs on a Linux App Service Plan (B1) with HTTPS enforced and always-on enabled. It reaches Cosmos DB over the public endpoint — no VNET, private endpoint, or private DNS zone is required.
| File | Purpose |
|---|---|
src/Program.cs |
Entry point — configures DI, HTTP client, routing, and the catch-all proxy route |
src/CosmosProxyHandler.cs |
Core proxy handler — request forwarding, AES-256-GCM encrypt/decrypt, metadata stamping |
src/AiFoundryPingService.cs |
BackgroundService that creates an AI Foundry agent and pings it every 60 s |
src/ThreadContextCache.cs |
In-memory cache (IMemoryCache) mapping AI Foundry thread IDs to tenant/user pairs |
src/CosmosDBProxy.csproj |
.NET 8 project — depends on Azure.AI.Agents.Persistent and Azure.Identity |
infra/main.arm.json |
ARM template — Cosmos DB, Web App, AI Foundry project, Storage, AI Search, RBAC |
infra/deploy.ps1 |
PowerShell deployment script — build, zip, ARM deploy, code deploy |
monitoring/validation-queries.kql |
KQL queries for Application Insights — ping health, proxy success rate, encryption counts, error tracking |
AiFoundryPingService runs as a hosted BackgroundService:
- Connects to AI Foundry using
DefaultAzureCredential - Creates (or finds) an agent named
cosmos-proxy-ping - Every 60 seconds: creates a thread, sends a health-check message, polls for the agent reply, then deletes the thread
- Stores thread → tenant/user mappings in
ThreadContextCacheso the proxy can resolve tenant context on intercepted writes
Requires PROJECT_ENDPOINT and optionally MODEL_DEPLOYMENT_NAME (defaults to gpt-4o-mini).
| Variable | Required | Description |
|---|---|---|
COSMOS_ENDPOINT |
Yes | Upstream Cosmos DB endpoint (e.g. https://<account>.documents.azure.com) |
ENCRYPTION_KEY |
Yes | Base64-encoded AES-256 key (must decode to ≥ 32 bytes) |
PROJECT_ENDPOINT |
No | AI Foundry project endpoint — enables the background ping service |
MODEL_DEPLOYMENT_NAME |
No | Model deployment for the ping agent (default: gpt-4o-mini) |
The infra/deploy.ps1 script handles the full deployment:
# Full deployment (infrastructure + code)
.\infra\deploy.ps1 -ResourceGroupName "my-rg" -Location "eastus"
# Code-only deployment (skip ARM template)
.\infra\deploy.ps1 -ResourceGroupName "my-rg" -AppOnlySteps: dotnet publish → zip → ARM template deploy → az webapp deploy.
cd src
export COSMOS_ENDPOINT="https://<your-account>.documents.azure.com"
export ENCRYPTION_KEY="<base64-encoded-32-byte-key>"
dotnet runRequests to http://localhost:5000/dbs/mydb/colls/mycoll/docs will be proxied to the real Cosmos endpoint.
The monitoring/validation-queries.kql file contains Application Insights (KQL) queries for:
- Agent ping success count and time-series trends
- Proxy request throughput and success rate
- Encryption/decryption operation counts
- Error and exception tracking
- Tenant context resolution verification