Skip to content

Commit 41c7860

Browse files
committed
Add CDK application code: Stack definition and entry point
1 parent b16c33e commit 41c7860

File tree

3 files changed

+178
-0
lines changed

3 files changed

+178
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env node
2+
import * as cdk from 'aws-cdk-lib';
3+
import { PostgresLambdaStack } from '../lib/postgres-lambda-stack';
4+
5+
const app = new cdk.App();
6+
new PostgresLambdaStack(app, 'PostgresLambdaStack', {
7+
/* If you don't specify 'env', this stack will be environment-agnostic.
8+
* Account/Region-dependent features and context lookups will not work,
9+
* but a single synthesized template can be deployed anywhere. */
10+
11+
/* Uncomment the next line to specialize this stack for the AWS Account
12+
* and Region that are implied by the current CLI configuration. */
13+
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
14+
15+
/* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
16+
});
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import * as cdk from 'aws-cdk-lib';
2+
import { Construct } from 'constructs';
3+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
4+
import * as rds from 'aws-cdk-lib/aws-rds';
5+
import * as lambda from 'aws-cdk-lib/aws-lambda';
6+
import * as iam from 'aws-cdk-lib/aws-iam';
7+
import * as cr from 'aws-cdk-lib/custom-resources';
8+
import * as path from 'path';
9+
10+
export class PostgresLambdaStack extends cdk.Stack {
11+
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
12+
super(scope, id, props);
13+
14+
// Create a VPC for our application
15+
const vpc = new ec2.Vpc(this, 'PostgresLambdaVpc', {
16+
maxAzs: 2,
17+
natGateways: 1,
18+
});
19+
20+
// Create a PostgreSQL Aurora Serverless v2 cluster
21+
const dbCluster = new rds.DatabaseCluster(this, 'PostgresCluster', {
22+
engine: rds.DatabaseClusterEngine.auroraPostgres({
23+
version: rds.AuroraPostgresEngineVersion.VER_17_4,
24+
}),
25+
vpc: vpc,
26+
writer: rds.ClusterInstance.serverlessV2('writer'),
27+
serverlessV2MinCapacity: 0.5,
28+
serverlessV2MaxCapacity: 1,
29+
defaultDatabaseName: 'demodb',
30+
credentials: rds.Credentials.fromGeneratedSecret('postgres'),
31+
});
32+
33+
// Create a Lambda function that calls PostgreSQL
34+
const lambdaToPostgres = new lambda.Function(this, 'LambdaToPostgres', {
35+
runtime: lambda.Runtime.NODEJS_LATEST,
36+
handler: 'index.handler',
37+
code: lambda.Code.fromAsset(path.join(__dirname, '../lambda/lambda-to-postgres')),
38+
vpc,
39+
vpcSubnets: {
40+
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
41+
},
42+
environment: {
43+
DB_SECRET_ARN: dbCluster.secret?.secretArn || '',
44+
DB_NAME: 'demodb',
45+
},
46+
timeout: cdk.Duration.seconds(30),
47+
});
48+
49+
// Grant Lambda access to the DB
50+
dbCluster.connections.allowDefaultPortTo(lambdaToPostgres);
51+
52+
// Grant the Lambda function permission to read the database secret
53+
dbCluster.secret?.grantRead(lambdaToPostgres);
54+
55+
// Create a Lambda function that is called by PostgreSQL
56+
const postgresFunction = new lambda.Function(this, 'PostgresFunction', {
57+
runtime: lambda.Runtime.NODEJS_LATEST,
58+
handler: 'index.handler',
59+
code: lambda.Code.fromAsset(path.join(__dirname, '../lambda/postgres-to-lambda')),
60+
environment: {
61+
FUNCTION_NAME: 'PostgresFunction',
62+
},
63+
timeout: cdk.Duration.seconds(30),
64+
});
65+
66+
67+
// Create a role for PostgreSQL to assume to invoke Lambda
68+
const postgresLambdaRole = new iam.Role(this, 'PostgresLambdaRole', {
69+
assumedBy: new iam.ServicePrincipal('rds.amazonaws.com'),
70+
});
71+
72+
postgresFunction.grantInvoke(postgresLambdaRole);
73+
74+
75+
const l1DbCluster = dbCluster.node.defaultChild as rds.CfnDBCluster
76+
const exisitingProperty = (l1DbCluster.associatedRoles as []) || [];
77+
console.log(exisitingProperty);
78+
79+
const newRole: { FeatureName: string, RoleArn: string } = {
80+
FeatureName: "Lambda", // Changed to PascalCase
81+
RoleArn: postgresLambdaRole.roleArn // Changed to PascalCase
82+
};
83+
84+
const updatedRoles: { [key in 'featureName' | 'FeatureName' | 'roleArn' | 'RoleArn']?: string; }[] = [...exisitingProperty, newRole];
85+
86+
l1DbCluster.addPropertyOverride('AssociatedRoles', updatedRoles);
87+
88+
// Create Lambda function for PostgreSQL setup
89+
const setupFunction = new lambda.Function(this, 'PostgresSetupFunction', {
90+
runtime: lambda.Runtime.NODEJS_LATEST,
91+
handler: 'index.handler',
92+
code: lambda.Code.fromAsset(path.join(__dirname, '../lambda/postgres-setup')),
93+
vpc,
94+
vpcSubnets: {
95+
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
96+
},
97+
environment: {
98+
DB_SECRET_ARN: dbCluster.secret?.secretArn || '',
99+
DB_NAME: 'demodb',
100+
POSTGRES_FUNCTION_NAME: postgresFunction.functionName,
101+
AWS_REGION: this.region,
102+
},
103+
timeout: cdk.Duration.minutes(5),
104+
});
105+
106+
// Grant setup function access to the DB and secrets
107+
dbCluster.connections.allowDefaultPortTo(setupFunction);
108+
dbCluster.secret?.grantRead(setupFunction);
109+
110+
// Create custom resource to trigger setup
111+
const setupProvider = new cr.Provider(this, 'PostgresSetupProvider', {
112+
onEventHandler: setupFunction,
113+
});
114+
115+
new cdk.CustomResource(this, 'PostgresSetupResource', {
116+
serviceToken: setupProvider.serviceToken,
117+
});
118+
119+
// Output the database endpoint and secret ARN
120+
new cdk.CfnOutput(this, 'DBClusterEndpoint', {
121+
value: dbCluster.clusterEndpoint.hostname,
122+
description: 'The endpoint of the database cluster',
123+
});
124+
125+
new cdk.CfnOutput(this, 'DBSecretArn', {
126+
value: dbCluster.secret?.secretArn || 'No secret created',
127+
description: 'The ARN of the database credentials secret',
128+
});
129+
130+
new cdk.CfnOutput(this, 'LambdaToPostgresFunctionName', {
131+
value: lambdaToPostgres.functionName,
132+
description: 'The name of the Lambda function that calls PostgreSQL',
133+
});
134+
135+
new cdk.CfnOutput(this, 'PostgresFunctionName', {
136+
value: postgresFunction.functionName,
137+
description: 'The name of the Lambda function that is called by PostgreSQL',
138+
});
139+
140+
new cdk.CfnOutput(this, 'PostgresLambdaRoleArn', {
141+
value: postgresLambdaRole.roleArn,
142+
description: 'The ARN of the role that PostgreSQL can assume to invoke Lambda',
143+
});
144+
}
145+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// import * as cdk from 'aws-cdk-lib';
2+
// import { Template } from 'aws-cdk-lib/assertions';
3+
// import * as PostgresLambda from '../lib/postgres-lambda-stack';
4+
5+
// example test. To run these tests, uncomment this file along with the
6+
// example resource in lib/postgres-lambda-stack.ts
7+
test('SQS Queue Created', () => {
8+
// const app = new cdk.App();
9+
// // WHEN
10+
// const stack = new PostgresLambda.PostgresLambdaStack(app, 'MyTestStack');
11+
// // THEN
12+
// const template = Template.fromStack(stack);
13+
14+
// template.hasResourceProperties('AWS::SQS::Queue', {
15+
// VisibilityTimeout: 300
16+
// });
17+
});

0 commit comments

Comments
 (0)