CDKTF basic config and install can read my before story
Terraform CDK(CDKTF) create aws s3 bucket
this time we need to deploy serverless apps
import {Construct} from "constructs";
import {App, TerraformStack} from "cdktf";
import {S3Bucket} from "@cdktf/provider-aws/lib/s3-bucket";
import {DbInstance} from "@cdktf/provider-aws/lib/db-instance";
import {AwsProvider} from "@cdktf/provider-aws/lib/provider";
import {S3BucketWebsiteConfiguration} from "@cdktf/provider-aws/lib/s3-bucket-website-configuration";
import {IamRole} from "@cdktf/provider-aws/lib/iam-role";
import {S3BucketObject} from "@cdktf/provider-aws/lib/s3-bucket-object";
import {S3BucketPolicy} from "@cdktf/provider-aws/lib/s3-bucket-policy";
import {S3BucketPublicAccessBlock} from "@cdktf/provider-aws/lib/s3-bucket-public-access-block";
import {ApiGatewayRestApi} from "@cdktf/provider-aws/lib/api-gateway-rest-api";
import {LambdaFunction} from "@cdktf/provider-aws/lib/lambda-function";
import {ApiGatewayResource} from "@cdktf/provider-aws/lib/api-gateway-resource";
// import {ApiGatewayMethod} from "@cdktf/provider-aws/lib/api-gateway-method";
// import {ApiGatewayIntegration} from "@cdktf/provider-aws/lib/api-gateway-integration";
import {ApiGatewayStage} from "@cdktf/provider-aws/lib/api-gateway-stage";
import {ApiGatewayDeployment} from "@cdktf/provider-aws/lib/api-gateway-deployment";
// import {ApiGatewayIntegrationResponse} from "@cdktf/provider-aws/lib/api-gateway-integration-response";
// import {ApiGatewayMethodResponse} from "@cdktf/provider-aws/lib/api-gateway-method-response";
import {CloudwatchLogGroup} from "@cdktf/provider-aws/lib/cloudwatch-log-group";
import {IamPolicyAttachment} from "@cdktf/provider-aws/lib/iam-policy-attachment";
import * as path from "path";
import {LambdaLayerVersion} from "@cdktf/provider-aws/lib/lambda-layer-version";
import {SecurityGroup} from "@cdktf/provider-aws/lib/security-group";
import {VpcSecurityGroupIngressRule} from "@cdktf/provider-aws/lib/vpc-security-group-ingress-rule";
import {DbSubnetGroup} from "@cdktf/provider-aws/lib/db-subnet-group";
import {Subnet} from "@cdktf/provider-aws/lib/subnet";
import {Vpc} from "@cdktf/provider-aws/lib/vpc";
// import {NatGateway} from "@cdktf/provider-aws/lib/nat-gateway";
import {RouteTable} from "@cdktf/provider-aws/lib/route-table";
import {InternetGateway} from "@cdktf/provider-aws/lib/internet-gateway";
import {MainRouteTableAssociation} from "@cdktf/provider-aws/lib/main-route-table-association";
import {IamPolicy} from "@cdktf/provider-aws/lib/iam-policy";
import {VpcSecurityGroupEgressRule} from "@cdktf/provider-aws/lib/vpc-security-group-egress-rule";
// import {DbSubnetGroup} from "@cdktf/provider-aws/lib/db-subnet-group";
class MyStack extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);
new AwsProvider(this, 'aws', {
region: 'ap-east-1', // Update the region as needed
accessKey: '', // your accessKey
secretKey: '' // your secretKey
});
const my_vpc = new Vpc(this, "MyVpc", {
cidrBlock: '10.0.0.0/16',
enableDnsHostnames: true
})
new Subnet(this, 'public_subnet1', {
vpcId: my_vpc.id,
cidrBlock: "10.0.101.0/24",
availabilityZone: "ap-east-1a"
});
new Subnet(this, 'public_subnet2', {
vpcId: my_vpc.id,
cidrBlock: "10.0.102.0/24",
availabilityZone: "ap-east-1b"
});
new Subnet(this, 'public_subnet3', {
vpcId: my_vpc.id,
cidrBlock: "10.0.103.0/24",
availabilityZone: "ap-east-1c"
});
const private_subnet1 = new Subnet(this, 'private_subnet1', {
vpcId: my_vpc.id,
cidrBlock: "10.0.1.0/24",
availabilityZone: "ap-east-1a"
});
const private_subnet2 = new Subnet(this, 'private_subnet2', {
vpcId: my_vpc.id,
cidrBlock: "10.0.2.0/24",
availabilityZone: "ap-east-1b"
});
const private_subnet3 = new Subnet(this, 'private_subnet3', {
vpcId: my_vpc.id,
cidrBlock: "10.0.3.0/24",
availabilityZone: "ap-east-1c"
});
// new NatGateway(this, 'NatGateway', {
// subnetId: private_subnet1.id,
// connectivityType: "private"
// });
const igw = new InternetGateway(this, 'MyInternetGateway', {
vpcId: my_vpc.id,
});
// Create a route table for the public subnet and add a route to the internet gateway
const main_route = new RouteTable(this, 'PublicRouteTable', {
vpcId: my_vpc.id,
route: [
{
cidrBlock: '0.0.0.0/0',
gatewayId: igw.id,
}
]
});
new MainRouteTableAssociation(this, 'MainRouteTableAssociation', {
vpcId: my_vpc.id,
routeTableId: main_route.id
})
// publicRouteTable.createRoute('PublicRoute', {
// destinationCidrBlock: '0.0.0.0/0',
// gatewayId: igw.id,
// });
// Create an S3 bucket
const noodle_web = new S3Bucket(this, 'myS3Bucket', {
bucket: '', // Replace with your preferred bucket name
serverSideEncryptionConfiguration: {
rule: {
applyServerSideEncryptionByDefault: {
sseAlgorithm: 'AES256',
},
}
},
});
new S3BucketPublicAccessBlock(this, 'S3BucketPublicAccessBlock', {
bucket: noodle_web.id,
blockPublicAcls: false,
blockPublicPolicy: false,
ignorePublicAcls: false,
restrictPublicBuckets: false,
})
new S3BucketWebsiteConfiguration(this, 'noodle', {
bucket: noodle_web.bucket,
indexDocument: {
suffix: 'index.html'
}
});
const bucketPolicy = JSON.stringify({
Version: '2012-10-17',
Statement: [
{
Sid: 'AllowGetObject',
Effect: 'Allow',
Principal: '*',
Action: ['s3:GetObject'],
Resource: [`${noodle_web.arn}/*`],
},
],
});
new S3BucketPolicy(this, 'MyBucketPolicy', {
bucket: noodle_web.bucket,
policy: bucketPolicy,
});
const ingress_psql_sg = new SecurityGroup(this, 'rdsSecurityGroup', {
vpcId: my_vpc.id,
});
new VpcSecurityGroupIngressRule(this, 'ingress_psql_rule', {
securityGroupId: ingress_psql_sg.id,
cidrIpv4: "10.0.0.0/8",
fromPort: 5432,
ipProtocol: "tcp",
toPort: 5432
})
new VpcSecurityGroupEgressRule(this, 'Egress_psql_rule', {
securityGroupId: ingress_psql_sg.id,
cidrIpv4: "0.0.0.0/0",
fromPort: 5432,
ipProtocol: "tcp",
toPort: 5432
})
new S3BucketObject(this, 'website_noodle', {
bucket: noodle_web.id,
key: 'index.html',
source: '/Users/jimmywong/IdeaProjects/noodle/noodles/website/index.html',
contentType: "text/html"
})
const Db_Subnet_Group = new DbSubnetGroup(this, 'myDbSubnetGroup', {
name: 'my-db-subnet-group',
description: 'My custom DB subnet group',
subnetIds: [private_subnet1.id, private_subnet2.id, private_subnet3.id]
});
// Create an RDS PostgreSQL database
new DbInstance(this, 'myRdsInstance', {
allocatedStorage: 20,
engine: 'postgres',
instanceClass: 'db.t3.micro',
// instanceClass: 'db.m5.large',
username: '',// your sql username
password: '', // your sql password
skipFinalSnapshot: true,
vpcSecurityGroupIds: [ingress_psql_sg.id],
dbSubnetGroupName: Db_Subnet_Group.name,
// publiclyAccessible: true,
identifier: "" // your db name
});
const lambdaRole = new IamRole(this, 'lambdaRole', {
name: 'AWSLambdaBasicExecutionRole',
assumeRolePolicy: JSON.stringify({
Version: '2012-10-17',
Statement: [
{
Action: 'sts:AssumeRole',
Effect: 'Allow',
Principal: {
Service: 'lambda.amazonaws.com',
},
},
],
}),
});
const lambda_policy = new IamPolicy(this, 'lambda_policy', {
policy: JSON.stringify({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeNetworkInterfaces",
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:DescribeInstances",
"ec2:AttachNetworkInterface",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
})
})
// Attach a managed policy to the IAM role (optional)
new IamPolicyAttachment(this, 'MyPolicyAttachment', {
name: 'inputLambdaPolicyAttachment',
policyArn: lambda_policy.arn, // Replace with the desired policy ARN
roles: [lambdaRole.name],
});
const lambda_layer = new LambdaLayerVersion(this, 'pgLayer', {
filename: path.resolve(__dirname, '/Users/jimmywong/IdeaProjects/noodle/noodles/lambda-layer-pg/lambda-layer-pg.zip'), // Replace with the path to your ZIP archive
layerName: 'pg_layer',
compatibleRuntimes: ['nodejs18.x'], // Adjust the runtime version as needed
});
const input_file_path = '/Users/jimmywong/IdeaProjects/noodle/noodles/lambda/input_data_to_db.zip';
const output_file_path = '/Users/jimmywong/IdeaProjects/noodle/noodles/lambda/output_data_from_db.zip'
// Create the Lambda function
const input_lambda = new LambdaFunction(this, 'my_input_LambdaFunction', {
functionName: 'input_noodle_data',
runtime: 'nodejs18.x',
handler: 'input_data_to_db.handler',
filename: input_file_path,
role: lambdaRole.arn,
vpcConfig: {
subnetIds: [private_subnet1.id, private_subnet2.id, private_subnet3.id],
securityGroupIds: [ingress_psql_sg.id]
},
// filename: '/Users/jimmywong/IdeaProjects/noodle/noodles/lambda/input_data_to_db.zip',
layers: [lambda_layer.arn],
});
const output_lambda = new LambdaFunction(this, 'my_output_LambdaFunction', {
functionName: 'query_noodle_data',
runtime: 'nodejs18.x',
handler: 'output_data_from_db.handler',
role: lambdaRole.arn,
filename: output_file_path,
layers: [lambda_layer.arn],
vpcConfig: {
subnetIds: [private_subnet1.id, private_subnet2.id, private_subnet3.id],
securityGroupIds: [ingress_psql_sg.id]
}
});
const restApi = new ApiGatewayRestApi(this, 'MyRestApi', {
name: 'my-rest-api', // Replace with your desired REST API name
description: 'My REST API description', // Replace with your desired REST API description
});
new ApiGatewayResource(this, 'MyResource', { //aws_api_gateway_resource
restApiId: restApi.id,
parentId: restApi.rootResourceId,
pathPart: 'submit', // Replace with your desired resource path
});
// Create a POST method for the resource
// const input_method = new ApiGatewayMethod(this, 'MyPostMethod', {
// restApiId: restApi.id,
// resourceId: resource.id,
// httpMethod: 'POST',
// authorization: 'NONE', // No authorization required for this example, update as needed
// });
// Create a GET method for the resource
// const output_method = new ApiGatewayMethod(this, 'MyGetMethod', {
// restApiId: restApi.id,
// resourceId: resource.id,
// httpMethod: 'GET',
// authorization: 'NONE', // No authorization required for this example, update as needed
// });
// Create the integration response for CORS
// const ApiGatewayMethodResponse_input = new ApiGatewayMethodResponse(this, 'ApiGatewayMethodResponseinput', {
// restApiId: restApi.id,
// resourceId: resource.id,
// httpMethod: input_method.httpMethod,
// statusCode: "200",
// responseModels: {
// "application/json": "Empty"
// },
// responseParameters: {
// "method.response.header.Access-Control-Allow-Headers": true,
// "method.response.header.Access-Control-Allow-Methods": true,
// "method.response.header.Access-Control-Allow-Origin": true
// },
// });
// new ApiGatewayIntegrationResponse(this, 'MyIntegrationResponse_input', {
// restApiId: restApi.id,
// resourceId: resource.id,
// httpMethod: input_method.httpMethod,
// statusCode: ApiGatewayMethodResponse_input.statusCode,
// responseParameters: {
// "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
// "method.response.header.Access-Control-Allow-Methods": "'POST'",
// "method.response.header.Access-Control-Allow-Origin": "'*'"
// },
// });
// new ApiGatewayIntegration(this, 'PostApiGatewayIntegration', {
// restApiId: restApi.id,
// resourceId: resource.id,
// integrationHttpMethod: "POST",
// type: "AWS",
// uri: input_lambda.invokeArn,
// httpMethod: input_method.httpMethod
// });
const ApiGatewayDeployment_input = new ApiGatewayDeployment(this, 'ApiGatewayDeployment', {
restApiId: restApi.id,
});
new ApiGatewayStage(this, 'ApiGatewayStage1', {
stageName: "", // your stage name
restApiId: restApi.id,
deploymentId: ApiGatewayDeployment_input.id
});
//
// new ApiGatewayIntegration(this, 'GetApiGatewayIntegration', {
// restApiId: restApi.id,
// resourceId: resource.id,
// integrationHttpMethod: "GET",
// type: "AWS",
// uri: output_lambda.invokeArn,
// httpMethod: output_method.httpMethod,
// });
// new ApiGatewayIntegrationResponse(this, 'MyIntegrationResponse_output', {
// restApiId: restApi.id,
// resourceId: resource.id,
// httpMethod: "GET",
// statusCode: "200",
// // responseParameters: {
// // "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
// // "method.response.header.Access-Control-Allow-Methods": "'GET'",
// // "method.response.header.Access-Control-Allow-Origin": "'*'"
// // },
// });
// new ApiGatewayMethodResponse(this, 'ApiGatewayMethodResponseoutput', {
// restApiId: restApi.id,
// resourceId: resource.id,
// httpMethod: output_method.httpMethod,
// statusCode: "200",
// responseModels: {
// "application/json": "Empty"
// },
// // responseParameters: {
// // "method.response.header.Access-Control-Allow-Headers": true,
// // "method.response.header.Access-Control-Allow-Methods": true,
// // "method.response.header.Access-Control-Allow-Origin": true
// // },
// });
const input_cloudwatch_path = '/aws/lambda/' + input_lambda.functionName
new CloudwatchLogGroup(this, 'lambda_input_log_group', {
name: input_cloudwatch_path,
retentionInDays: 7,
});
const output_cloudwatch_path = '/aws/lambda/' + output_lambda.functionName
new CloudwatchLogGroup(this, 'lambda_output_log_group', {
name: output_cloudwatch_path,
retentionInDays: 7,
});
}
}
const app = new App();
new MyStack(app, "noodles");
app.synth();
this code will create new s3 with the object, also, it will create api-gateway and lambda with the in and out like the diagram
the file structure like
so, we can develop the apps use nodejs with the typescript in the cdktf, about the lib part i used pg, if you need the details, you can read the REF Doc
in this project, you need ready lambda code , website html code with css and javascript, i don’t know why the resource and deploy have issues in the apigateway with cdktf, i will keep to improve this part, my code should work in create api gateway , but may need the user custom deploy in the console
you can get the api in your new Stages
REF:
Top comments (0)