Skip to main content
This page covers the Azure-specific bits. The actual Helm install (values, ingress, env vars) lives in Install with Helm.

Persistent storage

The shared RWX PVC (used by prismeai-functions) must be backed by Azure Files with ZRS. Premium tier recommended for hot workloads.

Infrastructure as Code

Use Bicep, ARM templates or the Terraform azurerm provider; Azure’s documentation is the canonical reference. Prisme.ai doesn’t ship a dedicated Azure IaC bundle.

DNS and TLS

Create two records (Azure DNS or your registrar):
A certificate covering both is the easiest path. Manage it in Key Vault and reference it from AGIC, or generate it via cert-manager.

Microsoft Entra ID passwordless auth

Entra ID lets you replace static passwords for Redis and PostgreSQL with short-lived tokens, using system-assigned or user-assigned managed identities. Two modes are available. They differ in how the token reaches the pod, and that difference decides what you have to configure: Option B (recommended) is the better choice for a shared cluster. It is also the one with more moving parts, so work through the checklist at the end of this section before concluding it does not work.

1. Create the managed identity

Keep both GUIDs. They are not interchangeable and you will need each in a different place:
  • clientId goes in your Helm values (azureManagedIdentityClientId) and in the ServiceAccount annotation.
  • objectId (also called principalId) is what you grant on Redis and register in PostgreSQL.

2. Wire the identity to AKS

Option A (simplest): bind to the cluster

Simplest, and applies to every workload on the node.
Then set every azureSystemIdentity: true in your Helm values (see the table in step 3 for the full list). No ServiceAccount and no pod label are needed: IMDS answers regardless. First, make sure the cluster can do it at all:
Then create one federated credential per namespace — the subject contains the namespace, so core and apps need separate credentials:
Workload Identity then needs three things on every backend workload. Missing any one of them silently disables federation:
The Helm charts before prismeai-core 1.12.1 and prismeai-apps 0.7.1 cannot set item 3. No backend chart exposes a podLabels hook, so the pod label cannot be applied from values. When using old charts, Option B needs a post-install patch — see Applying the pod label below. Without the label the AKS webhook injects nothing and the Azure SDK falls back to IMDS, which only works if the identity happens to be attached to your node pool. It does not report an error.
In your values, for every backend chart (everything except prismeai-console and prismeai-pages):
Set serviceAccount.annotations on every backend subchart, not just one. Each subchart renders its own ServiceAccount object; when they share a name, the last one applied wins, so a single annotated definition is overwritten by the unannotated ones that follow it.

If your chart exposes global.podLabels

Newer charts (prismeai-core >= 1.12.1 and prismeai-apps >= 0.7.1) add two hooks that replace both the repetition above and the post-install patch below. Check with helm show values <chart> | grep podLabels; if it is there, the whole Workload Identity configuration below must be set for both the core values file and the apps values file :
You still set serviceAccount.name: prismeai-backends-sa on each backend. The global annotations are merged into every ServiceAccount the release creates, so the last-one-wins problem disappears, and Applying the pod label becomes unnecessary.

3. Set the identity on every client

Each backing-service client is configured independently. Setting only some of them leaves the rest on username/password authentication, which fails or silently keeps using an old secret. There are seven, across two Helm releases: For Option A, set azureSystemIdentity: true at those same seven paths instead.
Four of the seven are Redis. Configuring “the PostgreSQL ones” plus the broker still leaves sessions, contexts and the crawler/searchengine cache authenticating with a password.Watch global.storage.searchengines in particular: the key says searchengines, but its value is a Redis URL (redis://user:password@host:6379/4) and it configures a Redis client. It is in the apps release, not core.
Managed identity covers Redis and PostgreSQL only. Elasticsearch is configured separately with ELASTIC_USER / ELASTIC_PASSWORD and has no identity option, and Blob storage uses UPLOADS_STORAGE_AZURE_BLOB_CONNECTION_STRING — an account key. Both remain credential-based no matter what you set above, so “passwordless” applies to two of the four backing services.

4. Remove static passwords

A static password takes precedence and silently disables managed identity. The service starts, connects and works normally, using the password — nothing reports that the identity is unused. Check your existingSecret contents too: a leftover password key has the same effect.

5. Redis

  1. Open your Azure Cache for Redis / Azure Managed Redis instance.
  2. Settings → Authentication → Microsoft Entra Authentication — enable it.
  3. Add a data access policy for the identity’s objectId, not its clientId:
The client sends the token’s oid claim as the Redis username, which is the objectId. Assigning the policy to the clientId by mistake produces WRONGPASS with a perfectly valid token.

6. PostgreSQL

Connect as your Entra administrator, to the postgres maintenance database:
Connect to postgres, not to your application database. Azure installs the pgaadauth_* functions only in the maintenance database; any database created afterwards is cloned from template1 and does not have them. Running the next command in your application database fails with function pgaadauth_create_principal(...) does not exist, whose hint about type casts is misleading — the function is simply not there.
Register the managed identity as a Postgres role, passing its objectId explicitly:
pgaadauth_create_principal('PrismeaiIdentity', false, false) also exists and asks the server to resolve the name against Entra. For a user-assigned managed identity prefer the _with_oid form: it needs no directory lookup, so it works regardless of tenant restrictions. Keep the name-resolving form for human users and groups.
Roles are cluster-wide, so the principal now exists for every database. Grants are not — reconnect to your application database and run:
Verify the mapping before moving on:
PrismeaiIdentity should appear with principaltype = service and an objectid equal to the identity’s objectId.

7. Put the Postgres principal in the connection URL

Under Entra authentication the Azure token supplies the password; the username must come from the connection URL:
Use that URL for the url key of each PostgreSQL existingSecretglobal.storage.permissions, prismeai-api-gateway.storage.users and prismeai-runtime.storage.collections.
The separate .user value has no effect under Entra authentication: the chart omits *_STORAGE_USER whenever azureManagedIdentityClientId or azureSystemIdentity is set. If the principal is in neither the URL nor that variable, the driver sends an empty username and PostgreSQL answers no PostgreSQL user name specified in startup packet, which points nowhere near the real cause.

Applying the pod label

Skip this section if your chart exposes global.podLabels (see above) — it exists only for charts that do not. Otherwise, patch the deployments after every helm install or helm upgrade — the upgrade rewrites the pod template and drops the label:
Repeat for your apps namespace.
If your deployment pipeline rebuilds the Helm package from source on every run — a common GitOps arrangement — this patch is reverted on the next deploy and is not a durable fix. On such a pipeline, Option B needs a chart that exposes global.podLabels, or use Option A.

Verify it actually worked

This is the only reliable check, and it takes one command:
  • Prints a path → the webhook fired and the pod is authenticating through federation. Option B is working.
  • Prints nothing → the webhook did not fire. The pod is falling back to IMDS, whatever else appears to work. Re-check the pod label, the ServiceAccount annotation, and that serviceAccount.name is set on that chart.
A pod can pass every functional test while printing nothing here — if the identity is also attached to your node pool, IMDS answers and the system behaves normally, sharing one identity across every pod on the node. Functional success is not evidence that Option B is configured.

Troubleshooting

Every message below was produced on a real cluster by breaking one thing deliberately, so they are what you will actually see.
Read a ChainedTokenCredential error to the end. The Azure SDK tries each credential in turn and reports them all, so the first two lines are usually EnvironmentCredential is unavailable — which is noise. The real cause is last:

The federated credential does not match

Azure quotes the subject it received. Compare it character by character with your az identity federated-credential create --subject. The subject is always system:serviceaccount:<namespace>:<serviceaccountname>, so a mismatch means the namespace is wrong, serviceAccount.name was not set on that chart, or the pod fell back to the default ServiceAccount. AADSTS70021 is the same failure for the issuer rather than the subject — check --issuer against az aks show --query oidcIssuerProfile.issuerUrl.

A leftover password, on a server expecting a token

Nothing is wrong with the token — none was requested. A password is still set, so the driver short-circuits Azure authentication and sends that password where PostgreSQL expects a token. Remove it (step 4).

Both PostgreSQL and Redis fail together

They share exactly one thing: acquiring a token. Everything downstream — scopes, drivers, grants, servers — is separate. So a fault that takes out both is upstream of both: the pod label, the ServiceAccount annotation, or workload identity not enabled on the cluster. Do not start by looking at the databases.

Symptom table


Ingress annotations

Application Gateway exposes two distinct settings:
  • Backend request timeout (≈ 60 s, under the api-gateway server keep-alive of 70 s): socket reuse between client requests.
  • Idle request timeout (≈ 300 s): kill an in-flight request only after this much inactivity, so SSE / long LLM streams aren’t dropped.
The full annotation reference lives in Helm install: Ingress and load balancer.

Next Steps

Install with Helm

Configure values and deploy core + apps namespaces.

Databases

PostgreSQL or MongoDB, Redis, Elasticsearch or OpenSearch.

Install products

Fresh-install walkthrough.

Migration v27

Migrate an existing instance to v27.