Demonstrates fully passwordless authentication to Azure Database for PostgreSQL Flexible Server using Azure Workload Identity and Microsoft Entra access tokens. No static passwords anywhere.
| Actor | Token mechanism |
|---|---|
| Temporal server pods | azure-token-refresher sidecar writes /azure/pgpass; Temporal reads it via passwordCommand on every connection |
| Schema Job | one-shot initContainer writes /azure/pgpass; schema container reads it via passwordCommand |
| Operator (probe + schema inspection) | obtains an Entra token natively in-process via the Go Azure Workload Identity SDK — no sidecar, no shell required on the distroless image |
This resolved #47.
How it works #
The operator generates all the Azure Workload Identity wiring (ServiceAccount,
sidecars, initContainers, passwordCommand, inspector Jobs) from a single
cluster-level persistence.azureWorkloadIdentity.clientId field. You provide the
managed identity’s client ID; the operator synthesizes the rest.
Prerequisites #
- AKS with the OIDC issuer and Workload Identity enabled
(
az aks update --enable-oidc-issuer --enable-workload-identity). - A managed identity (or app registration) with a federated credential bound to
the operator-generated ServiceAccount
<cluster-name>-azurein the cluster’s namespace (e.g.,azure-wi-azurein namespacedefault). - The identity mapped to a Postgres role on the Flexible Server:
SELECT pgaadauth_create_principal('temporal-identity', false, false); - Microsoft Entra authentication enabled on the Flexible Server.
- The
azure.extensionsallow-list set tobtree_gin,pg_trgm(required by Temporal’s SQL visibility schema).
Apply #
kubectl apply -f temporalcluster.yaml
The operator creates the ServiceAccount (<cluster-name>-azure) and wires the
server pods, schema Jobs, and its own probe/schema-inspection Jobs. Ensure your
managed identity’s federated credential is bound to this generated ServiceAccount
before applying.
Manifests #
temporalcluster.yaml #
# Passwordless Flexible Server auth via Azure Workload Identity.
# The operator generates ALL the wiring (ServiceAccount, sidecars,
# initContainers, passwordCommand, inspector Jobs) from a single
# `persistence.azureWorkloadIdentity.clientId` field.
apiVersion: temporal.bmor10.com/v1alpha1
kind: TemporalCluster
metadata:
name: azure-wi
spec:
version: "1.31.1"
numHistoryShards: 512
ui:
enabled: true
persistence:
azureWorkloadIdentity:
clientId: REPLACE_WITH_MANAGED_IDENTITY_CLIENT_ID
defaultStore:
sql:
pluginName: postgres12
host: REPLACE_ME.postgres.database.azure.com
port: 5432
database: temporal
# The Postgres role created for the managed identity via
# pgaadauth_create_principal (usually the identity's display name).
user: temporal-identity
tls: { enabled: true }
visibilityStore:
sql:
pluginName: postgres12
host: REPLACE_ME.postgres.database.azure.com
port: 5432
database: temporal_visibility
user: temporal-identity
tls: { enabled: true }