TerraformでLambdaをデプロイするときに、AWS Lambda Terraform moduleを使っていて、uvで管理しているPythonコードをLambdaでデプロイしたい人向けです。
現時点のバージョンである 7.20.1
を前提として書きます。
最初に
以下のIssueが対応されれば、公式で機能提供されるようになると思いますので、必要な人はウォッチしておくと良いかもです。
対応方針
現時点では対応されていないので commands
を上手く使って実装することにしました。
それに伴い、裏の仕様も確認する必要あったのでコードを見ていたのですが、以下の package.py
ファイルを読みながら検討すると commands
に何を渡せば良いかわかりやすいです。
対応してみる
module以下のようなディレクトリ構成を想定しています。
ディレクトリ構成
uvを利用したシンプルな構成
module/lambda/
├── code
│ ├── .python-version
│ ├── pyproject.toml
│ ├── src
│ │ └── main.py
│ └── uv.lock
└── main.tf
Pythonコード
シンプルに requests
を利用するコードです。
import requests
def handler(event: dict, context: dict) -> None:
resp = requests.get("http://checkip.amazonaws.com/")
print(f"{resp.text=}")
[project]
name = "code"
version = "0.1.0"
readme = "README.md"
requires-python = ">=3.11"
dependencies = ["requests>=2.32.3"]
[dependency-groups]
dev = ["types-requests>=2.32.0.20241016"]
Terraform
variable "s3_bucket" {
type = string
}
variable "s3_lambda_package_prefix" {
type = string
}
locals {
function_name = "lambda-python-uv"
role_name = "lambda-python-uv"
runtime = "python3.11"
}
data "aws_iam_policy_document" "this" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
resource "aws_iam_role" "this" {
name = local.role_name
assume_role_policy = data.aws_iam_policy_document.this.json
}
# Lambda のベーシック実行ポリシー(AWS 管理ポリシー)をアタッチ
resource "aws_iam_role_policy_attachment" "this" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
role = aws_iam_role.this.name
}
# コードをLambda にデプロイするためのパッケージを作成
module "package" {
source = "terraform-aws-modules/lambda/aws"
version = "7.20.1"
runtime = local.runtime
create_role = false
create_function = false
create_package = true
recreate_missing_package = false
store_on_s3 = true
s3_bucket = var.s3_bucket
source_path = [
{
path = "${path.module}/code"
commands = [
"mkdir -p ./dist/",
"cp -a ./src/ ./dist/",
"uv export --no-dev --format requirements-txt > ./dist/requirements.txt",
"uv run pip install --no-compile --no-deps --prefix= --target=./dist --requirement=./dist/requirements.txt",
":zip ./dist/",
"rm -fR ./dist/"
]
}
]
}
# Lambda 関数を作成
module "lambda" {
source = "terraform-aws-modules/lambda/aws"
version = "7.20.1"
function_name = local.function_name
handler = "src.main.handler"
runtime = local.runtime
lambda_role = aws_iam_role.this.arn
create_role = false
create_function = true
create_package = false
s3_existing_package = module.package.s3_object
recreate_missing_package = false
timeout = 60
memory_size = 256
}
ポイントとなっているのはこの部分です。
書いてあるとおりなのですが、dist
というディレクトリを作成して、そこにソースコード入れて pip
でモジュールも一緒に入れ込んでいます。ちなみにこの方法は ↑に書いた package.py
の中の方法を参考にして、uv
と組み合わせています。
commands = [
"mkdir -p ./dist/",
"cp -a ./src/ ./dist/",
"uv export --no-dev --format requirements-txt > ./dist/requirements.txt",
"uv run pip install --no-compile --no-deps --prefix= --target=./dist --requirement=./dist/requirements.txt",
":zip ./dist/",
"rm -fR ./dist/"
]
デプロイしてみる
AWS Lambda Terraform moduleを使って、pip
や poetry
使った時と同じようにPythonパッケージがフラットに入った状態でデプロイできています。
実行すると正常終了して、requests
モジュールでGlobal IPが取得できています。
コードの構成によっては調整必要な場合もあるかもしれませんが、この方法を参考に package.py
を参考に構成考えれば解決できると思います。