This tutorial will walk through how to to use Key Vault references in our Java application without modifying our data source example app or adding any dependencies. Using Key Vault to store our secrets (such as database connection strings) give us a centralized storage location with management tools like role-based access control, secrets rotation, and encryption.
This tutorial continues from the data source configuration example. You will need to complete that tutorial before doing this one.
A system-assigned identity is tied to your application and is deleted if your app is deleted. An app can only have one system-assigned identity. System-assigned identity support is generally available for Windows apps.
az webapp identity assign --name <app_name> --resource-group <resource_group_of_app>
Save the principalId
-
Let's spin up a Key Vault named
java-app-key-vault
. For information about the parameters specified below, runaz keyvault create --help
.az keyvault create --name java-app-key-vault \ --resource-group <your_resource_group> \ --location <location> \ --enabled-for-deployment true \ --enabled-for-disk-encryption true \ --enabled-for-template-deployment true \ --sku standard
-
Give the System assigned identity
get
andlist
access to the Key Vaultaz keyvault set-policy --name java-app-key-vault \ --secret-permission get list \ --object-id <the principal ID from earlier>
-
Now we will add the Postgres username, password, and URL to the Key Vault. If you followed the tutorial on data sources, you should still have the secrets saved as environment variables on your machine. (If you are using Powershell, use the
$env:ENV_VAR
syntax to inject the environment variable into the command).az keyvault secret set --name POSTGRES-USERNAME \ --value $POSTGRES_USERNAME \ --vault-name java-app-key-vault az keyvault secret set --name POSTGRES-PASSWORD \ --value $POSTGRES_PASSWORD \ --vault-name java-app-key-vault az keyvault secret set --name POSTGRES-URL \ --value $POSTGRES_URL \ --vault-name java-app-key-vault
Our Spring app will be able to access these secrets when deployed on App Service through Application Settings. We will now configure the App Service plugin to set the Application Settings when we deploy the app.
-
First, we need the URI's of our three secrets. Run the commands below and copy the
id
value in each.az keyvault secret show --vault-name java-app-key-vault --name POSTGRES-URL az keyvault secret show --vault-name java-app-key-vault --name POSTGRES-USERNAME az keyvault secret show --vault-name java-app-key-vault --name POSTGRES-PASSWORD
-
Add the following to the Azure App Service plugin section of the
pom.xml
. Adding this configuration will create Application Settings with the given name and value. The value of each setting should be a Key Vault reference with the correspondingid
from the previous step.<appSettings> <property> <name>SPRING_DATASOURCE_URL</name> <value>@Microsoft.KeyVault(SecretUri=YOUR_SECRET_UIR)</value> </property> <property> <name>SPRING_DATASOURCE_USERNAME</name> <value>@Microsoft.KeyVault(SecretUri=YOUR_SECRET_UIR)</value> </property> <property> <name>SPRING_DATASOURCE_PASSWORD</name> <value>@Microsoft.KeyVault(SecretUri=YOUR_SECRET_UIR)</value> </property> </appSettings>
A Key Vault reference is of the form
@Microsoft.KeyVault(SecretUri=<SecretURI>)
, where<SecretURI>
is data-plane URI of a secret in Key Vault, including a version. There is an alternate syntax documented here.
The Key Vault references will be replaced with the actual secrets when our App Service boots up. This means our Spring Application needs to resolve the connection strings at runtime, but currently gets these strings at build time. We also want to be able to use our H2 database for development, and optionally connect to the production DB from our local machine to run tests. To fill all these requirements, we will create two new configuration files: application-dev.properties
, and application-prod.properties
.
-
Create a file under
src/main/resources
namedapplication-dev.properties
. Copy/paste the following into the file:# =============================== # = DATA SOURCE # =============================== # Set here configurations for the database connection spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.username=sa spring.datasource.password= spring.datasource.driver-class-name=org.h2.Driver # =============================== # = JPA / HIBERNATE # =============================== # Allows Hibernate to generate SQL optimized for a particular DBMS spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect # App Service server.port=8080
-
Create a file under
src/main/resources
namedapplication-dev.properties
. Copy/paste the following into the file. We do not set the connection strings here. Instead, Spring will resolve them at runtime by looking for the uppercase and underscored versions ofspring.datasource.url
,spring.datasource.username
, andspring.datasource.password
.# =============================== # = DATA SOURCE # =============================== # The connection URL, username, and password will be sourced from environment variables # on App Service # Set here configurations for the database connection spring.datasource.driver-class-name=org.postgresql.Driver # =============================== # = JPA / HIBERNATE # =============================== # Allows Hibernate to generate SQL optimized for a particular DBMS spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect # App Service server.port=80
-
Now we can slim-down our original
application.properties
file. Replace the contents ofapplication.properties
with the following.# Active profile is set by Maven spring.profiles.active=@spring.profiles.active@ # =============================== # = DATA SOURCE # =============================== # Keep the connection alive if idle for a long time (needed in production) spring.datasource.testWhileIdle=true spring.datasource.validationQuery=SELECT 1 # =============================== # = JPA / HIBERNATE # =============================== # Show or not log for each sql query spring.jpa.show-sql=true # Hibernate ddl auto (create, create-drop, update): with "create-drop" the database # schema will be automatically created afresh for every start of application spring.jpa.hibernate.ddl-auto=create # Naming strategy spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
-
Finally, we can also slim down our Maven profiles because we have moved th information to the new properties files. The profile section of your
pom.xml
should now be the following:<profiles> <profile> <!-- This profile will configure Spring to use an in-memory database for local development and testing. --> <id>dev</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <spring.profiles.active>dev</spring.profiles.active> </properties> </profile> <profile> <!-- This profile will configure the application to use our Azure PostgreSQL server. --> <id>prod</id> <properties> <spring.profiles.active>prod</spring.profiles.active> </properties> </profile> </profiles>
Check that the development profile works as expected by running the following commands and opening a browser to http://localhost:8080/
.
mvn clean package -Pdev
java -jar target/app.jar
Before deploying to App Service, you can build your application with the production profile and test against your PostgreSQL DB from your local machine. To do so, simply rename the three environment variables beginning with POSTGRES_
and rename them to SPRING_DATASOURCE_URL
, SPRING_DATASOURCE_USERNAME
, and SPRING_DATASOURCE_PASSWORD
respectively. Run the following commands to build and start your app. Spring will resolve the connection strings in the environment variables at runtime.
mvn clean package -Pprod
java -jar target/app.jar
If you did not rename your local environment variables, skip the tests with
mvn clean package -Pprod -DskipTests
Finally, deploy the production app to App Service with mvn azure-webapp:deploy
. Browse to the application and test that it works properly.
- Azure SDK