From 8943259917e12b7be146e0f0e995e2850d35e987 Mon Sep 17 00:00:00 2001 From: Kenji Kono Date: Fri, 20 Mar 2026 13:07:51 +0900 Subject: [PATCH 1/2] feat(database): enable Data API and connection logging (#122) Why: The Aurora Serverless v2 cluster lacks operational tooling for debugging and diagnostics. Without Data API, operators must use SSH tunneling via Bastion Host to run ad-hoc queries. Without connection logs, diagnosing unexpected auto-pause resumes or connection pool exhaustion requires guesswork. What: - Enable RDS Data API for direct SQL execution from AWS CLI/Console - Add log_connections and log_disconnections parameters - Export PostgreSQL logs to CloudWatch Logs with 1-week retention --- cdk/lib/constructs/database.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cdk/lib/constructs/database.ts b/cdk/lib/constructs/database.ts index 3f9cdf9..32c78cf 100644 --- a/cdk/lib/constructs/database.ts +++ b/cdk/lib/constructs/database.ts @@ -1,5 +1,6 @@ import { CfnOutput, Stack, Token } from 'aws-cdk-lib'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as logs from 'aws-cdk-lib/aws-logs'; import * as rds from 'aws-cdk-lib/aws-rds'; import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager'; import { Construct } from 'constructs'; @@ -34,11 +35,16 @@ export class Database extends Construct implements ec2.IConnectable { credentials: rds.Credentials.fromUsername(engine.defaultUsername ?? 'admin', { excludeCharacters: ' %+~`#$&*()|[]{}:;<>?!\'/@"\\,=^', }), + enableDataApi: true, + cloudwatchLogsExports: ['postgresql'], + cloudwatchLogsRetention: logs.RetentionDays.ONE_WEEK, parameterGroup: new rds.ParameterGroup(this, 'ParameterGroup', { engine, parameters: { // Close idle connection after 60 seconds for Aurora auto-pause idle_session_timeout: '60000', + log_connections: '1', + log_disconnections: '1', }, }), }); From 2313cf67b1e3e26ed89ff59f019e1419a2ba19c8 Mon Sep 17 00:00:00 2001 From: Kenji Kono Date: Fri, 20 Mar 2026 13:09:07 +0900 Subject: [PATCH 2/2] test: update CDK snapshots --- ...pp-starter-kit-without-domain.test.ts.snap | 108 ++++++++++++++++++ ...-fullstack-webapp-starter-kit.test.ts.snap | 108 ++++++++++++++++++ 2 files changed, 216 insertions(+) diff --git a/cdk/test/__snapshots__/serverless-fullstack-webapp-starter-kit-without-domain.test.ts.snap b/cdk/test/__snapshots__/serverless-fullstack-webapp-starter-kit-without-domain.test.ts.snap index c3bca2c..02c2776 100644 --- a/cdk/test/__snapshots__/serverless-fullstack-webapp-starter-kit-without-domain.test.ts.snap +++ b/cdk/test/__snapshots__/serverless-fullstack-webapp-starter-kit-without-domain.test.ts.snap @@ -1754,6 +1754,10 @@ exports[`Snapshot test 2`] = ` "DBSubnetGroupName": { "Ref": "DatabaseClusterSubnets5540150D", }, + "EnableCloudwatchLogsExports": [ + "postgresql", + ], + "EnableHttpEndpoint": true, "Engine": "aurora-postgresql", "EngineVersion": "16.6", "MasterUserPassword": { @@ -1798,6 +1802,30 @@ exports[`Snapshot test 2`] = ` "Type": "AWS::RDS::DBCluster", "UpdateReplacePolicy": "Snapshot", }, + "DatabaseClusterLogRetentionpostgresql025D39CE": { + "Properties": { + "LogGroupName": { + "Fn::Join": [ + "", + [ + "/aws/rds/cluster/", + { + "Ref": "DatabaseCluster5B53A178", + }, + "/postgresql", + ], + ], + }, + "RetentionInDays": 7, + "ServiceToken": { + "Fn::GetAtt": [ + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A", + "Arn", + ], + }, + }, + "Type": "Custom::LogRetention", + }, "DatabaseClusterSecretAttachmentDC8466C0": { "Properties": { "SecretId": { @@ -2022,6 +2050,8 @@ exports[`Snapshot test 2`] = ` "Family": "aurora-postgresql16", "Parameters": { "idle_session_timeout": "60000", + "log_connections": "1", + "log_disconnections": "1", }, }, "Type": "AWS::RDS::DBClusterParameterGroup", @@ -2182,6 +2212,83 @@ exports[`Snapshot test 2`] = ` "Type": "Custom::CrossRegionExportReader", "UpdateReplacePolicy": "Delete", }, + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A": { + "DependsOn": [ + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB", + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB", + ], + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-123456789012-us-west-2", + "S3Key": "REDACTED", + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB", + "Arn", + ], + }, + "Runtime": "nodejs22.x", + "Timeout": 900, + }, + "Type": "AWS::Lambda::Function", + }, + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + ], + ], + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:PutRetentionPolicy", + "logs:DeleteRetentionPolicy", + ], + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB", + "Roles": [ + { + "Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "LookupVersionArnc8730278af02f875114ca902814c77b68f19b0087110E04D0A": { "DeletionPolicy": "Delete", "DependsOn": [ @@ -3995,6 +4102,7 @@ service iptables save", "WebappMigrationTrigger42AFC1D9": { "DeletionPolicy": "Delete", "DependsOn": [ + "DatabaseClusterLogRetentionpostgresql025D39CE", "DatabaseCluster5B53A178", "DatabaseClusterSecretAttachmentDC8466C0", "DatabaseClusterSecretD1FB634F", diff --git a/cdk/test/__snapshots__/serverless-fullstack-webapp-starter-kit.test.ts.snap b/cdk/test/__snapshots__/serverless-fullstack-webapp-starter-kit.test.ts.snap index 520dfb6..544dbcb 100644 --- a/cdk/test/__snapshots__/serverless-fullstack-webapp-starter-kit.test.ts.snap +++ b/cdk/test/__snapshots__/serverless-fullstack-webapp-starter-kit.test.ts.snap @@ -1670,6 +1670,10 @@ exports[`Snapshot test 2`] = ` "DBSubnetGroupName": { "Ref": "DatabaseClusterSubnets5540150D", }, + "EnableCloudwatchLogsExports": [ + "postgresql", + ], + "EnableHttpEndpoint": true, "Engine": "aurora-postgresql", "EngineVersion": "16.6", "MasterUserPassword": { @@ -1714,6 +1718,30 @@ exports[`Snapshot test 2`] = ` "Type": "AWS::RDS::DBCluster", "UpdateReplacePolicy": "Snapshot", }, + "DatabaseClusterLogRetentionpostgresql025D39CE": { + "Properties": { + "LogGroupName": { + "Fn::Join": [ + "", + [ + "/aws/rds/cluster/", + { + "Ref": "DatabaseCluster5B53A178", + }, + "/postgresql", + ], + ], + }, + "RetentionInDays": 7, + "ServiceToken": { + "Fn::GetAtt": [ + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A", + "Arn", + ], + }, + }, + "Type": "Custom::LogRetention", + }, "DatabaseClusterSecretAttachmentDC8466C0": { "Properties": { "SecretId": { @@ -1938,6 +1966,8 @@ exports[`Snapshot test 2`] = ` "Family": "aurora-postgresql16", "Parameters": { "idle_session_timeout": "60000", + "log_connections": "1", + "log_disconnections": "1", }, }, "Type": "AWS::RDS::DBClusterParameterGroup", @@ -2099,6 +2129,83 @@ exports[`Snapshot test 2`] = ` "Type": "Custom::CrossRegionExportReader", "UpdateReplacePolicy": "Delete", }, + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A": { + "DependsOn": [ + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB", + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB", + ], + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-123456789012-us-west-2", + "S3Key": "REDACTED", + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB", + "Arn", + ], + }, + "Runtime": "nodejs22.x", + "Timeout": 900, + }, + "Type": "AWS::Lambda::Function", + }, + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + ], + ], + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:PutRetentionPolicy", + "logs:DeleteRetentionPolicy", + ], + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB", + "Roles": [ + { + "Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "LookupVersionArnc8730278af02f875114ca902814c77b68f19b0087110E04D0A": { "DeletionPolicy": "Delete", "DependsOn": [ @@ -3801,6 +3908,7 @@ service iptables save", "WebappMigrationTrigger42AFC1D9": { "DeletionPolicy": "Delete", "DependsOn": [ + "DatabaseClusterLogRetentionpostgresql025D39CE", "DatabaseCluster5B53A178", "DatabaseClusterSecretAttachmentDC8466C0", "DatabaseClusterSecretD1FB634F",