0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【初心者向け】TerraformでLambda LayerとLambda関数をデプロイしてみよう

0
Last updated at Posted at 2026-07-16

どうもこんにちは。

今回はTerraformを使って、PythonのLambdaレイヤーと、そのレイヤーを使うLambda関数をまとめてデプロイしてみます。

Lambdaレイヤーは、依存ライブラリや共通処理を関数本体から分けられる便利な仕組みですが、最初は「ZIPはどこで作るのか」「S3を用意すべきか」「レイヤーを関数にどう設定するのか」が少し分かりにくいです。

この記事では、ローカルのソースコードをTerraformでZIP化し、レイヤーとLambda関数を同時に作る最小構成を扱います。検証済みのコードは本プロジェクトにあります。

この記事は誰向け?

この記事は以下のような方向けに書いています。

  • Terraformを初めて使う方
  • LambdaレイヤーをTerraformでデプロイしたい方
  • Pythonの共通処理を複数Lambdaで使い回したい方
  • STGと本番で同じ構成を使いたい方
  • TerraformとAWS CDKのどちらを選ぶか迷っている方

Githubリポジトリ

この検証でやりたいこと

今回のゴールは次の4点です。

  • TerraformでLambdaレイヤーを作る
  • 作成したレイヤーをLambda関数へ関連付ける
  • レイヤーと関数の単体テスト、およびTerraformの構成テストを実行する
  • STG/本番へ同じコードから安全にデプロイできるようにする

構成は次のとおりです。

app.pyfrom shared.message import greetingとしてレイヤーのモジュールをimportします。LambdaのPythonレイヤーはpython/配下のファイルを/opt/pythonに展開するため、このディレクトリ構成にしています。

前提

今回の環境は次のとおりです。

  • Terraform 1.7以上
  • Python 3以上(単体テスト用)
  • AWS CLIとAWS認証情報
  • Lambda、Lambdaレイヤー、IAM Roleを作成・更新できるIAM権限
  • デプロイリージョン: ap-northeast-1

AWS認証情報は、ローカルアクセスキーをソースコードへ書かず、AWS CLIのプロファイルまたはIAM Identity Center (SSO)を使うのがおすすめです。

aws configure sso
aws sso login --profile my-profile
export AWS_PROFILE=my-profile

0. プロジェクト構成

最終的な構成は以下です。

.
├── main.tf
├── variables.tf
├── versions.tf
├── outputs.tf
├── Makefile
├── src/
│   ├── function/
│   │   └── app.py
│   └── layer/
│       └── python/
│           └── shared/
│               ├── __init__.py
│               └── message.py
└── tests/
    ├── lambda.tftest.hcl
    ├── test_app.py
    └── test_layer.py

src/layer/の下のpythonディレクトリが重要です。
Pythonランタイムでレイヤーを使う場合、この階層でZIP化する必要があります。

レイヤーで指定したロジックは、app.pyなどの呼び出し元のメインファイルでimport shared.messageと記述することで動きます。

1. Terraformをインストールする

macOSではHashiCorp公式のHomebrew tapからインストールできます。

brew tap hashicorp/tap
brew install hashicorp/tap/terraform
terraform version

Linux/Windowsのインストール方法は、Terraform公式ドキュメントを参照してください。

Terraformは最初にterraform initを実行します。このコマンドは、AWSを操作するhashicorp/awsやZIPを作るhashicorp/archiveのようなProviderをダウンロードします。

terraform init

初期化後に作られる.terraform.lock.hclはProviderのバージョンを固定するファイルです。チームやCIで同じバージョンを使うため、Git管理対象に入れます。一方、.terraform/terraform.tfstate、生成したbuild/はGit管理しません。

2. Terraformは何をしているのか

Terraformは、HCLという設定言語で「AWS上にどういう状態を作りたいか」を書くIaCツールです。

今回なら次の流れでコマンドや操作を実行していきます。

基本的なデプロイフローとして、terraform planで「何が作成・変更・削除されるか」を確認してから、terraform applyするのが基本です。

terraform.tfstateにはリソースの対応関係や値が保存されるため、漏えいしない場所に保管します。

3.レイヤーとLambdaのZIPを作る

Terraformのarchive_fileデータソースを使うと、外部のZIP作成スクリプトなしでディレクトリをZIPにできます。

main.tfではレイヤー用と関数用にそれぞれZIPを作ります。

main.tf
locals {
  layer_name = "${var.function_name}-shared"
}

data "archive_file" "layer" {
  type        = "zip"
  source_dir  = "${path.module}/src/layer"
  output_path = "${path.module}/build/${local.layer_name}.zip"
}

data "archive_file" "function" {
  type        = "zip"
  source_dir  = "${path.module}/src/function"
  output_path = "${path.module}/build/${var.function_name}.zip"
}

source_code_hashにZIPのハッシュを渡すことで、ソースコードが変わったときにTerraformが更新を検知できます。

main.tf
source_code_hash = data.archive_file.layer.output_base64sha256

S3を使うべき?

今回のような小さなレイヤーと関数なら、TerraformからLambda APIへ直接ZIPを送る方式を選びます。S3バケット、アップロード権限、ライフサイクル管理が不要で、構成がシンプルだからです。

次の場合はS3を使う構成を検討します。

  • ZIPが直接アップロードの上限(50 MB)を超える
  • CIで作った同じ成果物を複数環境・複数プロジェクトへ配布したい
  • 成果物を長期間保管し、デプロイ元を明確にしたい
  • 依存ライブラリが大きく、レイヤーをビルド専用環境で作る必要がある

特にネイティブ依存を含むPythonレイヤーは、Lambdaと同じLinux環境でビルドする必要があります。この場合はCIやDockerでZIPを作り、S3へ置く方式が扱いやすくなります。

4. Lambdaレイヤーをデプロイする

レイヤーはaws_lambda_layer_versionで作成します。

main.tf
resource "aws_lambda_layer_version" "shared" {
  layer_name          = local.layer_name
  description         = "Shared Python utilities for ${var.function_name}."
  filename            = data.archive_file.layer.output_path
  source_code_hash    = data.archive_file.layer.output_base64sha256
  compatible_runtimes = [var.runtime]

  lifecycle {
    create_before_destroy = true
  }
}

レイヤーはバージョン付きで公開されます。create_before_destroyを付けると、新しいレイヤーバージョンを公開してから古いものを破棄する順序になります。

レイヤーの中身は次のシンプルな共通関数です。

src/layer/python/shared/message.py
def greeting(name: str) -> str:
    normalized_name = name.strip() or "world"
    return f"Hello, {normalized_name}!"

5. Lambda関数をデプロイし、レイヤーを設定する

Lambdaが実行するときのIAM RoleもTerraformで作ります。今回はCloudWatch Logsへの出力だけが必要なので、AWS管理ポリシーAWSLambdaBasicExecutionRoleを付与しています。

main.tf
resource "aws_iam_role_policy_attachment" "basic_execution" {
  role       = aws_iam_role.lambda.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

関数側ではlayersに、作成したレイヤーのARNを渡します。

main.tf
resource "aws_lambda_function" "app" {
  function_name    = var.function_name
  filename         = data.archive_file.function.output_path
  source_code_hash = data.archive_file.function.output_base64sha256
  handler          = "app.lambda_handler"
  role             = aws_iam_role.lambda.arn
  runtime          = var.runtime
  layers           = [aws_lambda_layer_version.shared.arn]

  depends_on = [aws_iam_role_policy_attachment.basic_execution]
}

layers = [aws_lambda_layer_version.shared.arn]が、レイヤーと関数をつなぐ行です。レイヤーARNが変わればTerraformは関数側の設定も更新します。

関数はレイヤーのモジュールをimportしています。

src/function/app.py
import json

from shared.message import greeting


def lambda_handler(event, _context):
    name = event.get("name", "world")
    return {
        "statusCode": 200,
        "headers": {"content-type": "application/json"},
        "body": json.dumps({"message": greeting(name)}),
    }

ここまでできたら、実際の変更を確認してデプロイします。

terraform fmt -recursive
terraform validate
terraform plan
terraform apply

デプロイ後はAWS CLIから呼び出せます。

aws lambda invoke \
  --function-name "$(terraform output -raw lambda_function_name)" \
  --cli-binary-format raw-in-base64-out \
  --payload '{"name":"Terraform"}' \
  response.json

cat response.json

レスポンス内のmessageHello, Terraform!なら、Lambda関数がレイヤーの関数を呼べています。

6. テストを実行する

このプロジェクトには2種類のテストがあります。

テスト コマンド 確認すること
Python単体テスト make test レイヤーの関数そのもの、およびLambdaハンドラのレスポンス
Terraformテスト terraform test Lambdaのlayersに公開したレイヤーARNが設定されること

Pythonテストは標準ライブラリのunittestだけを使います。

make test

tests/test_layer.pyはレイヤーのgreeting関数を直接テストします。名前の整形、空白の除去、空文字時の既定値を確認しています。

tests/test_app.pyはレイヤーと同じPythonパスを通してLambdaハンドラを読み込み、レイヤーをimportできることも確認します。

TerraformテストはモックAWS Providerを使うため、AWS認証情報も実リソース作成も不要です。

terraform test

CIでは、最低でも以下をpull requestごとに実行すると安心です。

terraform fmt -check -recursive
terraform validate
terraform test
make test

7. STGと本番を分けてデプロイする

環境を分けるときに重要なのは、リソース名Terraform stateを両方分けることです。同じstateをSTGと本番で共有すると、片方のapplyがもう片方の構成を変更してしまいます。

まず環境別の変数ファイルを用意します。

# environments/stg.tfvars
aws_region    = "ap-northeast-1"
function_name = "terraform-layer-demo-stg"

tags = {
  Environment = "stg"
  ManagedBy   = "Terraform"
}
# environments/prod.tfvars
aws_region    = "ap-northeast-1"
function_name = "terraform-layer-demo-prod"

tags = {
  Environment = "prod"
  ManagedBy   = "Terraform"
}

小さな構成では、Terraform workspaceを使ってstateを分離できます。

# STG
terraform workspace new stg
terraform workspace select stg
terraform plan -var-file=environments/stg.tfvars
terraform apply -var-file=environments/stg.tfvars

# 本番
terraform workspace new prod
terraform workspace select prod
terraform plan -var-file=environments/prod.tfvars
terraform apply -var-file=environments/prod.tfvars

ローカルbackendでもworkspaceごとにstateは分かれます。ただしこれは導入しやすい最小構成です。チームや本番運用では、stateをローカルに置かず、アクセス制御・暗号化・ロックを設定したリモートbackendを使います。環境ごとにAWSアカウントや権限が大きく異なる場合は、workspaceだけに頼らず、環境ごとのディレクトリ・バックエンド設定・CI/CDパイプラインも分けます。

さらに安全性を上げるなら、STGと本番でAWSアカウント自体を分け、AWS_PROFILEも分けます。

AWS_PROFILE=my-stg-profile terraform apply -var-file=environments/stg.tfvars
AWS_PROFILE=my-prod-profile terraform apply -var-file=environments/prod.tfvars

本番では次のようなことを運用ルールにすると事故を減らせます。

  • terraform apply前に保存済みのterraform plan -out=tfplanをレビューする
  • 本番のapplyはCI/CDからだけ実行する
  • Terraform stateをGitに入れない
  • IAM権限をSTG/本番で分離する
  • 本番アカウントへの人手の管理者アクセスを最小化する

8. TerraformとAWS CDKはどちらを選ぶ?

AWS CDKもTerraformもInfrastructure as Codeのツールです。ただしデプロイの仕組みと得意な場面が異なります。

観点 Terraform AWS CDK
記述言語 HCL TypeScript、Python、Java、C#、Goなどから自由に選択
デプロイ先 AWS、Azure、GCP、SaaSなど複数Provider AWS
デプロイ基盤 Terraform stateを使いProviderがAPIを操作 CloudFormationテンプレートを生成してデプロイ
再利用 Module Construct
AWSの抽象化 リソースを比較的そのまま記述 高レベルConstructによる便利な既定値がある
導入時の準備 Provider初期化、state設計 CDK CLI、言語ランタイム、通常はbootstrap

Terraformを選びやすい

Terraformを選びやすいのは、次のような場合かなと思います。

  • AWS以外のクラウドやGitHub、Datadogなども同じIaCで管理したい
  • HCLでインフラ設定を宣言的に読みやすく管理したい
  • 既にTerraform Moduleや運用基盤がある

AWS CDKを選びやすい

AWS CDKを選びやすいのは、次のような場合です。

  • AWS専用のシステムで、アプリと同じTypeScript/Pythonで書きたい
  • Constructを使って、複数AWSリソースの定型構成を短く書きたい
  • CloudFormationのスタック管理やロールバックを中心に運用したい

今回のようなLambdaとレイヤーの最小構成なら、どちらでも十分に実装できます。マルチクラウドや複数SaaSをまたぐ構成ならTerraform、AWSのConstructを積極的に使いたいアプリチームならCDK、という基準から始めると選びやすいです。

まとめ

今回はTerraformでLambdaレイヤーとLambda関数をデプロイしました。

  • archive_fileでソースディレクトリをZIP化できる
  • 小さなZIPはTerraformから直接デプロイするとシンプル
  • aws_lambda_layer_version.shared.arnをLambdaのlayersに設定する
  • レイヤー単体、Lambda単体、Terraformの配線を分けてテストできる
  • 環境別tfvarsとworkspaceでSTG/本番のリソース名とstateを分離する
  • 本番ではリモートstate、CI/CD、別AWSアカウントを組み合わせる

レイヤーに外部ライブラリを追加する、EventBridgeやAPI GatewayからLambdaを呼ぶ、といった構成にもこのまま発展させられます。

参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?