Skip to content

Commit e32dc7a

Browse files
authored
feat(database): enable Data API and connection logging (#123)
## Issue close #122 ## Problem The Aurora Serverless v2 cluster lacks operational tooling for debugging and diagnostics: - **No Data API**: Operators must use SSH tunneling via Bastion Host to run ad-hoc SQL queries, which is cumbersome during incident response. - **No connection logging**: Diagnosing unexpected auto-pause resumes or connection pool exhaustion requires guesswork, as PostgreSQL connection events are not recorded. ## Solution Add three CDK-native properties to the existing `DatabaseCluster` construct: 1. **`enableDataApi: true`** — Enables the [RDS Data API](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html), allowing direct SQL execution from AWS CLI/Console without SSH tunneling. 2. **`log_connections` / `log_disconnections`** — Standard PostgreSQL parameters that record connection open/close events in the PostgreSQL log. 3. **`cloudwatchLogsExports: ["postgresql"]`** with **1-week retention** — Exports PostgreSQL logs to CloudWatch Logs for centralized analysis ([Aurora PostgreSQL CloudWatch Publishing](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.CloudWatch.Publishing.html)). ## Changes - `cdk/lib/constructs/database.ts`: - Added `import * as logs from "aws-cdk-lib/aws-logs"` - Added `enableDataApi: true` to `DatabaseCluster` - Added `cloudwatchLogsExports: ["postgresql"]` and `cloudwatchLogsRetention: logs.RetentionDays.ONE_WEEK` - Added `log_connections: "1"` and `log_disconnections: "1"` to the existing `ParameterGroup` ## Verification - `npm run build` passes in `cdk/` - After deployment: - `aws rds-data execute-statement` can run SQL against the cluster - CloudWatch log group `/aws/rds/cluster/<cluster-name>/postgresql` is created - Connection/disconnection events appear in the log stream
1 parent 764a4fa commit e32dc7a

File tree

3 files changed

+222
-0
lines changed

3 files changed

+222
-0
lines changed

cdk/lib/constructs/database.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { CfnOutput, Stack, Token } from 'aws-cdk-lib';
22
import * as ec2 from 'aws-cdk-lib/aws-ec2';
3+
import * as logs from 'aws-cdk-lib/aws-logs';
34
import * as rds from 'aws-cdk-lib/aws-rds';
45
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
56
import { Construct } from 'constructs';
@@ -34,11 +35,16 @@ export class Database extends Construct implements ec2.IConnectable {
3435
credentials: rds.Credentials.fromUsername(engine.defaultUsername ?? 'admin', {
3536
excludeCharacters: ' %+~`#$&*()|[]{}:;<>?!\'/@"\\,=^',
3637
}),
38+
enableDataApi: true,
39+
cloudwatchLogsExports: ['postgresql'],
40+
cloudwatchLogsRetention: logs.RetentionDays.ONE_WEEK,
3741
parameterGroup: new rds.ParameterGroup(this, 'ParameterGroup', {
3842
engine,
3943
parameters: {
4044
// Close idle connection after 60 seconds for Aurora auto-pause
4145
idle_session_timeout: '60000',
46+
log_connections: '1',
47+
log_disconnections: '1',
4248
},
4349
}),
4450
});

cdk/test/__snapshots__/serverless-fullstack-webapp-starter-kit-without-domain.test.ts.snap

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,6 +1754,10 @@ exports[`Snapshot test 2`] = `
17541754
"DBSubnetGroupName": {
17551755
"Ref": "DatabaseClusterSubnets5540150D",
17561756
},
1757+
"EnableCloudwatchLogsExports": [
1758+
"postgresql",
1759+
],
1760+
"EnableHttpEndpoint": true,
17571761
"Engine": "aurora-postgresql",
17581762
"EngineVersion": "16.6",
17591763
"MasterUserPassword": {
@@ -1798,6 +1802,30 @@ exports[`Snapshot test 2`] = `
17981802
"Type": "AWS::RDS::DBCluster",
17991803
"UpdateReplacePolicy": "Snapshot",
18001804
},
1805+
"DatabaseClusterLogRetentionpostgresql025D39CE": {
1806+
"Properties": {
1807+
"LogGroupName": {
1808+
"Fn::Join": [
1809+
"",
1810+
[
1811+
"/aws/rds/cluster/",
1812+
{
1813+
"Ref": "DatabaseCluster5B53A178",
1814+
},
1815+
"/postgresql",
1816+
],
1817+
],
1818+
},
1819+
"RetentionInDays": 7,
1820+
"ServiceToken": {
1821+
"Fn::GetAtt": [
1822+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A",
1823+
"Arn",
1824+
],
1825+
},
1826+
},
1827+
"Type": "Custom::LogRetention",
1828+
},
18011829
"DatabaseClusterSecretAttachmentDC8466C0": {
18021830
"Properties": {
18031831
"SecretId": {
@@ -2022,6 +2050,8 @@ exports[`Snapshot test 2`] = `
20222050
"Family": "aurora-postgresql16",
20232051
"Parameters": {
20242052
"idle_session_timeout": "60000",
2053+
"log_connections": "1",
2054+
"log_disconnections": "1",
20252055
},
20262056
},
20272057
"Type": "AWS::RDS::DBClusterParameterGroup",
@@ -2182,6 +2212,83 @@ exports[`Snapshot test 2`] = `
21822212
"Type": "Custom::CrossRegionExportReader",
21832213
"UpdateReplacePolicy": "Delete",
21842214
},
2215+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A": {
2216+
"DependsOn": [
2217+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB",
2218+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB",
2219+
],
2220+
"Properties": {
2221+
"Code": {
2222+
"S3Bucket": "cdk-hnb659fds-assets-123456789012-us-west-2",
2223+
"S3Key": "REDACTED",
2224+
},
2225+
"Handler": "index.handler",
2226+
"Role": {
2227+
"Fn::GetAtt": [
2228+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB",
2229+
"Arn",
2230+
],
2231+
},
2232+
"Runtime": "nodejs22.x",
2233+
"Timeout": 900,
2234+
},
2235+
"Type": "AWS::Lambda::Function",
2236+
},
2237+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB": {
2238+
"Properties": {
2239+
"AssumeRolePolicyDocument": {
2240+
"Statement": [
2241+
{
2242+
"Action": "sts:AssumeRole",
2243+
"Effect": "Allow",
2244+
"Principal": {
2245+
"Service": "lambda.amazonaws.com",
2246+
},
2247+
},
2248+
],
2249+
"Version": "2012-10-17",
2250+
},
2251+
"ManagedPolicyArns": [
2252+
{
2253+
"Fn::Join": [
2254+
"",
2255+
[
2256+
"arn:",
2257+
{
2258+
"Ref": "AWS::Partition",
2259+
},
2260+
":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
2261+
],
2262+
],
2263+
},
2264+
],
2265+
},
2266+
"Type": "AWS::IAM::Role",
2267+
},
2268+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB": {
2269+
"Properties": {
2270+
"PolicyDocument": {
2271+
"Statement": [
2272+
{
2273+
"Action": [
2274+
"logs:PutRetentionPolicy",
2275+
"logs:DeleteRetentionPolicy",
2276+
],
2277+
"Effect": "Allow",
2278+
"Resource": "*",
2279+
},
2280+
],
2281+
"Version": "2012-10-17",
2282+
},
2283+
"PolicyName": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB",
2284+
"Roles": [
2285+
{
2286+
"Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB",
2287+
},
2288+
],
2289+
},
2290+
"Type": "AWS::IAM::Policy",
2291+
},
21852292
"LookupVersionArnc8730278af02f875114ca902814c77b68f19b0087110E04D0A": {
21862293
"DeletionPolicy": "Delete",
21872294
"DependsOn": [
@@ -3995,6 +4102,7 @@ service iptables save",
39954102
"WebappMigrationTrigger42AFC1D9": {
39964103
"DeletionPolicy": "Delete",
39974104
"DependsOn": [
4105+
"DatabaseClusterLogRetentionpostgresql025D39CE",
39984106
"DatabaseCluster5B53A178",
39994107
"DatabaseClusterSecretAttachmentDC8466C0",
40004108
"DatabaseClusterSecretD1FB634F",

cdk/test/__snapshots__/serverless-fullstack-webapp-starter-kit.test.ts.snap

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,6 +1670,10 @@ exports[`Snapshot test 2`] = `
16701670
"DBSubnetGroupName": {
16711671
"Ref": "DatabaseClusterSubnets5540150D",
16721672
},
1673+
"EnableCloudwatchLogsExports": [
1674+
"postgresql",
1675+
],
1676+
"EnableHttpEndpoint": true,
16731677
"Engine": "aurora-postgresql",
16741678
"EngineVersion": "16.6",
16751679
"MasterUserPassword": {
@@ -1714,6 +1718,30 @@ exports[`Snapshot test 2`] = `
17141718
"Type": "AWS::RDS::DBCluster",
17151719
"UpdateReplacePolicy": "Snapshot",
17161720
},
1721+
"DatabaseClusterLogRetentionpostgresql025D39CE": {
1722+
"Properties": {
1723+
"LogGroupName": {
1724+
"Fn::Join": [
1725+
"",
1726+
[
1727+
"/aws/rds/cluster/",
1728+
{
1729+
"Ref": "DatabaseCluster5B53A178",
1730+
},
1731+
"/postgresql",
1732+
],
1733+
],
1734+
},
1735+
"RetentionInDays": 7,
1736+
"ServiceToken": {
1737+
"Fn::GetAtt": [
1738+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A",
1739+
"Arn",
1740+
],
1741+
},
1742+
},
1743+
"Type": "Custom::LogRetention",
1744+
},
17171745
"DatabaseClusterSecretAttachmentDC8466C0": {
17181746
"Properties": {
17191747
"SecretId": {
@@ -1938,6 +1966,8 @@ exports[`Snapshot test 2`] = `
19381966
"Family": "aurora-postgresql16",
19391967
"Parameters": {
19401968
"idle_session_timeout": "60000",
1969+
"log_connections": "1",
1970+
"log_disconnections": "1",
19411971
},
19421972
},
19431973
"Type": "AWS::RDS::DBClusterParameterGroup",
@@ -2099,6 +2129,83 @@ exports[`Snapshot test 2`] = `
20992129
"Type": "Custom::CrossRegionExportReader",
21002130
"UpdateReplacePolicy": "Delete",
21012131
},
2132+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A": {
2133+
"DependsOn": [
2134+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB",
2135+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB",
2136+
],
2137+
"Properties": {
2138+
"Code": {
2139+
"S3Bucket": "cdk-hnb659fds-assets-123456789012-us-west-2",
2140+
"S3Key": "REDACTED",
2141+
},
2142+
"Handler": "index.handler",
2143+
"Role": {
2144+
"Fn::GetAtt": [
2145+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB",
2146+
"Arn",
2147+
],
2148+
},
2149+
"Runtime": "nodejs22.x",
2150+
"Timeout": 900,
2151+
},
2152+
"Type": "AWS::Lambda::Function",
2153+
},
2154+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB": {
2155+
"Properties": {
2156+
"AssumeRolePolicyDocument": {
2157+
"Statement": [
2158+
{
2159+
"Action": "sts:AssumeRole",
2160+
"Effect": "Allow",
2161+
"Principal": {
2162+
"Service": "lambda.amazonaws.com",
2163+
},
2164+
},
2165+
],
2166+
"Version": "2012-10-17",
2167+
},
2168+
"ManagedPolicyArns": [
2169+
{
2170+
"Fn::Join": [
2171+
"",
2172+
[
2173+
"arn:",
2174+
{
2175+
"Ref": "AWS::Partition",
2176+
},
2177+
":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
2178+
],
2179+
],
2180+
},
2181+
],
2182+
},
2183+
"Type": "AWS::IAM::Role",
2184+
},
2185+
"LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB": {
2186+
"Properties": {
2187+
"PolicyDocument": {
2188+
"Statement": [
2189+
{
2190+
"Action": [
2191+
"logs:PutRetentionPolicy",
2192+
"logs:DeleteRetentionPolicy",
2193+
],
2194+
"Effect": "Allow",
2195+
"Resource": "*",
2196+
},
2197+
],
2198+
"Version": "2012-10-17",
2199+
},
2200+
"PolicyName": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB",
2201+
"Roles": [
2202+
{
2203+
"Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB",
2204+
},
2205+
],
2206+
},
2207+
"Type": "AWS::IAM::Policy",
2208+
},
21022209
"LookupVersionArnc8730278af02f875114ca902814c77b68f19b0087110E04D0A": {
21032210
"DeletionPolicy": "Delete",
21042211
"DependsOn": [
@@ -3801,6 +3908,7 @@ service iptables save",
38013908
"WebappMigrationTrigger42AFC1D9": {
38023909
"DeletionPolicy": "Delete",
38033910
"DependsOn": [
3911+
"DatabaseClusterLogRetentionpostgresql025D39CE",
38043912
"DatabaseCluster5B53A178",
38053913
"DatabaseClusterSecretAttachmentDC8466C0",
38063914
"DatabaseClusterSecretD1FB634F",

0 commit comments

Comments
 (0)