Part 2 — Passwordless Connection with Serice Connector for Java on Azure

Jay Lee
6 min readOct 18, 2022

Part 1 — Using Service Connector with Java on Azure is to build up for introducing this new feature, Passwordless Connection. It literally means you can connect to backing services without using a password. If you remember, all the approaches so far use passwords in one way or another to connect to the backing services. It is just a matter of where the password comes into the picture, like environment variable or property source directly from Key Vault or Azure App Configuration. Passwordless Connection is to eliminate password usage in the application completely.

The name itself might mislead you to believe some cutting-edge technology is involved, but that is not the case. To be fair, using managed identity to connect PostgreSQL without a password has existed for quite some time. What’s new is the Service Connector (Microsoft.ServiceLinker) and enhanced Java SDK that makes the entire integration seamless. The identity client for PostgreSQL invokes MSI to get an access token and hand it over to JDBC Driver so it can authenticate by using the access token as a password.

Passwordless Connection

Today, Passwordless Connection is supported for the following services on Azure.

  • Azure Database for MySQL
  • Azure Database for PostgreSQL
  • Azure SQL Database
  • Azure Event Hub for Kafka

Let’s test Passwordless Connection with our previous setup in Part 1.

Passwordless Connection with Service Connector on Azure Container Apps

Initially, it requires a bit of setup, and you can find a detailed guide here. Make sure that you’re using the latest version of az CLI. Here is the version I’m using for this article.

$ az version
"azure-cli": "2.41.0",
"azure-cli-core": "2.41.0",
"azure-cli-telemetry": "1.0.8",
"extensions": {
"aks-preview": "0.5.108",
"connectedk8s": "1.3.4",
"containerapp": "0.3.13",
"k8s-extension": "1.3.5",
"log-analytics": "0.2.2",
"spring": "1.1.10",
"spring-cloud": "3.1.5"

Let’s start with PostgreSQL. We need to assign Azure AD admin to the PostgreSQL server. There will be only one admin per each PostgreSQL server.

$ az postgres server ad-admin create \
--resource-group sandbox-rg \
--server-name postgres-jay \
--display-name ***@***.com \
--object-id ***

Next, we will create a Service Connector using System managed identity. At the time of writing, creating Service Connector with managed identity for Spring Boot can only be done with az CLI.

$ az containerapp connection create postgres \
--resource-group ***-rg \
--name postgres-sc \
--container postgres-sc \
--target-resource-group ***-rg \
--server *** \
--database postgres \
--client-type springBoot \
Connection name is not specified, use generated one: --connection postgres_bg3hd
Checking if Container App enables System Identity...
Connecting to database...
Adding new AAD user aad_postgres_bg3hd to database...

I can see from PostgreSQL that Service Connector indeed created the AAD user. This new user is created by ad-admin, which we created in the previous step.

AAD User aad_postgres_bg3hd

Additionally, Service Connector set the Spring Boot properties.


Service Connector is ready, and now we need to modify our sample app so that it can leverage the managed identity-based Service Connector. Azure SDK provides a new library as below.


That’s easy. Deploy a sample app again and go to Log Stream to see if it’s working.

Log stream shows app is using Managed Identity

Passwordless Connection on AKS

Technically, Passwordless Connection should work fine on AKS today as it’s using Managed Identity under the hood. As of today, Passwordless Connection works with Pod Identity, but it doesn’t with AAD Workload Identity. Let me leave the basic setup here, just in case you want to try it.

  1. Create AAD user on PostgreSQL

Service Connector support is missing on AKS, so this step should be done manually.

$ cat << EOF > create_ad_user.sql
SET aad_validate_oids_in_tenant = off;
GRANT ALL PRIVILEGES ON DATABASE postgres TO "aks_passwordless";

2. deployment.yml

apiVersion: apps/v1
kind: Deployment
name: postgres-sc
app: postgres-sc
replicas: 1
name: postgres-sc
aadpodidbinding: jay-managedidentity
app: postgres-sc
- name: postgres-sc
image: eggboy/postgres-sc:0.0.4
imagePullPolicy: Always
- name: spring.datasource.url
value: "jdbc:postgresql://***"
- name: spring.datasource.username
value: "aks_passwordless@***"
restartPolicy: Always
app: postgres-sc

3. Check the logs

$ kubectl logs [pod name]
2022-10-18 15:26:24.700 INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2022-10-18 15:26:26.660 INFO 1 --- [onPool-worker-1] c.a.identity.ManagedIdentityCredential : Azure Identity => Managed Identity environment: AZURE VM IMDS ENDPOINT
2022-10-18 15:26:26.661 INFO 1 --- [onPool-worker-1] c.a.identity.ManagedIdentityCredential : Azure Identity => getToken() result for scopes []: SUCCESS
2022-10-18 15:26:27.184 INFO 1 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2022-10-18 15:26:27.232 INFO 1 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2022-10-18 15:26:27.279 INFO 1 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.6.11.Final
2022-10-18 15:26:27.455 INFO 1 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2022-10-18 15:26:27.562 INFO 1 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL10Dialect
2022-10-18 15:26:27.737 INFO 1 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2022-10-18 15:26:27.744 INFO 1 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2022-10-18 15:26:27.748 INFO 1 --- [onPool-worker-1] c.m.a.msal4j.AcquireTokenSilentSupplier : Returning token from cache
2022-10-18 15:26:27.749 INFO 1 --- [onPool-worker-1] c.a.identity.ManagedIdentityCredential : Azure Identity => Managed Identity environment: AZURE VM IMDS ENDPOINT
2022-10-18 15:26:27.749 INFO 1 --- [onPool-worker-1] c.a.identity.ManagedIdentityCredential : Azure Identity => getToken() result for scopes []: SUCCESS
2022-10-18 15:26:27.782 WARN 1 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure to disable this warning
2022-10-18 15:26:28.025 INFO 1 --- [ main] AbstractAzureServiceClientBuilderFactory : Will configure the default credential of type DefaultAzureCredential for class
2022-10-18 15:26:28.305 INFO 1 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 13 endpoint(s) beneath base path '/actuator'
2022-10-18 15:26:28.348 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2022-10-18 15:26:28.365 INFO 1 --- [ main] .s.p.PostgresServiceconnectorApplication : Started PostgresServiceconnectorApplication in 6.507 seconds (JVM running for 6.987)


Wrapping Up

Passwordless Connection with Service Connector has enough potential to replace all the previous practices once it becomes GA. Especially since it doesn’t require any code change, I don’t see any reason not to use it for the production system. I’m looking forward to seeing the support for AAD Workload Identity soon.

If you liked my article, please leave a few claps or start following me. You can get notified whenever I publish something new. Let’s stay connected on Linkedin, too! Thanks so much for reading!



Jay Lee

Cloud Native Enthusiast. Java, Spring, Python, Golang, Kubernetes.