This section describes the deployment steps for the reference implementation of a modern web application pattern with Java on Microsoft Azure. These steps guide you through using the jump box that is deployed when performing a network isolated deployment because access to resources will be restricted from public network access and must be performed from a machine connected to the vnet.
We recommend that you use a Dev Container to deploy this application. The requirements are as follows:
- Azure Subscription.
- Visual Studio Code.
- Docker Desktop.
- Permissions to register an application in Microsoft Entra ID.
- Visual Studio Code Dev Containers extension.
If you do not wish to use a Dev Container, please refer to the prerequisites for detailed information on how to set up your development system to build, run, and deploy the application.
Note
These steps are used to connect to a Linux jump box where you can deploy the code. The jump box is not designed to be a build server. You should use a devOps pipeline to manage build agents and deploy code into the environment. Also note that for this content the jump box is a Linux VM. This can be swapped with a Windows VM based on your organization's requirements.
The following detailed deployment steps assume you are using a Dev Container inside Visual Studio Code.
-
Start a terminal in the dev container and authenticated to Azure and have the appropriate subscription selected. Run the following command to authenticate:
az login
If you have multiple tenants, you can use the following command to log into the tenant:
az login --tenant <tenant-id>
-
Set the subscription to the one you want to use (you can use az account list to list available subscriptions):
export AZURE_SUBSCRIPTION_ID="<your-subscription-id>"
az account set --subscription $AZURE_SUBSCRIPTION_ID
-
Azure Developer CLI (azd) has its own authentication context. Run the following command to authenticate to Azure:
azd auth login
If you have multiple tenants, you can use the following command to log into the tenant:
azd auth login --tenant-id <tenant-id>
-
Create a new AZD environment to store your deployment configuration values:
azd env new <pick_a_name>
-
Set the default subscription for the azd context:
azd env set AZURE_SUBSCRIPTION_ID $AZURE_SUBSCRIPTION_ID
-
To create the prod deployment:
azd env set ENVIRONMENT prod
-
Production is a multi-region deployment. Choose an Azure region for the primary deployment (Run az account list-locations --query '[].{Location: name}' to see a list of locations):
azd env set AZURE_LOCATION <pick_a_region>
You want to make sure the region has availability zones. Azure Database for PostgreSQL - Flexible Server zone-redundant high availability requires availability zones.
-
Choose an Azure region for the secondary deployment:
azd env set AZURE_SECONDARY_LOCATION <pick_a_region>
We encourage readers to choose paired regions for multi-regional web apps. Paired regions typically offer low network latency, data residency in the same geography, and sequential updating. Read Azure paired regions to learn more about these regions.
-
Optional: Set the App Registration Service Management Reference:
azd env set AZURE_SERVICE_MANAGEMENT_REFERENCE <service_management_reference>
-
Run the following command to create the Azure resources (about 45-minutes to provision):
azd provision
When successful the output of the deployment will be displayed in the terminal.
Outputs: bastion_host_name = "vnet-bast-nickcontosocams-prod" frontdoor_url = "https://fd-nickcontosocams-prod-facscqd0a2gqf2eh.z02.azurefd.net" hub_resource_group = "rg-nickcontosocams-hub-prod" jumpbox_resource_id = "/subscriptions/1234/resourceGroups/rg-nickcontosocams-hub-prod/providers/Microsoft.Compute/virtualMachines/vm-jumpbox" primary_app_service_name = "app-nickcontosocams-eastus-prod" primary_spoke_resource_group = "rg-nickcontosocams-spoke-prod" secondary_app_service_name = "app-nickcontosocams-centralus-prod" secondary_spoke_resource_group = "rg-nickcontosocams-spoke2-prod"
These values are available to you by running
azd env get-values
.
In order to deploy the application, you need to set up the deployment environment. This includes running the script ./scripts/setup-deployment-env.sh
in order to export environment variables used in subsequent steps.
-
Run the following command to set up the deployment environment:
./scripts/setup-deployment-env.sh
This script will create the
deployment-env
file in the root of the project. -
Source the environment variables:
source deployment-env
This will make the environment variables available in the current terminal session.
-
Populate the
terraform.tfvars
file in theinfra/terraform-appconfig
folder with the following values:- primary_app_config_id
- secondary_app_config_id
- primary_app_config_keys
- secondary_app_config_keys
./scripts/setup-appconfig-tfvars.sh
-
Run the following command to build the Contoso Fiber application:
./mvnw clean install
This will create the jar file
cams.jar
in theapps/contoso-fiber/target/
directory andemail-processor.jar
in theapps/email-processor/target/
directory. Thecams.jar
will be used to deploy the application to Azure App Service. Theemail-processor.jar
file will be included in a Docker image and uploaded to Azure Container Registry.
-
Build the docker image
docker build --platform linux/amd64 -f apps/email-processor/Dockerfile -t $TARGET_EMAIL_PROCESSOR_IMAGE ./apps/email-processor/
-
Save the docker image to a file
docker save -o $TARGET_EMAIL_PROCESSOR_IMAGE_FILE $TARGET_EMAIL_PROCESSOR_IMAGE
-
Start a new terminal in the dev container
-
Run the following to set the environment variables for the bastion tunnel:
source deployment-env
-
Ensure the following environment variables are set:
echo $bastion_host_name echo $hub_resource_group echo $jumpbox_resource_id
-
We use the Azure CLI to create a bastion tunnel that allows us to connect to the jump box. Run the following command to create a bastion tunnel to the jump box:
az network bastion tunnel --name $bastion_host_name --resource-group $hub_resource_group --target-resource-id $jumpbox_resource_id --resource-port 22 --port 50022
NOTE
Now that the tunnel is open, change back to use the original terminal session to deploy the code.
-
Install the SSH extension for Azure CLI:
az extension add --name ssh
-
Obtain an SSH key from entra:
az ssh config --ip 127.0.0.1 -f ./ssh-config
-
From the teminal, upload the
terraform-appconfig
folderrsync -av -e "ssh -F ./ssh-config -p 50022" infra/terraform-appconfig/ 127.0.0.1:~/terraform-appconfig
-
From the terminal, use the following command to upload CAMS to the jump box.
rsync -av -e "ssh -F ./ssh-config -p 50022" apps/contoso-fiber/target/cams.jar 127.0.0.1:~/cams.jar
-
From the terminal, use the following command to upload the email processor docker image to the jump box.
rsync -av -e "ssh -F ./ssh-config -p 50022" $TARGET_EMAIL_PROCESSOR_IMAGE_FILE 127.0.0.1:~/$TARGET_EMAIL_PROCESSOR_IMAGE_FILE
-
From the terminal, use the following command to upload the deployment environment file.
rsync -av -e "ssh -F ./ssh-config -p 50022" deployment-env 127.0.0.1:~/deployment-env
-
Run the following command to start a shell session on the jump box:
az ssh vm --ip 127.0.0.1 --port 50022
-
Run the following command to log in to Azure from the 1. Login into Azure using:
az login --use-device-code
If you have multiple tenants, you can use the following command to log into the tenant:
az login --tenant <tenant-id> --use-device-code
-
Set the subscription id:
az account set --subscription <subscription_id>
-
Source the deployment environment file:
source deployment-env
This will make the environment variables available in the current terminal session.
-
Apply Terraform plan
cd ~/terraform-appconfig
terraform init terraform plan -out tfplan terraform apply tfplan
-
Verify that the keys were created.
az appconfig kv list -n $primary_app_config_name --auth-mode login az appconfig kv list -n $secondary_app_config_name --auth-mode login
-
Change to the home directory
cd
We will now configure the Contoso Fiber application to use Microsoft Entra authentication with Azure Database for PostgreSQL - Flexible Server. For more information, see Tutorial: Create a passwordless connection to a database service via Service Connector
-
Run the following command to install the serviceconnector-passwordless extension:
az extension add --name serviceconnector-passwordless --upgrade
-
Run the following commands to configure the application:
az webapp connection create postgres-flexible \ --source-id $primary_app_service_id \ --target-id $primary_database_id \ --client-type springBoot \ --system-identity
-
Deploy the application to the primary region using:
az webapp deploy --resource-group $primary_spoke_resource_group --name $primary_app_service_name --src-path cams.jar --type jar
-
Deploy the application to the secondary region using the following command.
This step is optional and can be done at a later time. There is a known issue with the deployment of the secondary region. See known_issues for more information.
az webapp deploy --resource-group $secondary_spoke_resource_group --name $secondary_app_service_name --src-path cams.jar --type jar --restart false
The
--restart false
flag is used to prevent the app from restarting after deployment. This is because the app is not yet configured to use the database as the secondary region is on stand by.WARNING
In some scenarios, the DNS entries for resources secured with Private Endpoint may have been cached incorrectly. It can take up to 10-minutes for the DNS cache to expire.
-
Add your user to the docker group
sudo usermod -aG docker $(id -u -n)
-
Restart the ssh connection to pick up the new group membership
exit az ssh vm --ip 127.0.0.1 --port 50022
-
Set the environment variables
source deployment-env
-
Load the docker image.
docker image load --input $TARGET_EMAIL_PROCESSOR_IMAGE_FILE
-
Verify the image was loaded.
docker images
You should see the image in the list of images.
REPOSITORY TAG IMAGE ID CREATED SIZE modern-java-web/email-processor 1.0.0 475b1e60e832 25 hours ago 530MB
-
Tag the image.
email_processor_image=$AZURE_CONTAINER_REGISTRY_ENDPOINT/$TARGET_EMAIL_PROCESSOR_IMAGE docker tag $TARGET_EMAIL_PROCESSOR_IMAGE $email_processor_image
-
Verify the image was tagged.
docker images
You should see the image in the list of images.
REPOSITORY TAG IMAGE ID CREATED SIZE crnikod1mwaprod.azurecr.io/modern-java-web/email-processor 1.0.0 475b1e60e832 25 hours ago 530MB modern-java-web/email-processor 1.0.0 475b1e60e832 25 hours ago 530MB
-
Log into ACR
az acr login -n $acr_name
-
Push the image to ACR
docker push $email_processor_image
-
Update the container app with the email processor image
az containerapp update -n email-processor -g $primary_spoke_resource_group --image $email_processor_image az containerapp update -n email-processor -g $secondary_spoke_resource_group --image $email_processor_image
Ensure that the image is updated in the container app.
az containerapp show -n email-processor -g $primary_spoke_resource_group --query "properties.template.containers[0].image" -o tsv
az containerapp show -n email-processor -g $secondary_spoke_resource_group --query "properties.template.containers[0].image" -o tsv
If the image is not updated, issue the update command again.
az containerapp update -n email-processor -g $primary_spoke_resource_group --image $email_processor_image az containerapp update -n email-processor -g $secondary_spoke_resource_group --image $email_processor_image
-
Exit the jumpbox using:
exit
-
Close the tunnel in the SEPARATE terminal using:
CTRL+C
-
Navigate to the Front Door URL in a browser to view the Contoso Fiber CAMS application.
You can learn more about the web app by reading the Pattern Simulations documentation.
To get the Front Door URL, run the following command from your local terminal:
azd env get-values --output json | jq -r .frontdoor_url
-
When you are done you can cleanup all the resources using:
azd down --force --purge