I would like to replace spring.datasource.password with a password from azure keyvault.
I have a project based on spring boot:
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.1</version>
I am using dependencies :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.13.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-keyvault</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-keyvault-secrets</artifactId>
</dependency>
I have created the config:
@Configuration
public class KeyVaultConfig {
@Bean
SecretClient secretClient(
@Value("${spring.cloud.azure.keyvault.secret.endpoint}") String keyVaultUri,
@Value("${azure.client.id}") String clientId,
@Value("${azure.client.secret}") String clientSecret,
@Value("${azure.tenant.id}") String tenantId) {
ClientSecretCredential credential = new ClientSecretCredentialBuilder()
.clientId(clientId)
.clientSecret(clientSecret)
.tenantId(tenantId)
.build();
return new SecretClientBuilder()
.vaultUrl(keyVaultUri)
.credential(credential)
.buildClient();
}
}
This config works fine when I do not want to inject properties. For instance this code:
@GetMapping("/status")
public String status() {
log.info("generating status page");
return "read from keyvault: " + keyVaultService.getSecret("psur-db-url");
}
works just fine. So this proves that the credentials are set correctly.
But when I try to use this setup with combination of properties:
spring.cloud.azure.compatibility-verifier.enabled=false
spring.cloud.azure.keyvault.secret.property-source-enabled=true
spring.datasource.url={my-secret-db-url}
spring.datasource.username=user
spring.datasource.password={my-secret-db-password}
I have tried different configurations in properties as well as configuring the ClientSecretCredential in different way. But I never managed the properties to be injected. Currently I am getting exception
Caused by: java.lang.IllegalArgumentException: URL must start with 'jdbc'
which suggest that the url was never replaced. I am suspecting that the SecretClient is not created with my config, because when I put a breakpoint there it is never reached. But this is just a guess. Am I missing something in my config? Or maybe the configuration is wrong?
The solution which currently works for me is:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.13.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-keyvault</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
I have removed the dependency to spring-cloud-azure-starter-keyvault-secrets.
I have entirely removed the KeyVaultConfig class. I am using the auto configred SecretClient (according to https://learn.microsoft.com/en-us/azure/developer/java/sdk/identity-azure-hosted-auth#default-azure-credential). I have setup environment variables in my Intellij according to (https://learn.microsoft.com/en-us/azure/developer/java/sdk/identity-azure-hosted-auth#environment-variables)
AZURE_CLIENT_ID ID of a Microsoft Entra application.
AZURE_TENANT_ID ID of the application's Microsoft Entra tenant.
AZURE_CLIENT_SECRET One of the application's client secrets.
spring.cloud.azure.keyvault.secret.property-sources[0].endpoint=https://dev-my-kv.vault.azure.net/
spring.datasource.url=${my-secret-db-url}
spring.datasource.username=user
spring.datasource.password=${my-secret-db-password}
Although this works I would prefere not to need to setup the environment variables so I am still looking for a way to create my own SecurityClient, inject into the context and reuse while connecting to keyvault.
Solution nr 2:
I am using same pom.xml as in Solution nr 1, as well I do not define my own SecretClient. I am just setting the client, secret and tenant in properties file:
spring.cloud.azure.credential.client-id=###
spring.cloud.azure.credential.client-secret=###
spring.cloud.azure.profile.tenant-id=###
spring.cloud.azure.keyvault.secret.property-sources[0].endpoint=https://dev-my-kv.vault.azure.net/
...
they will be used to create ClientSecretCredential which will be injected into SecretClient.