Table of contents
Open Table of contents
Goal
Goal behind this blog, is to deploy a lambda function in aws written in typescript using cdktf and esbuild and make that lambda function invocable from internet.
Prerequisite
I assume you have
- Basic
typescriptknowledge git,node,npmandterraform cliinstalledawsaccount and cli installed and configured withaccess_key_idandaccess_key_secret
Setup
Clone the repo nivekithan/blog-deploy-typescript-lambda
git clone https://github.com/nivekithan/blog-deploy-typescript-lambda.git lambda-cdktf
cd lambda-cdktf
Familiarizing with the ts-lambda source code
If you see the code you will find two directories named cdktf and ts-lambda. ts-lambda contains source code for our lambda function and cdktf contains source for managing our infrastructure.
Our lambda function in ts-lambda/src/index.ts is fairly simple
export const handler = async () => {
return {
statusCode: 200,
headers: {
"Content-Type": "text/html; charset=utf-8",
},
body: `<h1>Hello World, This is written in typescript</h1>`,
};
};
It exports a handler function which when called will return following html
<h1>Hello World, This is written in typescript</h1>
Deploying our application
We are not actually intreseted in what does our lambda function does. We are only intreseted in deploying that lambda function. So lets look into cdktf directory.
First lets see contents of package.json
// cdktf/package.json
{
"name": "cdktf",
"version": "1.0.0",
"description": "",
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@cdktf/provider-aws": "^9.0.51",
"cdktf": "^0.12.3",
"constructs": "^10.1.123",
"esbuild": "^0.15.10"
},
"devDependencies": {
"@types/node": "^18.8.0"
}
}
cdktfpackage allows us to manage our infrastructure intypescriptinstead ofHCL@cdktf/provider-awspackage is used to provision and manageawsinfrastructureconstructspackage is used to create reusable cloud resourceesbuildpackage is used to compiletypescripttojavascriptand bundle it to single file.
Lets take a look at main.ts
import { Construct } from "constructs";
import { App, TerraformOutput, TerraformStack } from "cdktf";
import * as aws from "@cdktf/provider-aws";
import path from "node:path";
import { TypescriptFunction } from "./lib/typescriptFunction";
const lambdaRolePolicy = {
Version: "2012-10-17",
Statement: [
{
Action: "sts:AssumeRole",
Principal: {
Service: "lambda.amazonaws.com",
},
Effect: "Allow",
Sid: "",
},
],
};
export type LambdaConfig = {
/**
* Absolute path to directory which contains src/index.js which then exports handler
* function
*/
path: string;
/**
* Lambda version
*/
version: string;
};
class LambdaStack extends TerraformStack {
constructor(scope: Construct, name: string, config: LambdaConfig) {
super(scope, name);
new aws.AwsProvider(this, "aws", {
region: "ap-south-1",
});
const sourceCodeAsset = new TypescriptFunction(this, "lambda-source-code", {
absPath: config.path,
handler: "index.handler",
});
// Create a s3 bucket
const bucket = new aws.s3.S3Bucket(this, "bucket", {
bucketPrefix: "slack-search-lambda",
});
// Upload source code to s3
const lambdaArchive = new aws.s3.S3Object(this, "lambda-archive", {
bucket: bucket.bucket,
key: `${sourceCodeAsset.asset.fileName}/${config.version}`,
source: sourceCodeAsset.asset.path,
});
// Create lambda role
const role = new aws.iam.IamRole(this, "lambda-exec", {
name: `learn-cdktf-${name}`,
assumeRolePolicy: JSON.stringify(lambdaRolePolicy),
});
new aws.iam.IamRolePolicyAttachment(this, "lambda-managed-policy", {
policyArn:
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
role: role.name,
});
const lambdaFunc = new aws.lambdafunction.LambdaFunction(
this,
"slack-search-lambda",
{
functionName: `slack-search-lambda`,
s3Bucket: bucket.bucket,
s3Key: lambdaArchive.key,
handler: "index.handler",
runtime: "nodejs16.x",
role: role.arn,
}
);
new aws.lambdafunction.LambdaPermission(
this,
"lambda-allow-public-access",
{
statementId: "FunctionURLAllowPublicAccess",
principal: "*",
action: "lambda:InvokeFunctionUrl",
functionName: lambdaFunc.functionName,
functionUrlAuthType: "NONE",
}
);
const lambdaFunctionUrl = new aws.lambdafunction.LambdaFunctionUrl(
this,
"lambdaFunctionUrl",
{ authorizationType: "NONE", functionName: lambdaFunc.functionName }
);
new TerraformOutput(this, "lambda-url", {
value: lambdaFunctionUrl.functionUrl,
});
}
}
const app = new App();
new LambdaStack(app, "cdktf", {
path: path.resolve(__dirname, "..", "ts-lambda"),
version: "0.0.5",
});
app.synth();
Deploying to aws
To deploy to aws first we have to install cdktf cli. For that run
pnpm i -g cdktf-cli
## if you prefer npm
npm i -g cdktf-cli
then install all packages in cdktf directory
## lambda-cdktf/cdktf/
pnpm i
## or if you prefer npm
npm i
Now to deploy run
## lambda-cdktf/cdktf
cdktf deploy
Then select Approve and thats it. Once the its deployed you will be able to see the lambda url.
For example, for me it shows
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
Outputs:
lambda-url = "https://3gcd3p4j4ypvucb5eo4bg3dcgm0dobsb.lambda-url.ap-south-1.on.aws/"
cdktf
lambda-url = https://3gcd3p4j4ypvucb5eo4bg3dcgm0dobsb.lambda-url.ap-south-1.on.aws/
Opening that url in browser shows

Hopefully it shows same for you too. If you face any let me know, I will help you to my best of efforts.
Cleanup
To destroy all the resource created and prevent accidental aws cost run
cdktf destroy