Securely Store Application Secrets in Azure Key Vault
This guide describes how to use the Graal Development Kit for Micronaut (GDK) to create an application that stores MySQL database connection details as secrets in an Azure Key Vault.
Instead of storing a database URL, username, or password in plain text or via environment variables, a secret manager such as Azure Key Vault provides a convenient way to securely store and retrieve sensitive data.
This guide describes how to use Azure Key Vault to store and retrieve details for a MySQL database connection, and demonstrates how to use Micronaut® integration with Azure Key Vault.
Prerequisites
-
JDK 17 or higher. See Setting up Your Desktop.
-
An Azure account. See Setting up Your Cloud Accounts.
-
The Azure CLI. Follow the Azure documentation for installing or updating the latest version of the Azure CLI.
-
An Azure user with sufficient permissions to create and manage Azure Key Vault secrets.
-
The GDK CLI. See Setting up Your Desktop. (Optional.)
Follow the steps below to create the application from scratch. However, you can also download the completed example:
The application ZIP file will be downloaded in your default downloads directory. Unzip it and proceed to the next steps.
A note regarding your development environment
Consider using Visual Studio Code, which provides native support for developing applications with the Graal Development Kit for Micronaut Extension Pack.
Note: If you use IntelliJ IDEA, enable annotation processing.
Windows platform: The GDK guides are compatible with Gradle only. Maven support is coming soon.
1. Create the Application
Create an application using the GDK Launcher.
-
Open the GDK Launcher in advanced mode.
- Create a new project using the following selections.
- Project Type: Application (Default)
- Project Name: azure-secrets-demo
- Base Package: com.example (Default)
- Clouds: Azure
- Build Tool: Gradle (Groovy) or Maven
- Language: Java (Default)
- Test Framework: JUnit (Default)
- Java Version: 17 (Default)
- Micronaut Version: (Default)
- Cloud Services: Secret Management, Database
- Features: GraalVM Native Image (Default)
- Sample Code: Yes (Default)
- Click Generate Project, then click Download Zip. The GDK Launcher creates an application with the package
com.example
in a directory named azure-secrets-demo. The application ZIP file will be downloaded to your default downloads directory. Unzip it, open it in your code editor, and proceed to the next steps.
Alternatively, use the GDK CLI as follows:
gdk create-app com.example.azure-secrets-demo \
--clouds=azure \
--services=secretmanagement,database \
--features=graalvm \
--build=gradle \
--jdk=17 \
--lang=java
gdk create-app com.example.azure-secrets-demo \
--clouds=azure \
--services=secretmanagement,database \
--features=graalvm \
--build=maven \
--jdk=17 \
--lang=java
Open the micronaut-cli.yml file, you can see what features are packaged with the application:
features: [app-name, azure-key-vault, data, data-jdbc, discovery-client, flyway, gdk-azure-cloud-app, gdk-azure-database, gdk-azure-secret-management, gdk-bom, gdk-license, graalvm, http-client, java, java-application, jdbc-hikari, junit, logback, maven, maven-enforcer-plugin, micronaut-http-validation, mysql, netty-server, properties, readme, serialization-jackson, shade, static-resources, test-resources, validation]
The GDK Launcher creates a multi-module project with two subprojects: azure for Microsoft Azure, and lib for common code and configuration shared across cloud platforms. You develop the application logic in the lib subproject, and keep the Microsoft Azure-specific configurations in the azure subproject.
2. Create Azure Cloud Resources
First define the environment variables:
export LOCATION=eastus
export RESOURCE_GROUP_NAME=gdkguides
set LOCATION=eastus
set RESOURCE_GROUP_NAME=gdkguides
$ENV LOCATION = "eastus"
$ENV RESOURCE_GROUP_NAME = "gdkguides"
2.1. Create a Resource Group
Some of the following commands use
jq
, which is a lightweight and flexible command-line JSON processor.
We recommend that you create a new resource group for this guide, but you can use an existing resource group instead.
Run the az group create command to create a resource group named gdkguides in the eastus region:
az group create --location $LOCATION --name $RESOURCE_GROUP_NAME
If you prefer using the region geographically closer to you, run az account list-locations
to list all available regions.
2.2. Create an Azure MySQL Database Instance
Create a MySQL database using the Create and Connect a Micronaut Application to an Azure Database for MySQL guide. Follow the steps in 2 (Create an Azure MySQL Database Instance).
2.3. Create an Azure Key Vault
Run the az keyvault create
command to create an Azure Key Vault:
az keyvault create \
--name gdkvault \
--resource-group $RESOURCE_GROUP_NAME \
--location $LOCATION \
--retention-days 7
The response should look like this:
{
"id": "/subscriptions/fe053d95...",
"location": "eastus",
"name": "gdkvault",
"properties": {
"accessPolicies": [],
...
"vaultUri": "https://gdkvault.vault.azure.net/"
},
"resourceGroup": "gdkguides",
...
"type": "Microsoft.KeyVault/vaults"
}
Save the value of the vaultUri attribute from the properties section in the response. This is your Vault URL, and it will be needed later.
2.4. Authorize Creating and Reading Secrets
Authorize creating and reading secrets by assigning the Key Vault Secrets Officer role to yourself:
az role assignment create \
--role "Key Vault Secrets Officer" \
--assignee <email> \
--scope "/subscriptions/<subscription-id>/resourceGroups/gdkguides/providers/Microsoft.KeyVault/vaults/gdkvault"
Replace <email> with the email address associated with your account, and <subscription-id> with your Azure Subscription ID.
Note that it can take a few minutes for the role grant to propagate.
2.5. Create Secrets
In this guide you store values for the database username, password, and URL in a Vault by creating secrets for the properties named "datasources-default-username", "datasources-default-password", and "datasources-default-url", which correspond to the datasources.default.username, datasources.default.password, and datasources.default.url configuration properties, respectively.
Azure Key Vault does not allow
_
or.
characters in the names of secrets, so replace the.
characters in the property names with-
, and Micronaut will convert them when resolving the values.
2.5.1. Create a Secret named "datasources-default-username"
The first secret is for the database username, named "datasources-default-username".
Run the az keyvault secret set
command to create the secret:
az keyvault secret set \
--vault-name gdkvault \
--name datasources-default-username \
--value azuredb_user
2.5.2. Create a Secret named "datasources-default-password"
Create a second secret with the name "datasources-default-password":
az keyvault secret set \
--vault-name gdkvault \
--name datasources-default-password \
--value M1cr0n4ut!
2.5.3. Create Secret named "datasources-default-url"
Create a third secret with the name "datasources-default-url":
az keyvault secret set \
--vault-name gdkvault \
--name datasources-default-url \
--value jdbc:mysql://gdkmysql.mysql.database.azure.com:3306/azuredb
3. Use a Vault with Micronaut
Micronaut Azure provides integration between Micronaut applications and Azure services, including using Vault as a distributed configuration source. The GDK Launcher added the appropriate dependencies to your build file when you selected the Database and Secret Management services in the GDK Launcher.
3.1. Modify the Micronaut Configuration
-
The Micronaut bootstrap configuration file contains the properties to resolve values from Azure Key Vault.
Modify the file named azure/src/main/resources/bootstrap.properties so that its contents matches the following:
micronaut.application.name=azure-secrets-demo-azure micronaut.config-client.enabled=true (1) azure.key-vault.vault-url=https://gdkvault.vault.azure.net/
1 Set the value of the
azure.key-vault.vault-url
property with the Vault URL you saved when you created the vault. -
The Micronaut application configuration file contains the properties for your database connection. Modify the file named azure/src/main/resources/application.properties so that its contents matches the following:
datasources.default.db-type=mysql datasources.default.dialect=MYSQL datasources.default.driver-class-name=com.mysql.cj.jdbc.Driver flyway.datasources.default.enabled=true
4. Start the Application
To run the application, use the following command, which starts the application on port 8080.
Run this command to test that you can create and store a new Genre
in the database:
curl -X "POST" "http://localhost:8080/genres" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{ "name": "music" }'
Then list the genres:
curl localhost:8080/genres/list
The response should look like this:
{"id":1,"name":"music"}
5. Generate a Native Executable Using GraalVM
The GDK supports compiling Java applications ahead-of-time into native executables using GraalVM Native Image. You can use the Gradle plugin for GraalVM Native Image building/Maven plugin for GraalVM Native Image building. Packaged as a native executable, it significantly reduces application startup time and memory footprint.
Prerequisites: Make sure you have installed a GraalVM JDK. The easiest way to get started is with SDKMAN!. For other installation options, visit the Downloads section.
-
To generate a native executable, use the following command:
-
You can then run the native executable with the following command:
azure/build/native/nativeCompile/azure-secrets-demo-azure
azure/target/azure-secrets-demo-azure
You can customize the name of the resulting binary by updating the Maven/Gradle plugin for GraalVM Native Image configuration.
The application starts on port 8080. Test it as described in the previous section. The application should behave identically as if you run it from a JAR file, but with reduced startup time and smaller memory footprint.
6. Clean up Cloud Resources
Once you are done with this guide, you can delete the Azure resources created to avoid incurring unnecessary charges.
Delete the resource group and all of its resources with:
az group delete --name $RESOURCE_GROUP_NAME
Alternatively, run these commands to delete resources individually:
az keyvault secret delete --name "datasources-default-username" --vault-name gdkvault
az keyvault secret delete --name "datasources-default-password" --vault-name gdkvault
az keyvault secret delete --name "datasources-default-url" --vault-name gdkvault
az keyvault delete --name gdkvault --resource-group $RESOURCE_GROUP_NAME
az mysql flexible-server delete --name gdkmysql --resource-group $RESOURCE_GROUP_NAME
az group delete --name $RESOURCE_GROUP_NAME
Summary
This guide demonstrated how to create a Java application that stores MySQL database connection details as secrets in an Azure Key Vault. You also saw how to package this application into a native executable.