PG::AzureWorkloadIdentity
PG::AzureWorkloadIdentity is a plugin for the pg gem that adds support for Microsoft Entra ID authentication when connecting to Azure Database for PostgreSQL flexible server, using Microsoft Entra Workload Identity — typically as projected into pods by AKS Workload Identity.
Entra ID authentication lets your application connect to the database using short-lived access tokens instead of a fixed password. Combined with Workload Identity, your pods never carry a long-lived secret at all: the Kubernetes service account JWT is automatically exchanged for an Entra access token via federated credentials, and that token is used as the database password. No client secrets, no certificate rotation, no password storage.
Installation
Install manually:
$ gem install pg-azure_workload_identity
or with Bundler:
$ bundle add pg-azure_workload_identity
Usage
To use Workload Identity authentication for your database connections, you need to
- enable Microsoft Entra authentication on your Azure Database for PostgreSQL flexible server,
- provide your application with a Workload Identity, and
- configure your application to use the gem's token generator.
1. Enable Microsoft Entra authentication on your database
Configure your Azure Database for PostgreSQL flexible server to allow Microsoft Entra authentication, either during server provisioning or on an existing server through the Security → Authentication pane. See Use Microsoft Entra ID for authentication with Azure Database for PostgreSQL for the step-by-step instructions.
Choose between Microsoft Entra authentication only (no password-based logins) and PostgreSQL and Microsoft Entra authentication (both modes available), then add at least one Microsoft Entra administrator. Only an Entra administrator can subsequently create additional Entra-mapped database roles.
Connect to the database as an Entra administrator and create a role for your application's identity. For a user-assigned managed identity, use its object (principal) ID:
select * from pgaadauth_create_principal_with_oid('my-app', '<managed-identity-object-id>', 'service', false, false);
Grant the role whatever privileges your application needs.
2. Provide your application with a Workload Identity
The recommended way to provide a credential to a pod in AKS is Microsoft Entra Workload ID. It works by federating a Kubernetes service account with an Entra ID identity, so a JWT projected into the pod can be exchanged for an Entra access token without ever sharing a long-lived secret.
The setup steps are (see Deploy and configure an AKS cluster with Workload ID for the full walkthrough):
- Enable the OIDC issuer and Workload Identity add-on on your AKS cluster.
- Create a user-assigned managed identity (or use an existing one).
- Create a federated identity credential on the managed identity, with the cluster's OIDC issuer as the
issuer,api://AzureADTokenExchangeas theaudience, andsystem:serviceaccount:<namespace>:<service-account-name>as thesubject. - Create a Kubernetes service account annotated with
azure.workload.identity/client-id: <managed-identity-client-id>and use it for your application's pods. - Add
azure.workload.identity/use: "true"to your pod template's labels.
Once that's in place, the Workload Identity mutating webhook automatically injects the following environment variables into your pod, which the gem uses to acquire access tokens:
| Variable | Provided by |
|---|---|
AZURE_TENANT_ID |
Workload Identity webhook |
AZURE_CLIENT_ID |
Workload Identity webhook (from the service-account annotation) |
AZURE_FEDERATED_TOKEN_FILE |
Workload Identity webhook (path to the projected service-account JWT) |
3. Configure your application to use the workload-identity token generator
Set the non-standard azure_workload_identity connection parameter to a truthy value (e.g. true). When the gem sees this parameter on a connection, it acquires an Entra access token using the environment variables above and injects it as the database password before handing the connection string to libpq.
You can set this parameter in
- the query string of a connection URI:
postgresql://my-app@my-server.postgres.database.azure.com:5432/appdb?sslmode=require&azure_workload_identity=true
- a
key=valuepair in a connection string:
user=my-app host=my-server.postgres.database.azure.com port=5432 dbname=appdb sslmode=require azure_workload_identity=true
- a
key: valuepair in a connection hash:
PG.connect(
user: "my-app",
host: "my-server.postgres.database.azure.com",
port: 5432,
dbname: "appdb",
sslmode: "require",
azure_workload_identity: "true"
)
database.yml, if you're using Rails:
production:
adapter: postgresql
username: my-app
host: my-server.postgres.database.azure.com
port: 5432
database: appdb
sslmode: require
azure_workload_identity: true
The gem caches access tokens in memory for their reported lifetime (with a small refresh threshold), so concurrent and back-to-back connections share a single token exchange. Tokens are refreshed automatically on demand before they expire.
If the defaults don't match your environment (for example, you're using a sovereign cloud or a non-OSS-RDBMS scope), you can install a custom token generator at boot:
PG::AzureWorkloadIdentity.auth_token_generator = PG::AzureWorkloadIdentity::AuthTokenGenerator.new(
identity_endpoint: "https://login.microsoftonline.us/<tenant-id>/oauth2/v2.0/token",
client_id: "<client-id>",
scope: "https://ossrdbms-aad.database.usgovcloudapi.net/.default",
grant_type: "client_credentials",
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
federated_token_file: "/var/run/secrets/azure/tokens/azure-identity-token"
)
The object you assign just needs to respond to #call and return a string token, so you can swap in an entirely different implementation when needed.
4. Set sslmode to verify-full (recommended)
Although Azure Database for PostgreSQL flexible server requires TLS by default (sslmode=require), the strongest setting is sslmode=verify-full — this also verifies the server's certificate chain and hostname, preventing man-in-the-middle attacks. You'll need to point sslrootcert at the Azure-issued certificate bundle; see TLS certificates for Azure Database for PostgreSQL flexible server for the current chain and download instructions. Note that this might no be possible when using private endpoints with entries in private DNS zones.
Development
After checking out the repo, run bundle install to install dependencies.
Then, run bundle exec rake to run the linter and unit tests.
The acceptance suite spins up a local PostgreSQL container via Docker Compose and exercises the full connection flow (with WebMock stubbing the Entra token endpoint) — run it with:
$ bundle exec rake test:acceptance
To release a new version:
- Update the version number in version.rb, and run
bundle installto update Gemfile.lock. - Update CHANGELOG.md.
- Submit the changes as a pull request.
- Once merged, tag the release and push the tag to GitHub.
Contributing
Bug reports and pull requests are welcome on GitHub.
Credits
This gem is built on the design and structure of pg-aws_rds_iam which provides the equivalent functionality for AWS RDS IAM authentication.
License
The gem is available as open source under the terms of the MIT License.