Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
deployment/
*.tf
*.log
.git/
1 change: 0 additions & 1 deletion config/credentials.yml.enc

This file was deleted.

Binary file removed deployment/ecs.zip
Binary file not shown.
1 change: 0 additions & 1 deletion deployment/ecs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
.sql
# Local .terraform directories
**/.terraform/*

.terraform*
.terraform.lock.hcl
# .tfstate files
Expand Down
4 changes: 2 additions & 2 deletions deployment/ecs/backend-prod.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
bucket = "mpath-terraform-state"
key = "mpath/ecs/vpc/terraform.tfstate"
bucket = "mpath-terraform-remote-state"
key = "mpath/vpc/terraform.tfstate"
region = "us-east-1"
encrypt = true
use_lockfile = true
2 changes: 2 additions & 0 deletions deployment/ecs/envs/bo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
app_env.json
app_env*.json
53 changes: 53 additions & 0 deletions deployment/ecs/envs/bo/app_env_local_file.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
data "aws_secretsmanager_secret_version" "db" {
secret_id = aws_secretsmanager_secret.db.id
}

locals {
db_secret = jsondecode(data.aws_secretsmanager_secret_version.db.secret_string)
db_host_from_rds = aws_db_instance.this.address
db_port_from_rds = aws_db_instance.this.port

effective_db_host = local.db_host_from_rds != "" ? local.db_host_from_rds : try(local.db_secret.host, "")
effective_db_port = local.db_port_from_rds != 0 ? local.db_port_from_rds : try(local.db_secret.port, 3306)
}

resource "local_file" "app_env_json" {
filename = "${path.module}/app_env.json"

content = templatefile("${path.module}/templates/app_env.json.tmpl", {
rails_env = var.rails_env
rails_log_to_stdout = var.rails_log_to_stdout
rails_serve_static = var.rails_serve_static
puma_port = var.puma_port
web_concurrency = var.web_concurrency
rails_max_threads = var.rails_max_threads
rails_min_threads = var.rails_min_threads

# handle null secret_key_base
secret_key_base = try(coalesce(var.secret_key_base, ""), "")

# Database fields (read-only from Secrets Manager + RDS)
db_name = try(local.db_secret.database, var.db_name)
db_host = local.effective_db_host
db_port = local.effective_db_port
db_user = local.db_secret.username
db_password = local.db_secret.password

# Office365 / SSO
office365_client_id = var.office365_client_id
office365_client_secret = var.office365_client_secret
office365_redirect_uri = var.office365_redirect_uri
office365_provider_url = var.office365_provider_url

# Keycloak
keycloak_client_id = var.keycloak_client_id
keycloak_client_secret = var.keycloak_client_secret
keycloak_realm = var.keycloak_realm
keycloak_server_url = var.keycloak_server_url

# SSL flag
use_ssl = var.use_ssl
})

file_permission = "0600"
}
2 changes: 1 addition & 1 deletion deployment/ecs/envs/bo/backend.hcl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
bucket = "mpath-terraform-state"
bucket = "mpath-terraform-remote-state"
key = "mpath/ecs/bo/terraform.tfstate"
region = "us-east-1"
encrypt = true
Expand Down
101 changes: 64 additions & 37 deletions deployment/ecs/envs/bo/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@ terraform {
backend "s3" {} # init with: terraform init -backend-config=backend.hcl
}

provider "aws" { region = var.aws_region }
provider "aws" {
region = var.aws_region
}

locals {
app_name = "mpath"
env = "bo"
env = var.environment
}


# Pull shared network from the ROOT stack
data "terraform_remote_state" "root" {
backend = "s3"
config = {
bucket = "mpath-terraform-state"
key = "mpath/root/terraform.tfstate"
region = var.aws_region
encrypt = true
bucket = "mpath-terraform-remote-state"
key = "mpath/vpc/terraform.tfstate"
region = var.aws_region
encrypt = true
}
}

Expand All @@ -35,56 +38,63 @@ locals {
}
}


data "aws_secretsmanager_secret_version" "mpath_cert" {
secret_id = "/mpath/acm/cert-arn"
}

locals {
_raw_cert_string = data.aws_secretsmanager_secret_version.mpath_cert.secret_string
_maybe_json = try(jsondecode(local._raw_cert_string), null)
_json_cert_arn = local._maybe_json == null ? "" : try(local._maybe_json.cert_arn, "")
acm_cert_from_sm = local._json_cert_arn != "" ? local._json_cert_arn : local._raw_cert_string

# Final selection: explicit var wins; else Secrets Manager
selected_acm_cert_arn = var.acm_certificate_arn != "" ? var.acm_certificate_arn : local.acm_cert_from_sm
# Or, if you’re worried about whitespace:
# selected_acm_cert_arn = trimspace(var.acm_certificate_arn) != "" ? var.acm_certificate_arn : local.acm_cert_from_sm
}



module "ecs_service" {
source = "../../modules/ecs"


cluster_name = "${local.app_name}-${local.env}"
service_name = "${local.app_name}-app-${local.env}"

vpc_id = local.vpc_id
subnet_ids = local.private_subnet_ids # ECS tasks -> private subnets

vpc_id = local.vpc_id
subnet_ids = local.private_subnet_ids
db_secret_arn = aws_secretsmanager_secret.db.arn
app_secret_arn = aws_secretsmanager_secret.app.arn
# Container / service
container_image = var.container_image
container_port = var.container_port # set to 8443 in terraform.tfvars
container_port = var.container_port
desired_count = var.desired_count
cpu = var.cpu
memory = var.memory
health_check_path = var.health_check_path

# Create ALB/TG/listeners *inside* the module (per-env ALB)
create_alb = true
public_subnet_ids = local.public_subnet_ids
alb_name = "${local.app_name}-${local.env}-alb"
# ALB/TG/listeners inside the module (per-env ALB)
create_alb = true
public_subnet_ids = local.public_subnet_ids
alb_name = "${local.app_name}-${local.env}-alb"
alb_deletion_protection = true
acm_certificate_arn = var.acm_certificate_arn
ssl_policy = var.ssl_policy
acm_certificate_arn = local.selected_acm_cert_arn
ssl_policy = var.ssl_policy

# Ops & deployments
platform_version = var.platform_version
deployment_maximum_percent = var.deployment_maximum_percent
deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent
deployment_circuit_breaker_enabled = var.deployment_circuit_breaker_enabled
deployment_circuit_breaker_rollback = var.deployment_circuit_breaker_rollback
container_insights_enabled = true
log_retention_days = var.log_retention_days
assign_public_ip = false
platform_version = var.platform_version
deployment_maximum_percent = var.deployment_maximum_percent
deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent
deployment_circuit_breaker_enabled = var.deployment_circuit_breaker_enabled
deployment_circuit_breaker_rollback = var.deployment_circuit_breaker_rollback
container_insights_enabled = true
log_retention_days = var.log_retention_days
assign_public_ip = false

tags = local.tags
}

output "bo_alb_dns_name" {
value = module.ecs_service.alb_dns_name
}

output "bo_target_group_arn" {
value = module.ecs_service.target_group_arn_effective
}

output "bo_ecs_service_sg" {
value = module.ecs_service.security_group_id
}

# Discover this env’s ALB (created by module.ecs_service)
data "aws_lb" "bo_alb" {
Expand All @@ -98,3 +108,20 @@ resource "aws_wafv2_web_acl_association" "mpath_web_acl_assoc" {
web_acl_arn = data.terraform_remote_state.root.outputs.waf_web_acl_arn
}

resource "random_password" "secret_key_base" {
length = 64
special = false
}

resource "aws_secretsmanager_secret" "app" {
name = "mpath/bo/app"
description = "mPATH BO app env"
}

resource "aws_secretsmanager_secret_version" "app" {
secret_id = aws_secretsmanager_secret.app.id
secret_string = jsonencode({
SECRET_KEY_BASE = random_password.secret_key_base.result
# ...other keys...
})
}
31 changes: 31 additions & 0 deletions deployment/ecs/envs/bo/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
output "rds_endpoint" {
description = "Full RDS endpoint (hostname:port)."
value = aws_db_instance.this.endpoint
}

output "rds_sg_id" {
description = "Security Group ID attached to the RDS instance."
value = aws_security_group.rds_mysql.id
}

output "rds_db_name" {
description = "Name of the database created."
value = var.db_name
}

output "db_secret_arn" {
description = "Secrets Manager secret ARN with DB credentials."
value = aws_secretsmanager_secret.db.arn
}

output "bo_alb_dns_name" {
value = module.ecs_service.alb_dns_name
}

output "bo_target_group_arn" {
value = module.ecs_service.target_group_arn_effective
}

output "bo_ecs_service_sg" {
value = module.ecs_service.security_group_id
}
97 changes: 97 additions & 0 deletions deployment/ecs/envs/bo/rds.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Password (only used if var.db_password == null)
resource "random_password" "db" {
length = 24
special = true
override_special = "!@#%^*-_=+"
}

locals {
db_password_final = coalesce(var.db_password, random_password.db.result)
derived_ecs_tasks_sg_name = coalesce(var.ecs_tasks_sg_name, "mpath-${local.env}-ecs-tasks-sg")
}

# DB Subnet Group (use private subnets from main.tf locals)
resource "aws_db_subnet_group" "this" {
name = "${var.db_identifier}-subnets"
subnet_ids = local.private_subnet_ids
tags = merge(local.tags, { Name = "${var.db_identifier}-subnets" })
}

# Security Group for RDS – allow MySQL from ECS tasks only
resource "aws_security_group" "rds_mysql" {
name = "${var.db_identifier}-sg"
description = "Allow MySQL from ECS tasks"
vpc_id = local.vpc_id

ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [module.ecs_service.service_sg_id]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

tags = merge(local.tags, { Name = "${var.db_identifier}-sg" })
}

# RDS Instance – MySQL 8, db.t3.micro, gp2, single-AZ
resource "aws_db_instance" "this" {
identifier = var.db_identifier
engine = "mysql"
engine_version = "8.0" # safe major pin
instance_class = "db.t3.micro"

storage_type = "gp2"
allocated_storage = var.db_allocated_storage
max_allocated_storage = 100

db_subnet_group_name = aws_db_subnet_group.this.name
vpc_security_group_ids = [aws_security_group.rds_mysql.id]
port = 3306

# Credentials
username = var.db_username
password = local.db_password_final
db_name = var.db_name

# Availability & security
multi_az = false
publicly_accessible = false
storage_encrypted = true

# Backups & lifecycle
backup_retention_period = 7
deletion_protection = false
skip_final_snapshot = false

apply_immediately = true

tags = merge(local.tags, { Name = var.db_identifier })
}

# Secrets Manager – JSON bundle for ECS injection
resource "aws_secretsmanager_secret" "db" {
name = var.secret_name
description = "mPATH ${upper(local.env)} DB connection"
kms_key_id = var.kms_key_id
tags = merge(local.tags, { Name = var.secret_name })
}

resource "aws_secretsmanager_secret_version" "db" {
secret_id = aws_secretsmanager_secret.db.id
secret_string = jsonencode({
adapter = "mysql2"
username = var.db_username
password = local.db_password_final
host = aws_db_instance.this.address
port = 3306
database = var.db_name
url = "mysql2://${var.db_username}:${local.db_password_final}@${aws_db_instance.this.address}:3306/${var.db_name}?encoding=utf8mb4&ssl_mode=required"
})
}
26 changes: 26 additions & 0 deletions deployment/ecs/envs/bo/templates/app_env.json.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"RAILS_ENV": "${rails_env}",
"RAILS_LOG_TO_STDOUT": ${rails_log_to_stdout},
"RAILS_SERVE_STATIC_FILES": ${rails_serve_static},
"PUMA_PORT": ${puma_port},
"WEB_CONCURRENCY": ${web_concurrency},
"RAILS_MAX_THREADS": ${rails_max_threads},
"RAILS_MIN_THREADS": ${rails_min_threads},

"SECRET_KEY_BASE": "${secret_key_base}",

"DATABASE_URL": "",
"DATABASE_NAME": "${db_name}",
"DATABASE_HOST": "${db_host}",
"DATABASE_PORT": ${db_port},
"DATABASE_USER": "${db_user}",
"DATABASE_USERNAME": "${db_user}",
"DATABASE_PASSWORD": "${db_password}",

"OFFICE365_CLIENT_ID": "${office365_client_id}",
"OFFICE365_CLIENT_SECRET": "${office365_client_secret}",
"OFFICE365_REDIRECT_URI": "${office365_redirect_uri}",
"OFFICE365_PROVIDER_URL": "${office365_provider_url}",

"USE_SSL": ${use_ssl}
}
Loading
Loading