From 0f4e89f06ef407046d2d9fb9e5c149469ef48b77 Mon Sep 17 00:00:00 2001 From: Souvik <88985517+slancerk@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:58:07 +0530 Subject: [PATCH] Terraform added (#13973) * Terraform added * docs updated * EOL --- .../version-3.16.0-LTS/setup/docker.md | 8 + .../version-3.16.0-LTS/setup/ecs.md | 9 + terraform/Azure_VM/install_tooljet.sh | 12 + terraform/Azure_VM/main.tf | 151 ++++++ terraform/Azure_VM/output.tf | 14 + terraform/Azure_VM/terraform.tfvars | 4 + terraform/Azure_VM/variables.tf | 29 ++ terraform/EC2/datasource.tf | 16 + terraform/EC2/ec2.tf | 90 ++++ terraform/EC2/install_tooljet.sh | 12 + terraform/EC2/output.tf | 12 + terraform/EC2/sg.tf | 29 ++ terraform/EC2/terraform.tfvars | 2 + terraform/EC2/variables.tf | 16 + terraform/ECS/data.tf | 3 + terraform/ECS/main.tf | 479 ++++++++++++++++++ terraform/ECS/redis.tf | 39 ++ terraform/ECS/terraform.tfvars | 24 + terraform/ECS/variables.tf | 60 +++ 19 files changed, 1009 insertions(+) create mode 100644 terraform/Azure_VM/install_tooljet.sh create mode 100644 terraform/Azure_VM/main.tf create mode 100644 terraform/Azure_VM/output.tf create mode 100644 terraform/Azure_VM/terraform.tfvars create mode 100644 terraform/Azure_VM/variables.tf create mode 100644 terraform/EC2/datasource.tf create mode 100644 terraform/EC2/ec2.tf create mode 100644 terraform/EC2/install_tooljet.sh create mode 100644 terraform/EC2/output.tf create mode 100644 terraform/EC2/sg.tf create mode 100644 terraform/EC2/terraform.tfvars create mode 100644 terraform/EC2/variables.tf create mode 100644 terraform/ECS/data.tf create mode 100644 terraform/ECS/main.tf create mode 100644 terraform/ECS/redis.tf create mode 100644 terraform/ECS/terraform.tfvars create mode 100644 terraform/ECS/variables.tf diff --git a/docs/versioned_docs/version-3.16.0-LTS/setup/docker.md b/docs/versioned_docs/version-3.16.0-LTS/setup/docker.md index 914bf398b7..975431f0d1 100644 --- a/docs/versioned_docs/version-3.16.0-LTS/setup/docker.md +++ b/docs/versioned_docs/version-3.16.0-LTS/setup/docker.md @@ -19,6 +19,14 @@ To enable ToolJet AI features in your ToolJet deployment, whitelist https://api- :::: +### Provisioning VMs with Terraform (Optional) + +If you don’t already have a server, you can use Terraform scripts to quickly spin up a VM on AWS or Azure VM and then deploy ToolJet with Docker. + +⚙️ Deploy on [AWS EC2](https://github.com/ToolJet/ToolJet/tree/develop/terraform/EC2) + +⚙️ Deploy on [Azure VM](https://github.com/ToolJet/ToolJet/tree/develop/terraform/Azure_VM) + ### Installing Docker and Docker Compose Install docker and docker-compose on the server. diff --git a/docs/versioned_docs/version-3.16.0-LTS/setup/ecs.md b/docs/versioned_docs/version-3.16.0-LTS/setup/ecs.md index d16c4e819d..072c6d9dc0 100644 --- a/docs/versioned_docs/version-3.16.0-LTS/setup/ecs.md +++ b/docs/versioned_docs/version-3.16.0-LTS/setup/ecs.md @@ -15,6 +15,8 @@ You should setup a PostgreSQL database manually to be used by ToolJet. We recomm ToolJet comes with a **built-in Redis setup**, which is used for multiplayer editing and background jobs. However, for **multi-service setup**, it's recommended to use an **external Redis instance**. ::: +### ⚙️ Deploy using CloudFormation + You can effortlessly deploy Amazon Elastic Container Service (ECS) by utilizing a [CloudFormation template](https://aws.amazon.com/cloudformation/): To deploy all the services at once, simply employ the following template: @@ -29,6 +31,13 @@ If you already have existing services and wish to integrate ToolJet seamlessly i curl -LO https://tooljet-deployments.s3.us-west-1.amazonaws.com/cloudformation/Cloudformation-deploy.yml ``` +### ⚙️ Deploy using Terraform + +If you prefer **(IaC)** with Terraform, ToolJet also provides **ECS deployment scripts**. + +📂 Repository: [ToolJet Terraform for ECS](https://github.com/ToolJet/ToolJet/tree/develop/terraform/ECS) + + ## ToolJet Follow the steps below to deploy ToolJet on a ECS cluster. diff --git a/terraform/Azure_VM/install_tooljet.sh b/terraform/Azure_VM/install_tooljet.sh new file mode 100644 index 0000000000..e73597ac27 --- /dev/null +++ b/terraform/Azure_VM/install_tooljet.sh @@ -0,0 +1,12 @@ +#!/bin/bash +sudo apt upgrade -y +sudo apt update -y +sudo apt install -y apt-transport-https ca-certificates curl software-properties-common + +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - +sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" +sudo apt update -y + +sudo apt install -y docker-ce +sudo systemctl start docker +sudo systemctl enable docker diff --git a/terraform/Azure_VM/main.tf b/terraform/Azure_VM/main.tf new file mode 100644 index 0000000000..759452aa72 --- /dev/null +++ b/terraform/Azure_VM/main.tf @@ -0,0 +1,151 @@ +# Define the Azure provider +provider "azurerm" { + features {} + subscription_id = var.subscription_id + client_id = var.client_id + client_secret = var.client_secret + tenant_id = var.tenant_id + +} + +# Generate a TLS private key for SSH access +resource "tls_private_key" "tooljet_key" { + algorithm = "RSA" + rsa_bits = 2048 +} + +# Resource Group +resource "azurerm_resource_group" "tooljet_rg" { + name = var.resource_group_name + location = var.location +} + +# Virtual Network +resource "azurerm_virtual_network" "tooljet_vnet" { + name = "TooljetVNet" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.tooljet_rg.location + resource_group_name = azurerm_resource_group.tooljet_rg.name +} + +# Subnet +resource "azurerm_subnet" "tooljet_subnet" { + name = "TooljetSubnet" + resource_group_name = azurerm_resource_group.tooljet_rg.name + virtual_network_name = azurerm_virtual_network.tooljet_vnet.name + address_prefixes = ["10.0.1.0/24"] +} + +# Public IP +resource "azurerm_public_ip" "tooljet_public_ip" { + name = "TooljetPublicIP" + resource_group_name = azurerm_resource_group.tooljet_rg.name + location = azurerm_resource_group.tooljet_rg.location + allocation_method = "Static" + sku = "Standard" +} + +# # Network Security Group (NSG) with Ingress Rules +# resource "azurerm_network_security_group" "tooljet_nsg" { +# name = "TooljetNSG" +# location = azurerm_resource_group.tooljet_rg.location +# resource_group_name = azurerm_resource_group.tooljet_rg.name + +# dynamic "security_rule" { +# for_each = zip(tolist(["22", "80", "443", "3000"]), range(length(["22", "80", "443", "3000"]))) +# content { +# name = "AllowPort-${security_rule.value}" +# priority = 100 + (security_rule.value * 10) +# direction = "Inbound" +# access = "Allow" +# protocol = "Tcp" +# source_port_range = "*" +# destination_port_range = security_rule.value +# source_address_prefix = "*" +# destination_address_prefix = "*" +# } +# } +# } + +resource "azurerm_network_security_group" "tooljet_nsg" { + name = "TooljetNSG" + location = azurerm_resource_group.tooljet_rg.location + resource_group_name = azurerm_resource_group.tooljet_rg.name + + dynamic "security_rule" { + for_each = { + "22" = 100, + "80" = 110, + "443" = 120, + "3000" = 130 + } + content { + name = "AllowPort-${security_rule.key}" + priority = security_rule.value # Assign priority from the map + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = security_rule.key + source_address_prefix = "*" + destination_address_prefix = "*" + } + } +} + + +# Network Interface with NSG +resource "azurerm_network_interface" "tooljet_nic" { + name = "TooljetNIC" + location = azurerm_resource_group.tooljet_rg.location + resource_group_name = azurerm_resource_group.tooljet_rg.name + + ip_configuration { + name = "TooljetIPConfig" + subnet_id = azurerm_subnet.tooljet_subnet.id + private_ip_address_allocation = "Dynamic" + public_ip_address_id = azurerm_public_ip.tooljet_public_ip.id + } + +} + +# Associate NSG with Subnet +resource "azurerm_subnet_network_security_group_association" "tooljet_nsg_association" { + subnet_id = azurerm_subnet.tooljet_subnet.id + network_security_group_id = azurerm_network_security_group.tooljet_nsg.id +} + +# Virtual Machine +resource "azurerm_linux_virtual_machine" "tooljet_vm" { + name = "TooljetVM" + location = azurerm_resource_group.tooljet_rg.location + resource_group_name = azurerm_resource_group.tooljet_rg.name + size = var.vm_size + admin_username = var.vm_admin_username + network_interface_ids = [azurerm_network_interface.tooljet_nic.id] + + admin_ssh_key { + username = var.vm_admin_username + public_key = tls_private_key.tooljet_key.public_key_openssh + } + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + disk_size_gb = 16 + } + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "24_04-lts" # equivalent to a recent Ubuntu LTS version + version = "latest" + } + + + custom_data = base64encode(file("${path.module}/install_tooljet.sh")) # Assuming the script is in the module path + + tags = { + Name = "TooljetAppServer" + } +} diff --git a/terraform/Azure_VM/output.tf b/terraform/Azure_VM/output.tf new file mode 100644 index 0000000000..b7259ae8bb --- /dev/null +++ b/terraform/Azure_VM/output.tf @@ -0,0 +1,14 @@ + +# Outputs +output "tooljet_private_key" { + value = tls_private_key.tooljet_key.private_key_pem + sensitive = true +} + +output "public_ip_address" { + value = azurerm_public_ip.tooljet_public_ip.ip_address +} + +output "vm_id" { + value = azurerm_linux_virtual_machine.tooljet_vm.id +} diff --git a/terraform/Azure_VM/terraform.tfvars b/terraform/Azure_VM/terraform.tfvars new file mode 100644 index 0000000000..baa7ab0988 --- /dev/null +++ b/terraform/Azure_VM/terraform.tfvars @@ -0,0 +1,4 @@ +subscription_id = "" +client_id = "" +client_secret = "" +tenant_id = "" diff --git a/terraform/Azure_VM/variables.tf b/terraform/Azure_VM/variables.tf new file mode 100644 index 0000000000..ad97234be3 --- /dev/null +++ b/terraform/Azure_VM/variables.tf @@ -0,0 +1,29 @@ +variable "subscription_id" { + default = "" +} +variable "tenant_id" { + default = "" +} +variable "client_id" { + default = "" +} +variable "client_secret" { + default = "" +} +variable "location" { + default = "East US" +} + +variable "resource_group_name" { + default = "TooljetResourceGroup" +} + +variable "vm_size" { + type = string + default = "Standard_DS1_v2" +} + +variable "vm_admin_username" { + type = string + default = "azureuser" +} diff --git a/terraform/EC2/datasource.tf b/terraform/EC2/datasource.tf new file mode 100644 index 0000000000..610804d341 --- /dev/null +++ b/terraform/EC2/datasource.tf @@ -0,0 +1,16 @@ +data "aws_ami" "latest_custom_ami" { + most_recent = true + + owners = ["099720109477"] + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-20240927"] + } + + # Optional: Add more filters if necessary + filter { + name = "virtualization-type" + values = ["hvm"] + } +} diff --git a/terraform/EC2/ec2.tf b/terraform/EC2/ec2.tf new file mode 100644 index 0000000000..840eeb3656 --- /dev/null +++ b/terraform/EC2/ec2.tf @@ -0,0 +1,90 @@ +# Define provider +provider "aws" { + region = var.region +} + +# Generate a TLS private key for EC2 access +resource "tls_private_key" "tooljet_key" { + algorithm = "RSA" + rsa_bits = 2048 +} +# Define the key pair for EC2 access +resource "aws_key_pair" "tooljet_key" { + key_name = "tooljet-key" + public_key = tls_private_key.tooljet_key.public_key_openssh # file("~/.ssh/tooljet.pub") +} + +# Create a VPC +resource "aws_vpc" "tooljet_vpc" { + cidr_block = "10.0.0.0/16" + enable_dns_support = true + enable_dns_hostnames = true + + tags = { + Name = "TooljetVPC" + } +} + +# Create an Internet Gateway for the VPC +resource "aws_internet_gateway" "tooljet_igw" { + vpc_id = aws_vpc.tooljet_vpc.id + + tags = { + Name = "TooljetInternetGateway" + } +} + +# Create a public subnet +resource "aws_subnet" "tooljet_public_subnet" { + vpc_id = aws_vpc.tooljet_vpc.id + cidr_block = "10.0.1.0/24" + map_public_ip_on_launch = true + + tags = { + Name = "TooljetPublicSubnet" + } +} + +# Create a route table for the public subnet +resource "aws_route_table" "tooljet_public_route_table" { + vpc_id = aws_vpc.tooljet_vpc.id + + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.tooljet_igw.id + } + + tags = { + Name = "TooljetPublicRouteTable" + } +} + +# Associate the public route table with the public subnet +resource "aws_route_table_association" "tooljet_public_subnet_assoc" { + subnet_id = aws_subnet.tooljet_public_subnet.id + route_table_id = aws_route_table.tooljet_public_route_table.id +} + +# Define the EC2 instance +resource "aws_instance" "tooljet_instance" { + ami = var.ami_id != "" ? var.ami_id : data.aws_ami.latest_custom_ami.id + instance_type = var.instance_type + key_name = aws_key_pair.tooljet_key.key_name + #security_groups = [aws_security_group.tooljet_sg.name] + availability_zone = var.aws_instance_tooljet_instance_AZ + # Associate instance with the subnet and security group + subnet_id = aws_subnet.tooljet_public_subnet.id + vpc_security_group_ids = [aws_security_group.tooljet_sg.id] + associate_public_ip_address = true + depends_on = [aws_security_group.tooljet_sg] + # Root EBS volume configuration + root_block_device { + volume_size = 16 + volume_type = "gp3" + } + # Load the shell script using file() function + user_data = file("${path.module}/install_tooljet.sh") + tags = { + Name = "TooljetAppServer" + } +} diff --git a/terraform/EC2/install_tooljet.sh b/terraform/EC2/install_tooljet.sh new file mode 100644 index 0000000000..e73597ac27 --- /dev/null +++ b/terraform/EC2/install_tooljet.sh @@ -0,0 +1,12 @@ +#!/bin/bash +sudo apt upgrade -y +sudo apt update -y +sudo apt install -y apt-transport-https ca-certificates curl software-properties-common + +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - +sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" +sudo apt update -y + +sudo apt install -y docker-ce +sudo systemctl start docker +sudo systemctl enable docker diff --git a/terraform/EC2/output.tf b/terraform/EC2/output.tf new file mode 100644 index 0000000000..6451568bda --- /dev/null +++ b/terraform/EC2/output.tf @@ -0,0 +1,12 @@ +output "tooljet_private_key" { + value = tls_private_key.tooljet_key.private_key_pem + sensitive = true +} +# Output instance details +output "instance_ip" { + value = aws_instance.tooljet_instance.public_ip +} + +output "instance_id" { + value = aws_instance.tooljet_instance.id +} diff --git a/terraform/EC2/sg.tf b/terraform/EC2/sg.tf new file mode 100644 index 0000000000..547527565c --- /dev/null +++ b/terraform/EC2/sg.tf @@ -0,0 +1,29 @@ +# List of ports for ingress +variable "ingress_ports" { + default = [22, 80, 443, 3000] +} + +# Define the security group +resource "aws_security_group" "tooljet_sg" { + vpc_id = aws_vpc.tooljet_vpc.id + name = "tooljet-sg" + description = "Allow SSH, HTTP, and HTTPS" + + dynamic "ingress" { + for_each = var.ingress_ports + content { + from_port = ingress.value + to_port = ingress.value + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + } + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + +} + diff --git a/terraform/EC2/terraform.tfvars b/terraform/EC2/terraform.tfvars new file mode 100644 index 0000000000..0314130e89 --- /dev/null +++ b/terraform/EC2/terraform.tfvars @@ -0,0 +1,2 @@ +region = "" +aws_instance_tooljet_instance_AZ = "" diff --git a/terraform/EC2/variables.tf b/terraform/EC2/variables.tf new file mode 100644 index 0000000000..c43fb1207e --- /dev/null +++ b/terraform/EC2/variables.tf @@ -0,0 +1,16 @@ +variable "region" { + default = "us-east-1" +} + +variable "ami_id" { + type = string + default = "" +} + +variable "instance_type" { + type = string + default = "t2.medium" +} +variable "aws_instance_tooljet_instance_AZ" { + default = "" +} diff --git a/terraform/ECS/data.tf b/terraform/ECS/data.tf new file mode 100644 index 0000000000..997130f190 --- /dev/null +++ b/terraform/ECS/data.tf @@ -0,0 +1,3 @@ + +data "aws_caller_identity" "current" {} + diff --git a/terraform/ECS/main.tf b/terraform/ECS/main.tf new file mode 100644 index 0000000000..dc06c04b44 --- /dev/null +++ b/terraform/ECS/main.tf @@ -0,0 +1,479 @@ +provider "aws" { + region = var.region +} + + +resource "aws_iam_role" "ecs_task_execution_role" { + name = "ToolJet-task-execution-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Principal = { + Service = "ecs-tasks.amazonaws.com" + } + Action = "sts:AssumeRole" + }] + }) +} + +resource "aws_iam_role_policy_attachment" "ecs_cloudwatch_logs_policy" { + role = aws_iam_role.ecs_task_execution_role.name + policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" +} + +resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy" { + role = aws_iam_role.ecs_task_execution_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" +} + +resource "aws_iam_policy" "ssm_policy" { + name = "ToolJet-ssm-policy" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Action = [ + "ssmmessages:CreateControlChannel", + "ssmmessages:CreateDataChannel", + "ssmmessages:OpenControlChannel", + "ssmmessages:OpenDataChannel" + ] + Resource = "*" + }] + }) +} + +resource "aws_iam_policy" "secrets_manager_policy" { + name = "ToolJet-secrets-manager-policy" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Action = "secretsmanager:GetSecretValue" + Resource = "arn:aws:secretsmanager:${var.region}:${data.aws_caller_identity.current.account_id}:secret:tooljet-secret" + }] + }) +} + +resource "aws_cloudwatch_log_group" "ecs_log_group" { + name = "/ecs/ToolJet" + retention_in_days = 30 +} + +resource "aws_cloudwatch_log_group" "postgrest_log_group" { + name = "/ecs/postgrest" + retention_in_days = 30 +} + +resource "aws_ecs_cluster" "tooljet_cluster" { + name = "ToolJet" + + setting { + name = "containerInsights" + value = "enabled" + } +} +resource "aws_ecs_task_definition" "tooljet_task_definition" { + family = var.AppName + network_mode = "awsvpc" + requires_compatibilities = ["FARGATE"] + cpu = "4096" + memory = "8192" + execution_role_arn = aws_iam_role.ecs_task_execution_role.arn + task_role_arn = aws_iam_role.ecs_task_execution_role.arn + + container_definitions = jsonencode([ + { + name = var.AppName + image = "tooljet/tooljet:ee-lts-latest" + cpu = 2048 + memory = 4096 + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = aws_cloudwatch_log_group.ecs_log_group.name + awslogs-region = var.region + awslogs-stream-prefix = "tooljet" + + } + } + portMappings = [{ + containerPort = 3000 + hostPort = 3000 + protocol = "tcp" + name = "tooljet" + }] + command = ["npm", "run", "start:prod"] + environment = [ + { + name = "TOOLJET_HOST" + value = aws_lb.tooljet_lb.dns_name + }, + { + name = "TOOLJET_DB" + value = var.TOOLJET_DB + }, + { + name = "TOOLJET_DB_HOST" + value = aws_db_instance.tooljet_database.endpoint + }, + { + name = "TOOLJET_DB_USER" + value = var.TOOLJET_DB_USER + }, + { + name = "TOOLJET_DB_PASS" + value = var.TOOLJET_DB_PASS + }, + { + name = "PG_HOST" + value = aws_db_instance.tooljet_database.endpoint + }, + { + name = "PG_USER" + value = var.PG_USER + }, + { + name = "PG_PASS" + value = var.PG_PASS + }, + { + name = "PG_DB" + value = var.PG_DB + }, + { + name = "LOCKBOX_MASTER_KEY" + value = var.LOCKBOX_MASTER_KEY + }, + { + name = "SECRET_KEY_BASE" + value = var.SECRET_KEY_BASE + }, + { + name = "DEPLOYMENT_PLATFORM" + value = "aws:ecs" + }, + { + name = "REDIS_HOST" + value = var.REDIS_HOST + }, + { + name = "REDIS_PORT" + value = var.REDIS_PORT + }, + { + name = "REDIS_USER" + value = var.REDIS_USER + }, + { + name = "REDIS_PASSWORD" + value = var.REDIS_PASSWORD + }, + { + name = "PGSSLMODE" + value = "require" + }, + # use this incase using RDS with SSL + # { + # name = "NODE_EXTRA_CA_CERTS" + # value = "/certs/global-bundle.pem" + # } + { + name = "PGRST_HOST" + value = "127.0.0.1:3002" + }, + { + name = "PGRST_SERVER_PORT" + value = "3002" + }, + { + name = "PGRST_DB_URI" + value = "postgres://${var.TOOLJET_DB_USER}:${var.TOOLJET_DB_PASS}@${aws_db_instance.tooljet_database.endpoint}/${var.TOOLJET_DB}" + }, + { + name = "PGRST_JWT_SECRET" + value = var.PGRST_JWT_SECRET + } + ] + }, + { + name = "redis" + image = "redis:6.2" + cpu = 512 + memory = 1024 + essential = true + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = aws_cloudwatch_log_group.ecs_log_group.name + awslogs-region = var.region + awslogs-stream-prefix = "redis" + } + } + portMappings = [{ + containerPort = 6379 + hostPort = 6379 + protocol = "tcp" + name = "redis" + }] + }, + { + name = "postgrest" + image = "postgrest/postgrest:v12.2.0" + cpu = 512 + memory = 1024 + essential = true + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = aws_cloudwatch_log_group.postgrest_log_group.name + awslogs-region = var.region + awslogs-stream-prefix = "postgrest" + awslogs-create-group = "true" + } + } + portMappings = [{ + containerPort = 3002 + hostPort = 3002 + protocol = "tcp" + name = "postgrest" + appProtocol = "http" + }] + environment = [ + { + name = "PGRST_HOST" + value = "127.0.0.1:3002" + }, + { + name = "PGRST_LOG_LEVEL" + value = "info" + }, + { + name = "PGRST_DB_PRE_CONFIG" + value = "postgrest.pre_config" + }, + { + name = "PGRST_SERVER_PORT" + value = "3002" + }, + { + name = "PGRST_DB_URI" + value = "postgres://${var.TOOLJET_DB_USER}:${var.TOOLJET_DB_PASS}@${aws_db_instance.tooljet_database.endpoint}/${var.TOOLJET_DB}" + }, + { + name = "PGRST_JWT_SECRET" + value = var.PGRST_JWT_SECRET + } + ] + } + ]) + + depends_on = [ + aws_db_instance.tooljet_database + ] +} + +# depends_on = [ +# aws_db_instance.tooljet_database, +# aws_memorydb_cluster.tooljet_mem_cluster +# ] + + + +resource "aws_ecs_service" "tooljet_service" { + name = var.ServiceName + cluster = aws_ecs_cluster.tooljet_cluster.id + task_definition = aws_ecs_task_definition.tooljet_task_definition.arn + launch_type = "FARGATE" + desired_count = 2 + + network_configuration { + subnets = [aws_subnet.subnet1.id, aws_subnet.subnet2.id] + security_groups = [aws_security_group.task_sg.id] + assign_public_ip = true + } + + load_balancer { + target_group_arn = aws_lb_target_group.tooljet_target_group.arn + container_name = var.AppName + container_port = 3000 + } + + health_check_grace_period_seconds = 900 # tooljet requires 900 seconds to start + deployment_maximum_percent = 200 + deployment_minimum_healthy_percent = 100 +} + +resource "aws_lb" "tooljet_lb" { + name = "tooljet-lb" + internal = false + load_balancer_type = "application" + security_groups = [aws_security_group.lb_sg.id] + subnets = [aws_subnet.subnet1.id, aws_subnet.subnet2.id] +} + +resource "aws_lb_target_group" "tooljet_target_group" { + name = "tooljet-tg" + port = 3000 + protocol = "HTTP" + vpc_id = aws_vpc.tooljet_vpc.id + target_type = "ip" + + health_check { + path = "/api/health" + protocol = "HTTP" + } +} + +resource "aws_lb_listener" "listener_80" { + load_balancer_arn = aws_lb.tooljet_lb.arn + port = 80 + protocol = "HTTP" + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.tooljet_target_group.arn + } +} + +# resource "aws_lb_listener" "listener_443" { +# load_balancer_arn = aws_lb.tooljet_lb.arn +# port = 443 +# protocol = "HTTPS" +# ssl_policy = "ELBSecurityPolicy-2016-08" +# certificate_arn = "" # Replace with your certificate ARN + +# default_action { +# type = "forward" +# target_group_arn = aws_lb_target_group.tooljet_target_group.arn +# } +# } + +resource "aws_vpc" "tooljet_vpc" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "subnet1" { + vpc_id = aws_vpc.tooljet_vpc.id + cidr_block = "10.0.1.0/24" + availability_zone = var.aws_subnet_subnet1_AZ + map_public_ip_on_launch = true +} + +resource "aws_subnet" "subnet2" { + vpc_id = aws_vpc.tooljet_vpc.id + cidr_block = "10.0.2.0/24" + availability_zone = var.aws_subnet_subnet2_AZ + map_public_ip_on_launch = true +} + +resource "aws_internet_gateway" "tooljet_igw" { + vpc_id = aws_vpc.tooljet_vpc.id +} + +resource "aws_route_table" "public_rt" { + vpc_id = aws_vpc.tooljet_vpc.id +} + +resource "aws_route" "public_route" { + route_table_id = aws_route_table.public_rt.id + destination_cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.tooljet_igw.id +} + +resource "aws_route_table_association" "subnet1_association" { + subnet_id = aws_subnet.subnet1.id + route_table_id = aws_route_table.public_rt.id +} + +resource "aws_route_table_association" "subnet2_association" { + subnet_id = aws_subnet.subnet2.id + route_table_id = aws_route_table.public_rt.id +} + +resource "aws_security_group" "task_sg" { + vpc_id = aws_vpc.tooljet_vpc.id + + ingress { + description = "ToolJet application port" + from_port = 3000 + to_port = 3000 + protocol = "tcp" + security_groups = [aws_security_group.lb_sg.id] + } + ingress { + description = "PostgREST port" + from_port = 3002 + to_port = 3002 + protocol = "tcp" + security_groups = [aws_security_group.lb_sg.id] + } + + ingress { + description = "Redis port (internal)" + from_port = 6379 + to_port = 6379 + protocol = "tcp" + self = true + } + + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_security_group" "lb_sg" { + vpc_id = aws_vpc.tooljet_vpc.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + } + + +resource "aws_db_instance" "tooljet_database" { + allocated_storage = 100 + instance_class = "db.t3.micro" + engine = "postgres" + engine_version = "16" + db_name = "postgres" + username = "postgres" + password = "postgres" + vpc_security_group_ids = [aws_security_group.rds_sg.id] + db_subnet_group_name = aws_db_subnet_group.rds_subnet_group.name + skip_final_snapshot = true +} + +resource "aws_security_group" "rds_sg" { + vpc_id = aws_vpc.tooljet_vpc.id + + ingress { + description = "PostgreSQL" + from_port = 5432 + to_port = 5432 + protocol = "tcp" + security_groups = [aws_security_group.task_sg.id] + } +} + +resource "aws_db_subnet_group" "rds_subnet_group" { + name = "tooljet-rds-subnet-group" + subnet_ids = [aws_subnet.subnet1.id, aws_subnet.subnet2.id] +} diff --git a/terraform/ECS/redis.tf b/terraform/ECS/redis.tf new file mode 100644 index 0000000000..064bb24d71 --- /dev/null +++ b/terraform/ECS/redis.tf @@ -0,0 +1,39 @@ +# resource "aws_memorydb_subnet_group" "tooljet_subnet_group" { +# name = "tooljet-subnet-group" +# description = "ToolJet MemoryDB subnet group" +# subnet_ids = [aws_subnet.subnet1.id, aws_subnet.subnet2.id] +# } + +# resource "aws_memorydb_cluster" "tooljet_mem_cluster" { +# acl_name = "open-access" +# name = "tooljet-mem-cluster" +# node_type = "db.t4g.small" +# num_shards = 1 +# subnet_group_name = aws_memorydb_subnet_group.tooljet_subnet_group.name +# port = 6379 + +# security_group_ids = [aws_security_group.memdb_sg.id] + +# # maintenance_window = "sun:23:00-mon:01:30" +# # snapshot_retention_limit = 7 +# } + +# resource "aws_security_group" "memdb_sg" { +# name = "tooljet-memdb-sg" +# description = "Security group for ToolJet MemoryDB" +# vpc_id = aws_vpc.tooljet_vpc.id + +# ingress { +# from_port = 6379 +# to_port = 6379 +# protocol = "tcp" +# cidr_blocks = ["0.0.0.0/0"] +# } + +# egress { +# from_port = 0 +# to_port = 0 +# protocol = "-1" +# cidr_blocks = ["0.0.0.0/0"] +# } +# } diff --git a/terraform/ECS/terraform.tfvars b/terraform/ECS/terraform.tfvars new file mode 100644 index 0000000000..88008c2555 --- /dev/null +++ b/terraform/ECS/terraform.tfvars @@ -0,0 +1,24 @@ +region = "" +aws_subnet_subnet1_AZ = "" +aws_subnet_subnet2_AZ = "" + +# Environment Variables +TOOLJET_DB = "" +TOOLJET_DB_USER = "" +TOOLJET_DB_PASS = "" +# TOOLJET_DB_HOST = "" # use in case of external DB + +PG_USER = "" +PG_PASS = "" +PG_DB = "" +# PG_HOST = "" # use in case of external DB +LOCKBOX_MASTER_KEY = "" +SECRET_KEY_BASE = "" + +# Redis +REDIS_HOST = "" +REDIS_PORT = "" +REDIS_USER = "" +REDIS_PASSWORD = "" + +PGRST_JWT_SECRET = "" diff --git a/terraform/ECS/variables.tf b/terraform/ECS/variables.tf new file mode 100644 index 0000000000..5f970226d5 --- /dev/null +++ b/terraform/ECS/variables.tf @@ -0,0 +1,60 @@ +variable "region" { + default = "us-east-1" +} +variable "aws_subnet_subnet1_AZ" { + default = "us-east-1c" +} +variable "aws_subnet_subnet2_AZ" { + default = "us-east-1d" +} +variable "AppName" { + default = "ToolJet" +} +variable "ServiceName" { + default = "Tooljet-service" +} +variable "TOOLJET_DB" { + default = "" +} +variable "TOOLJET_DB_HOST" { + default = "" +} +variable "TOOLJET_DB_USER" { + default = "" +} +variable "TOOLJET_DB_PASS" { + default = "" +} +variable "PG_HOST" { + default = "" +} +variable "PG_USER" { + default = "postgres" +} +variable "PG_PASS" { + default = "postgres" +} +variable "PG_DB" { + default = "postgres" +} +variable "LOCKBOX_MASTER_KEY" { + default = "" +} +variable "SECRET_KEY_BASE" { + default = "" +} +variable "REDIS_HOST" { + default = "127.0.0.1" +} +variable "REDIS_PORT" { + default = "" +} +variable "REDIS_USER" { + default = "" +} +variable "REDIS_PASSWORD" { + default = "" +} +variable "PGRST_JWT_SECRET" { + default = "" +}