From 56eb640437be02aa33af178ddebf1e17c474764d Mon Sep 17 00:00:00 2001 From: Kai Siren Date: Tue, 15 Oct 2024 14:16:27 -0700 Subject: [PATCH 01/13] initial checkin --- .../app-config/build_repository.tf | 20 ++ infra/ecs-terraform/app-config/dev.tf | 32 ++++ .../env-config/environment-variables.tf | 27 +++ .../app-config/env-config/outputs.tf | 52 ++++++ .../app-config/env-config/variables.tf | 91 +++++++++ infra/ecs-terraform/app-config/main.tf | 60 ++++++ infra/ecs-terraform/app-config/outputs.tf | 31 ++++ infra/ecs-terraform/app-config/prod.tf | 40 ++++ infra/ecs-terraform/app-config/staging.tf | 31 ++++ .../build-repository/.terraform.lock.hcl | 57 ++++++ infra/ecs-terraform/build-repository/main.tf | 59 ++++++ .../build-repository/shared.s3.tfbackend | 4 + .../ecs-terraform/service/.terraform.lock.hcl | 76 ++++++++ infra/ecs-terraform/service/dev.s3.tfbackend | 4 + infra/ecs-terraform/service/image_tag.tf | 56 ++++++ infra/ecs-terraform/service/main.tf | 172 ++++++++++++++++++ infra/ecs-terraform/service/outputs.tf | 24 +++ infra/ecs-terraform/service/prod.s3.tfbackend | 4 + infra/ecs-terraform/service/search.tf | 14 ++ infra/ecs-terraform/service/secrets.tf | 16 ++ .../service/sfn_copy_oracle_data.tf | 100 ++++++++++ .../service/sfn_load_transform.tf | 94 ++++++++++ .../service/staging.s3.tfbackend | 4 + infra/ecs-terraform/service/variables.tf | 10 + 24 files changed, 1078 insertions(+) create mode 100644 infra/ecs-terraform/app-config/build_repository.tf create mode 100644 infra/ecs-terraform/app-config/dev.tf create mode 100644 infra/ecs-terraform/app-config/env-config/environment-variables.tf create mode 100644 infra/ecs-terraform/app-config/env-config/outputs.tf create mode 100644 infra/ecs-terraform/app-config/env-config/variables.tf create mode 100644 infra/ecs-terraform/app-config/main.tf create mode 100644 infra/ecs-terraform/app-config/outputs.tf create mode 100644 infra/ecs-terraform/app-config/prod.tf create mode 100644 infra/ecs-terraform/app-config/staging.tf create mode 100644 infra/ecs-terraform/build-repository/.terraform.lock.hcl create mode 100644 infra/ecs-terraform/build-repository/main.tf create mode 100644 infra/ecs-terraform/build-repository/shared.s3.tfbackend create mode 100644 infra/ecs-terraform/service/.terraform.lock.hcl create mode 100644 infra/ecs-terraform/service/dev.s3.tfbackend create mode 100644 infra/ecs-terraform/service/image_tag.tf create mode 100644 infra/ecs-terraform/service/main.tf create mode 100644 infra/ecs-terraform/service/outputs.tf create mode 100644 infra/ecs-terraform/service/prod.s3.tfbackend create mode 100644 infra/ecs-terraform/service/search.tf create mode 100644 infra/ecs-terraform/service/secrets.tf create mode 100644 infra/ecs-terraform/service/sfn_copy_oracle_data.tf create mode 100644 infra/ecs-terraform/service/sfn_load_transform.tf create mode 100644 infra/ecs-terraform/service/staging.s3.tfbackend create mode 100644 infra/ecs-terraform/service/variables.tf diff --git a/infra/ecs-terraform/app-config/build_repository.tf b/infra/ecs-terraform/app-config/build_repository.tf new file mode 100644 index 000000000..4d240f0f3 --- /dev/null +++ b/infra/ecs-terraform/app-config/build_repository.tf @@ -0,0 +1,20 @@ +data "external" "account_ids_by_name" { + program = ["${path.module}/../../../bin/account-ids-by-name.sh"] +} + +locals { + image_repository_name = "${local.project_name}-${local.app_name}" + image_repository_region = module.project_config.default_region + image_repository_account_name = module.project_config.network_configs[local.shared_network_name].account_name + image_repository_account_id = data.external.account_ids_by_name.result[local.image_repository_account_name] + + build_repository_config = { + name = local.image_repository_name + region = local.image_repository_region + network_name = local.shared_network_name + account_name = local.image_repository_account_name + account_id = local.image_repository_account_id + repository_arn = "arn:aws:ecr:${local.image_repository_region}:${local.image_repository_account_id}:repository/${local.image_repository_name}" + repository_url = "${local.image_repository_account_id}.dkr.ecr.${local.image_repository_region}.amazonaws.com/${local.image_repository_name}" + } +} diff --git a/infra/ecs-terraform/app-config/dev.tf b/infra/ecs-terraform/app-config/dev.tf new file mode 100644 index 000000000..0ecb76699 --- /dev/null +++ b/infra/ecs-terraform/app-config/dev.tf @@ -0,0 +1,32 @@ +module "dev_config" { + source = "./env-config" + app_name = local.app_name + default_region = module.project_config.default_region + environment = "dev" + has_database = local.has_database + database_instance_count = 2 + database_enable_http_endpoint = true + has_incident_management_service = local.has_incident_management_service + database_max_capacity = 16 + database_min_capacity = 2 + + has_search = true + # https://docs.aws.amazon.com/opensearch-service/latest/developerguide/what-is.html#choosing-version + search_engine_version = "OpenSearch_2.15" + + # Runs, but with everything disabled. + # See api/src/data_migration/command/load_transform.py for argument specifications. + load_transform_args = [ + "poetry", + "run", + "flask", + "data-migration", + "load-transform", + "--no-load", + "--no-transform", + "--no-set-current", + ] + + service_override_extra_environment_variables = { + } +} diff --git a/infra/ecs-terraform/app-config/env-config/environment-variables.tf b/infra/ecs-terraform/app-config/env-config/environment-variables.tf new file mode 100644 index 000000000..1897fb343 --- /dev/null +++ b/infra/ecs-terraform/app-config/env-config/environment-variables.tf @@ -0,0 +1,27 @@ +locals { + # Map from environment variable name to environment variable value + # This is a map rather than a list so that variables can be easily + # overridden per environment using terraform's `merge` function + default_extra_environment_variables = { + # Example environment variables + # WORKER_THREADS_COUNT = 4 + # LOG_LEVEL = "info" + # DB_CONNECTION_POOL_SIZE = 5 + } + + # Configuration for secrets + # List of configurations for defining environment variables that pull from SSM parameter + # store. Configurations are of the format + # { + # ENV_VAR_NAME = { + # manage_method = "generated" # or "manual" for a secret that was created and stored in SSM manually + # secret_store_name = "/ssm/param/name" + # } + # } + secrets = { + API_AUTH_TOKEN = { + manage_method = "manual" + secret_store_name = "/api/${var.environment}/api-auth-token" + } + } +} diff --git a/infra/ecs-terraform/app-config/env-config/outputs.tf b/infra/ecs-terraform/app-config/env-config/outputs.tf new file mode 100644 index 000000000..c9375fa4f --- /dev/null +++ b/infra/ecs-terraform/app-config/env-config/outputs.tf @@ -0,0 +1,52 @@ +output "search_config" { + value = var.has_search ? { + instance_type = var.search_master_instance_type + instance_count = var.search_data_instance_count + dedicated_master_type = var.search_data_instance_type + engine_version = var.search_engine_version + volume_size = var.search_data_volume_size + } : null +} + +output "database_config" { + value = var.has_database ? { + region = var.default_region + cluster_name = "${var.app_name}-${var.environment}" + access_policy_name = "${var.app_name}-${var.environment}-db-access" + app_username = "app" + migrator_username = "migrator" + schema_name = var.app_name + app_access_policy_name = "${var.app_name}-${var.environment}-app-access" + migrator_access_policy_name = "${var.app_name}-${var.environment}-migrator-access" + instance_count = var.database_instance_count + enable_http_endpoint = var.database_enable_http_endpoint + max_capacity = var.database_max_capacity + min_capacity = var.database_min_capacity + } : null +} + +output "service_config" { + value = { + region = var.default_region + extra_environment_variables = merge( + local.default_extra_environment_variables, + var.service_override_extra_environment_variables + ) + + secrets = local.secrets + } +} + +output "incident_management_service_integration" { + value = var.has_incident_management_service ? { + integration_url_param_name = "/monitoring/${var.app_name}/${var.environment}/incident-management-integration-url" + } : null +} + +output "domain" { + value = var.domain +} + +output "load_transform_args" { + value = var.load_transform_args +} diff --git a/infra/ecs-terraform/app-config/env-config/variables.tf b/infra/ecs-terraform/app-config/env-config/variables.tf new file mode 100644 index 000000000..8d7ab88b8 --- /dev/null +++ b/infra/ecs-terraform/app-config/env-config/variables.tf @@ -0,0 +1,91 @@ +variable "app_name" { + type = string +} + +variable "environment" { + description = "name of the application environment (e.g. dev, staging, prod)" + type = string +} + +variable "default_region" { + description = "default region for the project" + type = string +} + +variable "has_search" { + type = bool + default = false +} + +variable "search_data_instance_type" { + type = string + default = "or1.medium.search" +} + +variable "search_master_instance_type" { + type = string + default = "m6g.large.search" +} + +variable "search_engine_version" { + type = string +} + +variable "search_data_instance_count" { + type = number + default = 3 +} + +variable "search_data_volume_size" { + type = number + default = 20 +} + +variable "has_database" { + type = bool +} + +variable "database_instance_count" { + description = "Number of database instances. Should be 2+ for production environments." + type = number + default = 1 +} + +variable "database_enable_http_endpoint" { + description = "Enable HTTP endpoint (data API). Enables the Query Editor in the AWS Console." + type = bool + default = false +} + +variable "database_max_capacity" { + description = "Maximum capacity of the Aurora Serverless v2 cluster" + type = number +} + +variable "database_min_capacity" { + description = "Minimum capacity of the Aurora Serverless v2 cluster" + type = number +} + +variable "has_incident_management_service" { + type = bool +} + +variable "domain" { + type = string + description = "DNS domain of the website managed by HHS" + default = null +} + +variable "service_override_extra_environment_variables" { + type = map(string) + description = <..s3.tfbackend + # + # Projects/applications that use the same AWS account for all environments + # will refer to the same account for all environments. For example, if the + # project has a single account named "myaccount", then infra/accounts will + # have one tfbackend file myaccount.XXXXX.s3.tfbackend, and the + # account_names_by_environment map will look like: + # + # account_names_by_environment = { + # shared = "myaccount" + # dev = "myaccount" + # staging = "myaccount" + # prod = "myaccount" + # } + # + # Projects/applications that have separate AWS accounts for each environment + # might have a map that looks more like this: + # + # account_names_by_environment = { + # shared = "dev" + # dev = "dev" + # staging = "staging" + # prod = "prod" + # } + account_names_by_environment = { + shared = "simpler-grants-gov" + dev = "simpler-grants-gov" + staging = "simpler-grants-gov" + prod = "simpler-grants-gov" + } + + # The name of the network that contains the resources shared across all + # application environments, such as the build repository. + # The list of networks can be found in /infra/networks + # by looking for the backend config files of the form: + # .s3.tfbackend + shared_network_name = "dev" +} + +module "project_config" { + source = "../../project-config" +} diff --git a/infra/ecs-terraform/app-config/outputs.tf b/infra/ecs-terraform/app-config/outputs.tf new file mode 100644 index 000000000..26f012550 --- /dev/null +++ b/infra/ecs-terraform/app-config/outputs.tf @@ -0,0 +1,31 @@ +output "app_name" { + value = local.app_name +} + +output "account_names_by_environment" { + value = local.account_names_by_environment +} + +output "environments" { + value = local.environments +} + +output "has_database" { + value = local.has_database +} + +output "has_incident_management_service" { + value = local.has_incident_management_service +} + +output "image_repository_name" { + value = local.image_repository_name +} + +output "build_repository_config" { + value = local.build_repository_config +} + +output "environment_configs" { + value = local.environment_configs +} diff --git a/infra/ecs-terraform/app-config/prod.tf b/infra/ecs-terraform/app-config/prod.tf new file mode 100644 index 000000000..2d9fd1993 --- /dev/null +++ b/infra/ecs-terraform/app-config/prod.tf @@ -0,0 +1,40 @@ +module "prod_config" { + source = "./env-config" + app_name = local.app_name + default_region = module.project_config.default_region + environment = "prod" + has_database = local.has_database + domain = "api.simpler.grants.gov" + database_instance_count = 2 + database_enable_http_endpoint = true + has_incident_management_service = local.has_incident_management_service + database_max_capacity = 32 + database_min_capacity = 2 + + has_search = false + # https://aws.amazon.com/opensearch-service/pricing/ + search_master_instance_type = "m6g.large.search" + # 20 is the minimum volume size for the or1.medium.search instance type. + # Scale the `search_data_volume_size` number to meet your storage needs. + search_data_instance_type = "or1.medium.search" + search_data_volume_size = 20 + search_data_instance_count = 3 + # Scale this number to meet your compute needs. + # https://docs.aws.amazon.com/opensearch-service/latest/developerguide/what-is.html#choosing-version + search_engine_version = "OpenSearch_2.15" + + # See api/src/data_migration/command/load_transform.py for argument specifications. + load_transform_args = [ + "poetry", + "run", + "flask", + "data-migration", + "load-transform", + "--load", + "--transform", + "--set-current", + ] + + service_override_extra_environment_variables = { + } +} diff --git a/infra/ecs-terraform/app-config/staging.tf b/infra/ecs-terraform/app-config/staging.tf new file mode 100644 index 000000000..a14b8b73b --- /dev/null +++ b/infra/ecs-terraform/app-config/staging.tf @@ -0,0 +1,31 @@ +module "staging_config" { + source = "./env-config" + app_name = local.app_name + default_region = module.project_config.default_region + environment = "staging" + has_database = local.has_database + database_instance_count = 2 + database_enable_http_endpoint = true + has_incident_management_service = local.has_incident_management_service + database_max_capacity = 16 + database_min_capacity = 2 + + has_search = false + # https://docs.aws.amazon.com/opensearch-service/latest/developerguide/what-is.html#choosing-version + search_engine_version = "OpenSearch_2.15" + + # See api/src/data_migration/command/load_transform.py for argument specifications. + load_transform_args = [ + "poetry", + "run", + "flask", + "data-migration", + "load-transform", + "--load", + "--transform", + "--set-current", + ] + + service_override_extra_environment_variables = { + } +} diff --git a/infra/ecs-terraform/build-repository/.terraform.lock.hcl b/infra/ecs-terraform/build-repository/.terraform.lock.hcl new file mode 100644 index 000000000..8b6028214 --- /dev/null +++ b/infra/ecs-terraform/build-repository/.terraform.lock.hcl @@ -0,0 +1,57 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.68.0" + constraints = "~> 5.68.0" + hashes = [ + "h1:+UrZt3d4yqwyJG3EWGqDnDcOLcGRHoDMmUwLyZnLuqM=", + "h1:C0VIWK9EIjEB17oy+sJoMprCJWrU6eQ8ZG5eWVZMejY=", + "h1:J3selLplasdTyf03y+6NsPVMEP+EIXxbrWWL/k5KkeU=", + "h1:NmVKS7bzeZMXgIsYED9S7bphwyIfxq+zrOgmNQaZkds=", + "h1:QU+d0rw5poZpVyplpBg5XQ5JsGnLRkZve5dR0lKZ+9U=", + "h1:RQqWbA6pPd45LV1XgPD6jjnu/aJx2D4N3fysXI/Z7D4=", + "h1:Um4YQIlz9R5RTHenazNpvrww8JMYvPuBsx/h3xbiKbA=", + "h1:VQdsOk3FmBRuPGO6IAuUdVnjM8SJM3gEa+6GRgeK1Sk=", + "h1:Yh6RgnJgWM1tvIqDvYhoAtIx8hRRJuk1dbvfPOzkCvM=", + "h1:cOHTlyS7Zs3AIvTK2qoqw+hyTL1VhEDhFmACby7ULSg=", + "h1:n1X7MVAm1J+cxX4nTWTQRBx+VFQ0ma2AMJ5Ll3URlLo=", + "h1:n3zh4AViHnVMTtZ1Gp4BXZaVKRzVbS/bAYjy7vYqNPQ=", + "h1:tG19vttbOVn4Ht5AoSGcEEt6vpiZnPDSpPdyTn549u0=", + "h1:xAMlU1V+RL/Afwu/rzJu6acaAMMm4bBvRhKkaV7jSBI=", + "zh:045f37b115a6c94a05c6a5f2aacfe4cecbaf4b40b56917ba852d988d487e94bf", + "zh:0c388f1a94e7941cf7e6abcd8d958a3e325e513cb60affa3cac82e75c7bbbb73", + "zh:15b1f2587c06bff35a15f2d1c22eab395d549908daf05582608d729cdf54ba40", + "zh:16a9c0c7fa7a33aa22313d4444aeecde20831bf51f9b481a0406e3cf583378fc", + "zh:3330c0d49fb329dff6de17913e1a774e75aa0913106c3197814c73c3a12a4c3f", + "zh:40920318f774ff397c7b6a01b5e89e46eb1a55d7dc9943a310669a9357b9b501", + "zh:838fbac358bb72f46c8d359a28a3effb6a9d7137cdd72b9e4d2f0fcf803dc462", + "zh:84e694c0720bf54b3b8521bf6e05700abe4a1b3e7dd2a104efd1eb55ae5866a0", + "zh:90606c399498027d7d07ab78a71b574a5d8b982c4372e6b67479f7e39e153e2f", + "zh:9162cf25d5c0fdf672c9bbc4c3c84dd87ab6a15b4971df1f32aea6b477c0e028", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9cd8ec40a88b25e9f0f7d7f51460a921f4529554a260ffbe5083ddeba2f41ae3", + "zh:adeffac1d01a35bc8d2497ccceb9978b4746872143016c2c631de6cb38b6aa8d", + "zh:c7b682c81f9ae850669deb6239a66d8aa960abed984aad25db2d3954c09c2616", + "zh:d10b9f40934e14d55cfc5731d728507e50d014561322e9e0c84b33ab255a4d51", + ] +} + +provider "registry.terraform.io/hashicorp/external" { + version = "2.3.4" + hashes = [ + "h1:cCabxnWQ5fX1lS7ZqgUzsvWmKZw9FA7NRxAZ94vcTcc=", + "zh:037fd82cd86227359bc010672cd174235e2d337601d4686f526d0f53c87447cb", + "zh:0ea1db63d6173d01f2fa8eb8989f0809a55135a0d8d424b08ba5dabad73095fa", + "zh:17a4d0a306566f2e45778fbac48744b6fd9c958aaa359e79f144c6358cb93af0", + "zh:298e5408ab17fd2e90d2cd6d406c6d02344fe610de5b7dae943a58b958e76691", + "zh:38ecfd29ee0785fd93164812dcbe0664ebbe5417473f3b2658087ca5a0286ecb", + "zh:59f6a6f31acf66f4ea3667a555a70eba5d406c6e6d93c2c641b81d63261eeace", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:ad0279dfd09d713db0c18469f585e58d04748ca72d9ada83883492e0dd13bd58", + "zh:c69f66fd21f5e2c8ecf7ca68d9091c40f19ad913aef21e3ce23836e91b8cbb5f", + "zh:d4a56f8c48aa86fc8e0c233d56850f5783f322d6336f3bf1916e293246b6b5d4", + "zh:f2b394ebd4af33f343835517e80fc876f79361f4688220833bc3c77655dd2202", + "zh:f31982f29f12834e5d21e010856eddd19d59cd8f449adf470655bfd19354377e", + ] +} diff --git a/infra/ecs-terraform/build-repository/main.tf b/infra/ecs-terraform/build-repository/main.tf new file mode 100644 index 000000000..f0174b4fa --- /dev/null +++ b/infra/ecs-terraform/build-repository/main.tf @@ -0,0 +1,59 @@ +data "aws_iam_role" "github_actions" { + name = module.project_config.github_actions_role_name +} + +locals { + # Set project tags that will be used to tag all resources. + tags = merge(module.project_config.default_tags, { + application = module.app_config.app_name + application_role = "build-repository" + description = "Backend resources required for storing built release candidate artifacts to be used for deploying to environments." + }) + + # Get list of AWS account ids for the application environments that + # will need access to the build repository + app_account_names = values(module.app_config.account_names_by_environment) + account_ids_by_name = data.external.account_ids_by_name.result + app_account_ids = [for account_name in local.app_account_names : local.account_ids_by_name[account_name] if contains(keys(local.account_ids_by_name), account_name)] +} + +terraform { + required_version = "< 1.10" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.68.0" + } + } + + backend "s3" { + encrypt = "true" + } +} + +provider "aws" { + region = module.app_config.build_repository_config.region + default_tags { + tags = local.tags + } +} + +module "project_config" { + source = "../../project-config" +} + +module "app_config" { + source = "../app-config" +} + +data "external" "account_ids_by_name" { + program = ["../../../bin/account-ids-by-name.sh"] +} + +module "container_image_repository" { + source = "../../modules/container-image-repository" + name = module.app_config.image_repository_name + push_access_role_arn = data.aws_iam_role.github_actions.arn + app_account_ids = local.app_account_ids +} diff --git a/infra/ecs-terraform/build-repository/shared.s3.tfbackend b/infra/ecs-terraform/build-repository/shared.s3.tfbackend new file mode 100644 index 000000000..094584a1b --- /dev/null +++ b/infra/ecs-terraform/build-repository/shared.s3.tfbackend @@ -0,0 +1,4 @@ +bucket = "simpler-grants-gov-315341936575-us-east-1-tf" +key = "infra/api/build-repository/shared.tfstate" +dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" +region = "us-east-1" diff --git a/infra/ecs-terraform/service/.terraform.lock.hcl b/infra/ecs-terraform/service/.terraform.lock.hcl new file mode 100644 index 000000000..7463d5bf3 --- /dev/null +++ b/infra/ecs-terraform/service/.terraform.lock.hcl @@ -0,0 +1,76 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.68.0" + constraints = "~> 5.68.0" + hashes = [ + "h1:+UrZt3d4yqwyJG3EWGqDnDcOLcGRHoDMmUwLyZnLuqM=", + "h1:C0VIWK9EIjEB17oy+sJoMprCJWrU6eQ8ZG5eWVZMejY=", + "h1:J3selLplasdTyf03y+6NsPVMEP+EIXxbrWWL/k5KkeU=", + "h1:NmVKS7bzeZMXgIsYED9S7bphwyIfxq+zrOgmNQaZkds=", + "h1:QU+d0rw5poZpVyplpBg5XQ5JsGnLRkZve5dR0lKZ+9U=", + "h1:RQqWbA6pPd45LV1XgPD6jjnu/aJx2D4N3fysXI/Z7D4=", + "h1:Um4YQIlz9R5RTHenazNpvrww8JMYvPuBsx/h3xbiKbA=", + "h1:VQdsOk3FmBRuPGO6IAuUdVnjM8SJM3gEa+6GRgeK1Sk=", + "h1:Yh6RgnJgWM1tvIqDvYhoAtIx8hRRJuk1dbvfPOzkCvM=", + "h1:cOHTlyS7Zs3AIvTK2qoqw+hyTL1VhEDhFmACby7ULSg=", + "h1:n1X7MVAm1J+cxX4nTWTQRBx+VFQ0ma2AMJ5Ll3URlLo=", + "h1:n3zh4AViHnVMTtZ1Gp4BXZaVKRzVbS/bAYjy7vYqNPQ=", + "h1:tG19vttbOVn4Ht5AoSGcEEt6vpiZnPDSpPdyTn549u0=", + "h1:xAMlU1V+RL/Afwu/rzJu6acaAMMm4bBvRhKkaV7jSBI=", + "zh:045f37b115a6c94a05c6a5f2aacfe4cecbaf4b40b56917ba852d988d487e94bf", + "zh:0c388f1a94e7941cf7e6abcd8d958a3e325e513cb60affa3cac82e75c7bbbb73", + "zh:15b1f2587c06bff35a15f2d1c22eab395d549908daf05582608d729cdf54ba40", + "zh:16a9c0c7fa7a33aa22313d4444aeecde20831bf51f9b481a0406e3cf583378fc", + "zh:3330c0d49fb329dff6de17913e1a774e75aa0913106c3197814c73c3a12a4c3f", + "zh:40920318f774ff397c7b6a01b5e89e46eb1a55d7dc9943a310669a9357b9b501", + "zh:838fbac358bb72f46c8d359a28a3effb6a9d7137cdd72b9e4d2f0fcf803dc462", + "zh:84e694c0720bf54b3b8521bf6e05700abe4a1b3e7dd2a104efd1eb55ae5866a0", + "zh:90606c399498027d7d07ab78a71b574a5d8b982c4372e6b67479f7e39e153e2f", + "zh:9162cf25d5c0fdf672c9bbc4c3c84dd87ab6a15b4971df1f32aea6b477c0e028", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9cd8ec40a88b25e9f0f7d7f51460a921f4529554a260ffbe5083ddeba2f41ae3", + "zh:adeffac1d01a35bc8d2497ccceb9978b4746872143016c2c631de6cb38b6aa8d", + "zh:c7b682c81f9ae850669deb6239a66d8aa960abed984aad25db2d3954c09c2616", + "zh:d10b9f40934e14d55cfc5731d728507e50d014561322e9e0c84b33ab255a4d51", + ] +} + +provider "registry.terraform.io/hashicorp/external" { + version = "2.3.4" + hashes = [ + "h1:cCabxnWQ5fX1lS7ZqgUzsvWmKZw9FA7NRxAZ94vcTcc=", + "zh:037fd82cd86227359bc010672cd174235e2d337601d4686f526d0f53c87447cb", + "zh:0ea1db63d6173d01f2fa8eb8989f0809a55135a0d8d424b08ba5dabad73095fa", + "zh:17a4d0a306566f2e45778fbac48744b6fd9c958aaa359e79f144c6358cb93af0", + "zh:298e5408ab17fd2e90d2cd6d406c6d02344fe610de5b7dae943a58b958e76691", + "zh:38ecfd29ee0785fd93164812dcbe0664ebbe5417473f3b2658087ca5a0286ecb", + "zh:59f6a6f31acf66f4ea3667a555a70eba5d406c6e6d93c2c641b81d63261eeace", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:ad0279dfd09d713db0c18469f585e58d04748ca72d9ada83883492e0dd13bd58", + "zh:c69f66fd21f5e2c8ecf7ca68d9091c40f19ad913aef21e3ce23836e91b8cbb5f", + "zh:d4a56f8c48aa86fc8e0c233d56850f5783f322d6336f3bf1916e293246b6b5d4", + "zh:f2b394ebd4af33f343835517e80fc876f79361f4688220833bc3c77655dd2202", + "zh:f31982f29f12834e5d21e010856eddd19d59cd8f449adf470655bfd19354377e", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.6.3" + hashes = [ + "h1:zG9uFP8l9u+yGZZvi5Te7PV62j50azpgwPunq2vTm1E=", + "zh:04ceb65210251339f07cd4611885d242cd4d0c7306e86dda9785396807c00451", + "zh:448f56199f3e99ff75d5c0afacae867ee795e4dfda6cb5f8e3b2a72ec3583dd8", + "zh:4b4c11ccfba7319e901df2dac836b1ae8f12185e37249e8d870ee10bb87a13fe", + "zh:4fa45c44c0de582c2edb8a2e054f55124520c16a39b2dfc0355929063b6395b1", + "zh:588508280501a06259e023b0695f6a18149a3816d259655c424d068982cbdd36", + "zh:737c4d99a87d2a4d1ac0a54a73d2cb62974ccb2edbd234f333abd079a32ebc9e", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:a357ab512e5ebc6d1fda1382503109766e21bbfdfaa9ccda43d313c122069b30", + "zh:c51bfb15e7d52cc1a2eaec2a903ac2aff15d162c172b1b4c17675190e8147615", + "zh:e0951ee6fa9df90433728b96381fb867e3db98f66f735e0c3e24f8f16903f0ad", + "zh:e3cdcb4e73740621dabd82ee6a37d6cfce7fee2a03d8074df65086760f5cf556", + "zh:eff58323099f1bd9a0bec7cb04f717e7f1b2774c7d612bf7581797e1622613a0", + ] +} diff --git a/infra/ecs-terraform/service/dev.s3.tfbackend b/infra/ecs-terraform/service/dev.s3.tfbackend new file mode 100644 index 000000000..6bd430c25 --- /dev/null +++ b/infra/ecs-terraform/service/dev.s3.tfbackend @@ -0,0 +1,4 @@ +bucket = "simpler-grants-gov-315341936575-us-east-1-tf" +key = "infra/api/service/dev.tfstate" +dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" +region = "us-east-1" diff --git a/infra/ecs-terraform/service/image_tag.tf b/infra/ecs-terraform/service/image_tag.tf new file mode 100644 index 000000000..ce1bc75c2 --- /dev/null +++ b/infra/ecs-terraform/service/image_tag.tf @@ -0,0 +1,56 @@ +# Make the "image_tag" variable optional so that "terraform plan" +# and "terraform apply" work without any required variables. +# +# This works as follows: + +# 1. Accept an optional variable during a terraform plan/apply. (see "image_tag" variable in variables.tf) + +# 2. Read the output used from the last terraform state using "terraform_remote_state". +# Get the backend config by parsing the backend config file +locals { + backend_config_file_path = "${path.module}/${var.environment_name}.s3.tfbackend" + backend_config_file = file("${path.module}/${var.environment_name}.s3.tfbackend") + + # Use regex to parse backend config file to get a map of variables to their + # defined values since there is no built-in terraform function that does that + # + # The backend config file consists of lines that look like + # = " match[1] } + tfstate_bucket = local.backend_config["bucket"] + tfstate_key = local.backend_config["key"] +} +data "terraform_remote_state" "current_image_tag" { + # Don't do a lookup if image_tag is provided explicitly. + # This saves some time and also allows us to do a first deploy, + # where the tfstate file does not yet exist. + count = var.image_tag == null ? 1 : 0 + backend = "s3" + + config = { + bucket = local.tfstate_bucket + key = local.tfstate_key + region = local.service_config.region + } + + defaults = { + image_tag = null + } +} + +# 3. Prefer the given variable if provided, otherwise default to the value from last time. +locals { + image_tag = (var.image_tag == null + ? data.terraform_remote_state.current_image_tag[0].outputs.image_tag + : var.image_tag) +} + +# 4. Store the final value used as a terraform output for next time. +output "image_tag" { + value = local.image_tag +} diff --git a/infra/ecs-terraform/service/main.tf b/infra/ecs-terraform/service/main.tf new file mode 100644 index 000000000..69a9e2a6c --- /dev/null +++ b/infra/ecs-terraform/service/main.tf @@ -0,0 +1,172 @@ +# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc +data "aws_vpc" "network" { + filter { + name = "tag:Name" + values = [module.project_config.network_configs[var.environment_name].vpc_name] + } +} + +# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet +data "aws_subnets" "private" { + filter { + name = "vpc-id" + values = [data.aws_vpc.network.id] + } + filter { + name = "tag:subnet_type" + values = ["private"] + } +} + +# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet +data "aws_subnets" "public" { + filter { + name = "vpc-id" + values = [data.aws_vpc.network.id] + } + filter { + name = "tag:subnet_type" + values = ["public"] + } +} + +locals { + # The prefix key/value pair is used for Terraform Workspaces, which is useful for projects with multiple infrastructure developers. + # By default, Terraform creates a workspace named “default.” If a non-default workspace is not created this prefix will equal “default”, + # if you choose not to use workspaces set this value to "dev" + prefix = terraform.workspace == "default" ? "" : "${terraform.workspace}-" + + # Add environment specific tags + tags = merge(module.project_config.default_tags, { + environment = var.environment_name + description = "Application resources created in ${var.environment_name} environment" + }) + + service_name = "${local.prefix}${module.app_config.app_name}-${var.environment_name}" + + is_temporary = startswith(terraform.workspace, "t-") + + environment_config = module.app_config.environment_configs[var.environment_name] + service_config = local.environment_config.service_config + database_config = local.environment_config.database_config + incident_management_service_integration_config = local.environment_config.incident_management_service_integration + domain = local.environment_config.domain +} + +terraform { + required_version = "< 1.10" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.68.0" + } + } + + backend "s3" { + encrypt = "true" + } +} + +provider "aws" { + region = local.service_config.region + default_tags { + tags = local.tags + } +} + +module "project_config" { + source = "../../project-config" +} + +module "app_config" { + source = "../app-config" +} + +data "aws_rds_cluster" "db_cluster" { + count = module.app_config.has_database ? 1 : 0 + cluster_identifier = local.database_config.cluster_name +} + +data "aws_acm_certificate" "cert" { + count = local.domain != null ? 1 : 0 + domain = local.domain +} + +data "aws_iam_policy" "app_db_access_policy" { + count = module.app_config.has_database ? 1 : 0 + name = local.database_config.app_access_policy_name +} + +data "aws_iam_policy" "migrator_db_access_policy" { + count = module.app_config.has_database ? 1 : 0 + name = local.database_config.migrator_access_policy_name +} + +# Retrieve url for external incident management tool (e.g. Pagerduty, Splunk-On-Call) + +data "aws_ssm_parameter" "incident_management_service_integration_url" { + count = module.app_config.has_incident_management_service ? 1 : 0 + name = local.incident_management_service_integration_config.integration_url_param_name +} + +module "service" { + source = "../../modules/service" + service_name = local.service_name + is_temporary = local.is_temporary + image_repository_name = module.app_config.image_repository_name + image_tag = local.image_tag + vpc_id = data.aws_vpc.network.id + public_subnet_ids = data.aws_subnets.public.ids + private_subnet_ids = data.aws_subnets.private.ids + cpu = 1024 + memory = 2048 + + cert_arn = local.domain != null ? data.aws_acm_certificate.cert[0].arn : null + + app_access_policy_arn = data.aws_iam_policy.app_db_access_policy[0].arn + migrator_access_policy_arn = data.aws_iam_policy.migrator_db_access_policy[0].arn + + db_vars = module.app_config.has_database ? { + security_group_ids = data.aws_rds_cluster.db_cluster[0].vpc_security_group_ids + connection_info = { + host = data.aws_rds_cluster.db_cluster[0].endpoint + port = data.aws_rds_cluster.db_cluster[0].port + user = local.database_config.app_username + db_name = data.aws_rds_cluster.db_cluster[0].database_name + schema_name = local.database_config.schema_name + } + } : null + + extra_environment_variables = merge(local.service_config.extra_environment_variables, { "ENVIRONMENT" : var.environment_name }) + + secrets = concat( + [for secret_name in keys(local.service_config.secrets) : { + name = secret_name + valueFrom = module.secrets[secret_name].secret_arn + }], + local.environment_config.search_config != null ? [{ + name = "SEARCH_USERNAME" + valueFrom = data.aws_ssm_parameter.search_username_arn[0].arn + }] : [], + local.environment_config.search_config != null ? [{ + name = "SEARCH_PASSWORD" + valueFrom = data.aws_ssm_parameter.search_password_arn[0].arn + }] : [], + local.environment_config.search_config != null ? [{ + name = "SEARCH_ENDPOINT" + valueFrom = data.aws_ssm_parameter.search_endpoint_arn[0].arn + }] : [] + ) +} + +module "monitoring" { + source = "../../modules/monitoring" + #Email subscription list: + email_alerts_subscription_list = ["grantsalerts@navapbc.com"] + + # Module takes service and ALB names to link all alerts with corresponding targets + service_name = local.service_name + load_balancer_arn_suffix = module.service.load_balancer_arn_suffix + incident_management_service_integration_url = module.app_config.has_incident_management_service ? data.aws_ssm_parameter.incident_management_service_integration_url[0].value : null +} diff --git a/infra/ecs-terraform/service/outputs.tf b/infra/ecs-terraform/service/outputs.tf new file mode 100644 index 000000000..fe319eabf --- /dev/null +++ b/infra/ecs-terraform/service/outputs.tf @@ -0,0 +1,24 @@ +output "service_endpoint" { + description = "The public endpoint for the service." + value = module.service.public_endpoint +} + +output "service_cluster_name" { + value = module.service.cluster_name +} + +output "service_name" { + value = local.service_name +} + +output "application_log_group" { + value = module.service.application_log_group +} + +output "application_log_stream_prefix" { + value = module.service.application_log_stream_prefix +} + +output "migrator_role_arn" { + value = module.service.migrator_role_arn +} diff --git a/infra/ecs-terraform/service/prod.s3.tfbackend b/infra/ecs-terraform/service/prod.s3.tfbackend new file mode 100644 index 000000000..7ec37c16c --- /dev/null +++ b/infra/ecs-terraform/service/prod.s3.tfbackend @@ -0,0 +1,4 @@ +bucket = "simpler-grants-gov-315341936575-us-east-1-tf" +key = "infra/api/service/prod.tfstate" +dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" +region = "us-east-1" diff --git a/infra/ecs-terraform/service/search.tf b/infra/ecs-terraform/service/search.tf new file mode 100644 index 000000000..2af273406 --- /dev/null +++ b/infra/ecs-terraform/service/search.tf @@ -0,0 +1,14 @@ +data "aws_ssm_parameter" "search_username_arn" { + count = local.environment_config.search_config != null ? 1 : 0 + name = "/search/${local.service_name}/username" +} + +data "aws_ssm_parameter" "search_password_arn" { + count = local.environment_config.search_config != null ? 1 : 0 + name = "/search/${local.service_name}/password" +} + +data "aws_ssm_parameter" "search_endpoint_arn" { + count = local.environment_config.search_config != null ? 1 : 0 + name = "/search/${local.service_name}/endpoint" +} diff --git a/infra/ecs-terraform/service/secrets.tf b/infra/ecs-terraform/service/secrets.tf new file mode 100644 index 000000000..e65eaa0cc --- /dev/null +++ b/infra/ecs-terraform/service/secrets.tf @@ -0,0 +1,16 @@ +module "secrets" { + for_each = local.service_config.secrets + + source = "../../modules/secret" + + # When generating secrets and storing them in parameter store, append the + # terraform workspace to the secret store path if the environment is temporary + # to avoid conflicts with existing environments. + # Don't do this for secrets that are managed manually since the temporary + # environments will need to share those secrets. + secret_store_name = (each.value.manage_method == "generated" && local.is_temporary ? + "${each.value.secret_store_name}/${terraform.workspace}" : + each.value.secret_store_name + ) + manage_method = each.value.manage_method +} diff --git a/infra/ecs-terraform/service/sfn_copy_oracle_data.tf b/infra/ecs-terraform/service/sfn_copy_oracle_data.tf new file mode 100644 index 000000000..fd104da1d --- /dev/null +++ b/infra/ecs-terraform/service/sfn_copy_oracle_data.tf @@ -0,0 +1,100 @@ +# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group +resource "aws_cloudwatch_log_group" "copy_oracle_data" { + name_prefix = "/aws/vendedlogs/states/${local.service_name}-copy-oracle-data" + + # Conservatively retain logs for 5 years. + # Looser requirements may allow shorter retention periods + retention_in_days = 1827 + + # checkov:skip=CKV_AWS_158:skip requirement to encrypt with customer managed KMS key +} + +# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sfn_state_machine +resource "aws_sfn_state_machine" "copy_oracle_data" { + + name = "${local.service_name}-copy-oracle-data" + role_arn = module.service.task_role_arn + + definition = jsonencode({ + "StartAt" : "ExecuteECSTask", + "States" : { + "ExecuteECSTask" : { + "Type" : "Task", + # docs: https://docs.aws.amazon.com/step-functions/latest/dg/connect-ecs.html + "Resource" : "arn:aws:states:::ecs:runTask.sync", + "Parameters" : { + "Cluster" : module.service.cluster_arn, + "TaskDefinition" : module.service.task_definition_arn, + "LaunchType" : "FARGATE", + "NetworkConfiguration" : { + "AwsvpcConfiguration" : { + "Subnets" : data.aws_subnets.private.ids, + "SecurityGroups" : [module.service.app_security_group_id], + } + }, + "Overrides" : { + "ContainerOverrides" : [ + { + "Name" : local.service_name, + "Environment" : [ + { + "Name" : "FLASK_APP", + "Value" : "src.app:create_app()", + } + ] + "Command" : [ + "poetry", + "run", + "flask", + "data-migration", + "copy-oracle-data", + ] + } + ] + } + }, + "End" : true + } + } + }) + + logging_configuration { + log_destination = "${aws_cloudwatch_log_group.copy_oracle_data.arn}:*" + include_execution_data = true + level = "ERROR" + } + + tracing_configuration { + enabled = true + } +} + +# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule_group +resource "aws_scheduler_schedule_group" "copy_oracle_data" { + name = "${local.service_name}-copy-oracle-data" +} + +# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule +resource "aws_scheduler_schedule" "copy_oracle_data" { + # checkov:skip=CKV_AWS_297:Ignore the managed customer KMS key requirement for now + + name = "${local.service_name}-copy-oracle-data" + state = "ENABLED" + group_name = aws_scheduler_schedule_group.copy_oracle_data.id + schedule_expression = "rate(2 minutes)" + schedule_expression_timezone = "US/Eastern" + + flexible_time_window { + mode = "OFF" + } + + # target is the state machine + target { + arn = aws_sfn_state_machine.copy_oracle_data.arn + role_arn = module.service.task_role_arn + + retry_policy { + maximum_retry_attempts = 0 # dont retry, just wait for the next execution + } + } +} diff --git a/infra/ecs-terraform/service/sfn_load_transform.tf b/infra/ecs-terraform/service/sfn_load_transform.tf new file mode 100644 index 000000000..194cc68dc --- /dev/null +++ b/infra/ecs-terraform/service/sfn_load_transform.tf @@ -0,0 +1,94 @@ +# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group +resource "aws_cloudwatch_log_group" "load_transform" { + name_prefix = "/aws/vendedlogs/states/${local.service_name}-load-transform" + + # Conservatively retain logs for 5 years. + # Looser requirements may allow shorter retention periods + retention_in_days = 1827 + + # checkov:skip=CKV_AWS_158:skip requirement to encrypt with customer managed KMS key +} + +# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sfn_state_machine +resource "aws_sfn_state_machine" "load_transform" { + + name = "${local.service_name}-load-transform" + role_arn = module.service.task_role_arn + + definition = jsonencode({ + "StartAt" : "ExecuteECSTask", + "States" : { + "ExecuteECSTask" : { + "Type" : "Task", + # docs: https://docs.aws.amazon.com/step-functions/latest/dg/connect-ecs.html + "Resource" : "arn:aws:states:::ecs:runTask.sync", + "Parameters" : { + "Cluster" : module.service.cluster_arn, + "TaskDefinition" : module.service.task_definition_arn, + "LaunchType" : "FARGATE", + "NetworkConfiguration" : { + "AwsvpcConfiguration" : { + "Subnets" : data.aws_subnets.private.ids, + "SecurityGroups" : [module.service.app_security_group_id], + } + }, + "Overrides" : { + "ContainerOverrides" : [ + { + "Name" : local.service_name, + "Environment" : [ + { + "Name" : "FLASK_APP", + "Value" : "src.app:create_app()", + } + ] + "Command" : module.app_config.environment_configs[var.environment_name].load_transform_args + } + ] + } + }, + "End" : true + } + } + }) + + logging_configuration { + log_destination = "${aws_cloudwatch_log_group.load_transform.arn}:*" + include_execution_data = true + level = "ERROR" + } + + tracing_configuration { + enabled = true + } +} + +# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule_group +resource "aws_scheduler_schedule_group" "load_transform" { + name = "${local.service_name}-load-transform" +} + +# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule +resource "aws_scheduler_schedule" "load_transform" { + # checkov:skip=CKV_AWS_297:Ignore the managed customer KMS key requirement for now + + name = "${local.service_name}-load-transform" + state = "ENABLED" + group_name = aws_scheduler_schedule_group.load_transform.id + schedule_expression = "rate(1 days)" + schedule_expression_timezone = "US/Eastern" + + flexible_time_window { + mode = "OFF" + } + + # target is the state machine + target { + arn = aws_sfn_state_machine.load_transform.arn + role_arn = module.service.task_role_arn + + retry_policy { + maximum_retry_attempts = 0 # dont retry, just wait for the next execution + } + } +} diff --git a/infra/ecs-terraform/service/staging.s3.tfbackend b/infra/ecs-terraform/service/staging.s3.tfbackend new file mode 100644 index 000000000..3bd263b17 --- /dev/null +++ b/infra/ecs-terraform/service/staging.s3.tfbackend @@ -0,0 +1,4 @@ +bucket = "simpler-grants-gov-315341936575-us-east-1-tf" +key = "infra/api/service/staging.tfstate" +dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" +region = "us-east-1" diff --git a/infra/ecs-terraform/service/variables.tf b/infra/ecs-terraform/service/variables.tf new file mode 100644 index 000000000..19a5f312f --- /dev/null +++ b/infra/ecs-terraform/service/variables.tf @@ -0,0 +1,10 @@ +variable "environment_name" { + type = string + description = "name of the application environment" +} + +variable "image_tag" { + type = string + description = "image tag to deploy to the environment" + default = null +} From e6cd404b30eb526925c9ebe388a8cd1c7633ca29 Mon Sep 17 00:00:00 2001 From: Kai Siren Date: Tue, 15 Oct 2024 14:23:48 -0700 Subject: [PATCH 02/13] update app config --- infra/ecs-terraform/app-config/dev.tf | 35 ++-------- .../app-config/env-config/outputs.tf | 41 ----------- .../app-config/env-config/variables.tf | 70 ------------------- infra/ecs-terraform/app-config/prod.tf | 45 ++---------- infra/ecs-terraform/app-config/staging.tf | 36 ++-------- 5 files changed, 17 insertions(+), 210 deletions(-) diff --git a/infra/ecs-terraform/app-config/dev.tf b/infra/ecs-terraform/app-config/dev.tf index 0ecb76699..acca0d21f 100644 --- a/infra/ecs-terraform/app-config/dev.tf +++ b/infra/ecs-terraform/app-config/dev.tf @@ -1,32 +1,7 @@ module "dev_config" { - source = "./env-config" - app_name = local.app_name - default_region = module.project_config.default_region - environment = "dev" - has_database = local.has_database - database_instance_count = 2 - database_enable_http_endpoint = true - has_incident_management_service = local.has_incident_management_service - database_max_capacity = 16 - database_min_capacity = 2 - - has_search = true - # https://docs.aws.amazon.com/opensearch-service/latest/developerguide/what-is.html#choosing-version - search_engine_version = "OpenSearch_2.15" - - # Runs, but with everything disabled. - # See api/src/data_migration/command/load_transform.py for argument specifications. - load_transform_args = [ - "poetry", - "run", - "flask", - "data-migration", - "load-transform", - "--no-load", - "--no-transform", - "--no-set-current", - ] - - service_override_extra_environment_variables = { - } + source = "./env-config" + app_name = local.app_name + default_region = module.project_config.default_region + environment = "dev" + service_override_extra_environment_variables = {} } diff --git a/infra/ecs-terraform/app-config/env-config/outputs.tf b/infra/ecs-terraform/app-config/env-config/outputs.tf index c9375fa4f..c48529a3e 100644 --- a/infra/ecs-terraform/app-config/env-config/outputs.tf +++ b/infra/ecs-terraform/app-config/env-config/outputs.tf @@ -1,30 +1,3 @@ -output "search_config" { - value = var.has_search ? { - instance_type = var.search_master_instance_type - instance_count = var.search_data_instance_count - dedicated_master_type = var.search_data_instance_type - engine_version = var.search_engine_version - volume_size = var.search_data_volume_size - } : null -} - -output "database_config" { - value = var.has_database ? { - region = var.default_region - cluster_name = "${var.app_name}-${var.environment}" - access_policy_name = "${var.app_name}-${var.environment}-db-access" - app_username = "app" - migrator_username = "migrator" - schema_name = var.app_name - app_access_policy_name = "${var.app_name}-${var.environment}-app-access" - migrator_access_policy_name = "${var.app_name}-${var.environment}-migrator-access" - instance_count = var.database_instance_count - enable_http_endpoint = var.database_enable_http_endpoint - max_capacity = var.database_max_capacity - min_capacity = var.database_min_capacity - } : null -} - output "service_config" { value = { region = var.default_region @@ -36,17 +9,3 @@ output "service_config" { secrets = local.secrets } } - -output "incident_management_service_integration" { - value = var.has_incident_management_service ? { - integration_url_param_name = "/monitoring/${var.app_name}/${var.environment}/incident-management-integration-url" - } : null -} - -output "domain" { - value = var.domain -} - -output "load_transform_args" { - value = var.load_transform_args -} diff --git a/infra/ecs-terraform/app-config/env-config/variables.tf b/infra/ecs-terraform/app-config/env-config/variables.tf index 8d7ab88b8..0e91d3231 100644 --- a/infra/ecs-terraform/app-config/env-config/variables.tf +++ b/infra/ecs-terraform/app-config/env-config/variables.tf @@ -11,72 +11,6 @@ variable "default_region" { description = "default region for the project" type = string } - -variable "has_search" { - type = bool - default = false -} - -variable "search_data_instance_type" { - type = string - default = "or1.medium.search" -} - -variable "search_master_instance_type" { - type = string - default = "m6g.large.search" -} - -variable "search_engine_version" { - type = string -} - -variable "search_data_instance_count" { - type = number - default = 3 -} - -variable "search_data_volume_size" { - type = number - default = 20 -} - -variable "has_database" { - type = bool -} - -variable "database_instance_count" { - description = "Number of database instances. Should be 2+ for production environments." - type = number - default = 1 -} - -variable "database_enable_http_endpoint" { - description = "Enable HTTP endpoint (data API). Enables the Query Editor in the AWS Console." - type = bool - default = false -} - -variable "database_max_capacity" { - description = "Maximum capacity of the Aurora Serverless v2 cluster" - type = number -} - -variable "database_min_capacity" { - description = "Minimum capacity of the Aurora Serverless v2 cluster" - type = number -} - -variable "has_incident_management_service" { - type = bool -} - -variable "domain" { - type = string - description = "DNS domain of the website managed by HHS" - default = null -} - variable "service_override_extra_environment_variables" { type = map(string) description = < Date: Tue, 15 Oct 2024 14:37:45 -0700 Subject: [PATCH 03/13] workon dockerfile --- ecs-terraform/Dockerfile | 3 + ecs-terraform/Makefile | 75 +++++++++++++++++++ ecs-terraform/docker-compose.yml | 5 ++ .../env-config/environment-variables.tf | 14 +--- infra/ecs-terraform/app-config/main.tf | 4 +- 5 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 ecs-terraform/Dockerfile create mode 100644 ecs-terraform/Makefile create mode 100644 ecs-terraform/docker-compose.yml diff --git a/ecs-terraform/Dockerfile b/ecs-terraform/Dockerfile new file mode 100644 index 000000000..6ea36a7be --- /dev/null +++ b/ecs-terraform/Dockerfile @@ -0,0 +1,3 @@ +FROM hashicorp/terraform:1.9.7 AS release + +COPY ../ . diff --git a/ecs-terraform/Makefile b/ecs-terraform/Makefile new file mode 100644 index 000000000..89db2fc9c --- /dev/null +++ b/ecs-terraform/Makefile @@ -0,0 +1,75 @@ +############# +# Constants # +############# + +ORG ?= HHS +REPO ?= simpler-grants-gov +SPRINT_PROJECT ?= 13 +ROADMAP_PROJECT ?= 12 +OUTPUT_DIR ?= data +SPRINT_FILE ?= $(OUTPUT_DIR)/sprint-data.json +ROADMAP_FILE ?= $(OUTPUT_DIR)/roadmap-data.json +ISSUE_FILE ?= $(OUTPUT_DIR)/issue-data.json +SPRINT ?= @current +UNIT ?= points +ACTION ?= show-results +MIN_TEST_COVERAGE ?= 80 +APP_NAME ?= grants-analytics + +# Required for CI to work properly +SHELL = /bin/bash -o pipefail + +ifdef CI + DOCKER_EXEC_ARGS := -T -e CI -e GH_TOKEN -e ANALYTICS_SLACK_BOT_TOKEN -e ANALYTICS_REPORTING_CHANNEL_ID +else + DOCKER_EXEC_ARGS := -e GH_TOKEN +endif + +# By default, all python/poetry commands will run inside of the docker container +# if you wish to run this natively, add PY_RUN_APPROACH=local to your environment vars +# You can set this by either running `export PY_RUN_APPROACH=local` in your shell or add +# it to your ~/.zshrc file (and run `source ~/.zshrc`) +ifeq "$(PY_RUN_APPROACH)" "local" +POETRY := poetry run +GITHUB := gh +else +POETRY := docker compose run $(DOCKER_EXEC_ARGS) --rm $(APP_NAME) poetry run +GITHUB := docker compose run $(DOCKER_EXEC_ARGS) --rm $(APP_NAME) gh +endif + +# Docker user configuration +# This logic is to avoid issues with permissions and mounting local volumes, +# which should be owned by the same UID for Linux distros. Mac OS can use root, +# but it is best practice to run things as with least permission where possible + +# Can be set by adding user= and/ or uid= after the make command +# If variables are not set explicitly: try looking up values from current +# environment, otherwise fixed defaults. +# uid= defaults to 0 if user= set (which makes sense if user=root, otherwise you +# probably want to set uid as well). +ifeq ($(user),) +RUN_USER ?= $(or $(strip $(USER)),nodummy) +RUN_UID ?= $(or $(strip $(shell id -u)),4000) +else +RUN_USER = $(user) +RUN_UID = $(or $(strip $(uid)),0) +endif + +export RUN_USER +export RUN_UID + +################## +# Build Commands # +################## + +build: + docker compose build + +release-build: + docker buildx build \ + --target release \ + --platform=linux/amd64 \ + --build-arg RUN_USER=$(RUN_USER) \ + --build-arg RUN_UID=$(RUN_UID) \ + $(OPTS) \ + . diff --git a/ecs-terraform/docker-compose.yml b/ecs-terraform/docker-compose.yml new file mode 100644 index 000000000..6d19e7157 --- /dev/null +++ b/ecs-terraform/docker-compose.yml @@ -0,0 +1,5 @@ +services: + ecs-terraform: + build: + context: . + container_name: ecs-terraform diff --git a/infra/ecs-terraform/app-config/env-config/environment-variables.tf b/infra/ecs-terraform/app-config/env-config/environment-variables.tf index 1897fb343..d667582b1 100644 --- a/infra/ecs-terraform/app-config/env-config/environment-variables.tf +++ b/infra/ecs-terraform/app-config/env-config/environment-variables.tf @@ -2,12 +2,7 @@ locals { # Map from environment variable name to environment variable value # This is a map rather than a list so that variables can be easily # overridden per environment using terraform's `merge` function - default_extra_environment_variables = { - # Example environment variables - # WORKER_THREADS_COUNT = 4 - # LOG_LEVEL = "info" - # DB_CONNECTION_POOL_SIZE = 5 - } + default_extra_environment_variables = {} # Configuration for secrets # List of configurations for defining environment variables that pull from SSM parameter @@ -18,10 +13,5 @@ locals { # secret_store_name = "/ssm/param/name" # } # } - secrets = { - API_AUTH_TOKEN = { - manage_method = "manual" - secret_store_name = "/api/${var.environment}/api-auth-token" - } - } + secrets = {} } diff --git a/infra/ecs-terraform/app-config/main.tf b/infra/ecs-terraform/app-config/main.tf index 89a260452..87859b73f 100644 --- a/infra/ecs-terraform/app-config/main.tf +++ b/infra/ecs-terraform/app-config/main.tf @@ -1,6 +1,6 @@ locals { - app_name = "api" - environments = ["dev", "prod"] + app_name = "ecs-terraform" + environments = ["dev", "staging", "prod"] project_name = module.project_config.project_name has_database = true has_incident_management_service = false From 82faca189ce9e756bec24f94e8c7d8fb1811d7e2 Mon Sep 17 00:00:00 2001 From: Kai Siren Date: Tue, 15 Oct 2024 14:50:18 -0700 Subject: [PATCH 04/13] releases publishing --- bin/publish-release.sh | 16 +++++----- ecs-terraform/Makefile | 32 ------------------- .../app-config/.terraform.lock.hcl | 21 ++++++++++++ infra/ecs-terraform/app-config/prod.tf | 2 +- infra/ecs-terraform/app-config/staging.tf | 2 +- .../build-repository/shared.s3.tfbackend | 2 +- infra/ecs-terraform/service/dev.s3.tfbackend | 2 +- infra/ecs-terraform/service/prod.s3.tfbackend | 2 +- .../service/staging.s3.tfbackend | 2 +- 9 files changed, 35 insertions(+), 46 deletions(-) create mode 100644 infra/ecs-terraform/app-config/.terraform.lock.hcl diff --git a/bin/publish-release.sh b/bin/publish-release.sh index a835c12af..bc669580a 100755 --- a/bin/publish-release.sh +++ b/bin/publish-release.sh @@ -1,6 +1,6 @@ #!/bin/bash -set -euo pipefail +set -euox pipefail APP_NAME=$1 IMAGE_NAME=$2 @@ -15,12 +15,12 @@ echo " IMAGE_NAME=$IMAGE_NAME" echo " IMAGE_TAG=$IMAGE_TAG" # Need to init module when running in CD since GitHub actions does a fresh checkout of repo -terraform -chdir="infra/$APP_NAME/app-config" init > /dev/null -terraform -chdir="infra/$APP_NAME/app-config" apply -refresh-only -auto-approve> /dev/null +terraform -chdir="infra/$APP_NAME/app-config" init >/dev/null +terraform -chdir="infra/$APP_NAME/app-config" apply -refresh-only -auto-approve >/dev/null IMAGE_REPOSITORY_NAME=$(terraform -chdir="infra/$APP_NAME/app-config" output -raw image_repository_name) REGION=$(./bin/current-region.sh) -read -r IMAGE_REGISTRY_ID IMAGE_REPOSITORY_URL <<< "$(aws ecr describe-repositories --repository-names "$IMAGE_REPOSITORY_NAME" --query "repositories[0].[registryId,repositoryUri]" --output text)" +read -r IMAGE_REGISTRY_ID IMAGE_REPOSITORY_URL <<<"$(aws ecr describe-repositories --repository-names "$IMAGE_REPOSITORY_NAME" --query "repositories[0].[registryId,repositoryUri]" --output text)" IMAGE_REGISTRY="$IMAGE_REGISTRY_ID.dkr.ecr.$REGION.amazonaws.com" echo "Build repository info:" @@ -30,13 +30,13 @@ echo " IMAGE_REPOSITORY_NAME=$IMAGE_REPOSITORY_NAME" echo " IMAGE_REPOSITORY_URL=$IMAGE_REPOSITORY_URL" echo echo "Authenticating Docker with ECR" -aws ecr get-login-password --region "$REGION" \ - | docker login --username AWS --password-stdin "$IMAGE_REGISTRY" +aws ecr get-login-password --region "$REGION" | + docker login --username AWS --password-stdin "$IMAGE_REGISTRY" echo echo "Check if tag has already been published..." RESULT="" -RESULT=$(aws ecr describe-images --repository-name "$IMAGE_REPOSITORY_NAME" --image-ids "imageTag=$IMAGE_TAG" --region "$REGION" 2> /dev/null ) || true -if [ -n "$RESULT" ];then +RESULT=$(aws ecr describe-images --repository-name "$IMAGE_REPOSITORY_NAME" --image-ids "imageTag=$IMAGE_TAG" --region "$REGION" 2>/dev/null) || true +if [ -n "$RESULT" ]; then echo "Image with tag $IMAGE_TAG already published" exit 0 fi diff --git a/ecs-terraform/Makefile b/ecs-terraform/Makefile index 89db2fc9c..69cb0833f 100644 --- a/ecs-terraform/Makefile +++ b/ecs-terraform/Makefile @@ -2,41 +2,9 @@ # Constants # ############# -ORG ?= HHS -REPO ?= simpler-grants-gov -SPRINT_PROJECT ?= 13 -ROADMAP_PROJECT ?= 12 -OUTPUT_DIR ?= data -SPRINT_FILE ?= $(OUTPUT_DIR)/sprint-data.json -ROADMAP_FILE ?= $(OUTPUT_DIR)/roadmap-data.json -ISSUE_FILE ?= $(OUTPUT_DIR)/issue-data.json -SPRINT ?= @current -UNIT ?= points -ACTION ?= show-results -MIN_TEST_COVERAGE ?= 80 -APP_NAME ?= grants-analytics - # Required for CI to work properly SHELL = /bin/bash -o pipefail -ifdef CI - DOCKER_EXEC_ARGS := -T -e CI -e GH_TOKEN -e ANALYTICS_SLACK_BOT_TOKEN -e ANALYTICS_REPORTING_CHANNEL_ID -else - DOCKER_EXEC_ARGS := -e GH_TOKEN -endif - -# By default, all python/poetry commands will run inside of the docker container -# if you wish to run this natively, add PY_RUN_APPROACH=local to your environment vars -# You can set this by either running `export PY_RUN_APPROACH=local` in your shell or add -# it to your ~/.zshrc file (and run `source ~/.zshrc`) -ifeq "$(PY_RUN_APPROACH)" "local" -POETRY := poetry run -GITHUB := gh -else -POETRY := docker compose run $(DOCKER_EXEC_ARGS) --rm $(APP_NAME) poetry run -GITHUB := docker compose run $(DOCKER_EXEC_ARGS) --rm $(APP_NAME) gh -endif - # Docker user configuration # This logic is to avoid issues with permissions and mounting local volumes, # which should be owned by the same UID for Linux distros. Mac OS can use root, diff --git a/infra/ecs-terraform/app-config/.terraform.lock.hcl b/infra/ecs-terraform/app-config/.terraform.lock.hcl new file mode 100644 index 000000000..2483500b9 --- /dev/null +++ b/infra/ecs-terraform/app-config/.terraform.lock.hcl @@ -0,0 +1,21 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/external" { + version = "2.3.4" + hashes = [ + "h1:cCabxnWQ5fX1lS7ZqgUzsvWmKZw9FA7NRxAZ94vcTcc=", + "zh:037fd82cd86227359bc010672cd174235e2d337601d4686f526d0f53c87447cb", + "zh:0ea1db63d6173d01f2fa8eb8989f0809a55135a0d8d424b08ba5dabad73095fa", + "zh:17a4d0a306566f2e45778fbac48744b6fd9c958aaa359e79f144c6358cb93af0", + "zh:298e5408ab17fd2e90d2cd6d406c6d02344fe610de5b7dae943a58b958e76691", + "zh:38ecfd29ee0785fd93164812dcbe0664ebbe5417473f3b2658087ca5a0286ecb", + "zh:59f6a6f31acf66f4ea3667a555a70eba5d406c6e6d93c2c641b81d63261eeace", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:ad0279dfd09d713db0c18469f585e58d04748ca72d9ada83883492e0dd13bd58", + "zh:c69f66fd21f5e2c8ecf7ca68d9091c40f19ad913aef21e3ce23836e91b8cbb5f", + "zh:d4a56f8c48aa86fc8e0c233d56850f5783f322d6336f3bf1916e293246b6b5d4", + "zh:f2b394ebd4af33f343835517e80fc876f79361f4688220833bc3c77655dd2202", + "zh:f31982f29f12834e5d21e010856eddd19d59cd8f449adf470655bfd19354377e", + ] +} diff --git a/infra/ecs-terraform/app-config/prod.tf b/infra/ecs-terraform/app-config/prod.tf index cbb5c91ab..b1f3ae284 100644 --- a/infra/ecs-terraform/app-config/prod.tf +++ b/infra/ecs-terraform/app-config/prod.tf @@ -1,4 +1,4 @@ -module "dev_config" { +module "prod_config" { source = "./env-config" app_name = local.app_name default_region = module.project_config.default_region diff --git a/infra/ecs-terraform/app-config/staging.tf b/infra/ecs-terraform/app-config/staging.tf index 5245a9134..c5e88ec71 100644 --- a/infra/ecs-terraform/app-config/staging.tf +++ b/infra/ecs-terraform/app-config/staging.tf @@ -1,4 +1,4 @@ -module "dev_config" { +module "staging_config" { source = "./env-config" app_name = local.app_name default_region = module.project_config.default_region diff --git a/infra/ecs-terraform/build-repository/shared.s3.tfbackend b/infra/ecs-terraform/build-repository/shared.s3.tfbackend index 094584a1b..92c2aadf5 100644 --- a/infra/ecs-terraform/build-repository/shared.s3.tfbackend +++ b/infra/ecs-terraform/build-repository/shared.s3.tfbackend @@ -1,4 +1,4 @@ bucket = "simpler-grants-gov-315341936575-us-east-1-tf" -key = "infra/api/build-repository/shared.tfstate" +key = "infra/ecs-terraform/build-repository/shared.tfstate" dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" region = "us-east-1" diff --git a/infra/ecs-terraform/service/dev.s3.tfbackend b/infra/ecs-terraform/service/dev.s3.tfbackend index 6bd430c25..038a6a86f 100644 --- a/infra/ecs-terraform/service/dev.s3.tfbackend +++ b/infra/ecs-terraform/service/dev.s3.tfbackend @@ -1,4 +1,4 @@ bucket = "simpler-grants-gov-315341936575-us-east-1-tf" -key = "infra/api/service/dev.tfstate" +key = "infra/ecs-terraform/service/dev.tfstate" dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" region = "us-east-1" diff --git a/infra/ecs-terraform/service/prod.s3.tfbackend b/infra/ecs-terraform/service/prod.s3.tfbackend index 7ec37c16c..e137583c2 100644 --- a/infra/ecs-terraform/service/prod.s3.tfbackend +++ b/infra/ecs-terraform/service/prod.s3.tfbackend @@ -1,4 +1,4 @@ bucket = "simpler-grants-gov-315341936575-us-east-1-tf" -key = "infra/api/service/prod.tfstate" +key = "infra/ecs-terraform/service/prod.tfstate" dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" region = "us-east-1" diff --git a/infra/ecs-terraform/service/staging.s3.tfbackend b/infra/ecs-terraform/service/staging.s3.tfbackend index 3bd263b17..4d1876d50 100644 --- a/infra/ecs-terraform/service/staging.s3.tfbackend +++ b/infra/ecs-terraform/service/staging.s3.tfbackend @@ -1,4 +1,4 @@ bucket = "simpler-grants-gov-315341936575-us-east-1-tf" -key = "infra/api/service/staging.tfstate" +key = "infra/ecs-terraform/service/staging.tfstate" dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" region = "us-east-1" From f277a3f9a857119f5c705697d040e12274fe9cb4 Mon Sep 17 00:00:00 2001 From: Kai Siren Date: Tue, 15 Oct 2024 14:50:43 -0700 Subject: [PATCH 05/13] git restore --- bin/publish-release.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/publish-release.sh b/bin/publish-release.sh index bc669580a..a835c12af 100755 --- a/bin/publish-release.sh +++ b/bin/publish-release.sh @@ -1,6 +1,6 @@ #!/bin/bash -set -euox pipefail +set -euo pipefail APP_NAME=$1 IMAGE_NAME=$2 @@ -15,12 +15,12 @@ echo " IMAGE_NAME=$IMAGE_NAME" echo " IMAGE_TAG=$IMAGE_TAG" # Need to init module when running in CD since GitHub actions does a fresh checkout of repo -terraform -chdir="infra/$APP_NAME/app-config" init >/dev/null -terraform -chdir="infra/$APP_NAME/app-config" apply -refresh-only -auto-approve >/dev/null +terraform -chdir="infra/$APP_NAME/app-config" init > /dev/null +terraform -chdir="infra/$APP_NAME/app-config" apply -refresh-only -auto-approve> /dev/null IMAGE_REPOSITORY_NAME=$(terraform -chdir="infra/$APP_NAME/app-config" output -raw image_repository_name) REGION=$(./bin/current-region.sh) -read -r IMAGE_REGISTRY_ID IMAGE_REPOSITORY_URL <<<"$(aws ecr describe-repositories --repository-names "$IMAGE_REPOSITORY_NAME" --query "repositories[0].[registryId,repositoryUri]" --output text)" +read -r IMAGE_REGISTRY_ID IMAGE_REPOSITORY_URL <<< "$(aws ecr describe-repositories --repository-names "$IMAGE_REPOSITORY_NAME" --query "repositories[0].[registryId,repositoryUri]" --output text)" IMAGE_REGISTRY="$IMAGE_REGISTRY_ID.dkr.ecr.$REGION.amazonaws.com" echo "Build repository info:" @@ -30,13 +30,13 @@ echo " IMAGE_REPOSITORY_NAME=$IMAGE_REPOSITORY_NAME" echo " IMAGE_REPOSITORY_URL=$IMAGE_REPOSITORY_URL" echo echo "Authenticating Docker with ECR" -aws ecr get-login-password --region "$REGION" | - docker login --username AWS --password-stdin "$IMAGE_REGISTRY" +aws ecr get-login-password --region "$REGION" \ + | docker login --username AWS --password-stdin "$IMAGE_REGISTRY" echo echo "Check if tag has already been published..." RESULT="" -RESULT=$(aws ecr describe-images --repository-name "$IMAGE_REPOSITORY_NAME" --image-ids "imageTag=$IMAGE_TAG" --region "$REGION" 2>/dev/null) || true -if [ -n "$RESULT" ]; then +RESULT=$(aws ecr describe-images --repository-name "$IMAGE_REPOSITORY_NAME" --image-ids "imageTag=$IMAGE_TAG" --region "$REGION" 2> /dev/null ) || true +if [ -n "$RESULT" ];then echo "Image with tag $IMAGE_TAG already published" exit 0 fi From e4beb9faeb61a7edbc0747174ad9ef7aa88a027e Mon Sep 17 00:00:00 2001 From: Kai Siren Date: Tue, 15 Oct 2024 15:00:13 -0700 Subject: [PATCH 06/13] deploy service --- infra/ecs-terraform/service/main.tf | 76 +------------ infra/ecs-terraform/service/search.tf | 14 --- .../service/sfn_copy_oracle_data.tf | 100 ------------------ .../service/sfn_load_transform.tf | 94 ---------------- 4 files changed, 4 insertions(+), 280 deletions(-) delete mode 100644 infra/ecs-terraform/service/search.tf delete mode 100644 infra/ecs-terraform/service/sfn_copy_oracle_data.tf delete mode 100644 infra/ecs-terraform/service/sfn_load_transform.tf diff --git a/infra/ecs-terraform/service/main.tf b/infra/ecs-terraform/service/main.tf index 69a9e2a6c..bad25b877 100644 --- a/infra/ecs-terraform/service/main.tf +++ b/infra/ecs-terraform/service/main.tf @@ -46,11 +46,8 @@ locals { is_temporary = startswith(terraform.workspace, "t-") - environment_config = module.app_config.environment_configs[var.environment_name] - service_config = local.environment_config.service_config - database_config = local.environment_config.database_config - incident_management_service_integration_config = local.environment_config.incident_management_service_integration - domain = local.environment_config.domain + environment_config = module.app_config.environment_configs[var.environment_name] + service_config = local.environment_config.service_config } terraform { @@ -83,37 +80,11 @@ module "app_config" { source = "../app-config" } -data "aws_rds_cluster" "db_cluster" { - count = module.app_config.has_database ? 1 : 0 - cluster_identifier = local.database_config.cluster_name -} - -data "aws_acm_certificate" "cert" { - count = local.domain != null ? 1 : 0 - domain = local.domain -} - -data "aws_iam_policy" "app_db_access_policy" { - count = module.app_config.has_database ? 1 : 0 - name = local.database_config.app_access_policy_name -} - -data "aws_iam_policy" "migrator_db_access_policy" { - count = module.app_config.has_database ? 1 : 0 - name = local.database_config.migrator_access_policy_name -} - -# Retrieve url for external incident management tool (e.g. Pagerduty, Splunk-On-Call) - -data "aws_ssm_parameter" "incident_management_service_integration_url" { - count = module.app_config.has_incident_management_service ? 1 : 0 - name = local.incident_management_service_integration_config.integration_url_param_name -} - module "service" { source = "../../modules/service" service_name = local.service_name is_temporary = local.is_temporary + enable_load_balancer = false image_repository_name = module.app_config.image_repository_name image_tag = local.image_tag vpc_id = data.aws_vpc.network.id @@ -122,51 +93,12 @@ module "service" { cpu = 1024 memory = 2048 - cert_arn = local.domain != null ? data.aws_acm_certificate.cert[0].arn : null - - app_access_policy_arn = data.aws_iam_policy.app_db_access_policy[0].arn - migrator_access_policy_arn = data.aws_iam_policy.migrator_db_access_policy[0].arn - - db_vars = module.app_config.has_database ? { - security_group_ids = data.aws_rds_cluster.db_cluster[0].vpc_security_group_ids - connection_info = { - host = data.aws_rds_cluster.db_cluster[0].endpoint - port = data.aws_rds_cluster.db_cluster[0].port - user = local.database_config.app_username - db_name = data.aws_rds_cluster.db_cluster[0].database_name - schema_name = local.database_config.schema_name - } - } : null - - extra_environment_variables = merge(local.service_config.extra_environment_variables, { "ENVIRONMENT" : var.environment_name }) + extra_environment_variables = local.service_config.extra_environment_variables secrets = concat( [for secret_name in keys(local.service_config.secrets) : { name = secret_name valueFrom = module.secrets[secret_name].secret_arn }], - local.environment_config.search_config != null ? [{ - name = "SEARCH_USERNAME" - valueFrom = data.aws_ssm_parameter.search_username_arn[0].arn - }] : [], - local.environment_config.search_config != null ? [{ - name = "SEARCH_PASSWORD" - valueFrom = data.aws_ssm_parameter.search_password_arn[0].arn - }] : [], - local.environment_config.search_config != null ? [{ - name = "SEARCH_ENDPOINT" - valueFrom = data.aws_ssm_parameter.search_endpoint_arn[0].arn - }] : [] ) } - -module "monitoring" { - source = "../../modules/monitoring" - #Email subscription list: - email_alerts_subscription_list = ["grantsalerts@navapbc.com"] - - # Module takes service and ALB names to link all alerts with corresponding targets - service_name = local.service_name - load_balancer_arn_suffix = module.service.load_balancer_arn_suffix - incident_management_service_integration_url = module.app_config.has_incident_management_service ? data.aws_ssm_parameter.incident_management_service_integration_url[0].value : null -} diff --git a/infra/ecs-terraform/service/search.tf b/infra/ecs-terraform/service/search.tf deleted file mode 100644 index 2af273406..000000000 --- a/infra/ecs-terraform/service/search.tf +++ /dev/null @@ -1,14 +0,0 @@ -data "aws_ssm_parameter" "search_username_arn" { - count = local.environment_config.search_config != null ? 1 : 0 - name = "/search/${local.service_name}/username" -} - -data "aws_ssm_parameter" "search_password_arn" { - count = local.environment_config.search_config != null ? 1 : 0 - name = "/search/${local.service_name}/password" -} - -data "aws_ssm_parameter" "search_endpoint_arn" { - count = local.environment_config.search_config != null ? 1 : 0 - name = "/search/${local.service_name}/endpoint" -} diff --git a/infra/ecs-terraform/service/sfn_copy_oracle_data.tf b/infra/ecs-terraform/service/sfn_copy_oracle_data.tf deleted file mode 100644 index fd104da1d..000000000 --- a/infra/ecs-terraform/service/sfn_copy_oracle_data.tf +++ /dev/null @@ -1,100 +0,0 @@ -# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group -resource "aws_cloudwatch_log_group" "copy_oracle_data" { - name_prefix = "/aws/vendedlogs/states/${local.service_name}-copy-oracle-data" - - # Conservatively retain logs for 5 years. - # Looser requirements may allow shorter retention periods - retention_in_days = 1827 - - # checkov:skip=CKV_AWS_158:skip requirement to encrypt with customer managed KMS key -} - -# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sfn_state_machine -resource "aws_sfn_state_machine" "copy_oracle_data" { - - name = "${local.service_name}-copy-oracle-data" - role_arn = module.service.task_role_arn - - definition = jsonencode({ - "StartAt" : "ExecuteECSTask", - "States" : { - "ExecuteECSTask" : { - "Type" : "Task", - # docs: https://docs.aws.amazon.com/step-functions/latest/dg/connect-ecs.html - "Resource" : "arn:aws:states:::ecs:runTask.sync", - "Parameters" : { - "Cluster" : module.service.cluster_arn, - "TaskDefinition" : module.service.task_definition_arn, - "LaunchType" : "FARGATE", - "NetworkConfiguration" : { - "AwsvpcConfiguration" : { - "Subnets" : data.aws_subnets.private.ids, - "SecurityGroups" : [module.service.app_security_group_id], - } - }, - "Overrides" : { - "ContainerOverrides" : [ - { - "Name" : local.service_name, - "Environment" : [ - { - "Name" : "FLASK_APP", - "Value" : "src.app:create_app()", - } - ] - "Command" : [ - "poetry", - "run", - "flask", - "data-migration", - "copy-oracle-data", - ] - } - ] - } - }, - "End" : true - } - } - }) - - logging_configuration { - log_destination = "${aws_cloudwatch_log_group.copy_oracle_data.arn}:*" - include_execution_data = true - level = "ERROR" - } - - tracing_configuration { - enabled = true - } -} - -# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule_group -resource "aws_scheduler_schedule_group" "copy_oracle_data" { - name = "${local.service_name}-copy-oracle-data" -} - -# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule -resource "aws_scheduler_schedule" "copy_oracle_data" { - # checkov:skip=CKV_AWS_297:Ignore the managed customer KMS key requirement for now - - name = "${local.service_name}-copy-oracle-data" - state = "ENABLED" - group_name = aws_scheduler_schedule_group.copy_oracle_data.id - schedule_expression = "rate(2 minutes)" - schedule_expression_timezone = "US/Eastern" - - flexible_time_window { - mode = "OFF" - } - - # target is the state machine - target { - arn = aws_sfn_state_machine.copy_oracle_data.arn - role_arn = module.service.task_role_arn - - retry_policy { - maximum_retry_attempts = 0 # dont retry, just wait for the next execution - } - } -} diff --git a/infra/ecs-terraform/service/sfn_load_transform.tf b/infra/ecs-terraform/service/sfn_load_transform.tf deleted file mode 100644 index 194cc68dc..000000000 --- a/infra/ecs-terraform/service/sfn_load_transform.tf +++ /dev/null @@ -1,94 +0,0 @@ -# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group -resource "aws_cloudwatch_log_group" "load_transform" { - name_prefix = "/aws/vendedlogs/states/${local.service_name}-load-transform" - - # Conservatively retain logs for 5 years. - # Looser requirements may allow shorter retention periods - retention_in_days = 1827 - - # checkov:skip=CKV_AWS_158:skip requirement to encrypt with customer managed KMS key -} - -# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sfn_state_machine -resource "aws_sfn_state_machine" "load_transform" { - - name = "${local.service_name}-load-transform" - role_arn = module.service.task_role_arn - - definition = jsonencode({ - "StartAt" : "ExecuteECSTask", - "States" : { - "ExecuteECSTask" : { - "Type" : "Task", - # docs: https://docs.aws.amazon.com/step-functions/latest/dg/connect-ecs.html - "Resource" : "arn:aws:states:::ecs:runTask.sync", - "Parameters" : { - "Cluster" : module.service.cluster_arn, - "TaskDefinition" : module.service.task_definition_arn, - "LaunchType" : "FARGATE", - "NetworkConfiguration" : { - "AwsvpcConfiguration" : { - "Subnets" : data.aws_subnets.private.ids, - "SecurityGroups" : [module.service.app_security_group_id], - } - }, - "Overrides" : { - "ContainerOverrides" : [ - { - "Name" : local.service_name, - "Environment" : [ - { - "Name" : "FLASK_APP", - "Value" : "src.app:create_app()", - } - ] - "Command" : module.app_config.environment_configs[var.environment_name].load_transform_args - } - ] - } - }, - "End" : true - } - } - }) - - logging_configuration { - log_destination = "${aws_cloudwatch_log_group.load_transform.arn}:*" - include_execution_data = true - level = "ERROR" - } - - tracing_configuration { - enabled = true - } -} - -# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule_group -resource "aws_scheduler_schedule_group" "load_transform" { - name = "${local.service_name}-load-transform" -} - -# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule -resource "aws_scheduler_schedule" "load_transform" { - # checkov:skip=CKV_AWS_297:Ignore the managed customer KMS key requirement for now - - name = "${local.service_name}-load-transform" - state = "ENABLED" - group_name = aws_scheduler_schedule_group.load_transform.id - schedule_expression = "rate(1 days)" - schedule_expression_timezone = "US/Eastern" - - flexible_time_window { - mode = "OFF" - } - - # target is the state machine - target { - arn = aws_sfn_state_machine.load_transform.arn - role_arn = module.service.task_role_arn - - retry_policy { - maximum_retry_attempts = 0 # dont retry, just wait for the next execution - } - } -} From f8c8d48e806392be96817037f5c35ff8357977ef Mon Sep 17 00:00:00 2001 From: Kai Siren Date: Tue, 15 Oct 2024 15:01:01 -0700 Subject: [PATCH 07/13] dont spin up instances --- infra/ecs-terraform/service/main.tf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/infra/ecs-terraform/service/main.tf b/infra/ecs-terraform/service/main.tf index bad25b877..83a759b67 100644 --- a/infra/ecs-terraform/service/main.tf +++ b/infra/ecs-terraform/service/main.tf @@ -93,6 +93,9 @@ module "service" { cpu = 1024 memory = 2048 + # This is a task based service, not a web server, so we don't need to run any instances of the service at rest. + desired_instance_count = 0 + extra_environment_variables = local.service_config.extra_environment_variables secrets = concat( From a2088c702415175f77fe338ee703656804040c8e Mon Sep 17 00:00:00 2001 From: Kai Siren Date: Tue, 15 Oct 2024 15:57:54 -0700 Subject: [PATCH 08/13] copy in TLD --- ecs-terraform/.dockerignore | 3 +++ ecs-terraform/Dockerfile | 16 ++++++++++++++-- ecs-terraform/Makefile | 6 +++++- ecs-terraform/docker-compose.yml | 1 + 4 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 ecs-terraform/.dockerignore diff --git a/ecs-terraform/.dockerignore b/ecs-terraform/.dockerignore new file mode 100644 index 000000000..706a826f5 --- /dev/null +++ b/ecs-terraform/.dockerignore @@ -0,0 +1,3 @@ +node_modules +.next +.terraform diff --git a/ecs-terraform/Dockerfile b/ecs-terraform/Dockerfile index 6ea36a7be..09ed55202 100644 --- a/ecs-terraform/Dockerfile +++ b/ecs-terraform/Dockerfile @@ -1,3 +1,15 @@ -FROM hashicorp/terraform:1.9.7 AS release +FROM hashicorp/terraform:1.9.7 AS base -COPY ../ . +RUN mkdir -p /app +WORKDIR /app +COPY --from=top-level-directory . . + +FROM base AS dev + +RUN apk update \ + && apk upgrade \ + && apk add --no-cache \ + coreutils \ + bash + +FROM base AS release diff --git a/ecs-terraform/Makefile b/ecs-terraform/Makefile index 69cb0833f..bba414e02 100644 --- a/ecs-terraform/Makefile +++ b/ecs-terraform/Makefile @@ -31,10 +31,14 @@ export RUN_UID ################## build: - docker compose build + docker buildx build \ + --build-context top-level-directory=../ \ + --tag $(notdir $(shell pwd)):latest \ + . release-build: docker buildx build \ + --build-context top-level-directory=../ \ --target release \ --platform=linux/amd64 \ --build-arg RUN_USER=$(RUN_USER) \ diff --git a/ecs-terraform/docker-compose.yml b/ecs-terraform/docker-compose.yml index 6d19e7157..47962df3d 100644 --- a/ecs-terraform/docker-compose.yml +++ b/ecs-terraform/docker-compose.yml @@ -2,4 +2,5 @@ services: ecs-terraform: build: context: . + target: dev container_name: ecs-terraform From 30dde7d422d15364533063e110d8fea1fb10baf0 Mon Sep 17 00:00:00 2001 From: Kai Siren Date: Tue, 15 Oct 2024 16:01:19 -0700 Subject: [PATCH 09/13] ignore more stuff --- ecs-terraform/.dockerignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ecs-terraform/.dockerignore b/ecs-terraform/.dockerignore index 706a826f5..1ee866232 100644 --- a/ecs-terraform/.dockerignore +++ b/ecs-terraform/.dockerignore @@ -1,3 +1,5 @@ node_modules +analytics +api .next .terraform From 865529c441b9753c8877e30abe66aa7509adb6ed Mon Sep 17 00:00:00 2001 From: Kai Siren Date: Tue, 15 Oct 2024 16:11:30 -0700 Subject: [PATCH 10/13] cleanup image size --- ecs-terraform/.dockerignore | 2 ++ ecs-terraform/Dockerfile | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ecs-terraform/.dockerignore b/ecs-terraform/.dockerignore index 1ee866232..af53f4a9b 100644 --- a/ecs-terraform/.dockerignore +++ b/ecs-terraform/.dockerignore @@ -3,3 +3,5 @@ analytics api .next .terraform +**/.terraform/** +.git diff --git a/ecs-terraform/Dockerfile b/ecs-terraform/Dockerfile index 09ed55202..ef42bad0c 100644 --- a/ecs-terraform/Dockerfile +++ b/ecs-terraform/Dockerfile @@ -2,7 +2,10 @@ FROM hashicorp/terraform:1.9.7 AS base RUN mkdir -p /app WORKDIR /app -COPY --from=top-level-directory . . + +COPY --from=top-level-directory bin /app/bin +COPY --from=top-level-directory infra /app/infra +COPY --from=top-level-directory Makefile /app/Makefile FROM base AS dev From b29027cc50c51580cf66d0fc443c110484845c7b Mon Sep 17 00:00:00 2001 From: Kai Siren Date: Tue, 15 Oct 2024 16:14:57 -0700 Subject: [PATCH 11/13] more dockerignore --- .dockerignore | 3 +++ ecs-terraform/.dockerignore | 4 ---- infra/ecs-terraform/app-config/env-config/variables.tf | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..7107fbe67 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +node_modules +**/.terraform/** +.git diff --git a/ecs-terraform/.dockerignore b/ecs-terraform/.dockerignore index af53f4a9b..7107fbe67 100644 --- a/ecs-terraform/.dockerignore +++ b/ecs-terraform/.dockerignore @@ -1,7 +1,3 @@ node_modules -analytics -api -.next -.terraform **/.terraform/** .git diff --git a/infra/ecs-terraform/app-config/env-config/variables.tf b/infra/ecs-terraform/app-config/env-config/variables.tf index 0e91d3231..488051b90 100644 --- a/infra/ecs-terraform/app-config/env-config/variables.tf +++ b/infra/ecs-terraform/app-config/env-config/variables.tf @@ -11,6 +11,7 @@ variable "default_region" { description = "default region for the project" type = string } + variable "service_override_extra_environment_variables" { type = map(string) description = < Date: Tue, 15 Oct 2024 16:31:57 -0700 Subject: [PATCH 12/13] change entrypoint --- ecs-terraform/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/ecs-terraform/Dockerfile b/ecs-terraform/Dockerfile index ef42bad0c..d5275b315 100644 --- a/ecs-terraform/Dockerfile +++ b/ecs-terraform/Dockerfile @@ -2,6 +2,7 @@ FROM hashicorp/terraform:1.9.7 AS base RUN mkdir -p /app WORKDIR /app +ENTRYPOINT [ "sh" ] COPY --from=top-level-directory bin /app/bin COPY --from=top-level-directory infra /app/infra From 148e2141b60d5428477ef4d27bf97a70ea38e93b Mon Sep 17 00:00:00 2001 From: Kai Siren Date: Tue, 15 Oct 2024 16:34:30 -0700 Subject: [PATCH 13/13] fix sh --- ecs-terraform/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecs-terraform/Dockerfile b/ecs-terraform/Dockerfile index d5275b315..80dc53fcf 100644 --- a/ecs-terraform/Dockerfile +++ b/ecs-terraform/Dockerfile @@ -2,7 +2,7 @@ FROM hashicorp/terraform:1.9.7 AS base RUN mkdir -p /app WORKDIR /app -ENTRYPOINT [ "sh" ] +ENTRYPOINT [ "sh", "-c" ] COPY --from=top-level-directory bin /app/bin COPY --from=top-level-directory infra /app/infra