Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VM deployment, optional azure psql flex #26

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions modules/azure_vm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Azure VM Standalone Deployment

## Requirements

- VPC with desired subnets

## Usage

1. Directly use our module in your existing Terraform configuration and provide the required variables
- If you want to use an external Postgres instance (Flexible Server), set external_psql to true. This is recommended for production deployments.

```
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.74"
}
}
}

provider "azurerm" {
features {}
}


module "retool" {
source = "[email protected]:tryretool/retool-terraform.git//modules/azure_vm"
external_psql = "true" | "false"
psql_password = "<secure-password-for-external-psql>"
resource_group_name = "<resource-group-name>"
subnet_name = "<subnet-name>"
version_number = "<desired-retool-version eg 3.12.4>"
virtual_network_name = "<vnet-name>"
}

output "vm_public_ip" {
value = module.retool.vm_public_ip
description = "Public IP of VM Instance"
}
```
2. Copy `vm_script.sh` to your local Terraform directory.

3. Run `terraform plan` to confirm that the changes look good

4. Run `terraform apply` to apply the configuration changes

5. After a few minutes, SSH into your newly created EC2 instance using the Key Pair passed into `ssh_key_path`, defaults to `~/.ssh/id_rsa.pub`

6. Verify that the GitHub repository exists

```
sudo su -
cd /retool/retool-onpremise
```

7. Verify that the Dockerfile contains the correct Retool version number

```
# you should see the X.Y.Z version number specified
vim Dockerfile
```

8. Verify that all of the Docker containers are up and running. If one of them is not running or restarting, try re-creating the containers with (`docker-compose up -d`)

```
docker-ps
```

9. Modify your environment variables in `docker.env`. If you have an external RDS database (strongly recommended), replace the `POSTGRES_` environment variables with the new ones.

- If testing out your instance for the first time without SSL/HTTPS, you should uncomment `# COOKIE_INSECURE = true`
- Replace your `LICENSE_KEY` with your provided Retool license key
- If using Postgres Flexible server, add `POSTGRES_SSL_ENABLED=true`

10. Add any additional configuration needed to the `docker.env` file. You can refer to our documentation for [all additional environment variables](https://docs.retool.com/docs/environment-variables).

11. Access your Retool instance on the public IP that is given via the resource creation outputs. If no SSL certificate has been configured you need to access the instance on port 3000 (append :3000 to the end of the URL) and via http.

### Security Rules

You can configure the security group ingress and egress rules using input variables:

For example, to create a Retool instance accessible from anywhere, you can use the following value for `security_rules` which enables inbound traffic on ports (`30`, `443`, `22`, and `3000`) and all outbound traffic. Note that this is also the default behavior of this module.

```
security_rules = [
{
name = "GlobalHTTP"
priority = 300
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "*"
destination_address_prefix = "*"
},
{
name = "GlobalHTTPS"
priority = 310
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "*"
destination_address_prefix = "*"
},
{
name = "SSH"
priority = 320
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
},
{
name = "ApplicationPort"
priority = 330
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "3000"
source_address_prefix = "*"
destination_address_prefix = "*"
}
]
```

193 changes: 193 additions & 0 deletions modules/azure_vm/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.74"
}
}
}

locals {
flat_security_rules = {
for rule in var.security_rules :
rule.name => rule
}
}

data "azurerm_resource_group" "selected" {
name = var.resource_group_name
}

data "azurerm_virtual_network" "selected" {
name = var.virtual_network_name
resource_group_name = data.azurerm_resource_group.selected.name
}

data "azurerm_subnet" "selected" {
name = var.subnet_name
resource_group_name = data.azurerm_resource_group.selected.name
virtual_network_name = data.azurerm_virtual_network.selected.name
}

resource "azurerm_public_ip" "this" {
name = "retool_public_ip"
resource_group_name = data.azurerm_resource_group.selected.name
location = data.azurerm_resource_group.selected.location
allocation_method = "Static"
lifecycle {
create_before_destroy = true
}
}

resource "azurerm_network_interface" "this" {
name = "retoolni"
location = data.azurerm_resource_group.selected.location
resource_group_name = data.azurerm_resource_group.selected.name

ip_configuration {
name = "retool-ni-config"
subnet_id = data.azurerm_subnet.selected.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.this.id
}
}

resource "azurerm_network_security_group" "this" {
name = "retool-sg"
location = data.azurerm_resource_group.selected.location
resource_group_name = data.azurerm_resource_group.selected.name
}

resource "azurerm_network_security_rule" "this" {
for_each = local.flat_security_rules
name = each.value.name
priority = each.value.priority
direction = each.value.direction
access = each.value.access
protocol = each.value.protocol
source_port_range = each.value.source_port_range
destination_port_range = each.value.destination_port_range
source_address_prefix = each.value.source_address_prefix
destination_address_prefix = each.value.destination_address_prefix
resource_group_name = data.azurerm_resource_group.selected.name
network_security_group_name = azurerm_network_security_group.this.name


}

resource "azurerm_network_interface_security_group_association" "this" {
network_interface_id = azurerm_network_interface.this.id
network_security_group_id = azurerm_network_security_group.this.id
}

resource "azurerm_linux_virtual_machine" "this" {
name = "retool"
resource_group_name = data.azurerm_resource_group.selected.name
location = data.azurerm_resource_group.selected.location
size = var.instance_size
admin_username = "retooladmin"
network_interface_ids = [
azurerm_network_interface.this.id,
]

admin_ssh_key {
username = "retooladmin"
public_key = file(var.ssh_key_path)
}

os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
disk_size_gb = "160"
}

source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts-gen2"
version = "latest"
}
}


resource "azurerm_virtual_machine_extension" "this" {
name = "retool"
virtual_machine_id = azurerm_linux_virtual_machine.this.id
publisher = "Microsoft.Azure.Extensions"
type = "CustomScript"
type_handler_version = "2.0"

settings = <<SETTINGS
{
"script": "${base64encode(templatefile("vm_script.sh", {
version_number = "${var.version_number}"
}))}"
}
SETTINGS

}

resource "azurerm_subnet" "this" {
count = var.external_psql ? 1 : 0
name = "psql-flex-subnet"
resource_group_name = data.azurerm_resource_group.selected.name
virtual_network_name = data.azurerm_virtual_network.selected.name
address_prefixes = var.psql_subnet_cidr
service_endpoints = ["Microsoft.Storage"]
delegation {
name = "fs"
service_delegation {
name = "Microsoft.DBforPostgreSQL/flexibleServers"
actions = [
"Microsoft.Network/virtualNetworks/subnets/join/action",
]
}
}
}
resource "azurerm_private_dns_zone" "this" {
count = var.external_psql ? 1 : 0
name = "retool-dbs.postgres.database.azure.com"
resource_group_name = data.azurerm_resource_group.selected.name
}

resource "azurerm_private_dns_zone_virtual_network_link" "this" {
count = var.external_psql ? 1 : 0
name = "retool-internal.com"
private_dns_zone_name = azurerm_private_dns_zone.this[0].name
virtual_network_id = data.azurerm_virtual_network.selected.id
resource_group_name = data.azurerm_resource_group.selected.name
}

resource "azurerm_postgresql_flexible_server" "this" {
count = var.external_psql ? 1 : 0
name = var.psql_flex_name
resource_group_name = data.azurerm_resource_group.selected.name
location = data.azurerm_resource_group.selected.location
version = "12"
delegated_subnet_id = azurerm_subnet.this[0].id
private_dns_zone_id = azurerm_private_dns_zone.this[0].id
administrator_login = var.psql_user
administrator_password = var.psql_password
zone = "1"

storage_mb = 32768

sku_name = var.db_instance_size
depends_on = [azurerm_private_dns_zone_virtual_network_link.this[0]]

}

resource "azurerm_postgresql_flexible_server_database" "this" {
count = var.external_psql ? 1 : 0
name = "retool"
server_id = azurerm_postgresql_flexible_server.this[0].id
collation = "en_US.utf8"
charset = "utf8"
}

resource "azurerm_postgresql_flexible_server_configuration" "this" {
count = var.external_psql ? 1 : 0
name = "azure.extensions"
server_id = azurerm_postgresql_flexible_server.this[0].id
value = "UUID-OSSP"
}
14 changes: 14 additions & 0 deletions modules/azure_vm/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
output "flex_subnet" {
value = azurerm_subnet.this[0].id
description = "Subnet id of subnet for Azure Flexible servers"
}

output "private_dns_zone_id" {
value = azurerm_private_dns_zone.this[0].id
description = "Id of private dns zone"
}

output "vm_public_ip" {
value = azurerm_public_ip.this.ip_address
description = "Public IP of VM Instance"
}
Loading