I have a Java Spring boot application that just reads the secret from Azure Key Vault, below are steps used
and below is my Java Spring Boot application
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.contoso</groupId>
<artifactId>keyvault</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>keyvault</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
<azure.version>2.3.5</azure.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-keyvault-secrets-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-spring-boot-bom</artifactId>
<version>${azure.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
azure.keyvault.client-id=7a111923-xxxxxxxx-xxxxxx-31be31d233dd
azure.keyvault.client-key=gt_~k02yF_xxxxxx_vn3r1.GW
azure.keyvault.enabled=true
azure.keyvault.tenant-id=9cef136a-xxxx-xxxx-b7d3-d9d8a5a84182
azure.keyvault.uri=https://contosokvxxx.vault.azure.net/
KeyvaultApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;
@SpringBootApplication
@RestController
public class KeyvaultApplication {
public static void main(String[] args) {
SpringApplication.run(KeyvaultApplication.class, args);
}
@GetMapping("get")
public String get() {
return connectionString;
}
@Value("${connectionString}")
private String connectionString;
public void run(String... varl) throws Exception {
System.out.println(String.format("\nConnection String stored in Azure Key Vault:\n%s\n",connectionString));
}
}
It works and I could read the Secret from Azure Key Vault
so decided to remove the secrets from the application.properties and commented as below
#azure.keyvault.client-id=7a111923-1xxxxxxxx-31be31d233dd
#azure.keyvault.client-key=gt_~k02yF_xxxxxxxx-Hr6vn3r1.GW
azure.keyvault.enabled=true
#azure.keyvault.tenant-id=9cef136axxxxxxx-3-d9d8a5a84182
azure.keyvault.uri=https://contosokvxxx.vault.azure.net/
and added an Environment variable as shown below using setx
setx AzureServicesAuthConnectionString "RunAs=App;AppId=bb01c08b-xxxxxxxx-106;TenantId=9cef1-xxxxxxx-d9d8a5a84182;AppKey=xxxxxxxx-4Dpg-E3zrj~"
The project compiles without any isssues
>mvn clean compile package
but failed running
>mvn spring-boot:run
Error:
11:40:27.334 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
java.lang.IllegalStateException: Failed to configure KeyVault property source
at com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessorHelper.addKeyVaultPropertySource(KeyVaultEnvironmentPostProcessorHelper.java:110)
at com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessor.postProcessEnvironment(KeyVaultEnvironmentPostProcessor.java:47)
Caused by: java.lang.RuntimeException: Max retries 3 times exceeded. Error Details: ManagedIdentityCredential authentication unavailable. Connection to IMDS endpoint cannot be established, Network is unreachable: no further information.
at com.azure.core.http.policy.RetryPolicy.lambda$attemptAsync$1(RetryPolicy.java:116)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:88)
Error Details: ManagedIdentityCredential authentication unavailable. Connection to IMDS endpoint cannot be established, Network is unreachable: no further information.
The error means you could not connect to the Azure Instance Metadata Service endpoint, it is a REST Endpoint that is available at a well-known non-routable IP address (169.254.169.254
), MSI use it to get the token, it can be accessed only from within the Azure service e.g. web app, VM, etc. Simply put, you could not use MSI(managed identity) in local.
To use MSI get secret from the azure keyvault, follow this to deploy your application to azure web app, enable the system-assigned identity or user-assigned identity, then remove the azure.keyvault.client-key
from application.properties
, change the azure.keyvault.client-id
with the MSI's client id, add it to the access policy of the keyvault, details follow this.
azure.keyvault.client-id=56rqs994-0o66-43o3-9roo-8e3534d0cb23
azure.keyvault.enabled=true
azure.keyvault.tenant-id=72s988os-86s1-41ns-91ab-2q7pq011qo47
azure.keyvault.uri=https://contosokv.vault.azure.net/
Reference - Tutorial: Reading a secret from Azure Key Vault in a Spring Boot application