terraformでaws_lambda_functionとarchive_fileを使ってLambdaをデプロイする方法がある。
data "archive_file" "lambda" { type = "zip" source_file = "lambda.js" output_path = "lambda_function_payload.zip" } resource "aws_lambda_function" "test_lambda" { filename = data.archive_file.data.archive_file.nyan.output_path.output_path function_name = "lambda_function_name" role = aws_iam_role.iam_for_lambda.arn handler = "index.handler" source_code_hash = data.archive_file.lambda.output_base64sha256 runtime = "nodejs18.x" }
しかし、node_modulesと相性が悪くて、npm i
の実行タイミングが難しかったり、terraformのCIで毎回差分が出たりする。
リソースの方のarchive_fileとnull_resourceを組み合わせれば出来るかもしれないが、archive_file (Resource)はすでに非推奨で、やり方を考えるのにも手間がかかる。
なのでzipファイルのハッシュ値はtfstateに保持しつつ、ソースコードやpackage.jsonが変わったらnpm i
とzipファイルの再作成を行うterraform providerを作った。
使い方
以下のようなファイル構成だったとして
./ |-- lambda/ | |-- index.js | |-- node_modules/ | |-- package-lock.json | `-- package.json `-- main.tf
main.tfは次のようになる。
terraform { required_providers { lambdazip = { source = "winebarrel/lambdazip" version = ">= 0.5.0" } } } data "lambdazip_files_sha256" "triggers" { files = ["lambda/*.js", "lambda/*.json"] } resource "lambdazip_file" "app" { base_dir = "lambda" sources = ["**"] excludes = [".env"] output = "lambda.zip" before_create = "npm i" triggers = data.lambdazip_files_sha256.triggers.map } resource "aws_lambda_function" "app" { filename = lambdazip_file.app.output function_name = "my_func" role = aws_iam_role.lambda_app_role.arn handler = "index.handler" source_code_hash = lambdazip_file.app.base64sha256 runtime = "nodejs20.x" } resource "aws_iam_role" "lambda_app_role" { name = "lambda-app-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } Action = "sts:AssumeRole" } ] }) } resource "aws_iam_role_policy_attachment" "lambda_app_role" { role = aws_iam_role.lambda_app_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" }
- lambdazip_file.app作成時には
npm i
が実行されてからzipファイルが作成される - その後はtriggers属性に含まれるファイルを変更した場合にzipファイルが再作成されLamadaがデプロイされる
- node_modulesやlambda.zipを削除しても、base64sha256属性は変わらないのデプロイはされない
- CIでterraformを実行する場合も、node_modulesの有無にかかわらず不必要な差分がでることはない
おまけ: Goのデプロイ
GoをLambdaにデプロイする場合は、archive_fileとnull_resourceだけで出来るかもしれないが、lambdazipを使うとよりシンプルにかけると思う。
./ |-- lambda/ | |-- go.mod | |-- go.sum | `-- main.go `-- main.tf
data "lambdazip_files_sha256" "triggers" { files = ["lambda/*.go", "lambda/go.mod", "lambda/go.sum"] } resource "lambdazip_file" "app" { base_dir = "lambda" sources = ["bootstrap"] output = "lambda.zip" before_create = "GOOS=linux GOARCH=amd64 go build -o bootstrap main.go" triggers = data.lambdazip_files_sha256.triggers.map } resource "aws_lambda_function" "app" { filename = lambdazip_file.app.output function_name = "my_func" role = aws_iam_role.lambda_app_role.arn handler = "my-handler" source_code_hash = lambdazip_file.app.base64sha256 runtime = "provided.al2023" }