【Terraform】TerraformでもLambdaはTypeScriptで書きたい

【Terraform】TerraformでもLambdaはTypeScriptで書きたい
この記事をシェアする

こんにちは、クラウドビルダーズのきむです。

TerraformでもLambdaをTypeScriptで書きたい!!!!!

前回のブログではAWS CDKでデプロイするLambdaをTypeScriptで実装する方法を紹介しました。

業務ではTerrafromも使うことが多いのでTerraformでもTypeScriptでLambdaを実装する方法を紹介します。

やること

やることは前回のブログとほどんと変わりません。AWS CDKを使うのがTerrafromに変わった程度です。

  • LambdaをTypeScriptで記述する
  • node_modulesをLambda Layerに含める
  • 自作関数もLambdaLayerに含める
  • Terraformでデプロイ

フォルダ構成

最終的なフォルダ構成は下記のとおりです。

.
├── README.md
├── deploy.sh
├── dist
├── esbuild.ts
├── lambda
│   ├── lambda-layer
│   │   ├── index.ts
│   │   ├── now
│   │   │   └── index.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   ├── package-lock.json
│   ├── package.json
│   └── src
│       └── index.ts
├── main.tf
├── node_modules
├── output
├── package-lock.json
├── package.json
├── run.sh
├── terraform.tfstate
├── terraform.tfstate.backup
├── tsconfig.json
└── variables.tf

esbuildの実装

LambdaやLambda Layerの内容やフォルダ構成は前回から変更無しです。tsconfig.jsonについても変更はありません。

Lambda Layerは前回同様tscによるbuildを行いますが、Lamdaにデプロイするコードはesbuildによるビルドを行います。

AWS CDKのNodejsFunctionコンストラクタではesbuildでのビルドを行なっているのでTerraformでも同様の挙動にします。

esbuildをinstallしましょう。

npm install -D esbuild

次にビルドスクリプトを作成します。

import * as esbuild from 'esbuild';

const config: esbuild.BuildOptions = {
  entryPoints: ['lambda/src/index.ts'],
  bundle: true,
  minify: false,
  sourcemap: false,
  platform: 'node',
  target: ['es2020'],
  format: 'esm',
  outfile: 'dist/src/index.mjs',
  external: ['*'],
};

esbuild.build(config);

作成したesbuild.tsをts-nodeで実行することで指定したtsファイルがmjsファイルにコンパイルされます。

AWS CDKでの実装と同様にformat: ‘esm’,external: [‘*’]でコンパイル後の可読性の向上とLayerに含まれるライブラリを除外することでパッケージサイズの縮小を行なっています。

minify: trueとすることでさらにパッケージのサイズは縮小されますが、コンパイル後のコードが短縮された形式になります。

import{now as r}from"lambda-layer";var c=async(e,n)=>r();export{c as handler};

今回はLambda上でもわかりやすい形式にしたいのでminify: falseとしています。

作成したビルドスクリプトを実行するpackage.jsonのスクリプトを追加します。

{
  "name": "my-terraform",
  "version": "1.0.0",
  "scripts": {
    "build": "tsc",
    "esbuild": "ts-node esbuild.ts"
  },
  "devDependencies": {
    "@types/aws-lambda": "^8.10.143",
    "@types/jest": "^29.5.12",
    "@types/node": "^20.14.9",
    "jest": "^29.7.0",
    "ts-jest": "^29.1.5",
    "ts-node": "^10.9.2",
    "typescript": "~5.5.3",
    "esbuild": "^0.23.1"
  },
  "dependencies": {
    "dayjs": "^1.11.12"
  }
}

Terraformの実装

それではTerraformを実装していきます。記載量も少ないのでmain.tfに全て記載しています。

terraform {
  required_version = "1.8.4"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

data "aws_iam_policy_document" "lambda" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "lambda" {
  name               = "lambda-role"
  assume_role_policy = data.aws_iam_policy_document.lambda.json
}

resource "aws_iam_role_policy_attachment" "lambda" {
  role       = aws_iam_role.lambda.name
  policy_arn = "arn:aws:iam::aws:policy/AWSLambdaExecute"
}

data "archive_file" "layer" {
  type        = "zip"
  source_dir  = "${path.module}/dist"
  output_path = "${path.module}/output/layer.zip"
  excludes    = ["src"]
}

resource "aws_lambda_layer_version" "lambda" {
  layer_name = "common-layer"

  filename         = data.archive_file.layer.output_path
  source_code_hash = data.archive_file.layer.output_base64sha256

  compatible_runtimes = ["nodejs20.x"]
}

data "archive_file" "lambda" {
  type        = "zip"
  source_dir  = "${path.module}/dist/src"
  output_path = "${path.module}/output/lambda.zip"
}

resource "aws_lambda_function" "lambda" {
  function_name    = "test-lambda"
  filename         = data.archive_file.lambda.output_path
  runtime          = "nodejs20.x"
  handler          = "index.handler"
  role             = aws_iam_role.lambda.arn
  source_code_hash = data.archive_file.lambda.output_base64sha256
  memory_size      = 128
  timeout          = 3
  layers           = [aws_lambda_layer_version.lambda.arn]
}

デプロイ

それではデプロイしてみましょう。

今回も一連の処理をdeploy.shに定義しています。

前回のものと比較するとnpm run esbuildが追加されたのとcdk deployがterraform applyに変更されています。

#!/bin/bash
set -e

rm -rf dist output
mkdir -p dist/nodejs/node_modules
cp lambda/package.json lambda/package-lock.json dist/nodejs/

pushd dist/nodejs
npm ci
popd

pushd lambda/lambda-layer
npm run build
popd

npm run esbuild # esbuildを実行

terraform apply -auto-approve

実行権限を付与してdeploy.shを実行すればデプロイ完了です。

さいごに

AWS CDKとの違いはesbuildによるビルドスクリプトの実装をする点ですが、内容も難しいものではないためサクッとできてしまいます。ぜひ活用してみてください。

この記事をシェアする
著者:きむ
フェスが好きなエンジニア