diff --git a/main.tf b/main.tf index 8e10259c..114c3b72 100644 --- a/main.tf +++ b/main.tf @@ -160,12 +160,11 @@ module "services" { monitoring_telemetry = var.monitoring_telemetry # Data stores - postgres_username = module.database.postgres_database_username - postgres_password = module.database.postgres_database_password - postgres_host = module.database.postgres_database_address - postgres_port = module.database.postgres_database_port - redis_host = module.redis.redis_endpoint - redis_port = module.redis.redis_port + postgres_database_secret_arn = module.database.postgres_database_secret_arn + postgres_host = module.database.postgres_database_address + postgres_port = module.database.postgres_database_port + redis_host = module.redis.redis_endpoint + redis_port = module.redis.redis_port brainstore_enabled = var.enable_brainstore brainstore_default = var.brainstore_default @@ -379,5 +378,3 @@ module "brainstore" { cache_file_size_writer = var.brainstore_cache_file_size_writer locks_s3_path = var.brainstore_locks_s3_path } - - diff --git a/modules/services/iam.tf b/modules/services/iam.tf index d57b67d9..573c13fc 100644 --- a/modules/services/iam.tf +++ b/modules/services/iam.tf @@ -36,6 +36,12 @@ resource "aws_iam_policy" "api_handler_lambda_policies" { Action = ["lambda:InvokeFunction"] Effect = "Allow" Resource = aws_lambda_function.catchup_etl.arn + }, + { + Sid = "ReadPostgresSecret" + Action = ["secretsmanager:GetSecretValue"] + Effect = "Allow" + Resource = var.postgres_database_secret_arn } ] Version = "2012-10-17" @@ -70,6 +76,13 @@ resource "aws_iam_role_policy_attachment" "lambda_vpc_access" { policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" } +resource "aws_kms_grant" "default_role_postgres_secret" { + name = "${var.deployment_name}-default-role-postgres-secret" + key_id = var.kms_key_arn + grantee_principal = aws_iam_role.default_role.arn + operations = ["Decrypt"] +} + resource "aws_iam_role_policy" "default_role_policy" { name = "${var.deployment_name}-DefaultRolePolicy" role = aws_iam_role.default_role.id @@ -103,9 +116,20 @@ resource "aws_iam_role_policy" "default_role_policy" { "arn:aws:logs:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:log-group:/braintrust/${var.deployment_name}/*", ] }, + { + Sid = "ReadPostgresSecret" + Action = ["secretsmanager:GetSecretValue"] + Effect = "Allow" + Resource = var.postgres_database_secret_arn + }, + { + Sid = "UseKmsForPostgresSecret" + Action = [ + "kms:Decrypt" + ] + Effect = "Allow" + Resource = var.kms_key_arn + }, ] }) } - - - diff --git a/modules/services/lambda-aiproxy.tf b/modules/services/lambda-aiproxy.tf index 12217e0a..23d807fd 100644 --- a/modules/services/lambda-aiproxy.tf +++ b/modules/services/lambda-aiproxy.tf @@ -21,7 +21,9 @@ resource "aws_lambda_function" "ai_proxy" { # See https://github.com/tobilg/duckdb-nodejs-layer layers = concat( [local.duckdb_nodejs_arm64_layer_arn], - local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [] + local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [], + [data.aws_lambda_layer_version.aws_params_secrets_arm64.arn], + [aws_lambda_layer_version.secrets_wrapper.arn], ) logging_config { diff --git a/modules/services/lambda-apihandler.tf b/modules/services/lambda-apihandler.tf index c2705ed3..8b135760 100644 --- a/modules/services/lambda-apihandler.tf +++ b/modules/services/lambda-apihandler.tf @@ -8,7 +8,11 @@ locals { PRIMARY_ORG_NAME = var.primary_org_name BRAINTRUST_DEPLOYMENT_NAME = var.deployment_name - PG_URL = local.postgres_url + PG_HOST = var.postgres_host + PG_PORT = var.postgres_port + DATABASE_SECRETS_ARN = var.postgres_database_secret_arn + AWS_LAMBDA_EXEC_WRAPPER = "/opt/bin/aws-sm-wrapper.sh" + REDIS_HOST = var.redis_host REDIS_PORT = var.redis_port RESPONSE_BUCKET = local.lambda_responses_bucket_id @@ -81,7 +85,9 @@ resource "aws_lambda_function" "api_handler" { # See https://github.com/tobilg/duckdb-nodejs-layer layers = concat( [local.duckdb_nodejs_arm64_layer_arn], - local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [] + local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [], + [data.aws_lambda_layer_version.aws_params_secrets_arm64.arn], + [aws_lambda_layer_version.secrets_wrapper.arn], ) ephemeral_storage { diff --git a/modules/services/lambda-automation-cron.tf b/modules/services/lambda-automation-cron.tf index cdb7ead4..7a4dd13b 100644 --- a/modules/services/lambda-automation-cron.tf +++ b/modules/services/lambda-automation-cron.tf @@ -21,7 +21,9 @@ resource "aws_lambda_function" "automation_cron" { # See https://github.com/tobilg/duckdb-nodejs-layer layers = concat( [local.duckdb_nodejs_arm64_layer_arn], - local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [] + local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [], + [data.aws_lambda_layer_version.aws_params_secrets_arm64.arn], + [aws_lambda_layer_version.secrets_wrapper.arn], ) ephemeral_storage { @@ -31,10 +33,12 @@ resource "aws_lambda_function" "automation_cron" { environment { variables = merge({ ORG_NAME = var.braintrust_org_name - PG_URL = local.postgres_url REDIS_HOST = var.redis_host REDIS_PORT = var.redis_port REDIS_URL = "redis://${var.redis_host}:${var.redis_port}" + PG_HOST = var.postgres_host + PG_PORT = var.postgres_port + DATABASE_SECRETS_ARN = var.postgres_database_secret_arn BRAINSTORE_ENABLED = var.brainstore_enabled BRAINSTORE_BACKFILL_HISTORICAL_BATCH_SIZE = var.brainstore_etl_batch_size BRAINSTORE_BACKFILL_ENABLE_NONHISTORICAL = var.brainstore_default @@ -43,6 +47,8 @@ resource "aws_lambda_function" "automation_cron" { BRAINSTORE_REALTIME_WAL_BUCKET = local.brainstore_s3_bucket FUNCTION_SECRET_KEY = var.function_tools_secret_key CRON_OVERRIDE_SECRET_KEY = random_password.service_token_secret_key.result + AWS_LAMBDA_EXEC_WRAPPER = "/opt/bin/aws-sm-wrapper.sh" + }, var.extra_env_vars.AutomationCron, local.observability_enabled ? merge(local.datadog_env_vars, { diff --git a/modules/services/lambda-billing-cron.tf b/modules/services/lambda-billing-cron.tf index 93a54ab9..bd029c46 100644 --- a/modules/services/lambda-billing-cron.tf +++ b/modules/services/lambda-billing-cron.tf @@ -19,18 +19,25 @@ resource "aws_lambda_function" "billing_cron" { architectures = ["arm64"] kms_key_arn = var.kms_key_arn - layers = local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [] + layers = concat( + local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [], + [data.aws_lambda_layer_version.aws_params_secrets_arm64.arn], + [aws_lambda_layer_version.secrets_wrapper.arn], + ) environment { variables = merge({ ORG_NAME = var.braintrust_org_name - PG_URL = local.postgres_url REDIS_HOST = var.redis_host REDIS_PORT = var.redis_port + PG_HOST = var.postgres_host + PG_PORT = var.postgres_port + DATABASE_SECRETS_ARN = var.postgres_database_secret_arn CONTROL_PLANE_TELEMETRY = var.monitoring_telemetry TELEMETRY_DISABLE_AGGREGATION = var.disable_billing_telemetry_aggregation TELEMETRY_LOG_LEVEL = var.billing_telemetry_log_level SERVICE_TOKEN_SECRET_KEY = var.function_tools_secret_key + AWS_LAMBDA_EXEC_WRAPPER = "/opt/bin/aws-sm-wrapper.sh" }, var.extra_env_vars.BillingCron, local.observability_enabled ? merge(local.datadog_env_vars, { diff --git a/modules/services/lambda-catchup-etl.tf b/modules/services/lambda-catchup-etl.tf index e879920e..965d395b 100644 --- a/modules/services/lambda-catchup-etl.tf +++ b/modules/services/lambda-catchup-etl.tf @@ -18,19 +18,26 @@ resource "aws_lambda_function" "catchup_etl" { architectures = ["arm64"] kms_key_arn = var.kms_key_arn - layers = local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [] + layers = concat( + local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [], + [data.aws_lambda_layer_version.aws_params_secrets_arm64.arn], + [aws_lambda_layer_version.secrets_wrapper.arn], + ) environment { variables = merge({ ORG_NAME = var.braintrust_org_name - PG_URL = local.postgres_url REDIS_HOST = var.redis_host REDIS_PORT = var.redis_port + PG_HOST = var.postgres_host + PG_PORT = var.postgres_port + DATABASE_SECRETS_ARN = var.postgres_database_secret_arn BRAINSTORE_ENABLED = var.brainstore_enabled BRAINSTORE_URL = local.brainstore_url BRAINSTORE_WRITER_URL = local.brainstore_writer_url BRAINSTORE_REALTIME_WAL_BUCKET = local.brainstore_s3_bucket BRAINSTORE_BACKFILL_HISTORICAL_BATCH_SIZE = var.brainstore_etl_batch_size + AWS_LAMBDA_EXEC_WRAPPER = "/opt/bin/aws-sm-wrapper.sh" }, var.extra_env_vars.CatchupETL, local.observability_enabled ? merge(local.datadog_env_vars, { diff --git a/modules/services/lambda-migrate-database.tf b/modules/services/lambda-migrate-database.tf index f6ebf364..865f78f3 100644 --- a/modules/services/lambda-migrate-database.tf +++ b/modules/services/lambda-migrate-database.tf @@ -16,7 +16,11 @@ resource "aws_lambda_function" "migrate_database" { publish = true kms_key_arn = var.kms_key_arn - layers = local.observability_enabled ? [local.datadog_python_layer_arn, local.datadog_extension_layer_arn] : [] + layers = concat( + local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [], + [data.aws_lambda_layer_version.aws_params_secrets_x86_64.arn], + [aws_lambda_layer_version.secrets_wrapper.arn], + ) logging_config { log_format = local.observability_enabled ? "JSON" : "Text" @@ -25,8 +29,11 @@ resource "aws_lambda_function" "migrate_database" { environment { variables = merge({ BRAINTRUST_RUN_DRAFT_MIGRATIONS = var.run_draft_migrations - PG_URL = local.postgres_url INSERT_LOGS2 = "true" + PG_HOST = var.postgres_host + PG_PORT = var.postgres_port + DATABASE_SECRETS_ARN = var.postgres_database_secret_arn + AWS_LAMBDA_EXEC_WRAPPER = "/opt/bin/aws-sm-wrapper.sh" }, var.extra_env_vars.MigrateDatabaseFunction, local.observability_enabled ? merge(local.datadog_env_vars, { diff --git a/modules/services/lambda-quarantine-warmup.tf b/modules/services/lambda-quarantine-warmup.tf index 169c84b5..41ebe391 100644 --- a/modules/services/lambda-quarantine-warmup.tf +++ b/modules/services/lambda-quarantine-warmup.tf @@ -23,7 +23,9 @@ resource "aws_lambda_function" "quarantine_warmup" { # See https://github.com/tobilg/duckdb-nodejs-layer layers = concat( [local.duckdb_nodejs_arm64_layer_arn], - local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [] + local.observability_enabled ? [local.datadog_node_layer_arn, local.datadog_extension_arm_layer_arn] : [], + [data.aws_lambda_layer_version.aws_params_secrets_arm64.arn], + [aws_lambda_layer_version.secrets_wrapper.arn], ) environment { @@ -31,10 +33,14 @@ resource "aws_lambda_function" "quarantine_warmup" { ORG_NAME = var.braintrust_org_name BRAINTRUST_DEPLOYMENT_NAME = var.deployment_name - PG_URL = local.postgres_url REDIS_HOST = var.redis_host REDIS_PORT = var.redis_port + PG_HOST = var.postgres_host + PG_PORT = var.postgres_port + DATABASE_SECRETS_ARN = var.postgres_database_secret_arn + AWS_LAMBDA_EXEC_WRAPPER = "/opt/bin/aws-sm-wrapper.sh" + QUARANTINE_INVOKE_ROLE = var.use_quarantine_vpc && var.quarantine_invoke_role_arn != null ? var.quarantine_invoke_role_arn : "" QUARANTINE_FUNCTION_ROLE = var.use_quarantine_vpc && var.quarantine_function_role_arn != null ? var.quarantine_function_role_arn : "" QUARANTINE_PRIVATE_SUBNET_1_ID = var.use_quarantine_vpc ? var.quarantine_vpc_private_subnets[0] : "" diff --git a/modules/services/lambda-secrets-extension.tf b/modules/services/lambda-secrets-extension.tf new file mode 100644 index 00000000..5e1d8446 --- /dev/null +++ b/modules/services/lambda-secrets-extension.tf @@ -0,0 +1,87 @@ +# Region must be one of: us-east-1, us-east-2, us-west-2, eu-west-1, ca-central-1, ap-southeast-2 +# ARNs: https://docs.aws.amazon.com/systems-manager/latest/userguide/ps-integration-lambda-extensions.html#ps-integration-lambda-extensions-add +locals { + secrets_ext_arns_arm64 = { + us-east-1 = { + arn = "arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension-Arm64" + version = 61 + } + us-east-2 = { + arn = "arn:aws:lambda:us-east-2:590474943231:layer:AWS-Parameters-and-Secrets-Lambda-Extension-Arm64" + version = 67 + } + us-west-2 = { + arn = "arn:aws:lambda:us-west-2:345057560386:layer:AWS-Parameters-and-Secrets-Lambda-Extension-Arm64" + version = 61 + } + eu-west-1 = { + arn = "arn:aws:lambda:eu-west-1:015030872274:layer:AWS-Parameters-and-Secrets-Lambda-Extension-Arm64" + version = 63 + } + ca-central-1 = { + arn = "arn:aws:lambda:ca-central-1:200266452380:layer:AWS-Parameters-and-Secrets-Lambda-Extension-Arm64" + version = 62 + } + ap-southeast-2 = { + arn = "arn:aws:lambda:ap-southeast-2:665172237481:layer:AWS-Parameters-and-Secrets-Lambda-Extension-Arm64" + version = 63 + } + } + + secrets_ext_arns_x86_64 = { + us-east-1 = { + arn = "arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension" + version = 67 + } + us-east-2 = { + arn = "arn:aws:lambda:us-east-2:590474943231:layer:AWS-Parameters-and-Secrets-Lambda-Extension" + version = 73 + } + us-west-2 = { + arn = "arn:aws:lambda:us-west-2:345057560386:layer:AWS-Parameters-and-Secrets-Lambda-Extension" + version = 67 + } + eu-west-1 = { + arn = "arn:aws:lambda:eu-west-1:015030872274:layer:AWS-Parameters-and-Secrets-Lambda-Extension" + version = 63 + } + ca-central-1 = { + arn = "arn:aws:lambda:ca-central-1:200266452380:layer:AWS-Parameters-and-Secrets-Lambda-Extension" + version = 70 + } + ap-southeast-2 = { + arn = "arn:aws:lambda:ap-southeast-2:665172237481:layer:AWS-Parameters-and-Secrets-Lambda-Extension" + version = 63 + } + } +} + +data "aws_lambda_layer_version" "aws_params_secrets_arm64" { + layer_name = local.secrets_ext_arns_arm64[data.aws_region.current.id].arn + version = local.secrets_ext_arns_arm64[data.aws_region.current.id].version +} + +data "aws_lambda_layer_version" "aws_params_secrets_x86_64" { + layer_name = local.secrets_ext_arns_x86_64[data.aws_region.current.id].arn + version = local.secrets_ext_arns_x86_64[data.aws_region.current.id].version +} + +#----------------------------------------------------- +# TODO: relocate layer to `dist` and add to postbuild +#----------------------------------------------------- + +data "archive_file" "secrets_wrapper_layer" { + type = "zip" + source_dir = "${path.module}/secrets-wrapper" + output_path = "${path.module}/.build/wrapper_layer.zip" +} + +resource "aws_lambda_layer_version" "secrets_wrapper" { + layer_name = "secrets-env-wrapper" + description = "Exec wrapper that fetches Secrets Manager secrets and injects them as environment variables." + filename = data.archive_file.secrets_wrapper_layer.output_path + source_code_hash = data.archive_file.secrets_wrapper_layer.output_base64sha256 + compatible_runtimes = ["nodejs22.x", "python3.13"] + + compatible_architectures = ["arm64", "x86_64"] +} diff --git a/modules/services/main.tf b/modules/services/main.tf index 0016b53f..57b95c75 100644 --- a/modules/services/main.tf +++ b/modules/services/main.tf @@ -10,10 +10,10 @@ locals { observability_enabled = nonsensitive(var.internal_observability_api_key != null && var.internal_observability_api_key != "") datadog_node_layer_arn = "arn:aws:lambda:${data.aws_region.current.region}:464622532012:layer:Datadog-Node22-x:131" datadog_extension_arm_layer_arn = "arn:aws:lambda:${data.aws_region.current.region}:464622532012:layer:Datadog-Extension-ARM:90" - datadog_python_layer_arn = "arn:aws:lambda:${data.aws_region.current.region}:464622532012:layer:Datadog-Python313:118" - datadog_extension_layer_arn = "arn:aws:lambda:${data.aws_region.current.region}:464622532012:layer:Datadog-Extension:70" - nodejs_datadog_handler = "/opt/nodejs/node_modules/datadog-lambda-js/handler.handler" - python_datadog_handler = "datadog_lambda.handler.handler" + # datadog_python_layer_arn = "arn:aws:lambda:${data.aws_region.current.region}:464622532012:layer:Datadog-Python313:118" + # datadog_extension_layer_arn = "arn:aws:lambda:${data.aws_region.current.region}:464622532012:layer:Datadog-Extension:70" + nodejs_datadog_handler = "/opt/nodejs/node_modules/datadog-lambda-js/handler.handler" + python_datadog_handler = "datadog_lambda.handler.handler" datadog_env_vars = { DD_SITE = "${var.internal_observability_region}.datadoghq.com" DD_API_KEY = var.internal_observability_api_key != null ? var.internal_observability_api_key : "" @@ -36,7 +36,7 @@ locals { lambda => trimspace(data.http.lambda_versions[lambda].response_body) } - postgres_url = "postgres://${var.postgres_username}:${var.postgres_password}@${var.postgres_host}:${var.postgres_port}/postgres" + # postgres_url = "postgres://${var.postgres_username}:${var.postgres_password}@${var.postgres_host}:${var.postgres_port}/postgres" using_brainstore_writer = var.brainstore_writer_hostname != null && var.brainstore_writer_hostname != "" using_brainstore_fast_reader = var.brainstore_fast_reader_hostname != null && var.brainstore_fast_reader_hostname != "" brainstore_url = var.brainstore_enabled ? "http://${var.brainstore_hostname}:${var.brainstore_port}" : "" diff --git a/modules/services/outputs.tf b/modules/services/outputs.tf index 9a86fd54..d2c4c72c 100644 --- a/modules/services/outputs.tf +++ b/modules/services/outputs.tf @@ -42,4 +42,3 @@ output "ai_proxy_url_ssm_arn" { description = "The ARN of the SSM parameter containing the AI proxy URL" value = aws_ssm_parameter.ai_proxy_url.arn } - diff --git a/modules/services/secrets-wrapper/bin/aws-sm-wrapper.sh b/modules/services/secrets-wrapper/bin/aws-sm-wrapper.sh new file mode 100755 index 00000000..9df443e9 --- /dev/null +++ b/modules/services/secrets-wrapper/bin/aws-sm-wrapper.sh @@ -0,0 +1,65 @@ +#!/bin/bash +set -euo pipefail + +# ── Logging ────────────────────────────────────────────────────────────────── +log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] INFO $*" >&2; } +die() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] FATAL $*" >&2; exit 1; } + +# ── Resolve jq binary for current architecture ──────────────────────────────── +resolve_jq() { + local arch + arch=$(uname -m) + case "$arch" in + x86_64) echo "/opt/bin/jq-linux-amd64" ;; + aarch64) echo "/opt/bin/jq-linux-arm64" ;; + *) die "Unsupported architecture: $arch" ;; + esac +} + +JQ=$(resolve_jq) +[[ -x "$JQ" ]] || die "jq binary not found or not executable: $JQ" + +# ── Validate required environment variables ─────────────────────────────────── +require_env() { + local missing=() + for var in "$@"; do + [[ -n "${!var:-}" ]] || missing+=("$var") + done + (( ${#missing[@]} == 0 )) || die "Missing required environment variables: ${missing[*]}" +} + +require_env AWS_SESSION_TOKEN DATABASE_SECRETS_ARN PG_HOST PG_PORT + +# ── Fetch secret from AWS Secrets Manager via Lambda extension ──────────────── +log "Fetching database credentials from Secrets Manager (ARN: $DATABASE_SECRETS_ARN)" + +AWS_SM_RESPONSE=$( + curl --silent --show-error --fail --max-time 5 \ + -H "X-Aws-Parameters-Secrets-Token: $AWS_SESSION_TOKEN" \ + "http://localhost:2773/secretsmanager/get?secretId=${DATABASE_SECRETS_ARN}" +) || die "Failed to retrieve secret from Secrets Manager extension (is the Lambda extension running?)" + +[[ -n "$AWS_SM_RESPONSE" ]] || die "Secrets Manager returned an empty response" + +# ── Parse credentials ───────────────────────────────────────────────────────── +parse_secret() { + local key="$1" + local value + value=$(echo "$AWS_SM_RESPONSE" | "$JQ" -r --arg key "$key" \ + '.SecretString | fromjson | .[$key] // empty') \ + || die "jq failed to parse Secrets Manager response" + [[ -n "$value" ]] || die "Key '$key' missing or null in secret JSON" + echo "$value" +} + +PG_USERNAME=$(parse_secret "username") +PG_PASSWORD=$(parse_secret "password") + +export PG_USERNAME PG_PASSWORD +export PG_URL="postgres://${PG_USERNAME}:${PG_PASSWORD}@${PG_HOST}:${PG_PORT}/postgres" + +log "Database credentials loaded; PG_URL configured for host=${PG_HOST} port=${PG_PORT}" + +# ── Hand off to container command ───────────────────────────────────────────── +[[ $# -gt 0 ]] || die "No command provided to exec" +exec "$@" diff --git a/modules/services/variables.tf b/modules/services/variables.tf index 03b6bdcd..fc1e08cd 100644 --- a/modules/services/variables.tf +++ b/modules/services/variables.tf @@ -24,15 +24,9 @@ variable "service_subnet_ids" { description = "The subnet ids for the lambda functions that are the main braintrust service" } -variable "postgres_username" { +variable "postgres_database_secret_arn" { type = string - description = "The username of the postgres database" -} - -variable "postgres_password" { - type = string - description = "The password of the postgres database" - sensitive = true + description = "The ARN of the secrets manager secret containing the postgres database credentials" } variable "postgres_host" { diff --git a/modules/services/versions.tf b/modules/services/versions.tf index 7450b221..46a1f60d 100644 --- a/modules/services/versions.tf +++ b/modules/services/versions.tf @@ -13,5 +13,10 @@ terraform { source = "hashicorp/random" version = "~> 3.0" } + # Temporary addition until layer source is relocated + archive = { + source = "hashicorp/archive" + version = "~> 2.0" + } } }