diff --git a/sds/unity/terraform/add-mmgis.sh b/sds/unity/terraform/add-mmgis.sh new file mode 100644 index 00000000..0c051a3a --- /dev/null +++ b/sds/unity/terraform/add-mmgis.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# From https://gist.github.com/jamesmishra/18ee5d7d053db9958d0e4ccbb37f8e1d +set -Eeuxo pipefail +# Filesystem code is adapted from: +# https://github.com/GSA/devsecops-example/blob/03067f68ee2765f8477ae84235f7faa1d2f2cb70/terraform/files/attach-data-volume.sh +DEVICE=${local.block_device_path} +DEST=${var.persistent_volume_mount_path} +devpath=$(readlink -f $DEVICE) + +if [[ $(file -s $devpath) != *ext4* && -b $devpath ]]; then + # Filesystem has not been created. Create it! + mkfs -t ext4 $devpath +fi +# add to fstab if not present +if ! egrep "^$devpath" /etc/fstab; then + echo "$devpath $DEST ext4 defaults,nofail,noatime,nodiratime,barrier=0,data=writeback 0 2" | tee -a /etc/fstab > /dev/null +fi +mkdir -p $DEST +mount $DEST +chown ec2-user:ec2-user $DEST +chmod 0755 $DEST + +# Filesystem code is over +# Now we install docker and docker-compose. +# Adapted from: +# https://gist.github.com/npearce/6f3c7826c7499587f00957fee62f8ee9 +yum update -y +amazon-linux-extras install docker +systemctl start docker.service +usermod -a -G docker ec2-user +chkconfig docker on +yum install -y python3-pip +python3 -m pip install docker-compose + +# Put the docker-compose.yml file at the root of our persistent volume +cat > $DEST/docker-compose.yml <<-TEMPLATE +${var.docker_compose_str} +TEMPLATE + +# Write the systemd service that manages us bringing up the service +cat > /etc/systemd/system/mmgis.service <<-TEMPLATE +[Unit] +Description=${var.description} +After=${var.systemd_after_stage} +[Service] +Type=simple +User=${var.user} +ExecStart=/usr/local/bin/docker-compose -f $DEST/docker-compose.yml up +Restart=on-failure +[Install] +WantedBy=multi-user.target +TEMPLATE + +# Start the service. +systemctl start mmgis \ No newline at end of file diff --git a/sds/unity/terraform/bk.tf b/sds/unity/terraform/bk.tf new file mode 100644 index 00000000..130558e4 --- /dev/null +++ b/sds/unity/terraform/bk.tf @@ -0,0 +1,58 @@ +resource "aws_iam_instance_profile" "unity_mmgis_instance_profile" { + name = "unity-mmgis-instance-profile-tf" + + role = var.role + + tags = { + Name = "unity_mmgis_instance_profile" + } +} + +resource "aws_instance" "unity_mmgis_instance" { + ami = var.ami + instance_type = "t3.large" + + tags = { + Name = "unity-mmgis-instance-tf" + } + + #key_name = var.key_name + + vpc_security_group_ids = [var.sg_id] + + subnet_id = var.subnet_id + + iam_instance_profile = aws_iam_instance_profile.unity_mmgis_instance_profile.name + + block_device_path = "/dev/sdh" + user_data = file("./add-mmgis.sh") +} + +resource "aws_ebs_volume" "persistent" { + availability_zone = aws_instance.this.availability_zone + size = var.persistent_volume_size_gb +} + +resource "aws_volume_attachment" "persistent" { + device_name = local.block_device_path + volume_id = aws_ebs_volume.persistent.id + instance_id = aws_instance.this.id +} + +resource "aws_instance" "this" { + ami = data.aws_ami.latest_amazon_linux.id + availability_zone = var.availability_zone + instance_type = var.instance_type + key_name = var.key_name + associate_public_ip_address = var.associate_public_ip_address + vpc_security_group_ids = var.vpc_security_group_ids + subnet_id = var.subnet_id + iam_instance_profile = var.iam_instance_profile + user_data = local.user_data + tags = merge ( + { + Name = var.name + }, + var.tags + ) +} \ No newline at end of file diff --git a/sds/unity/terraform/lb.tf b/sds/unity/terraform/lb.tf new file mode 100644 index 00000000..0a5ea2b1 --- /dev/null +++ b/sds/unity/terraform/lb.tf @@ -0,0 +1,70 @@ +# target group +resource "aws_lb_target_group" "unity_mmgis_tg_tf" { + name = "unity-mmgis-tg-tf" + port = 8080 + protocol = "TCP" + target_type = "instance" + #vpc_id = data.aws_vpc.default.id + vpc_id = var.vpc_id + + health_check { + enabled = true + protocol = "HTTP" + port = 8080 + path = "/unity/v0/collections/MUR25-JPL-L4-GLOB-v4.2_analysed_sst/processes" + interval = 30 + timeout = 10 + matcher = 200 + healthy_threshold = 5 + unhealthy_threshold = 2 + } + + tags = { + Name = "unity_mmgis_tg_tf" + } +} + +# attach instance +resource "aws_lb_target_group_attachment" "unity_mmgis_tg_attachment_tf" { + target_group_arn = aws_lb_target_group.unity_mmgis_tg_tf.arn + target_id = aws_instance.unity_mmgis_instance.id + port = 8080 +} + +# create alb +resource "aws_lb" "unity-mmgis-lb-tf" { + name = "unity-mmgis-lb-tf" + load_balancer_type = "network" + internal = true + #security_groups = [var.sg_id] + #security_groups = [] + #subnets = [for subnet in aws_subnet.public : subnet.id] + subnets = var.subnet_ids + + enable_deletion_protection = false + + #access_logs { + # bucket = "tbd" + # prefix = "mmgis/tbd/unity-mmgis-lb" + # enabled = true + #} + + tags = { + Name = "unity-mmgis-lb-tf" + } +} + +resource "aws_lb_listener" "unity_mmgis_lb_listener" { + load_balancer_arn = aws_lb.unity-mmgis-lb-tf.arn + port = 80 + protocol = "TCP" + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.unity_mmgis_tg_tf.arn + } + + tags = { + Name = "unity_mmgis_lb_listener" + } +} \ No newline at end of file diff --git a/sds/unity/terraform/main.tf b/sds/unity/terraform/main.tf new file mode 100644 index 00000000..c757fda4 --- /dev/null +++ b/sds/unity/terraform/main.tf @@ -0,0 +1,99 @@ +data "aws_ssm_parameter" "vpc_id" { + name = "/unity/account/network/vpc_id" +} + +data "aws_ssm_parameter" "subnet_list" { + name = "/unity/account/network/subnet_list" +} + +#data "aws_ssm_parameter" "u-cs-ecs" { +# name = "/unity/account/ecs/execution_role_arn" +#} + +module "base" { + source = "" + project = var.project + venue = var.venue + subsystem = var.subsystem + capability = var.capability + custom_url = var.custom_url + groups = var.groups + api = var.api + component = var.component + desired_count = var.desired_count + app_protocol = var.app_protocol + app_listening_port = var.app_listening_port + environment = local.environment_vars + ecr_uri = var.ecr_uri + docker_image_name = var.docker_image_name + docker_image_tag = var.docker_image_tag + max_capacity = var.max_capacity + app_one_ecs = var.app_one_ecs + instance_type = var.instance_type + ebs_block_device_size = var.ebs_block_device_size + root_block_device_size = var.root_block_device_size + ebs_mount_directory = var.ebs_mount_directory + application_endpoint_url = var.application_endpoint_url + terraform_app_commit = var.terraform_app_commit + deployment_method = var.deployment_method + secrets = local.secrets + docker_volume_path = var.docker_volume_path + efs_config = { + efs_id = var.efs_id + efs_root_directory = var.efs_root_directory + } +} + +locals { + subnet_map = jsondecode(data.aws_ssm_parameter.subnet_list.value) + subnet_ids = nonsensitive(local.subnet_map["private"]) + public_subnet_ids = nonsensitive(local.subnet_map["public"]) +} + + +# Application environment variables +locals { + environment_vars = { + AWS_DEFAULT_REGION = module.base.aws_region + DOMAIN = module.base.cname + SERVER = var.server + AUTH = var.auth + NODE_ENV = var.node_env + DB_HOST = var.db_host + DB_PORT = var.db_port + DB_NAME = var.db_name + DB_USER = var.db_user + PORT = var.app_listening_port + DB_POOL_MAX = var.db_pool_max + DB_POOL_TIMEOUT = var.db_pool_timeout + DB_POOL_IDLE = var.db_pool_idle + CSSO_GROUPS = var.csso_groups + VERBOSE_LOGGING = var.verbose_logging + FRAME_ANCESTORS = var.frame_ancestors + FRAME_SRC = var.frame_src + THIRD_PARTY_COOKIES = var.third_party_cookies + ROOT_PATH = var.root_path + WEBSOCKET_ROOT_PATH = var.websocket_root_path + CLEARANCE_NUMBER = var.clearance_number + DISABLE_LINK_SHORTENER = var.disable_link_shortener + HIDE_CONFIG = var.hide_config + FORCE_CONFIG_PATH = var.force_config_path + LEADS = "[${join(", ", formatlist("\"%s\"", var.leads))}]" + ENABLE_MMGIS_WEBSOCKETS = var.enable_mmgis_websockets + ENABLE_CONFIG_WEBSOCKETS = var.enable_config_websockets + ENABLE_CONFIG_OVERRIDE = var.enable_config_override + MAIN_MISSION = var.main_mission + SKIP_CLIENT_INITIAL_LOGIN = var.skip_client_initial_login + GENERATE_SOURCEMAP = var.generate_sourcemap + SPICE_SCHEDULED_KERNEL_DOWNLOAD = var.spice_scheduled_kernel_download + SPICE_SCHEDULED_KERNEL_DOWNLOAD_ON_START = var.spice_scheduled_kernel_download_on_start + SPICE_SCHEDULED_KERNEL_cron_expr = var.spice_scheduled_kernel_cron_expr + } +} + +locals { + secrets = { + SECRET = var.secret + DB_PASS = var.db_pass + } +} \ No newline at end of file diff --git a/sds/unity/terraform/output.tf b/sds/unity/terraform/output.tf new file mode 100644 index 00000000..29a42d63 --- /dev/null +++ b/sds/unity/terraform/output.tf @@ -0,0 +1,84 @@ +# We try to match the API contract that `aws_instance` has. +# Descriptions for these outputs are copied from: +# https://www.terraform.io/docs/providers/aws/r/instance.html +output "id" { + description = "The instance ID" + value = aws_instance.this.id +} + +output "arn" { + description = "The ARN of the instance" + value = aws_instance.this.arn +} + +output "availability_zone" { + description = "The availability zone of the instance" + value = aws_instance.this.availability_zone +} + +output "placement_group" { + description = "The placement group of the instance" + value = aws_instance.this.placement_group +} + +output "public_dns" { + description = "The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC" + value = aws_instance.this.public_dns +} + +output "public_ip" { + description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use public_ip, as this field will change after the EIP is attached." + value = aws_instance.this.public_ip +} + +output "ipv6_addresses" { + description = "A list of assigned IPv6 addresses, if any" + value = aws_instance.this.ipv6_addresses +} + +output "primary_network_interface_id" { + description = "The ID of the instance's primary network interface" + value = aws_instance.this.primary_network_interface_id +} + +output "private_dns" { + description = " The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC" + value = aws_instance.this.private_dns +} + +output "private_ip" { + description = "The private IP address assigned to the instance" + value = aws_instance.this.private_ip +} + +output "security_groups" { + description = " The associated security groups." + value = aws_instance.this.security_groups +} + +output "vpc_security_group_ids" { + description = "The associated security groups in non-default VPC." + value = aws_instance.this.vpc_security_group_ids +} + +output "subnet_id" { + description = "The VPC subnet ID." + value = aws_instance.this.subnet_id +} + +output "credit_specification" { + description = " Credit specification of instance." + value = aws_instance.this.credit_specification +} + +output "instance_state" { + description = "The state of the instance. One of: pending, running, shutting-down, terminated, stopping, stopped. See Instance Lifecycle for more information." + value = aws_instance.this.instance_state +} + +# TODO: This is a list with the `aws_instance` resource and we are just +# returning a string. I know there is an obvious solution for this... +output "ebs_block_device_id" { + description = "The persistent block device that we are storing information on." + value = aws_ebs_volume.persistent.id +} \ No newline at end of file diff --git a/sds/unity/terraform/terraform.tf b/sds/unity/terraform/terraform.tf index c757fda4..e8dd1a6e 100644 --- a/sds/unity/terraform/terraform.tf +++ b/sds/unity/terraform/terraform.tf @@ -1,99 +1,84 @@ -data "aws_ssm_parameter" "vpc_id" { - name = "/unity/account/network/vpc_id" +# ===== OUR MAGIC DOCKER-COMPOSE.YML FILE HERE ===== +# It is also possible to get Terraform to read an external `docker-compose.yml` +# file and load it into this variable. +# We'll be showing off a demo nginx page. +variable "example_docker_compose" { + type = string + default = <