1. Overview

In this tutorial, we’ll explore the fundamental principles of cloud-native development and the benefits of using Spring Cloud Azure Key Vault.

2. What Is Spring Cloud Azure?

Spring Cloud Azure is a comprehensive suite of libraries and tools specifically designed to facilitate integration between Spring applications and Microsoft Azure services.

While it’s already possible to integrate Java applications with the Azure SDK, the introduction of Spring Cloud Azure takes this integration to a whole new level.

By leveraging the powerful set of APIs offered by Spring Cloud Azure, we can conveniently interact with various Azure services such as Azure Storage, Cosmos DB, and many others.

It simplifies the development process and enhances the overall security and performance of the application.

Spring Cloud Azure offers several modules for integrating our application with the most relevant Azure services. Let’s see a few examples:

We can find the complete list of available modules here.

3. Project Setup

To start with Azure cloud services, the first step is to sign up for an Azure subscription.

Once the subscription is in place, let’s install the Azure CLI. This command-line interface tool allows us to interact with Azure services from our local machine.

Next, let’s open a command prompt and run the command:

> az login

Once we’re logged in, we create a new resource group for our subscription:

>az group create --name spring_cloud_azure --location eastus

In addition to creating a resource group via the command line, we can create a new subscription using the Azure portal in our web browser. This provides an intuitive interface for managing our Azure resources and subscriptions.

As we move forward, the next step is to configure our IDE. In this tutorial, we’ll use IntelliJ as our chosen IDE.

The Azure Toolkit is a useful kit to employ when working on Azure-related development. It provides tools and resources specifically designed to help developers build and manage applications on the Azure platform.

So let’s install this plugin on our IDE and then go to Tools>Azure>Login. This will prompt us to enter our Azure credentials to authenticate our access to the platform.

4. Integration

We’ve completed the necessary preparations to integrate our Spring Application with an Azure Service.

In this tutorial, we’ll integrate the Azure Key Vault service into our application by utilizing the official Azure SDK for Java and the dedicated Spring Cloud module.

4.1. Azure Key Vault

Azure Key Vault is a robust cloud-based service that provides a secure and reliable way to store and manage sensitive data, including cryptographic keys, secrets, and certificates.

It can act as an external configuration source for applications. Instead of defining sensitive information as values in a configuration file, we can define them as secrets in Azure Key Vault and then securely inject them into the application at runtime.

To start, we need to create a new Key Vault on the resource group that we previously created. We can use the Azure CLI to do this, but we can also use the Azure portal if we prefer:

> az keyvault create --name new_keyvault --resource-group spring_cloud_azure --location eastus

After creating the Key Vault storage, let’s create two secrets in the Key Vault storage new_keyvault:

> az keyvault secret set --name my-database-secret --value my-database-secret-value --vault-name new_keyvault
> az keyvault secret set --name my-secret --value my-secret-value --vault-name new_keyvault

The first secret has the key my-database-secret and the value my-database-secret-value, while the second has the key my-secret and the value my-secret-value.

We can also check and confirm the creation of these secrets on the Azure portal:url azure key vault 2

4.2. Secret Client

Once we have defined these secrets, we can define SecretClient on our application.

SecretClient class provides a client-side interface for retrieving and managing secrets from Azure Key Vault.

So let’s define an interface:

public interface KeyVaultClient {

    SecretClient getSecretClient();

    default KeyVaultSecret getSecret(String key) {
        KeyVaultSecret secret;
        try {
            secret = getSecretClient().getSecret(key);
        } catch (Exception ex) {
            throw new NoSuchElementException(String.format("Unable to retrieve %s secret", key), ex);
        }
        return secret;
    }
}

The interface KeyVaultClient declares two methods:

  • The first method, getSecretClient(), returns an instance of the SecretClient class.
  • The second method, getSecret(), provides a default implementation for retrieving a specific secret nested in the object KeyVaultSecret, from the secret client.

Now let’s see two approaches to defining the SecretClient, one with the standard Azure SDK and the other with the Spring Cloud Module.

4.3. Integration Without Spring Cloud Azure

In this approach, we will configure SecretClient only with the API exposed by Microsoft’s Azure SDK.

So let’s add the azure-keyvault-extensions dependency to our pom.xml file:

<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-keyvault-extensions</artifactId>
    <version>1.2.6</version>
</dependency>

Now let’s define the necessary parameters for configuring the SecretClient in our application.yaml file:

azure:
  keyvault:
    vaultUrl: {$myVaultUrl}
    tenantId: {$myTenantId}
    clientId: {$myClientId}
    clientSecret: {$myClientSecret}

We should replace all the placeholders with the appropriate values.

One option is to hardcode the values directly into application.yaml. However, this approach requires storing multiple sensitive data, including the clientId or clientSecret, which can pose a security risk.

Instead of hardcoding these values, we can create a secret for each sensitive data and inject them into our configuration file using an Azure pipeline.

Next, we create a KeyVaultProperties class to handle this configuration:

@ConfigurationProperties("azure.keyvault")
@ConstructorBinding
public class KeyVaultProperties {
    private String vaultUrl;
    private String tenantId;
    private String clientId;
    private String clientSecret;
    //Standard constructors, getters and setters
}

Now let’s create our client class:

@EnableConfigurationProperties(KeyVaultProperties.class)
@Component("KeyVaultManuallyConfiguredClient")
public class KeyVaultManuallyConfiguredClient implements KeyVaultClient {

    private KeyVaultProperties keyVaultProperties;

    private SecretClient secretClient;

    @Override
    public SecretClient getSecretClient() {
        if (secretClient == null) {
            secretClient = new SecretClientBuilder()
              .vaultUrl(keyVaultProperties.getVaultUrl())
              .credential(new ClientSecretCredentialBuilder()
                .tenantId(keyVaultProperties.getTenantId())
                .clientId(keyVaultProperties.getClientId())
                .clientSecret(keyVaultProperties.getClientSecret())
                .build())
              .buildClient();
        }
        return secretClient;
    }
}

Once we inject this implementation of KeyVaultClient, the getSecret() default method returns the manually configured SecretClient object.

4.4. Integration With Spring Cloud Azure

As part of this approach, we’ll set up the SecretClient with Spring Cloud Azure Key Vault and leverage another useful feature of the framework: injecting the secret into the properties file.

So let’s add the spring-cloud-azure-starter-keyvault-secrets dependency to our pom.xml file:

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-keyvault-secrets</artifactId>
   <version>5.12.0-beta.1</version>
</dependency>

Next, let’s add the following properties to our application.yaml:

spring:
  cloud:
    azure:
      keyvault:
        secret:
          endpoint: {$key-vault-endpoint}

We should replace the key-vault-endpoint placeholder with the URI of our storage, defined in the Azure Portal under Resources > {our keyvault} > Vault URI.

Now let’s create our client class:

@Component("KeyVaultAutoconfiguredClient")
public class KeyVaultAutoconfiguredClient implements KeyVaultClient {
    private final SecretClient secretClient;

    public KeyVaultAutoconfiguredClient(SecretClient secretClient) {
        this.secretClient = secretClient;
    }

    @Override
    public SecretClient getSecretClient() {
        return secretClient;
    }
}

Once we inject this implementation of KeyVaultClient, the getSecret() default method will return the auto-configured SecretClient object. It’s unnecessary to specify any configuration values in our application.yaml, except the endpoint secret.

Spring Cloud will automatically populate all the SecretClient‘s credential parameters.

We can inject the secret with Spring Cloud Azure module also on our properties file. Let’s add on our application.yaml the properties:

spring:
  cloud:
    azure:
      compatibility-verifier:
        enabled: false
      keyvault:
        secret:
          property-sources[0]:
            name: key-vault-property-source-1
            endpoint: https://spring-cloud-azure.vault.azure.net/
          property-source-enabled: true

By setting the flag property-source-enabled, Spring Cloud Azure injects secrets from the Key Vault storage specified in keyvault-secret-property-sources[0].

Next, we can create a dynamic property in our application.yaml:

database:
  secret:
    value: ${my-database-secret}

When the application starts, Spring Cloud Azure replaces the ${my-database-secret} placeholder with the actual value of the secret my-database-secret defined in the Azure Key Vault.

4.5. Inject Secrets on Properties File

We have seen two ways to inject secrets into our properties file: using Spring Cloud Azure Key Vault or configuring an Azure pipeline.

If we use only Spring Cloud Azure Key Vault, we should hardcode the Key Vault endpoint in our application.yaml to enable the injection on our properties file, which can pose a security risk.

On the other hand, with Azure pipelines, there’s no need to hardcode any values. The pipeline will replace secrets in our application.yaml.

Therefore, we should use the Spring Cloud Azure Key Vault module solely for the benefits of the autoconfigured SecretClient and the other features while delegating the injection of secrets into our properties file to the Azure pipelines.

4.6. Run the Application

Now let’s run our Spring Boot Application:

@SpringBootApplication
public class Application implements CommandLineRunner {

    @Value("${database.secret.value}")
    private String mySecret;

    private final KeyVaultClient keyVaultClient;

    public Application(@Qualifier(value = "KeyVaultAutoconfiguredClient") KeyVaultAutoconfiguredClient keyVaultAutoconfiguredClient) {
        this.keyVaultClient = keyVaultAutoconfiguredClient;
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class);

    }

    @Override
    public void run(String... args) throws Exception {
        KeyVaultSecret keyVaultSecret = keyVaultClient.getSecret("my-secret");
        System.out.println("Hey, our secret is here ->" + keyVaultSecret.getValue());
        System.out.println("Hey, our secret is here from application properties file ->" + mySecret);
    }
}

Our application will retrieve the secret from the autoconfigured client and the one injected into the application.yaml upon startup, and then display both on the console.

5. Conclusion

In this article, we have discussed Spring Cloud integration with Azure.

We have learned that integrating Azure Key Vault and Spring applications can be much simpler and more concise using Spring Cloud Azure Key Vault instead of the Azure SDK provided by Microsoft.

As always, the complete source code of the examples can be found on GitHub.