diff --git a/.gitignore b/.gitignore index 06928cae6..4438c68b4 100644 --- a/.gitignore +++ b/.gitignore @@ -146,3 +146,6 @@ dmypy.json # vim *.swp + +# Terraform plan outputs +*.tfplan diff --git a/infra/api/app-config/env-config/environment-variables.tf b/infra/api/app-config/env-config/environment-variables.tf index 01d2e0618..1897fb343 100644 --- a/infra/api/app-config/env-config/environment-variables.tf +++ b/infra/api/app-config/env-config/environment-variables.tf @@ -12,11 +12,16 @@ locals { # Configuration for secrets # List of configurations for defining environment variables that pull from SSM parameter # store. Configurations are of the format - # { name = "ENV_VAR_NAME", ssm_param_name = "/ssm/param/name" } - secrets = [ - { - name = "API_AUTH_TOKEN" - ssm_param_name = "/api/${var.environment}/api-auth-token" + # { + # 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/api/app-config/env-config/outputs.tf b/infra/api/app-config/env-config/outputs.tf index 59bae8bf3..c9375fa4f 100644 --- a/infra/api/app-config/env-config/outputs.tf +++ b/infra/api/app-config/env-config/outputs.tf @@ -33,7 +33,7 @@ output "service_config" { var.service_override_extra_environment_variables ) - secrets = toset(local.secrets) + secrets = local.secrets } } diff --git a/infra/api/service/.terraform.lock.hcl b/infra/api/service/.terraform.lock.hcl index 8b6028214..7463d5bf3 100644 --- a/infra/api/service/.terraform.lock.hcl +++ b/infra/api/service/.terraform.lock.hcl @@ -55,3 +55,22 @@ provider "registry.terraform.io/hashicorp/external" { "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/api/service/main.tf b/infra/api/service/main.tf index 7e8b37585..e0966f515 100644 --- a/infra/api/service/main.tf +++ b/infra/api/service/main.tf @@ -145,17 +145,17 @@ module "service" { name = secret_name valueFrom = module.secrets[secret_name].secret_arn }], - local.environment_config.search_config.has_search ? [{ + local.environment_config.search_config != null ? [{ name = "SEARCH_USERNAME" - valueFrom = data.aws_ssm_parameter.search_username_arn.value + valueFrom = data.aws_ssm_parameter.search_username_arn[0].value }] : [], - local.environment_config.search_config.has_search ? [{ + local.environment_config.search_config != null ? [{ name = "SEARCH_PASSWORD" - valueFrom = data.aws_ssm_parameter.search_password_arn.value + valueFrom = data.aws_ssm_parameter.search_password_arn[0].value }] : [], - local.environment_config.search_config.has_search ? [{ + local.environment_config.search_config != null ? [{ name = "SEARCH_ENDPOINT" - valueFrom = data.aws_ssm_parameter.search_endpoint_arn.value + valueFrom = data.aws_ssm_parameter.search_endpoint_arn[0].value }] : [] ) } diff --git a/infra/api/service/plan.out b/infra/api/service/plan.out new file mode 100644 index 000000000..c6d8166bc Binary files /dev/null and b/infra/api/service/plan.out differ diff --git a/infra/api/service/search.tf b/infra/api/service/search.tf index c0be31593..d5a678a60 100644 --- a/infra/api/service/search.tf +++ b/infra/api/service/search.tf @@ -1,14 +1,14 @@ data "aws_ssm_parameter" "search_username_arn" { - count = local.environment_config.search_config.has_search ? 1 : 0 + count = local.environment_config.search_config != null ? 1 : 0 name = "/search/${local.prefix}${var.environment_name}/username" } data "aws_ssm_parameter" "search_password_arn" { - count = local.environment_config.search_config.has_search ? 1 : 0 + count = local.environment_config.search_config != null ? 1 : 0 name = "/search/${local.prefix}${var.environment_name}/password" } data "aws_ssm_parameter" "search_endpoint_arn" { - count = local.environment_config.search_config.has_search ? 1 : 0 + count = local.environment_config.search_config != null ? 1 : 0 name = "/search/${local.prefix}${var.environment_name}/endpoint" } diff --git a/infra/api/service/secrets.tf b/infra/api/service/secrets.tf new file mode 100644 index 000000000..e65eaa0cc --- /dev/null +++ b/infra/api/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/modules/secret/main.tf b/infra/modules/secret/main.tf new file mode 100644 index 000000000..8619c86e8 --- /dev/null +++ b/infra/modules/secret/main.tf @@ -0,0 +1,26 @@ +locals { + secret = var.manage_method == "generated" ? aws_ssm_parameter.secret[0] : data.aws_ssm_parameter.secret[0] + access_policy_name = "${trimprefix(replace(local.secret.name, "/", "-"), "/")}-access" +} + +resource "random_password" "secret" { + count = var.manage_method == "generated" ? 1 : 0 + + length = 64 + special = true + override_special = "!#$%&*()-_=+[]{}<>:?" +} + +resource "aws_ssm_parameter" "secret" { + count = var.manage_method == "generated" ? 1 : 0 + + name = var.secret_store_name + type = "SecureString" + value = random_password.secret[0].result +} + +data "aws_ssm_parameter" "secret" { + count = var.manage_method == "manual" ? 1 : 0 + + name = var.secret_store_name +} diff --git a/infra/modules/secret/outputs.tf b/infra/modules/secret/outputs.tf new file mode 100644 index 000000000..57ebfcf82 --- /dev/null +++ b/infra/modules/secret/outputs.tf @@ -0,0 +1,3 @@ +output "secret_arn" { + value = local.secret.arn +} diff --git a/infra/modules/secret/variables.tf b/infra/modules/secret/variables.tf new file mode 100644 index 000000000..44d287951 --- /dev/null +++ b/infra/modules/secret/variables.tf @@ -0,0 +1,22 @@ +variable "manage_method" { + type = string + description = <