> ## Content Index
> Fetch the complete content index at: https://devopscube.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Deploy Scalable Java Applications on AWS Using Terraform
- URL: https://devopscube.com/deploy-java-applications-aws-autoscaling/
- Published: 2023-11-27T15:58:36.000Z
- Updated: 2025-03-14T11:36:12.000Z
- Author: Aswin Vijayan
- Tags: DEVOPS PROJECTS, #Migrated-1741795015845, #wp, #wp-post, #Import 2025-03-12 15:57, #blog

In this [DevOps Project](https://devopscube.com/devops-projects/), you will learn about how to set up a scalable Java application with ASG, ALB, and RDS using Terraform

## Tools & Services Used

For this setup, I have used the following DevOps Tools

1. **GitHub**: Repository to store IaC
2. **Packer**: To build Java application [AMIs](https://devopscube.com/packer-tutorial-for-beginners/)
3. **Ansible**: To configure java application during the AMI building process
4. **Terraform**: To provision AWS resources
5. **Python Boto3**: To retrieve RDS credentials from AWS secrets manager.

Following are the AWS services used.

1. **IAM**: To create IAM Role/Instance Profile for Java Application Instance.
2. **RDS**: For application database
3. **AWS Secrets Manager**: To store RDS credential during RDS launch.
4. **AWS Parameter Store**: To get the RDS endpoint.
5. **Autoscaling Group**: To deploy the Java application.
6. **Application Load Balancer**: To have a static DNS endpoint for the java application running in the autoscaling group.

## Setup Architecture

![](https://storage.ghost.io/c/5f/2f/5f2f4d20-2abf-4534-8d40-7aa233aedd43/content/images/2025/03/image-151-4.png)

## Setup Prerequisites

Before starting the setup, you will need the following tools and utilities installed and configured on your workstation.

1. Packer
2. Ansible
3. Terraform
4. JDK-17
5. Maven-v3.9.4
6. [AWS CLI](https://devopscube.com/install-configure-aws-cli-linux/), configured with default region as us-west-2 and AWS credentials that have admin access to the AWS account.

We will be using the default VPC in the us-west-2 (Oregon) region. There will be four default subnets. We will be using three subnets from the following three availability zones:

1. us-west-2a
2. us-west-2b
3. us-west-2c

Ensure you have an **existing key pair in your AWS account for the Oregon region**. We will use it with instances to enable SSH access.

## Build Java Application

Clone this repository to your local system to build a Java Application.

```
git clone github.com/pet-clinic-project/pet-clinic-app.git
```

CD into `pet-clinic-app` folder and run the below command to build the application.

```
mvn clean install
```

After the build process finishes you can see a new folder named `target`, CD into the target folder you can find the JAR file for the application as given below

![](https://storage.ghost.io/c/5f/2f/5f2f4d20-2abf-4534-8d40-7aa233aedd43/content/images/2025/03/image-115-4.png)

## Provision AWS Services

Clone this repository to your local system and the code for this project is in the [03-scalable-java-app](https://github.com/techiescamp/devops-projects/tree/main/03-scalable-java-app?ref=devopscube.com) folder.

```
https://github.com/techiescamp/devops-projects.git
```

The folders and files inside the project folder are listed below.

```
.
├── README.md
├── ansible
│   ├── files
│   │   ├── application.properties
│   │   ├── properties.py
│   │   └── start.sh
│   ├── java-app.pkr.hcl
│   ├── java-app.yml
│   ├── roles
│   │   └── java
│   │       └── tasks
│   │           ├── app.yml
│   │           ├── backends.yml
│   │           ├── cloudwatch.yml
│   │           ├── java.yml
│   │           ├── main.yml
│   │           └── python.yml
│   └── templates
│       ├── config.json.j2
│       └── index.html.j2
└── terraform
    ├── alb-asg
    │   ├── main.tf
    │   ├── terraform.tfstate
    │   ├── terraform.tfstate.backup
    │   └── variable.tf
    ├── modules
    │   ├── alb-asg
    │   │   ├── alb.tf
    │   │   ├── asg.tf
    │   │   ├── iam-policy.tf
    │   │   └── variable.tf
    │   └── rds
    │       ├── main.tf
    │       ├── outputs.tf
    │       └── variables.tf
    ├── rds
    │   ├── main.tf
    │   ├── terraform.tfstate
    │   ├── terraform.tfstate.backup
    │   └── variables.tf
    └── vars
        ├── alb-asg.tfvars
        └── rds.tfvars
```

## Provision Java Application AMI

The first step is to provision an AMI for your Java Application. CD into the ansible folder and follow the below steps before creating the AMI.

**STEP 1:** Copy your JAR file inside the **`ansible/files`** folder and specify the name of your JAR file in the **`ansible/java-app.yml`** file as shown below.

```
---
- name: Install Java
  hosts: all
  become: yes
  remote_user: ubuntu

  vars:
    source_dir: files
    dest_dir: /home/ubuntu/
    files:
      - properties.py
      - start.sh
      - pet-clinic-1.0.1.jar

  roles:
    - java
```

**`pet-clinic-1.0.1.jar`** is the name of my JAR file, you make sure to replace with the name of your Jar file.

**STEP 2:** Modify the Python script **`ansible/files/properties.py`**

```
import boto3
import json

region = 'us-west-2'
parameter_store = '/dev/petclinic/rds_endpoint'
secret_name_tag = 'dev-rds-db'
file_path = "/opt/application.properties"

ssm = boto3.client('ssm', region_name=region)

rds_endpoint = ssm.get_parameter(Name=parameter_store)['Parameter']['Value']

secrets_client = boto3.client('secretsmanager', region_name=region)

secrets_list = secrets_client.list_secrets()
secret_arn = None
for secret in secrets_list['SecretList']:
    if 'Tags' in secret:
        for tag in secret['Tags']:
            if tag['Key'] == 'Name' and tag['Value'] == secret_name_tag:
                secret_arn = secret['ARN']
                break

if secret_arn is None:
    print(f"Secret with name tag '{secret_name_tag}' not found.")
    exit(1)

response = secrets_client.get_secret_value(SecretId=secret_arn)
secret_value = response['SecretString']

secret_data = json.loads(secret_value)

with open(file_path, 'r') as f:
    file_contents = f.read()

file_contents = file_contents.replace("spring.datasource.url=jdbc:mysql://localhost:3306/petclinic", f"spring.datasource.url=jdbc:mysql://{rds_endpoint}")
file_contents = file_contents.replace("spring.datasource.username=petclinic", f"spring.datasource.username={secret_data['username']}")
file_contents = file_contents.replace("spring.datasource.password=petclinic", f"spring.datasource.password={secret_data['password']}")

with open(file_path, 'w') as f:
        f.write(file_contents)
```

This Python script retrieves your RDS username and password from the secret manager and RDS endpoint from the parameter store.

First it creates an ssm client and retrieves the RDS endpoint from the Parameter Store. Then it creates a secret manager client and lists the secrets in the secrets manager, then uses the name tag to identify the secret which contains the username and password from the RDS database.

It updates the **`ansible/files/application.properties`** configuration file with the username, password, and endpoint, so that your application can connect to the RDS database.

**STEP 3:** Update your JAR file name inside the startup script **`ansible/files/start.sh`**

```
#!/bin/bash

JAR_FILE=/home/ubuntu/pet-clinic-1.0.1.jar
APP_PROPERTIES=/opt/application.properties
PROPERTIES_SCRIPT=/home/ubuntu/properties.py

sudo python3 ${PROPERTIES_SCRIPT}

sudo java -jar "${JAR_FILE}" --spring.config.location="${APP_PROPERTIES}" --spring.profiles.active=mysql &
```

By running this script it will run the **`ansible/files/properties.py`** and then run the Java application and connect it with the RDS database.

Replace **`pet-clinic-1.0.1.jar`** with your JAR file name.

**STEP 4:** Update the Packer template **`ansible/java-app.pkr.hcl`**

```
variable "ami_id" {
  type    = string
  default = "ami-03f65b8614a860c29"
}

locals {
    app_name = "java-app-1.0.1"
}

source "amazon-ebs" "java-app" {
  ami_name      = "PACKER-${local.app_name}"
  instance_type = "t2.medium"
  region        = "us-west-2"
  source_ami    = "${var.ami_id}"
  ssh_username  = "ubuntu"
  tags = {
    Env  = "DEMO"
    Name = "PACKER-${local.app_name}"
  }
}

build {
  sources = ["source.amazon-ebs.java-app"]

  provisioner "ansible" {
    playbook_file = "java-app.yml"
  }

}

```

In this template update the ami\_id, app\_name, and instance\_type you are going to use.

**STEP 5:** Now, validate your packer template using the command

```
packer validate java-app.pkr.hcl
```

**STEP 6:** If the packer template is valid build your AMI using the command

```
packer build java-app.pkr.hcl
```

Once your AMI is provisioned move on to the next process.

## RDS Provisioning

After provisioning the AMI, start provisioning the RDS database for the application. CD into the terraform folder and follow the steps.

**Step 1:** Modify **`terraform/vars/rds.tfvars`** file

CD into the **`terraform/vars`** folder and update the **`rds.tfvars`** file with vpc, subnets, and other configurations.

```
# Network Vars
region              = "us-west-2"
vpc_id              = "vpc-0a5ca4a92c2e10163"
subnet_ids          = ["subnet-058a7514ba8adbb07", "subnet-032f5077729435858", "subnet-0dbcd1ac168414927"]
multi_az            = false
publicly_accessible = true

# DB Vars
db_engine                   = "mysql"
db_storage_type             = "gp2"
db_username                 = "techiescamp"
db_instance_class           = "db.t2.micro"
db_storage_size             = 20
set_secret_manager_password = true
set_db_password             = false
db_password                 = "rdssecret"

# Security Group Vars
ingress_from_port   = 3306
ingress_to_port     = 3306
ingress_protocol    = "tcp"
ingress_cidr_blocks = ["0.0.0.0/0"]

egress_from_port   = 0
egress_to_port     = 0
egress_protocol    = "-1"
egress_cidr_blocks = ["0.0.0.0/0"]

# Backup vars
backup_retention_period  = 7
delete_automated_backups = true
copy_tags_to_snapshot    = true
skip_final_snapshot      = true
apply_immediately        = true

# Parameter store
parameter_store_secret_name = "/dev/petclinic/rds_endpoint"
type                        = "String"

# Tag Vars
owner       = "techiescamp"
environment = "dev"
cost_center = "techiescamp-commerce"
application = "rds"
```

**STEP 2:** After modifying **`terraform/vars/rds.tfvars`** with your configurations, initialize Terraform inside the **`terraform/rds`** folder.

```
terraform init
```

**STEP 3:** Before start provisioning RDS make sure you have given the right configurations using the plan command.

```
terraform plan -var-file=../vars/rds.tfvars
```

**STEP 4:** Start provisioning RDS

```
terraform apply -var-file=../vars/rds.tfvars
```

After the code runs successfully, validate that the RDS database has provisioned and active. Also check if the RDS endpoint is stored in the Parameter Store.

RDS username and password will be stored in the Secret Manager with a long random name as shown below.

![](https://storage.ghost.io/c/5f/2f/5f2f4d20-2abf-4534-8d40-7aa233aedd43/content/images/2025/03/image-116-5.png)

## ALB and ASG Provisioning

After provisioning the RDS database, start provisioning the ALB and ASG by following the steps below.

**Step 1:** Modify **`terraform/vars/alb-asg.tfvars`** file

CD into the **`terraform/vars/`** folder and update the **`alb-asg.tfvars`** fil with vpc, subnets, ami-id, key, and other configurations with your configuration.

```
#Network
region  = "us-west-2"
vpc_id  = "vpc-0a5ca4a92c2e10163"
subnets = ["subnet-058a7514ba8adbb07", "subnet-032f5077729435858", "subnet-0dbcd1ac168414927"]

#alb_sg
ingress_alb_from_port   = 80
ingress_alb_to_port     = 80
ingress_alb_protocol    = "tcp"
ingress_alb_cidr_blocks = ["0.0.0.0/0"]
egress_alb_from_port    = 0
egress_alb_to_port      = 0
egress_alb_protocol     = "-1"
egress_alb_cidr_blocks  = ["0.0.0.0/0"]

#alb
internal          = false
loadbalancer_type = "application"

#target_group
target_group_port                = 8080
target_group_protocol            = "HTTP"
target_type                      = "instance"
load_balancing_algorithm         = "round_robin"
health_check_path                = "/"
health_check_port                = 8080
health_check_protocol            = "HTTP"
health_check_interval            = 30
health_check_timeout             = 5
health_check_healthy_threshold   = 2
health_check_unhealthy_threshold = 2

#instance_sg
ingress_asg_cidr_from_port = 22
ingress_asg_cidr_to_port   = 22
ingress_asg_cidr_protocol  = "tcp"
ingress_asg_cidr_blocks    = ["0.0.0.0/0"]
ingress_asg_sg_from_port   = 8080
ingress_asg_sg_to_port     = 8080
ingress_asg_sg_protocol    = "tcp"
egress_asg_from_port       = 0
egress_asg_to_port         = 0
egress_asg_protocol        = "-1"
egress_asg_cidr_blocks     = ["0.0.0.0/0"]

#asg
max_size         = 3
min_size         = 1
desired_capacity = 2

#listener
listener_port     = 80
listener_protocol = "HTTP"
listener_type     = "forward"

#launch_template
ami_id               = "ami-0f7a74cccd5a223bc"
instance_type        = "t2.medium"
key_name             = "techiescamp"
user_data            = <<-EOF
#!/bin/bash
bash /home/ubuntu/start.sh
EOF
public_access        = true
instance_warmup_time = 30
target_value         = 50

owner       = "techiescamp"
environment = "dev"
cost_center = "techiescamp-commerce"
application = "pet-clinic"
```

Each instance will have an IAM role attached to it while provisioning. I attached an IAM role with admin permissions, you can modify the IAM role in the **`/terraform/modules/alb-asg/iam-policy.tf`** file with the permissions you require.

**STEP 2:** After modifying `**alb-asg.tfvar**s` with your configurations, initialize Terraform inside the **`terraform/alb-asg`** folder.

```
terraform init
```

**STEP 3:** Before start provisioning ALB and ASG make sure you have given the right configurations using the plan command.

```
terraform plan -var-file=../vars/alb-asg.tfvars
```

**STEP 4:** Start provisioning ALB and ASG

```
terraform apply -var-file=../vars/alb-asg.tfvars
```

After the code runs successfully, validate ALB and ASG has provisioned. And you can check the health of your instance in the target group.

![](https://storage.ghost.io/c/5f/2f/5f2f4d20-2abf-4534-8d40-7aa233aedd43/content/images/2025/03/2-8.png)

If your instances are healthy try to access your application in the browser using the load-balancer DNS as shown below.

![](https://storage.ghost.io/c/5f/2f/5f2f4d20-2abf-4534-8d40-7aa233aedd43/content/images/2025/03/3-5.png)

Make sure your instances are connected to the RDS database.

![](https://storage.ghost.io/c/5f/2f/5f2f4d20-2abf-4534-8d40-7aa233aedd43/content/images/2025/03/2023-11-08_19-03-11-1.png)