Exercise - Code a Java application that uses secrets

Completed

You're going to code a Java Web application, using Spring Boot, that connects to a database.

For security reasons, you'll need to secure that database access in the future. But first let's create the application infrastructure, and then configure the Java application to use.

Create the application infrastructure

In this exercise, you use Azure CLI to create the following resources:

  • An Azure resource group that contains all the resources for your application.
  • A PostgreSQL database server.
  • An Azure Spring Apps cluster and a Spring Boot application running inside this cluster.

You need to provide some environment variables at the beginning of the script that should be unique across Azure.

You also need to provide your local IP address to access the database from your local computer. This IP address should be an IPv4 Address. If you don't know your local IP address, you can go to the following website: https://www.whatismyip.com/

Set the following environment variables:

AZ_RESOURCE_GROUP=<YOUR_UNIQUE_RESOURCE_GROUP_NAME>
AZ_DATABASE_USERNAME=<YOUR_POSTGRESQL_USERNAME>
AZ_DATABASE_PASSWORD=<YOUR_POSTGRESQL_PASSWORD>
AZ_LOCAL_IP_ADDRESS=<YOUR_LOCAL_IP_ADDRESS>

Once those environment variables are set, you can run the following command to create the resources:

AZ_LOCATION=eastus
# Must be all lowercase
AZ_SPRING_CLOUD=spring-${AZ_RESOURCE_GROUP,,}

AZ_DATABASE_NAME=pgsql-${AZ_RESOURCE_GROUP}
AZ_DATABASE_USERNAME=${AZ_DATABASE_USERNAME}

az group create \
    --name $AZ_RESOURCE_GROUP \
    --location $AZ_LOCATION

This command can take several minutes to complete.

az postgres server create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_NAME \
    --location $AZ_LOCATION \
    --sku-name B_Gen5_1 \
    --storage-size 5120 \
    --admin-user $AZ_DATABASE_USERNAME \
    --admin-password $AZ_DATABASE_PASSWORD
az postgres server firewall-rule create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_NAME-database-allow-local-ip \
    --server $AZ_DATABASE_NAME \
    --start-ip-address $AZ_LOCAL_IP_ADDRESS \
    --end-ip-address $AZ_LOCAL_IP_ADDRESS

az postgres server firewall-rule create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_DATABASE_NAME-database-allow-azure-ip \
    --server $AZ_DATABASE_NAME \
    --start-ip-address 0.0.0.0 \
    --end-ip-address 0.0.0.0
az postgres db create \
    --resource-group $AZ_RESOURCE_GROUP \
    --name demo \
    --server-name $AZ_DATABASE_NAME

This command can take several minutes to complete.

az extension add --name spring

az spring create \
   --name $AZ_SPRING_CLOUD \
   --resource-group $AZ_RESOURCE_GROUP \
   --location $AZ_LOCATION \
   --sku Basic

This command can take several minutes to complete.

az spring app create \
   --resource-group $AZ_RESOURCE_GROUP \
   --service $AZ_SPRING_CLOUD \
   --name application \
   --runtime-version Java_11 \
   --assign-endpoint true

These scripts take some time to run, so you can let them run in the background and start coding the application in the meantime.

Configure the Java application

Get the application skeleton from the https://github.com/Azure-Samples/manage-secrets-in-java-applications GitHub repository, using the git clone command:

git clone https://github.com/Azure-Samples/manage-secrets-in-java-applications.git

This application uses Spring Data JPA to access the database. You can look at the CRUD Repository interface:

package com.example.demo;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ItemRepository extends CrudRepository<Item, Integer> {
}

Data being stored in the database is then exposed to the Web, using a Spring MVC REST Controller:

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ItemController {

    private final ItemRepository itemRepository;

    public ItemController(ItemRepository itemRepository) {
        this.itemRepository = itemRepository;
    }

    @GetMapping("/")
    String welcome() {
        return "Here are all the database items: " + itemRepository.findAll();
    }
}

This data is inserted in the database at startup time, using the src/main/resources/data.sql file:

insert into item (details) values ('This is a item from the database');

You can add more lines to this file if you want more data, or if you want to customize it.

To access the database, you need to configure the src/main/resources/application.properties file:

logging.level.org.springframework.jdbc.core=DEBUG

spring.datasource.url=jdbc:postgresql://${azureDatabaseName}.postgres.database.azure.com:5432/demo
spring.datasource.username=${azureDatabaseUsername}@${azureDatabaseName}
spring.datasource.password=${azureDatabasePassword}

spring.sql.init.mode=always

This configuration file has three variables that need to be configured:

  • ${azureDatabaseName} is the name of the PostgreSQL database that was configured earlier in the AZ_DATABASE_NAME environment variable. Type echo $AZ_DATABASE_NAME to see it.
  • ${azureDatabaseUsername} is the name of the database username that was configured earlier in the AZ_DATABASE_USERNAME environment variable. Type echo $AZ_DATABASE_USERNAME to see it.
  • ${azureDatabasePassword} is the name of the database password that was configured earlier in the AZ_DATABASE_PASSWORD environment variable. Type echo $AZ_DATABASE_PASSWORD to see it.

As we saw in the previous unit, it's a bad practice to hard-code those values in the application source code. But to test the application, you can write them temporarily and run the application:

./mvnw spring-boot:run

You can read the database content by accessing the Spring MVC Controller using the following command, or using a Web browser:

curl http://localhost:8080

Deploy the Java application to Azure

To deploy the application, you first need to package it as a Jar file:

./mvnw clean package

This command produces an executable Jar file in the target directory, that you deploy using the Azure CLI:

az spring app deploy \
   --resource-group $AZ_RESOURCE_GROUP \
   --service $AZ_SPRING_CLOUD \
   --name application \
   --artifact-path target/*.jar

If you encounter failures, you can look at the application logs by typing the following command:

az spring app logs \
   --resource-group $AZ_RESOURCE_GROUP \
   --service $AZ_SPRING_CLOUD \
   --name application

The application will then be available in the cloud, and you can access its data using a cURL command:

curl https://$AZ_SPRING_CLOUD-application.azuremicroservices.io

# Expected output:
#
# Here are all the database items: [Secret{Id=1, details='This is a item from the database'}]
#

Congratulations! You successfully created a Java application that connects to a database! Now you need to secure the database credentials in the next units.