CDK for TerraformでTypeScriptのLambda関数をデプロイするチュートリアルをやってみた

2022.12.19

以下のCDK for TerraformでTypeScriptのLambda関数をデプロイするチュートリアルをやってみたのでレポートします。

このチュートリアルをやってみたきっかけ

以前、「TypeScriptのLambda関数をTerraformだけでデプロイする」というのをやってみました。

やりたかったことは実現できたのですが、ビルド処理を自前で書かないといけない、かつその量が多いのが煩雑だなと感じました。というのもCDKだとaws-cdk-lib.aws_lambda_nodejs内の NodejsFunction constructを使えば、ビルド処理を自前で書く必要がなくCDKがよしなにやってくれるからです。

先にこのCDKの手軽さを知っていたので、これに比べるとTerraformは見劣りするなと。

そんな中、「CDK for Terraformでやったらどんな感じになるんだろう」と漠然と興味を持っていて、ちょうどピッタリのチュートリアルを見つけたので、やってみた次第です。なお私はCDK for Terraformは今回初めて触ります。

やってみた

基本的な解説はチュートリアル内で書いてあるので割愛して、その他で気になった点を記載していきます。

Prerequisites

前述の通り今回が初CDK for Terraformだったので、まず以下を先にやりました。

Explore CDKTF application

使用するサンプルリポジトリは以下です。

このサンプルリポジトリは、TerraformStackクラスを継承したLambdaStackからインスタンスを2つ作成しています。TerraformStack子クラスのインスタンス、つまりCDKでいう「スタック」毎にTerraformでいうところの「ルートモジュール」が作成されるみたいですね。

ビルド(トランスパイル)処理はチュートリアル範囲外!

Notice that both Lambda functions contain pre-compiled dist directories, so you do not have to compile them in this tutorial.

👀

なんと、このチュートリアルではTypeScript → JavaScriptのトランスパイル処理は対象外でした。確かに/lambda-hello-name/dist//lambda-hello-world/dist/にトランスパイル後のjsファイルがありますね… 最初に書いたとおりこのあたりの処理がCDK for Terraformでどのように(どれくらい楽に)書けるのか知りたかったのでちょっとガッカリです。(※ ですが、トランスパイル処理も実装している版がこの後登場しますのでお楽しみに)

TerraformAsset construct

コード内でTerraformAsset constructというものを使用しています。

ローカルファイルをコピーしてきたり、指定したディレクトリ以下のファイル群をZIPファイルにまとめたりすることができるConstructです。今回はこれを使ってjsファイルをZIPファイルにまとめてデプロイパッケージを作成しています。

ZIPファイルを作成するというと、Terraformではarchive_fileData Sourceがあります。archive_fileを使うともちろんZIPファイル化はTerraformレイヤで行われますが、このTerraformAsset constructはその前、つまりCDKがTerraformコードを生成する前に行われます。

View application stacks

npm installコマンドで以下のようにエラーになったのでNodeのバージョンを変更しました。最初のチュートリアルがv16.13推奨だったのでそれを使うことにしました。

% npm install
npm ERR! code EBADENGINE
npm ERR! engine Unsupported engine
npm ERR! engine Not compatible with your version of node/npm: @jest/console@29.2.1
npm ERR! notsup Not compatible with your version of node/npm: @jest/console@29.2.1
npm ERR! notsup Required: {"node":"^14.15.0 || ^16.10.0 || >=18.0.0"}
npm ERR! notsup Actual:   {"npm":"8.1.4","node":"v17.2.0"}

Deploy Hello World function

cdktf deploy lambda-hello-worldコマンドを実行すると、見慣れたterraform planterraform apply時に表示されるdiffが表示されました。ただ違うのは左列にstack名が表示される点です。 stackname

cdktf deploy lambda-hello-world lambda-hello-nameのように複数のスタックをまとめてデプロイできて、その際にどのスタックの出力内容なのか識別するためにこのstack名がいるみたいですね。とはいえ出力が混ざるのはわかりにくいですが…

deploy-in-parallel

Deploy Hello Name function

特に気になる点はありませんでした。

Clean up resources

以下コマンドで2スタック同時にdestroyしてみました。

% cdktf destroy lambda-hello-world lambda-hello-name

特に問題なくdestory完了しましたが、やはり出力が混ざるのはわかりにくいですね…

Next steps

この章にてfully-integratedブランチが紹介されており、こちらでは先程チュートリアル範囲外とお伝えしたビルド(トランスパイル)処理まで含まれているようです!

チェックしていきましょう。

さきほどのコードと大きく異なる点は、NodejsFunctionというクラスのインスタンスを生成している点です。

    const helloWorld = new NodejsFunction(this, 'hello-world', {
      handler: 'index.foo',
      path: path.join(__dirname, '..', 'lambda-hello-world')
    })

    const helloName = new NodejsFunction(this, 'hello-name', {
      handler: 'index.foo',
      path: path.join(__dirname, '..', 'lambda-hello-name')
    })

このNodejsFunctionクラスは /cdktf/lib/nodejs-lambda.tsで定義されています。

クラス名が最初に紹介したCDK(≠CDK for Terraform)のNodejsFunctionと同じなので期待してしまいましたが、同じ役割を担っているわけではないようです。このリポジトリで定義されているCDK for Terraformの方のNodejsFunctionは、esbuildでts→jsへのトランスパイルを行なって、デプロイパッケージ(zipファイル)を作成するだけです。Lambda関数の定義自体はmain.ts内にありますし、デプロイパッケージのアップロード先S3バケットの定義もmain.ts内にあります。

エラー発生1

このコードでcdk deployするとエラーになりました。

% cdktf deploy lambda-hello-world                  

0 Stacks deploying     0 Stacks done     0 Stacks waiting
Usage Error: The following dependencies are not included in the stacks to run: . Either add them or add the --ignore-missing-stack-dependencies flag.

エラーメッセージに書かれている通り、--ignore-missing-stack-dependenciesフラグを追加しました。

% cdktf deploy lambda-hello-world --ignore-missing-stack-dependencies

エラー発生2

非サポートのNode.jsのバージョンということでエラーになりました。

1 Stack deploying     0 Stacks done     0 Stacks waiting
[2022-12-18T15:40:17.974] [ERROR] default - ╷
│ Error: error creating Lambda Function (1): InvalidParameterValueException: The runtime parameter of nodejs10.x is no longer supported for creating or updating AWS Lambda functions. We recommend you use the new runtime (nodejs16.x) while creating or updating functions.
│ {
│   RespMetadata: {
│     StatusCode: 400,
│     RequestID: "3edca239-bbbc-4c14-a591-e815a50be01f"
│   },
│   Message_: "The runtime parameter of nodejs10.x is no longer supported for creating or updating AWS Lambda functions. We recommend you use the new runtime (nodejs16.x) while creating or updating functions.",
│   Type: "User"
│ }
│ 
│   with aws_lambda_function.learn-cdktf-lambda,
│   on cdk.tf.json line 175, in resource.aws_lambda_function.learn-cdktf-lambda:
│  175:       }
lambda-hello-world  ╷
                    │ Error: error creating Lambda Function (1): InvalidParameterValueException: The runtime parameter of nodejs10.x is no longer supported for creating or updating AWS Lambda functions. We recommend you use the new runtime (nodejs16.x) while creating or updating functions.
                    │ {
                    │   RespMetadata: {
                    │     StatusCode: 400,
                    │     RequestID: "3edca239-bbbc-4c14-a591-e815a50be01f"
                    │   },
                    │   Message_: "The runtime parameter of nodejs10.x is no longer supported for creating or updating AWS Lambda functions. We recommend you use the new runtime (nodejs16.x) while creating or updating functions.",
                    │   Type: "User"
                    │ }
                    │ 
                    │   with aws_lambda_function.learn-cdktf-lambda (learn-cdktf-lambda),
                    │   on cdk.tf.json line 175, in resource.aws_lambda_function.learn-cdktf-lambda (learn-cdktf-lambda):
                    │  175:       }
                    │ 
                    ╵


1 Stack deploying     0 Stacks done     0 Stacks waiting
non-zero exit code 1

このブランチの最終更新は2021年6月で、その後の 2021年7月30日にNode.js 10は非推奨になり新関数作成ができなくなったようです。

mainブランチと同じ14にNode.jsのバージョンを変更して対応しました。

 new LambdaStack(app, 'lambda-hello-world', {
  path: "../lambda-hello-world/dist",
  handler: "index.handler",
- runtime: "nodejs10.x",
+ runtime: "nodejs14.x",
  stageName: "hello-world",
  version: "v0.0.2"
 });

 new LambdaStack(app, 'lambda-hello-name', {
  path: "../lambda-hello-name/dist",
  handler: "index.handler",
- runtime: "nodejs10.x",
+ runtime: "nodejs14.x",
  stageName: "hello-name",
  version: "v0.0.1"
 });

感想

CDKと同程度の楽さでCDK for TerraformでもLambda関数がデプロイできることを期待していたのですが、現状CDK for TerraformではCDKほどの高抽象度でLambda関数をデプロイすることができず、Terraformで書く場合とほぼ同じレベルで書く必要がありそうです。今後に期待ですね。