diff --git a/rds-postgres-db/README.md b/rds-postgres-db/README.md index 448a9f9..39bf16b 100644 --- a/rds-postgres-db/README.md +++ b/rds-postgres-db/README.md @@ -1,13 +1,13 @@ # rds-postgres-db -A nullplatform dependency service that provisions and manages a **PostgreSQL database** within an existing RDS instance managed by [`rds-postgres-server`](../rds-postgres-server). It handles database creation, app-level user management, and per-link fine-grained access control — without creating any AWS infrastructure itself. +A nullplatform dependency service that provisions and manages a **PostgreSQL database** within an existing RDS instance managed by [`rds-postgres-server`](../rds-postgres-server). It handles database creation, app-level user management, and per-link fine-grained access control. Unlike `rds-postgres-server`, it does not provision any RDS/EC2 infrastructure — the one AWS resource it does create is the Secrets Manager secret holding the app-level PostgreSQL credentials. ## What It Does - Auto-discovers a compatible `rds-postgres-server` in the same nullplatform namespace using dimension matching - Creates a dedicated PostgreSQL database and application-level user within that server - Manages per-link permissions: each link to an application gets its own PostgreSQL user with scoped grants (`read`, `write`, or `read-write`) -- Stores connection credentials in nullplatform service and link attributes for injection into applications +- Stores connection credentials in nullplatform service and link attributes for injection into applications, and mirrors the app-level credentials into a Secrets Manager secret (same convention as the `rds-postgres-server` master secret) ## Architecture @@ -25,7 +25,7 @@ nullplatform Application postgresql_grant.* ``` -Unlike `rds-postgres-server`, this service creates no AWS resources. It only manages PostgreSQL-level objects (databases, roles, grants) on the shared RDS instance. +Unlike `rds-postgres-server`, this service creates no RDS/EC2 infrastructure — it only manages PostgreSQL-level objects (databases, roles, grants) on the shared RDS instance, plus one Secrets Manager secret for the app-level credentials it generates. ## Nullplatform Integration @@ -43,7 +43,8 @@ Unlike `rds-postgres-server`, this service creates no AWS resources. It only man | `username` | exported | Service-level PostgreSQL user | | `password` | hidden | Service-level PostgreSQL password | | `database_name` | exported | PostgreSQL database name | -| `master_secret_arn` | internal | Secrets Manager ARN (used for link operations) | +| `master_secret_arn` | internal | Secrets Manager ARN for the `rds-postgres-server` master credentials (used for link operations) | +| `app_secret_arn` | internal | Secrets Manager ARN for this service's own app-level credentials | ### Link Attributes (written per link) @@ -52,6 +53,7 @@ Unlike `rds-postgres-server`, this service creates no AWS resources. It only man | `username` | Per-link PostgreSQL user (`np_`) | | `password` | Per-link PostgreSQL password | | `database_name` | Database name (same as service-level database) | +| `app_secret_arn` | Secrets Manager ARN for the service-level app credentials (mirrored from the service attribute) | ## Link Parameters @@ -73,9 +75,9 @@ All access levels include `DEFAULT PRIVILEGES` so future tables created after th | Workflow | Trigger | What It Does | |---|---|---| -| `create` | Service created | Auto-discovers server, creates database + app user, writes service attributes | +| `create` | Service created | Auto-discovers server, creates database + app user, stores app credentials in Secrets Manager, writes service attributes | | `update` | Service updated | No-op (no configurable parameters) | -| `delete` | Service deleted | Reassigns owned objects to master, destroys app user; **database is preserved** | +| `delete` | Service deleted | Reassigns owned objects to master, destroys app user and its Secrets Manager secret; **database is preserved** | | `link` | Application linked | Creates per-link PostgreSQL user with scoped grants | | `unlink` | Application unlinked | Revokes grants only; user and database are **preserved** | @@ -97,6 +99,16 @@ username = "np_" This ensures usernames are stable and reproducible even if the service is recreated. +## Infrastructure Resources Created + +| Resource | Description | +|---|---| +| `postgresql_database` | The application database (preserved on delete) | +| `postgresql_role` | The service-level app user | +| `aws_secretsmanager_secret` | Stores the app-level credentials (`nullplatform/rds//app`); destroyed alongside the app user on service delete | + +No RDS, EC2, or VPC resources are created — those belong to the auto-discovered `rds-postgres-server`. + ## Requirements ### nullplatform Prerequisites @@ -113,9 +125,9 @@ This ensures usernames are stable and reproducible even if the service is recrea ### AWS IAM Permissions -This service requires minimal AWS permissions compared to `rds-postgres-server`. The agent needs: +This service requires fewer AWS permissions than `rds-postgres-server` (no RDS/EC2), but it does manage its own Secrets Manager secret. The agent needs: -- **Secrets Manager**: `GetSecretValue` — to retrieve the master PostgreSQL password from the ARN stored in service attributes +- **Secrets Manager**: `GetSecretValue` — to retrieve the master PostgreSQL password from the ARN stored in service attributes; plus `CreateSecret`, `PutSecretValue`, `UpdateSecret`, `DeleteSecret`, `DescribeSecret`, `TagResource`, `UntagResource`, `GetResourcePolicy`, `ListSecretVersionIds` — to create, update, and delete the app-level credentials secret this service owns - **S3**: full lifecycle on the `np-service-` bucket — `build_context` creates and manages its own per-service Terraform state bucket, same as `rds-postgres-server` No RDS or EC2 permissions are needed. @@ -131,12 +143,15 @@ applying it. Granting the agent itself permission to assume this role is handled separately, outside this module. This role and its policy are shared per **cluster**, not per linked -`rds-postgres-server` instance — the `GetSecretValue` grant is scoped to the -`nullplatform/rds/*` secret-name prefix (every master secret in the cluster -following that naming convention), not to the single secret this particular -service instance's link actually uses. Anything that assumes this role can -read the master password of any `rds-postgres-server` in the cluster, not -just the linked one. +`rds-postgres-server` instance — the Secrets Manager grant is scoped to the +`nullplatform/rds/*` secret-name prefix (every master and app secret in the +cluster following that naming convention), not to the single secrets this +particular service instance actually owns. Anything that assumes this role +can read, create, update, or delete any secret under that prefix — not just +the master password of the linked server, and not just this instance's own +app secret. This is a wider blast radius than a read-only grant would be; +narrowing it to per-instance secret ARNs would require generating the policy +per service instance instead of once per cluster. ### AssumeRole Setup Guide diff --git a/rds-postgres-db/db_setup/main.tf b/rds-postgres-db/db_setup/main.tf index fc4516c..0f16aa3 100644 --- a/rds-postgres-db/db_setup/main.tf +++ b/rds-postgres-db/db_setup/main.tf @@ -34,3 +34,30 @@ resource "postgresql_role" "app_user" { password = random_password.user.result login = true } + +# --------------------------------------------------------------------------- +# App credentials, mirrored into Secrets Manager (same convention as the +# rds-postgres-server master secret) so they're not only reachable via +# nullplatform service/link attributes. +# --------------------------------------------------------------------------- + +resource "aws_secretsmanager_secret" "app" { + name = "nullplatform/rds/${var.service_id}/app" + recovery_window_in_days = 0 + + tags = { + "managed-by" = "nullplatform" + "service-id" = var.service_id + } +} + +resource "aws_secretsmanager_secret_version" "app" { + secret_id = aws_secretsmanager_secret.app.id + secret_string = jsonencode({ + username = postgresql_role.app_user.name + password = random_password.user.result + host = var.db_host + port = var.db_port + dbname = postgresql_database.app.name + }) +} diff --git a/rds-postgres-db/db_setup/outputs.tf b/rds-postgres-db/db_setup/outputs.tf index 20b5ff5..0d9b824 100644 --- a/rds-postgres-db/db_setup/outputs.tf +++ b/rds-postgres-db/db_setup/outputs.tf @@ -28,3 +28,8 @@ output "database_name" { value = postgresql_database.app.name description = "Database name" } + +output "app_secret_arn" { + value = aws_secretsmanager_secret.app.arn + description = "ARN of the Secrets Manager secret holding the app-level credentials" +} diff --git a/rds-postgres-db/db_setup/providers.tf b/rds-postgres-db/db_setup/providers.tf index 976a2bf..0d6cd3f 100644 --- a/rds-postgres-db/db_setup/providers.tf +++ b/rds-postgres-db/db_setup/providers.tf @@ -8,9 +8,17 @@ terraform { source = "hashicorp/random" version = "~> 3.0" } + aws = { + source = "hashicorp/aws" + version = "~> 6.0" + } } } +provider "aws" { + region = var.region +} + provider "postgresql" { host = var.db_host port = var.db_port diff --git a/rds-postgres-db/db_setup/variables.tf b/rds-postgres-db/db_setup/variables.tf index 06f00c3..8d1527c 100644 --- a/rds-postgres-db/db_setup/variables.tf +++ b/rds-postgres-db/db_setup/variables.tf @@ -3,6 +3,12 @@ variable "service_id" { description = "Nullplatform service ID (used as keeper to stabilize password across re-applies)" } +variable "region" { + type = string + default = "us-east-1" + description = "AWS region (used to create the app credentials secret in Secrets Manager)" +} + variable "db_host" { type = string description = "RDS endpoint hostname" diff --git a/rds-postgres-db/deployment/main.tf b/rds-postgres-db/deployment/main.tf index 24f05dc..92c716c 100644 --- a/rds-postgres-db/deployment/main.tf +++ b/rds-postgres-db/deployment/main.tf @@ -39,6 +39,7 @@ resource "random_password" "master" { resource "aws_secretsmanager_secret" "master" { name = "nullplatform/rds/${var.instance_name}/master" recovery_window_in_days = 0 + kms_key_id = var.secret_kms_key_id tags = { "managed-by" = "nullplatform" diff --git a/rds-postgres-db/deployment/variables.tf b/rds-postgres-db/deployment/variables.tf index 673ba42..315c639 100644 --- a/rds-postgres-db/deployment/variables.tf +++ b/rds-postgres-db/deployment/variables.tf @@ -60,3 +60,9 @@ variable "maintenance_window" { default = "Mon:04:00-Mon:05:00" description = "Weekly time range for maintenance operations (UTC, ddd:hh:mm-ddd:hh:mm)" } + +variable "secret_kms_key_id" { + type = string + default = null + description = "KMS key ID or ARN used to encrypt the RDS master secret in Secrets Manager. If not set, AWS encrypts it with the default aws/secretsmanager managed key." +} diff --git a/rds-postgres-db/scripts/aws/build_db_setup_context b/rds-postgres-db/scripts/aws/build_db_setup_context index c323457..aea0221 100644 --- a/rds-postgres-db/scripts/aws/build_db_setup_context +++ b/rds-postgres-db/scripts/aws/build_db_setup_context @@ -132,4 +132,4 @@ export TOFU_IMPORT_DB_NAME="$DB_NAME_VAL" export TOFU_INIT_VARIABLES="-backend-config=bucket=${TFSTATE_BUCKET} -backend-config=key=db_setup.tfstate -backend-config=region=${REGION}" -export TOFU_VARIABLES="-var=service_id=${SERVICE_ID} -var=db_host=${DB_HOST} -var=db_port=${DB_PORT} -var=db_name=${DB_NAME_VAL} -var=db_username=${DB_USERNAME_VAL} -var=master_username=${MASTER_USER} -var=master_secret_arn=${MASTER_SECRET_ARN}" +export TOFU_VARIABLES="-var=service_id=${SERVICE_ID} -var=region=${REGION} -var=db_host=${DB_HOST} -var=db_port=${DB_PORT} -var=db_name=${DB_NAME_VAL} -var=db_username=${DB_USERNAME_VAL} -var=master_username=${MASTER_USER} -var=master_secret_arn=${MASTER_SECRET_ARN}" diff --git a/rds-postgres-db/scripts/aws/write_link_outputs b/rds-postgres-db/scripts/aws/write_link_outputs index 4495670..b54c907 100755 --- a/rds-postgres-db/scripts/aws/write_link_outputs +++ b/rds-postgres-db/scripts/aws/write_link_outputs @@ -25,6 +25,7 @@ USERNAME=$(echo "$SERVICE_JSON" | jq -r '.attributes.username / PASSWORD=$(echo "$SERVICE_JSON" | jq -r '.attributes.password // ""') DATABASE_NAME=$(echo "$SERVICE_JSON" | jq -r '.attributes.database_name // ""') MASTER_SECRET_ARN=$(echo "$SERVICE_JSON"| jq -r '.attributes.master_secret_arn // ""') +APP_SECRET_ARN=$(echo "$SERVICE_JSON" | jq -r '.attributes.app_secret_arn // ""') if [ -z "$USERNAME" ]; then echo "WARNING: No username found in service attributes. Skipping link attribute update." @@ -38,13 +39,15 @@ ATTRS=$(jq -n \ --arg password "$PASSWORD" \ --arg database_name "$DATABASE_NAME" \ --arg master_secret_arn "$MASTER_SECRET_ARN" \ + --arg app_secret_arn "$APP_SECRET_ARN" \ '{ hostname: $hostname, port: ($port | tonumber), username: $username, password: $password, database_name: $database_name, - master_secret_arn: $master_secret_arn + master_secret_arn: $master_secret_arn, + app_secret_arn: $app_secret_arn }') echo "Updating link ${LINK_ID_VAL} attributes:" diff --git a/rds-postgres-db/scripts/aws/write_service_outputs b/rds-postgres-db/scripts/aws/write_service_outputs index c7814c2..d2dfe61 100755 --- a/rds-postgres-db/scripts/aws/write_service_outputs +++ b/rds-postgres-db/scripts/aws/write_service_outputs @@ -23,6 +23,7 @@ USERNAME=$(echo "$ALL_OUTPUTS" | jq -r '.db_username.value // ""') PASSWORD=$(echo "$ALL_OUTPUTS" | jq -r '.db_password.value // ""') DATABASE_NAME=$(echo "$ALL_OUTPUTS" | jq -r '.database_name.value // ""') MASTER_SECRET_ARN=$(echo "$ALL_OUTPUTS"| jq -r '.master_secret_arn.value // ""') +APP_SECRET_ARN=$(echo "$ALL_OUTPUTS" | jq -r '.app_secret_arn.value // ""') if [ -z "$HOSTNAME" ]; then echo "WARNING: No hostname output found. Skipping service attribute update." @@ -36,21 +37,24 @@ ATTRS=$(jq -n \ --arg password "$PASSWORD" \ --arg database_name "$DATABASE_NAME" \ --arg master_secret_arn "$MASTER_SECRET_ARN" \ + --arg app_secret_arn "$APP_SECRET_ARN" \ '{ hostname: $hostname, port: ($port | tonumber), username: $username, password: $password, database_name: $database_name, - master_secret_arn: $master_secret_arn + master_secret_arn: $master_secret_arn, + app_secret_arn: $app_secret_arn }') echo "Updating service ${SERVICE_ID} attributes:" -echo " hostname: $HOSTNAME" -echo " port: $PORT" -echo " username: $USERNAME" -echo " database_name: $DATABASE_NAME" -echo " password: ****" +echo " hostname: $HOSTNAME" +echo " port: $PORT" +echo " username: $USERNAME" +echo " database_name: $DATABASE_NAME" +echo " app_secret_arn: $APP_SECRET_ARN" +echo " password: ****" np service patch --id "$SERVICE_ID" --body "{\"attributes\": $ATTRS}" echo "Service attributes updated successfully." diff --git a/rds-postgres-db/specs/links/connect.json.tpl b/rds-postgres-db/specs/links/connect.json.tpl index 8bab9c7..9713a75 100644 --- a/rds-postgres-db/specs/links/connect.json.tpl +++ b/rds-postgres-db/specs/links/connect.json.tpl @@ -76,6 +76,13 @@ "visibleOn": [], "editableOn": [], "description": "ARN of the Secrets Manager secret for master credentials (internal use)" + }, + "app_secret_arn": { + "type": "string", + "export": false, + "visibleOn": [], + "editableOn": [], + "description": "ARN of the Secrets Manager secret holding the app-level credentials (internal use)" } } }, diff --git a/rds-postgres-db/specs/requirements/aws/main.tf b/rds-postgres-db/specs/requirements/aws/main.tf index 879a052..67b40b2 100644 --- a/rds-postgres-db/specs/requirements/aws/main.tf +++ b/rds-postgres-db/specs/requirements/aws/main.tf @@ -21,20 +21,32 @@ resource "aws_iam_role" "nullplatform_rds_postgres_db" { } ################################################################################ -# Secrets Manager IAM policy — read-only access to the RDS master password +# Secrets Manager IAM policy — read the RDS master password, manage the +# app-level credentials secret this service creates in db_setup/ ################################################################################ resource "aws_iam_policy" "nullplatform_rds_postgres_db_secretsmanager_policy" { count = local.iam_create ? 1 : 0 name = "${local.policies_name_prefix}-rds-postgres-db-secretsmanager-policy" - description = "Policy for reading the RDS master password from Secrets Manager" + description = "Policy for reading the RDS master password and managing the app-level credentials secret in Secrets Manager" policy = jsonencode({ Version = "2012-10-17" Statement = [{ - Effect = "Allow" - Action = "secretsmanager:GetSecretValue" + Effect = "Allow" + Action = [ + "secretsmanager:CreateSecret", + "secretsmanager:DeleteSecret", + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue", + "secretsmanager:PutSecretValue", + "secretsmanager:UpdateSecret", + "secretsmanager:TagResource", + "secretsmanager:UntagResource", + "secretsmanager:GetResourcePolicy", + "secretsmanager:ListSecretVersionIds" + ] Resource = "arn:aws:secretsmanager:*:${data.aws_caller_identity.current.account_id}:secret:nullplatform/rds/*" }] }) diff --git a/rds-postgres-db/specs/requirements/aws/output.tf b/rds-postgres-db/specs/requirements/aws/output.tf index 3c192b9..7847b3f 100644 --- a/rds-postgres-db/specs/requirements/aws/output.tf +++ b/rds-postgres-db/specs/requirements/aws/output.tf @@ -14,7 +14,7 @@ output "permissions_role_id" { } output "secretsmanager_policy_arn" { - description = "ARN of the Secrets Manager read policy" + description = "ARN of the Secrets Manager policy (read master secret, manage app credentials secret)" value = local.iam_create ? aws_iam_policy.nullplatform_rds_postgres_db_secretsmanager_policy[0].arn : "" } diff --git a/rds-postgres-db/specs/service-spec.json.tpl b/rds-postgres-db/specs/service-spec.json.tpl index 09aca5e..8f26c72 100644 --- a/rds-postgres-db/specs/service-spec.json.tpl +++ b/rds-postgres-db/specs/service-spec.json.tpl @@ -64,6 +64,13 @@ "editableOn": [], "description": "ARN of the Secrets Manager secret for master credentials (internal use)", "order": 6 + }, + "app_secret_arn": { + "type": "string", + "visibleOn": [], + "editableOn": [], + "description": "ARN of the Secrets Manager secret holding the app-level credentials (internal use)", + "order": 7 } } }, diff --git a/rds-postgres-db/workflows/aws/delete.yaml b/rds-postgres-db/workflows/aws/delete.yaml index 9c8b2a8..61f73f0 100644 --- a/rds-postgres-db/workflows/aws/delete.yaml +++ b/rds-postgres-db/workflows/aws/delete.yaml @@ -55,9 +55,10 @@ steps: file: $SERVICE_PATH/scripts/aws/do_tofu configuration: TOFU_ACTION: destroy - # Destroy only the user. The database is preserved with master as owner - # so data remains available for potential future use. - TOFU_TARGETS: "postgresql_role.app_user,random_password.user" + # Destroy only the user and its Secrets Manager secret. The database is + # preserved with master as owner so data remains available for + # potential future use. + TOFU_TARGETS: "postgresql_role.app_user,random_password.user,aws_secretsmanager_secret_version.app,aws_secretsmanager_secret.app" - name: cleanup tfstate bucket type: script diff --git a/rds-postgres-server/README.md b/rds-postgres-server/README.md index a2124c7..49fa50f 100644 --- a/rds-postgres-server/README.md +++ b/rds-postgres-server/README.md @@ -65,9 +65,12 @@ Exposed in the nullplatform UI when creating or updating the service: | `instance_class` | string | `db.t3.micro` | `db.t3.micro`, `db.t3.small`, `db.t3.medium`, `db.m5.large` | Yes | | `allocated_storage` | number | `20` | 20–1000 (GB) | Yes | | `postgres_version` | string | `16` | `14`, `15`, `16` | No | +| `secret_kms_key_id` | string | _(none)_ | Any KMS key ID/ARN in the account | Yes | > `postgres_version` cannot be changed after creation because PostgreSQL major version upgrades require manual intervention and are not managed by this service. +> `secret_kms_key_id` controls which KMS key encrypts the master password secret in Secrets Manager. If left unset, AWS encrypts it with the default `aws/secretsmanager` managed key — pass a customer-managed key ARN here only if this service instance requires its own key. + ## Workflows | Workflow | Trigger | What It Does | diff --git a/rds-postgres-server/deployment/main.tf b/rds-postgres-server/deployment/main.tf index 8aa289d..51107c3 100644 --- a/rds-postgres-server/deployment/main.tf +++ b/rds-postgres-server/deployment/main.tf @@ -45,6 +45,7 @@ resource "random_password" "master" { resource "aws_secretsmanager_secret" "master" { name = "nullplatform/rds/${var.instance_name}/master" recovery_window_in_days = 0 + kms_key_id = var.secret_kms_key_id tags = { "managed-by" = "nullplatform" diff --git a/rds-postgres-server/deployment/variables.tf b/rds-postgres-server/deployment/variables.tf index 673ba42..315c639 100644 --- a/rds-postgres-server/deployment/variables.tf +++ b/rds-postgres-server/deployment/variables.tf @@ -60,3 +60,9 @@ variable "maintenance_window" { default = "Mon:04:00-Mon:05:00" description = "Weekly time range for maintenance operations (UTC, ddd:hh:mm-ddd:hh:mm)" } + +variable "secret_kms_key_id" { + type = string + default = null + description = "KMS key ID or ARN used to encrypt the RDS master secret in Secrets Manager. If not set, AWS encrypts it with the default aws/secretsmanager managed key." +} diff --git a/rds-postgres-server/scripts/aws/build_context b/rds-postgres-server/scripts/aws/build_context index 8c6b288..4f5b57d 100755 --- a/rds-postgres-server/scripts/aws/build_context +++ b/rds-postgres-server/scripts/aws/build_context @@ -27,6 +27,7 @@ SERVICE_ATTRS=$(echo "$CONTEXT" | jq -r '(.service.attributes // {}) * (.paramet INSTANCE_CLASS=$(echo "$SERVICE_ATTRS" | jq -r '.instance_class // "db.t3.micro"') ALLOCATED_STORAGE=$(echo "$SERVICE_ATTRS" | jq -r '.allocated_storage // 20') POSTGRES_VERSION=$(echo "$SERVICE_ATTRS" | jq -r '.postgres_version // "16"') +SECRET_KMS_KEY_ID=$(echo "$SERVICE_ATTRS" | jq -r '.secret_kms_key_id // ""') # --- Read static config from values.yaml ------------------------------------ # CRITICAL: $VALUES is a FILE PATH set by "np service workflow exec --values ", @@ -133,7 +134,17 @@ export TOFU_MODULE_DIR="$SERVICE_PATH/deployment" export TOFU_INIT_VARIABLES="-backend-config=bucket=${TFSTATE_BUCKET} -backend-config=key=terraform.tfstate -backend-config=region=${REGION}" -export TOFU_VARIABLES="-var=service_id=${SERVICE_ID} -var=instance_name=${INSTANCE_NAME} -var=region=${REGION} -var=vpc_id=${VPC_ID} -var=instance_class=${INSTANCE_CLASS} -var=allocated_storage=${ALLOCATED_STORAGE} -var=postgres_version=${POSTGRES_VERSION}" +TOFU_VARIABLES="-var=service_id=${SERVICE_ID} -var=instance_name=${INSTANCE_NAME} -var=region=${REGION} -var=vpc_id=${VPC_ID} -var=instance_class=${INSTANCE_CLASS} -var=allocated_storage=${ALLOCATED_STORAGE} -var=postgres_version=${POSTGRES_VERSION}" + +# Omit the -var entirely when unset, rather than passing an empty string — +# the Terraform variable defaults to null, which is what makes AWS fall back +# to the aws/secretsmanager managed key. A "" value would instead fail as an +# invalid KMS key ID. +if [ -n "$SECRET_KMS_KEY_ID" ]; then + TOFU_VARIABLES="${TOFU_VARIABLES} -var=secret_kms_key_id=${SECRET_KMS_KEY_ID}" +fi + +export TOFU_VARIABLES # --- Extract link context (link workflows only) ----------------------------- # These are consumed by build_permissions_context in the next workflow step. diff --git a/rds-postgres-server/specs/service-spec.json.tpl b/rds-postgres-server/specs/service-spec.json.tpl index d60120d..5dc49a4 100644 --- a/rds-postgres-server/specs/service-spec.json.tpl +++ b/rds-postgres-server/specs/service-spec.json.tpl @@ -46,6 +46,13 @@ "editableOn": ["create"], "order": 3 }, + "secret_kms_key_id": { + "type": "string", + "title": "Secret Encryption Key", + "description": "KMS key ID or ARN used to encrypt the master password secret in Secrets Manager. Leave empty to use the AWS-managed aws/secretsmanager key.", + "editableOn": ["create", "update"], + "order": 4 + }, "hostname": { "type": "string", "title": "Hostname", @@ -53,7 +60,7 @@ "visibleOn": ["read"], "editableOn": [], "description": "RDS endpoint hostname (auto-populated after creation)", - "order": 4 + "order": 5 }, "port": { "type": "number", @@ -62,7 +69,7 @@ "visibleOn": ["read"], "editableOn": [], "description": "RDS port (auto-populated after creation)", - "order": 5 + "order": 6 }, "db_instance_identifier": { "type": "string",