TypeScript Lambda mit cdktf

17. Dezember 2022

Hi,

In diesem Blog-Post möchte ich kurz erklären, was cdktf ist und wie man damit eine TypeScript-Lambda erstellen kann. Die Motivation dafür kam durch einen StackOverflow-Beitrag. Zunächst erkläre ich kurz, was cdktf überhaupt ist und wie man es initialisiert. Anschließend zeige ich die Lösung für die TypeScript-Lambda.

Was ist cdktf?

Das Cloud Development Kit for Terraform (cdktf) ist ein Toolkit zum Erstellen und Verwalten von Cloud-Infrastruktur wie AWS oder Azure mit Terraform. Es ermöglicht es dir, die Infrastruktur mithilfe einer Programmiersprache wie TypeScript oder Python zu definieren.

Setup cdktf

Den gesamten Code findest du in meinem Repository hier. Ich beschreibe aber noch kurz, wie das Repository erstellt wurde. Initialisiere dein cdktf-Repo mit:

cdktf init --template="typescript"
cdktf provider add "aws@~>4.0"

Optional kannst du noch Prettier und Linter hinzufügen. In den meisten meiner Projekte verwende ich diese, da sie mir ein schnelles Entwickeln ermöglichen, ohne dass ich mir Gedanken um die Formatierung machen muss.

Ich verwende das Community Terraform Lambda Modul, um die Lambda zu definieren. Es erlaubt mir, in nur wenigen Zeilen eine Lambda zu definieren, die mit einer Rolle konfiguriert wird und auch einfach um Policies erweitert werden kann. Das Coole ist, dass cdktf eine Type-Importierung unterstützt. Dafür muss einfach in der cdktf.json-Datei folgendes Modul hinzugefügt werden:

"terraformModules": [
    {
      "name": "lambda",
      "source": "terraform-aws-modules/lambda/aws",
      "version": "~> 3.0"
    }
  ],

Anschließend wird der Befehl cdktf get ausgeführt:

cdktf get

Nun kann das Modul im cdktf-Code verwendet werden:

import { Lambda } from './../.gen/modules/lambda';
...

Falls die Lambda eigene Dependencies hat, müssen diese noch installiert werden mit:

cd src/lambda
npm install

Deploye den cdktf-Stack mit:

cdktf deploy

Code

Die Lambda kann dann zum Beispiel in die main.ts integriert werden:

import { NodejsFunction } from './constructs/nodejs-function';

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name);

    const code = new NodejsFunction(this, 'code', {
      path: path.join(__dirname, 'lambda/filter-aurora.ts'),
    });

    new Lambda(this, 'FilterAuroraEventsLambda', {
      functionName: 'filter-aurora',
      handler: 'filter-aurora.handler',
      runtime: 'nodejs14.x',
      sourcePath: code.bundledPath,
      timeout: 15 * 60,
      attachPolicyStatements: true,
      policyStatements: {
        kms: {
          effect: 'Allow',
          actions: ['*'],
          resources: ['*'],
        },
        s3: {
          effect: 'Allow',
          actions: ['s3:*'],
          resources: ['*'],
        },
      },
    });
  }
}

const app = new App();
new MyStack(app, 'cdktf-lambda');
app.synth();

Ich benutze also das Custom Construct NodejsFunction, um den Code von TypeScript in JavaScript zu bundlen und der Lambda zu zeigen, wo sie den gebundelten JavaScript-Code finden kann. Das NodejsFunction Construct sieht so aus:

import { AssetType, TerraformAsset } from 'cdktf';
import { Construct } from 'constructs';
import { buildSync } from 'esbuild';
import * as path from 'path';

export interface NodejsFunctionProps {
  readonly path: string;
}

const bundle = (workingDirectory: string, entryPoint: string) => {
  buildSync({
    entryPoints: [entryPoint],
    platform: 'node',
    target: 'es2018',
    bundle: true,
    format: 'cjs',
    sourcemap: 'external',
    outdir: 'dist',
    absWorkingDir: workingDirectory,
  });

  return path.join(workingDirectory, 'dist');
};

export class NodejsFunction extends Construct {
  public readonly asset: TerraformAsset;
  public readonly bundledPath: string;

  constructor(scope: Construct, id: string, props: NodejsFunctionProps) {
    super(scope, id);

    const workingDirectory = path.resolve(path.dirname(props.path));
    const distPath = bundle(workingDirectory, path.basename(props.path));

    this.bundledPath = path.join(
      distPath,
      `${path.basename(props.path, '.ts')}.js`,
    );

    this.asset = new TerraformAsset(this, 'lambda-asset', {
      path: distPath,
      type: AssetType.ARCHIVE,
    });
  }
}

Wie zu sehen ist, bundelt esbuild den TypeScript Code zu JavaScript Code jedesmal wenn cdktf deploy ausgeführt wird.

Fazit

TypeScript-Lambdas mit cdktf zu bauen bedarf etwas mehr Aufwand im Vergleich zu aws cdk TypeScript-Lambdas. Trotzdem hält sich dieser Aufwand in Grenzen und ich habe dir hier gezeigt, wie du es machen kannst. Falls du den Beitrag hilfreich fandest, lass es mich bitte wissen!

Ich liebe es, an Open-Source-Projekten zu arbeiten. Viele Dinge kannst du bereits frei nutzen auf github.com/mmuller88. Wenn du meine Arbeit dort und meine Blog-Posts toll findest, denke doch bitte darüber nach, mich zu unterstützen:

Buy me a Ko-Fi

Oder

Buy me a Ko-Fi

Und schau doch mal auf meiner Seite vorbei

martinmueller.dev

Tagged in de2022awscdktf

Share