diff --git a/.infrastructure/alb.tf b/.infrastructure/alb.tf new file mode 100644 index 0000000000..c1c84a599d --- /dev/null +++ b/.infrastructure/alb.tf @@ -0,0 +1,53 @@ +## Application Load Balancer for CMS Strapi +module "cms_load_balancer" { + source = "git::https://github.com/terraform-aws-modules/terraform-aws-alb.git?ref=3e9c6cbaf4c1d858c3bbee6f086f0c8ef17522ab" # v9.6.0 + + name = "cms-load-balancer" + vpc_id = module.vpc.vpc_id + subnets = module.vpc.public_subnets + security_groups = [aws_security_group.cms_lb.id] + internal = false + create_security_group = false + load_balancer_type = "application" + + listeners = { + front_end_http = { + port = 80 + protocol = "HTTP" + redirect = { + port = "443" + protocol = "HTTPS" + status_code = "HTTP_301" + } + } + front_end_https = { + port = 443 + protocol = "HTTPS" + certificate_arn = module.cms_ssl_certificate.acm_certificate_arn + forward = { + target_group_key = "cms-target-group" + } + } + } + + target_groups = { + cms-target-group = { + name = "cms-target-group" + protocol = "HTTP" + port = var.cms_app_port + target_type = "ip" + vpc_id = module.vpc.vpc_id + + health_check = { + healthy_threshold = "3" + interval = "30" + protocol = "HTTP" + matcher = "204" + timeout = "3" + path = "/_health" + unhealthy_threshold = "2" + } + create_attachment = false + } + } +} \ No newline at end of file diff --git a/.infrastructure/15_database.tf b/.infrastructure/database.tf similarity index 100% rename from .infrastructure/15_database.tf rename to .infrastructure/database.tf diff --git a/.infrastructure/ecs.tf b/.infrastructure/ecs.tf new file mode 100644 index 0000000000..be28f90dda --- /dev/null +++ b/.infrastructure/ecs.tf @@ -0,0 +1,79 @@ +## ECS for CMS Strapi +module "cms_ecs_cluster" { + source = "git::https://github.com/terraform-aws-modules/terraform-aws-ecs.git//modules/cluster?ref=8b97783def49997d18a6fcb00dc21ce1edc0f538" # v5.9.0 + + cluster_name = "cms-ecs-cluster" +} + +data "template_file" "cms_app" { + template = file("./task-definitions/cms_app.json.tpl") + + vars = { + image = module.ecr.repository_url + fargate_cpu = var.cms_app_cpu + fargate_memory = var.cms_app_memory + aws_region = var.aws_region + db_host = module.cms_rds.cluster_endpoint + db_user = module.cms_rds.cluster_master_username + db_password_arn = module.secret_cms_database_password.ssm_parameter_arn + bucket_name = module.s3_bucket_cms.s3_bucket_id + admin_jwt_secret_arn = module.secret_cms_admin_jwt_secret.ssm_parameter_arn + db_name = module.cms_rds.cluster_database_name + db_client = "postgres" + container_port = var.cms_app_port + app_keys = module.secret_cms_app_keys.ssm_parameter_arn + api_token_salt = module.secret_cms_api_token_salt.ssm_parameter_arn + transfer_token_salt = module.secret_cms_transfer_token_salt.ssm_parameter_arn + jwt_secret = module.secret_cms_jwt_secret.ssm_parameter_arn + access_key_id = module.secret_cms_access_key_id.ssm_parameter_arn + access_key_secret = module.secret_cms_access_key_secret.ssm_parameter_arn + bucket_full_url = module.s3_bucket_cms.s3_bucket_bucket_regional_domain_name + cdn_url = "https://${module.cloudfront_cms.cloudfront_distribution_domain_name}" + aws_bucket_endpoint = "https://s3.${var.aws_region}.amazonaws.com" + repo_owner = "pagopa" + repo_name = "developer-portal" + workflow_id = "deploy_website.yaml" + target_branch = "main" + github_pat = module.secret_cms_github_pat.ssm_parameter_arn + } +} + +resource "aws_ecs_task_definition" "cms_task_def" { + family = "cms-task-def" + execution_role_arn = module.iam_role_ecs_task_execution.iam_role_arn + task_role_arn = module.iam_role_task_role.iam_role_arn + network_mode = "awsvpc" + requires_compatibilities = ["FARGATE"] + cpu = var.cms_app_cpu + memory = var.cms_app_memory + container_definitions = data.template_file.cms_app.rendered +} + +module "cms_ecs_service" { + source = "git::https://github.com/terraform-aws-modules/terraform-aws-ecs.git//modules/service?ref=8b97783def49997d18a6fcb00dc21ce1edc0f538" # v5.9.0 + + name = "cms-ecs" + cluster_arn = module.cms_ecs_cluster.arn + desired_count = 0 + create_task_definition = false + create_iam_role = false + create_task_exec_iam_role = false + create_security_group = false + launch_type = "FARGATE" + force_new_deployment = true + task_definition_arn = aws_ecs_task_definition.cms_task_def.arn + tasks_iam_role_arn = module.iam_role_task_role.iam_role_arn + task_exec_iam_role_arn = module.iam_role_ecs_task_execution.iam_role_arn + + security_group_ids = [aws_security_group.ecs_tasks.id] + subnet_ids = module.vpc.private_subnets + assign_public_ip = false + + load_balancer = { + cms-target-group = { + target_group_arn = module.cms_load_balancer.target_groups["cms-target-group"].arn + container_name = "cms-docker" + container_port = var.cms_app_port + } + } +} \ No newline at end of file diff --git a/.infrastructure/task-definitions/cms_app.json.tpl b/.infrastructure/task-definitions/cms_app.json.tpl new file mode 100644 index 0000000000..51ad2529a2 --- /dev/null +++ b/.infrastructure/task-definitions/cms_app.json.tpl @@ -0,0 +1,129 @@ +[ + { + "name": "cms-docker", + "image": "${image}", + "portMappings": [ + { + "containerPort": ${container_port} + } + ], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-create-group": "true", + "awslogs-group": "/ecs/cms-task-def", + "awslogs-region": "eu-south-1", + "awslogs-stream-prefix": "ecs" + }, + "secretOptions": [] + }, + "environment": [ + { + "name": "NODE_ENV", + "value": "production" + }, + { + "name": "DATABASE_CLIENT", + "value": "${db_client}" + }, + { + "name": "DATABASE_HOST", + "value": "${db_host}" + }, + { + "name": "DATABASE_NAME", + "value": "${db_name}" + }, + { + "name": "AWS_BUCKET_NAME", + "value": "${bucket_name}" + }, + { + "name": "DATABASE_USERNAME", + "value": "${db_user}" + }, + { + "name": "DATABASE_SSL", + "value": "false" + }, + { + "name": "DATABASE_PORT", + "value": "5432" + }, + { + "name": "DATABASE_SCHEMA", + "value": "public" + }, + { + "name": "AWS_REGION", + "value": "${aws_region}" + }, + { + "name": "AWS_BUCKET_FULL_URL", + "value": "${bucket_full_url}" + }, + { + "name": "CDN_URL", + "value": "${cdn_url}" + }, + { + "name": "AWS_BUCKET_ENDPOINT", + "value": "${aws_bucket_endpoint}" + }, + { + "name": "REPO_OWNER", + "value": "${repo_owner}" + }, + { + "name": "REPO_NAME", + "value": "${repo_name}" + }, + { + "name": "WORKFLOW_ID", + "value": "${workflow_id}" + }, + { + "name": "TARGET_BRANCH", + "value": "${target_branch}" + } + ], + "secrets" : [ + { + "name" : "DATABASE_PASSWORD", + "valueFrom" : "${db_password_arn}" + }, + { + "name": "ADMIN_JWT_SECRET", + "valueFrom": "${admin_jwt_secret_arn}" + }, + { + "name": "APP_KEYS", + "valueFrom": "${app_keys}" + }, + { + "name": "API_TOKEN_SALT", + "valueFrom": "${api_token_salt}" + }, + { + "name": "TRANSFER_TOKEN_SALT", + "valueFrom": "${transfer_token_salt}" + }, + { + "name": "JWT_SECRET", + "valueFrom": "${jwt_secret}" + }, + { + "name": "AWS_ACCESS_KEY_ID", + "valueFrom": "${access_key_id}" + }, + { + "name": "AWS_ACCESS_SECRET", + "valueFrom": "${access_key_secret}" + }, + { + "name": "GITHUB_PAT", + "valueFrom": "${github_pat}" + } + ] + } +] \ No newline at end of file diff --git a/.infrastructure/variables.tf b/.infrastructure/variables.tf index 20e977cfab..e9fb7cc493 100644 --- a/.infrastructure/variables.tf +++ b/.infrastructure/variables.tf @@ -67,6 +67,18 @@ variable "cms_app_port" { default = 1337 } +## CMS Strapi CPU +variable "cms_app_cpu" { + description = "Fargate instance CPU units to provision (1 vCPU = 1024 CPU units)" + default = "1024" ##### 1 vCPU +} + +## CMS Strapi RAM +variable "cms_app_memory" { + description = "Fargate instance memory to provision (in MiB)" + default = "3072" ##### 3 GB RAM +} + # CMS Strapi DNS variable "dns_domain_name_cms" { description = "DNS domain name of the Developer Portal's CMS"