Files
rentall-app/infrastructure/cdk/lib/rds-stack.ts
2026-01-21 14:18:07 -05:00

175 lines
4.8 KiB
TypeScript

import * as cdk from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as rds from "aws-cdk-lib/aws-rds";
import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager";
import { Construct } from "constructs";
interface RdsStackProps extends cdk.StackProps {
/**
* Environment name (dev, staging, prod)
*/
environment: string;
/**
* VPC to deploy the database in
*/
vpc: ec2.IVpc;
/**
* Database credentials secret from SecretsStack
*/
databaseSecret: secretsmanager.ISecret;
/**
* Database name (default: rentall)
*/
databaseName?: string;
/**
* Instance type (default: t3.micro for Free Tier)
*/
instanceType?: ec2.InstanceType;
/**
* Allocated storage in GB (default: 20)
*/
allocatedStorage?: number;
/**
* Enable Multi-AZ deployment (default: false for dev/staging)
*/
multiAz?: boolean;
/**
* Backup retention days (default: 7)
*/
backupRetentionDays?: number;
}
export class RdsStack extends cdk.Stack {
/**
* The RDS database instance
*/
public readonly database: rds.DatabaseInstance;
/**
* Security group for the database
*/
public readonly databaseSecurityGroup: ec2.SecurityGroup;
/**
* Database endpoint address
*/
public readonly dbEndpoint: string;
/**
* Database port
*/
public readonly dbPort: number;
constructor(scope: Construct, id: string, props: RdsStackProps) {
super(scope, id, props);
const {
environment,
vpc,
databaseSecret,
databaseName = "rentall",
instanceType = ec2.InstanceType.of(
ec2.InstanceClass.T3,
ec2.InstanceSize.MICRO
),
allocatedStorage = 20,
multiAz = false,
backupRetentionDays = 7,
} = props;
// Security group for the database
this.databaseSecurityGroup = new ec2.SecurityGroup(
this,
"DatabaseSecurityGroup",
{
vpc,
securityGroupName: `rentall-db-sg-${environment}`,
description: `Security group for RDS database (${environment})`,
allowAllOutbound: false,
}
);
// Create the RDS instance
this.database = new rds.DatabaseInstance(this, "Database", {
instanceIdentifier: `rentall-db-${environment}`,
engine: rds.DatabaseInstanceEngine.postgres({
version: rds.PostgresEngineVersion.VER_15,
}),
instanceType,
vpc,
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
securityGroups: [this.databaseSecurityGroup],
credentials: rds.Credentials.fromSecret(databaseSecret),
databaseName,
allocatedStorage,
maxAllocatedStorage: allocatedStorage * 2, // Allow storage autoscaling up to 2x
storageType: rds.StorageType.GP2,
multiAz,
autoMinorVersionUpgrade: true,
deletionProtection: environment === "prod",
removalPolicy:
environment === "prod"
? cdk.RemovalPolicy.RETAIN
: cdk.RemovalPolicy.DESTROY,
backupRetention: cdk.Duration.days(backupRetentionDays),
preferredBackupWindow: "03:00-04:00", // UTC
preferredMaintenanceWindow: "Sun:04:00-Sun:05:00", // UTC
storageEncrypted: true,
monitoringInterval: cdk.Duration.seconds(60),
enablePerformanceInsights: true,
performanceInsightRetention: rds.PerformanceInsightRetention.DEFAULT, // 7 days (free)
parameterGroup: new rds.ParameterGroup(this, "ParameterGroup", {
engine: rds.DatabaseInstanceEngine.postgres({
version: rds.PostgresEngineVersion.VER_15,
}),
parameters: {
// Enforce SSL connections
"rds.force_ssl": "1",
// Log slow queries (> 1 second)
log_min_duration_statement: "1000",
},
}),
publiclyAccessible: false,
});
this.dbEndpoint = this.database.dbInstanceEndpointAddress;
this.dbPort = this.database.dbInstanceEndpointPort
? parseInt(this.database.dbInstanceEndpointPort)
: 5432;
// Outputs
new cdk.CfnOutput(this, "DatabaseEndpoint", {
value: this.database.dbInstanceEndpointAddress,
description: "Database endpoint address",
exportName: `DatabaseEndpoint-${environment}`,
});
new cdk.CfnOutput(this, "DatabasePort", {
value: this.database.dbInstanceEndpointPort,
description: "Database port",
exportName: `DatabasePort-${environment}`,
});
new cdk.CfnOutput(this, "DatabaseSecurityGroupId", {
value: this.databaseSecurityGroup.securityGroupId,
description: "Database security group ID",
exportName: `DatabaseSecurityGroupId-${environment}`,
});
new cdk.CfnOutput(this, "DatabaseInstanceIdentifier", {
value: this.database.instanceIdentifier,
description: "Database instance identifier",
exportName: `DatabaseInstanceIdentifier-${environment}`,
});
}
}