「関数が10本を超えたあたりから、誰がどのバージョンをデプロイしたか追跡できなくなった」
こういった問題はTerraformでLambdaを管理することで根本的に解決できます。関数本体・実行ロール・CloudWatchロググループをひとつのHCLで一貫して定義すれば、デプロイの差分はすべてterraform planで可視化でき、変更履歴はGitで追跡できます。
この記事では、
aws_lambda_functionを起点に、IAMロール設計・zip配布・CloudWatchログ・環境変数の扱い方まで、実務で使える設計パターンを解説します。Rocky Linux 9.4 + Terraform v1.7.5、AWS Provider v5.54で動作確認済みです。この記事のポイント
・aws_lambda_function + aws_iam_role + aws_cloudwatch_log_group を3点セットで定義するのが基本
・source_code_hashでコード変更を検出し、差分があるときだけ再デプロイを走らせる
・LambdaのIAMロールはAWSLambdaBasicExecutionRoleを起点に最小権限を追加する
・ログ保持期間をterraformで強制管理しないと無制限になり請求が膨らむ
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜTerraformでLambdaを管理するのか
AWSコンソールで手動デプロイするLambdaは「作れるが管理できない」状態になりやすいです。ZIPをアップロードして環境変数を手入力し、IAMロールをドロップダウンで選ぶ手順は一見シンプルに見えます。しかし関数が10本を超えたあたりで問題が浮上します。どの関数にどのポリシーが付いているか、本番と開発で設定が揃っているか、先週の変更が何だったか——これらが誰にも把握できなくなります。
Terraformで管理する場合、以下の点が根本的に変わります。
・コード差分がterraform planで可視化される(「何が変わるか」が事前にわかる)
・IAMロール・ポリシー・ロググループがリソースとして明示的に定義される
・Gitでの変更追跡・レビュー・ロールバックが可能になる
・新しい環境を同じコードで再現でき、開発・ステージング・本番の設定ドリフトを防げる
定義する3リソースの全体像
Lambdaを本番品質でTerraform管理するには、最低限以下の3リソースをセットで定義します。・aws_iam_role:Lambda実行ロール(LambdaサービスがSTSでAssumeRoleできる信頼ポリシー付き)
・aws_cloudwatch_log_group:ロググループ(保持期間付きで事前作成)
・aws_lambda_function:関数本体(ロールARN・ランタイム・ハンドラー・zipを指定)
この3つには依存関係があります。Lambdaはデプロイ時にロールを参照し、実行時にCloudWatch Logsへの書き込み権限が必要です。Terraformの暗黙依存(参照変数を使う)か
depends_onで作成順序を明示します。
基本的な設定手順
1. IAMロールとポリシーを定義する
Lambdaに必要な最小構成は「Lambdaサービスが引き受けられる信頼ポリシー」と「CloudWatch Logsへの書き込み権限」です。# main.tf resource "aws_iam_role" "lambda_exec" { name = "my-function-exec-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } }] }) } resource "aws_iam_role_policy_attachment" "lambda_basic" { role = aws_iam_role.lambda_exec.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" }
AWSLambdaBasicExecutionRoleはAWS管理ポリシーで、CloudWatch LogsへのCreateLogStream・PutLogEvents・CreateLogGroupの3権限を付与します。LambdaをVPC内にデプロイする場合は、さらにAWSLambdaVPCAccessExecutionRoleを追加します。S3・DynamoDBなど他のAWSサービスにアクセスが必要な場合は、AWSLambdaBasicExecutionRoleはそのままにして、追加のインラインポリシーまたは管理ポリシーをアタッチします。基本ロールを修正するのではなく、追加する設計にしてください。
2. CloudWatchロググループを事前に定義する
ロググループをterraformで定義しない場合、Lambda初回実行時に自動作成されます。しかし自動作成されたグループは保持期間が「無制限」のままになるため、ログが永久蓄積されて請求が膨らみます。必ず明示的に定義して保持期間を設定してください。resource "aws_cloudwatch_log_group" "lambda_log" { name = "/aws/lambda/my-function" retention_in_days = 30 }
/aws/lambda/{関数名}の形式に合わせます。Lambdaはこの命名規則のロググループに自動的にログを書き込みます。retention_in_daysは本番30日・開発7日程度が目安です。
3. Lambda関数本体を定義する
resource "aws_lambda_function" "my_function" { function_name = "my-function" role = aws_iam_role.lambda_exec.arn handler = "index.handler" runtime = "nodejs20.x" filename = data.archive_file.lambda_zip.output_path source_code_hash = data.archive_file.lambda_zip.output_base64sha256 environment { variables = { ENV = "production" } } depends_on = [ aws_iam_role_policy_attachment.lambda_basic, aws_cloudwatch_log_group.lambda_log, ] }
source_code_hashはzipの内容が変わったときだけ再デプロイが走るように制御します。この値がなければコードを変更してもterraform apply時に差分が検出されず、古いコードのままになることがあります。depends_onでIAMポリシーアタッチメントとロググループの作成完了後にLambdaが作成されるよう順序を明示しています。
コードをzipで配布する2つの方法
1. archive_fileで自動zip化する(開発・小規模向け)
Terraform組み込みのarchive_fileデータソースを使うと、ローカルのソースファイルをzip化してLambdaに渡す処理を一貫して行えます。data "archive_file" "lambda_zip" { type = "zip" source_dir = "${path.module}/src" output_path = "${path.module}/function.zip" }
source_dirにLambdaのソースディレクトリを指定します。terraform applyのたびにzipが再生成され、output_base64sha256でコード変更が検出されます。注意点として、大規模なプロジェクトやnode_modules込みの場合はサイズ制限(直接アップロード250MB・zip 50MB)に当たることがあります。その場合は次のS3配布に切り替えます。
2. S3からzipを配布する(本番推奨)
本番環境では、CI/CDパイプラインがビルド済みzipをS3にアップロードし、TerraformはそのS3オブジェクトを参照する形が標準的です。resource "aws_lambda_function" "my_function" { function_name = "my-function" role = aws_iam_role.lambda_exec.arn handler = "index.handler" runtime = "nodejs20.x" s3_bucket = "my-deployment-bucket" s3_key = "functions/my-function/v1.0.0.zip" source_code_hash = filebase64sha256("function.zip") depends_on = [ aws_iam_role_policy_attachment.lambda_basic, aws_cloudwatch_log_group.lambda_log, ] }
s3_bucketとs3_keyを使う場合、filenameは指定しません(同時指定不可)。CI/CDパイプラインがzipのSHA256をTerraformに渡してsource_code_hashを更新することで、コード変更時だけLambdaの更新が走ります。実際のterraform plan出力例(S3配布・コード変更時):
# 検証サーバー(Rocky Linux 9.4)での実行例 $ terraform plan Terraform will perform the following actions: # aws_lambda_function.my_function will be updated in-place ~ resource "aws_lambda_function" "my_function" { id = "my-function" ~ source_code_hash = "dGVzdA==" -> "bmV3Y29kZQ==" # (15 unchanged attributes hidden) } Plan: 0 to add, 1 to change, 0 to destroy.
source_code_hashが変化したときだけ1件の変更として検出されます。変更のないリソースには差分が出ないため、意図しないリソース変更を防げます。
環境変数と機密情報の安全な設計
Lambdaの環境変数にAPIキーやデータベースパスワードをベタ書きすることは避けてください。sensitive = trueを付けたvariableを使っても、tfstateには平文で残ります。実務では、SSMパラメータストア(SecureString)のパラメータ名だけ環境変数に渡し、Lambda実行コード内でAPIを呼び出して復号する方法が標準です。
resource "aws_lambda_function" "my_function" { # ... 省略 ... environment { variables = { # パラメータ名のみを渡す。実値はLambdaコード内でSSM APIから取得する DB_PASSWORD_PARAM = "/prod/my-function/db-password" ENV = "production" } } }
GetParameterをコード内から呼び出して復号します。もし環境変数に実値を渡すしかない場合は、aws_ssm_parameterのdata sourceを使いつつ、
sensitive = trueのoutputをterraformに設定してplan/applyのログに値が表示されないようにします。ただしtfstateへの書き込みは防げないため、S3バックエンドへのアクセス制御(KMS暗号化・IAM境界ポリシー)が前提になります。
バージョン管理とエイリアスで本番デプロイを制御する
本番Lambdaでは、publish = trueでデプロイごとにバージョン番号を発行し、エイリアスで本番・開発を分ける設計が推奨されます。resource "aws_lambda_function" "my_function" { # ... 省略 ... publish = true } resource "aws_lambda_alias" "prod" { name = "prod" function_name = aws_lambda_function.my_function.function_name function_version = aws_lambda_function.my_function.version }
function_versionにaws_lambda_function.my_function.versionを参照させると、terraform applyのたびにエイリアスが最新のバージョン番号を指すように更新されます。APIゲートウェイや他のリソースからはエイリアスのARN(
aws_lambda_alias.prod.arn)を参照するようにします。これにより、コードをデプロイして新バージョンが発行されると、エイリアス経由で自動的に新バージョンにルーティングされます。カナリアリリース(新バージョンにトラフィックを段階的に移す)が必要な場合は
routing_configブロックを追加してadditional_version_weightsでトラフィック比率を設定できます。
トラブルシュート・エラー対処
1. 権限エラー:The role defined for the function cannot be assumed
apply直後にこのエラーが出ることがあります。IAMロールの伝播遅延が原因で、ロール作成の直後にLambdaが参照しようとするとSTSのAssumeRoleがまだ有効でないケースです。Error: creating Lambda Function (my-function): operation error Lambda: CreateFunction, https response error StatusCode: 400, The role defined for the function cannot be assumed by Lambda.
depends_onで依存順序を明示することです。前述の設定例のようにaws_iam_role_policy_attachmentをdepends_onに追加すると、ロールのポリシーアタッチ完了後にLambda作成が走ります。それでも発生する場合は、aws_iam_roleを先に単独applyしてから全体をapplyする方法が有効です。
2. ロググループ衝突:ResourceAlreadyExistsException
既にLambdaを手動デプロイ済みの環境にterraformを後付けする場合に発生します。Lambda実行時に自動作成されたロググループをterraformが新規作成しようとして衝突します。Error: creating CloudWatch Logs Log Group (/aws/lambda/my-function): ResourceAlreadyExistsException: The specified log group already exists
# 検証サーバーでの実行例 $ terraform import aws_cloudwatch_log_group.lambda_log /aws/lambda/my-function aws_cloudwatch_log_group.lambda_log: Importing from ID "/aws/lambda/my-function"... aws_cloudwatch_log_group.lambda_log: Import prepared! Prepared aws_cloudwatch_log_group for import aws_cloudwatch_log_group.lambda_log: Refreshing state... [id=/aws/lambda/my-function] Import successful!
terraform planで差分(保持期間の変更など)を確認してからapplyします。
3. コードを変更したのに再デプロイされない
source_code_hashを設定していない場合、zipファイルの内容が変わってもterraformが差分を検出できません。archive_fileを使っている場合はoutput_base64sha256を、S3配布の場合はfilebase64sha256("function.zip")を必ず設定してください。また、archive_fileのsource_dirを変えずにコードファイルだけ変更した場合でも、ファイルの中身が変わっていれば新しいzipのハッシュが変わります。反映されないときはterraform plan実行前にsrcディレクトリのファイルが実際に更新されているか確認してください。
本記事のまとめ
TerraformでLambda関数をデプロイする実践設計について解説しました。| やりたいこと | リソース・設定 |
|---|---|
| Lambda実行ロールを定義する | aws_iam_role + AWSLambdaBasicExecutionRole |
| ログ保持期間を設定する | aws_cloudwatch_log_group retention_in_days = 30 |
| コードをzip化して配布する | data.archive_file または s3_bucket + s3_key |
| コード変更を検出して再デプロイする | source_code_hash = output_base64sha256 |
| 機密情報を安全に渡す | SSMパラメータ名のみを環境変数に渡す設計 |
| バージョン管理とエイリアスを使う | publish = true + aws_lambda_alias |
depends_onで作成順序を制御する——この設計パターンを押さえることで、手動デプロイでは追跡できなかったインフラ差分をGitとterraform planで管理できるようになります。関数が増えてもコードは同じ構造で再現でき、IAM権限やログ保持期間がドリフトすることもなくなります。まず1関数をTerraform管理下に置いてみてください。
Terraformマニュアルを無料で受け取る >>
・Terraform入門|Infrastructure as CodeでAWSインフラをコード化する基礎ハンズオン
・TerraformでAWS VPCとEC2を構築する方法|HCL記法とterraformコマンドの実践
・TerraformでIAMポリシーをコード管理する方法|aws_iam_policy_documentで最小権限を宣言的に書く
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:TerraformでRDSをプロビジョニングする方法|aws_db_instanceとサブネットグループ・パラメータグループの実践設計
- この記事の属するカテゴリ:Terraformへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます