blob: ef41189b84857bca13525d931857e670747ad275 [file]
#
# Terraform file for deploying lnt.llvm.org.
#
terraform {
backend "s3" {
bucket = "lnt.llvm.org-terraform-state-prod"
key = "terraform.tfstate"
region = "us-west-2"
encrypt = true
}
}
locals {
availability_zone = "us-west-2a"
}
provider "aws" {
region = "us-west-2"
}
#
# Setup secrets and other variables
#
# Note that the LNT database password and the LNT authentication token for destructive actions
# must be stored in the AWS Secrets Manager under a secrets named `lnt.llvm.org-secrets`, and
# with the `lnt-db-password` and `lnt-auth-token` keys respectively. This secrets must exist
# in whatever AWS account is currently authenticated when running Terraform.
#
data "aws_secretsmanager_secret" "lnt_secrets" {
name = "lnt.llvm.org-secrets"
}
data "aws_secretsmanager_secret_version" "lnt_secrets_latest" {
secret_id = data.aws_secretsmanager_secret.lnt_secrets.id
}
locals {
# The Docker image to use for the webserver part of the LNT service
lnt_image = "56a3c8974301d2c70cc14676bf29974bb623ca6c"
# The port on the EC2 instance used by the Docker webserver for communication
lnt_external_port = "80"
# The database password for the lnt.llvm.org database.
lnt_db_password = jsondecode(data.aws_secretsmanager_secret_version.lnt_secrets_latest.secret_string)["lnt-db-password"]
# The authentication token to perform destructive operations on lnt.llvm.org.
lnt_auth_token = jsondecode(data.aws_secretsmanager_secret_version.lnt_secrets_latest.secret_string)["lnt-auth-token"]
}
#
# Setup the EC2 instance
#
data "aws_ami" "amazon_linux_2023" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-ecs-hvm-*-kernel-*-x86_64"]
}
}
data "cloudinit_config" "startup_scripts" {
base64_encode = true
part {
filename = "on-ec2-setup.sh"
content_type = "text/x-shellscript"
content = file("${path.module}/on-ec2-setup.sh")
}
part {
content_type = "text/cloud-config"
content = yamlencode({
write_files = [
{
path = "/etc/lnt/compose.yaml"
permissions = "0400" # read-only for owner
content = file("${path.module}/../docker/compose.yaml")
},
{
path = "/etc/lnt/ec2-volume-mapping.yaml"
permissions = "0400" # read-only for owner
content = file("${path.module}/ec2-volume-mapping.yaml")
},
{
path = "/etc/lnt/compose.env"
permissions = "0400" # read-only for owner
content = templatefile("${path.module}/compose.env.tpl", {
__db_password__ = local.lnt_db_password,
__auth_token__ = local.lnt_auth_token,
__lnt_image__ = local.lnt_image,
__lnt_nginx_config__ = "/etc/lnt/nginx.conf",
__lnt_external_port__ = local.lnt_external_port,
})
},
{
path = "/etc/lnt/nginx.conf"
permissions = "0400" # read-only for owner
content = file("${path.module}/../docker/nginx.conf")
},
{
path = "/etc/lnt/on-ec2-boot.sh"
permissions = "0400" # read-only for owner
content = file("${path.module}/on-ec2-boot.sh")
}
]
})
}
}
resource "aws_security_group" "server" {
name = "lnt.llvm.org/server-security-group"
description = "Allow SSH and HTTP traffic"
ingress {
description = "Allow incoming SSH traffic from anywhere"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "Allow incoming HTTP traffic from anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
description = "Allow outgoing traffic to anywhere"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "server" {
ami = data.aws_ami.amazon_linux_2023.id
availability_zone = local.availability_zone
instance_type = "t2.small"
security_groups = [aws_security_group.server.name]
tags = {
Name = "lnt.llvm.org/server"
}
# Deploy the cloud-init files to the instance on creation. Also make sure that
# changing the cloud-init data causes the instance to be re-created, otherwise
# the files are not updated. This should probably be the default Terraform behavior.
user_data_base64 = data.cloudinit_config.startup_scripts.rendered
user_data_replace_on_change = true
}
# Allocate a stable elastic IP for the webserver
resource "aws_eip" "stable_ip" {
instance = aws_instance.server.id
domain = "vpc"
}
output "web_server_public_ip" {
value = aws_eip.stable_ip.public_ip
}
#
# Setup the EBS volume attached to the instance that stores the DB
# and other instance-related configuration (e.g. the schema files,
# profiles and anything else that should persist).
#
resource "aws_ebs_volume" "persistent_state" {
availability_zone = local.availability_zone
size = 128 # GiB
type = "gp2"
tags = {
Name = "lnt.llvm.org/persistent-state"
}
}
resource "aws_volume_attachment" "persistent_state_attachment" {
instance_id = aws_instance.server.id
volume_id = aws_ebs_volume.persistent_state.id
device_name = "/dev/sdh"
}