forked from cdk-patterns/serverless
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy paththe-rds-proxy-stack.ts
105 lines (91 loc) · 3.89 KB
/
the-rds-proxy-stack.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import * as cdk from '@aws-cdk/core';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as rds from '@aws-cdk/aws-rds';
import * as secrets from '@aws-cdk/aws-secretsmanager';
const ssm = require('@aws-cdk/aws-ssm');
import * as lambda from '@aws-cdk/aws-lambda';
import apigw = require('@aws-cdk/aws-apigatewayv2');
import integrations = require('@aws-cdk/aws-apigatewayv2-integrations');
export class TheRdsProxyStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// RDS needs to be setup in a VPC
const vpc = new ec2.Vpc(this, 'Vpc', {
maxAzs: 2, // Default is all AZs in the region
});
// We need this security group to add an ingress rule and allow our lambda to query the proxy
let lambdaToRDSProxyGroup = new ec2.SecurityGroup(this, 'Lambda to RDS Proxy Connection', {
vpc
});
// We need this security group to allow our proxy to query our MySQL Instance
let dbConnectionGroup = new ec2.SecurityGroup(this, 'Proxy to DB Connection', {
vpc
});
dbConnectionGroup.addIngressRule(dbConnectionGroup, ec2.Port.tcp(3306), 'allow db connection');
dbConnectionGroup.addIngressRule(lambdaToRDSProxyGroup, ec2.Port.tcp(3306), 'allow lambda connection');
const databaseUsername = 'syscdk';
// Dynamically generate the username and password, then store in secrets manager
const databaseCredentialsSecret = new secrets.Secret(this, 'DBCredentialsSecret', {
secretName: id+'-rds-credentials',
generateSecretString: {
secretStringTemplate: JSON.stringify({
username: databaseUsername,
}),
excludePunctuation: true,
includeSpace: false,
generateStringKey: 'password'
}
});
new ssm.StringParameter(this, 'DBCredentialsArn', {
parameterName: 'rds-credentials-arn',
stringValue: databaseCredentialsSecret.secretArn,
});
// MySQL DB Instance (delete protection turned off because pattern is for learning.)
// re-enable delete protection for a real implementation
const rdsInstance = new rds.DatabaseInstance(this, 'DBInstance', {
engine: rds.DatabaseInstanceEngine.mysql({
version: rds.MysqlEngineVersion.VER_5_7_30
}),
credentials: rds.Credentials.fromSecret(databaseCredentialsSecret),
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL),
vpc,
removalPolicy: cdk.RemovalPolicy.DESTROY,
deletionProtection: false,
securityGroups: [dbConnectionGroup]
});
// Create an RDS Proxy
const proxy = rdsInstance.addProxy(id+'-proxy', {
secrets: [databaseCredentialsSecret],
debugLogging: true,
vpc,
securityGroups: [dbConnectionGroup]
});
// Workaround for bug where TargetGroupName is not set but required
let targetGroup = proxy.node.children.find((child:any) => {
return child instanceof rds.CfnDBProxyTargetGroup
}) as rds.CfnDBProxyTargetGroup
targetGroup.addPropertyOverride('TargetGroupName', 'default');
// Lambda to Interact with RDS Proxy
const rdsLambda = new lambda.Function(this, 'rdsProxyHandler', {
runtime: lambda.Runtime.NODEJS_12_X,
code: lambda.Code.asset('lambda-fns/rds'),
handler: 'rdsLambda.handler',
vpc: vpc,
securityGroups: [lambdaToRDSProxyGroup],
environment: {
PROXY_ENDPOINT: proxy.endpoint,
RDS_SECRET_NAME: id+'-rds-credentials'
}
});
databaseCredentialsSecret.grantRead(rdsLambda);
// defines an API Gateway Http API resource backed by our "rdsLambda" function.
let api = new apigw.HttpApi(this, 'Endpoint', {
defaultIntegration: new integrations.LambdaProxyIntegration({
handler: rdsLambda
})
});
new cdk.CfnOutput(this, 'HTTP API Url', {
value: api.url ?? 'Something went wrong with the deploy'
});
}
}