AWS Lambda + Terraform最小構成 2024/6

2024/06/07に公開

いつも忘れるのでメモ。

基本構成

  • ソースコードはECRにpushする(言語の差異を吸収するため)
  • タグはlatestを利用する(プログラムの変更に伴うリソースの変更が起きないようにするため)
  • main.tfのみ

main.tf

変更が必要な部分はmyをprefixに付けています。
variable使ってもよかったけど、結局リソース名にvariable使えないので置き換えて利用してください。

  • my_lambda_role
  • my_lambda_function
  • my-app
# プロバイダ
provider "aws" {
  region = "ap-northeast-1"
}

# 実行ロール
# lambda.amazonaws.comに対してAssumeRoleを許可する
resource "aws_iam_role" "my_lambda_role" {
  name = "my_lambda_role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "lambda.amazonaws.com"
        }
      }
    ]
  })

  description = "IAM Role for the lambda function"
}

# CloudWatch ログへの書き込みアクセス許可
# https://docs.aws.amazon.com/ja_jp/aws-managed-policy/latest/reference/AWSLambdaBasicExecutionRole.html
resource "aws_iam_role_policy_attachment" "lambda_policy_attachment" {
  role       = aws_iam_role.my_lambda_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# Lambda関数
resource "aws_lambda_function" "my_lambda_function" {
  function_name = "my_lambda_function"
  memory_size   = 128
  timeout       = 30
  role          = aws_iam_role.my_lambda_role.arn
  package_type  = "Image"
  image_uri     = "your-account-id.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:latest"
}

# ECRリポジトリ
resource "aws_ecr_repository" "lambda_repo" {
  name = "my-app"
}

デプロイ

# リソースの作成
terraform init
terraform apply -auto-approve

# ECRへのpush
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin account-id.dkr.ecr.us-west-2.amazonaws.com
docker build -t my-app .
docker tag my-app:latest account-id.dkr.ecr.us-west-2.amazonaws.com/my-app:latest
docker push account-id.dkr.ecr.us-west-2.amazonaws.com/my-app:latest

カスタマイズ

構造ログ

# Lambda関数
resource "aws_lambda_function" "my_lambda_function" {
  ...

  logging_config {
    log_format = "JSON"
  }
}

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function#logging_config

プライベートサブネットに配置する

RDSのようなプライベートサブネットに配置したサービスにアクセスする時に利用するため、作成のところは実際には使わないはず。

# VPC
resource "aws_vpc" "my_vpc" {
  cidr_block = "10.0.0.0/16"
}

# サブネット
resource "aws_subnet" "my_subnet" {
  vpc_id            = aws_vpc.my_vpc.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "ap-northeast-1a"
}

# インターネットゲートウェイ
resource "aws_internet_gateway" "my_igw" {
  vpc_id = aws_vpc.my_vpc.id
}

# ルートテーブル
resource "aws_route_table" "my_route_table" {
  vpc_id = aws_vpc.my_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.my_igw.id
  }
}

# ルートテーブルアソシエーション
resource "aws_route_table_association" "my_route_table_association" {
  subnet_id      = aws_subnet.my_subnet.id
  route_table_id = aws_route_table.my_route_table.id
}

# Lambdaセキュリティグループ
resource "aws_security_group" "lambda_sg" {
  vpc_id = aws_vpc.my_vpc.id

  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# Lambda関数
resource "aws_lambda_function" "my_lambda_function" {
  ...
  vpc_config {
    subnet_ids         = [aws_subnet.my_subnet.id]
    security_group_ids = [aws_security_group.lambda_sg.id]
  }
}

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function#vpc_config

Secrets Managerからシークレットを取得する

その他Lambdaからアクセスするリソースを追加する場合は、IAMロールにinline_policyを追加する形で対応すればよい。

resource "aws_secretsmanager_secret" "my_secret" {
  name = "my_secret"
}

resource "aws_iam_role" "my_lambda_role" {
  ...

  inline_policy {
    name = "my_lambda_policy"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Effect = "Allow"
          Action = [
            "secretsmanager:GetSecretValue"
          ]
          Resource = aws_secretsmanager_secret.my_secret.arn
        }
      ]
    })
  }
}

ソースコードとインフラをまとめて管理する

一番簡易なpackage_type=zipバージョン
terraform apply時にソースコードをzip化してLambdaにアップロードします。

# アーカイブ
data "archive_file" "my_app" {
  type        = "zip"
  source_dir  = "lambda.ts"
  output_path = "lambda_function.zip"
}

# Lambda関数
resource "aws_lambda_function" "my_lambda_function" {
  ...
  package_type = "Zip" # ← デフォルトなので省略可
  filename = data.archive_file.my_app.output_path
  source_code_hash = data.archive_file.my_app.output_base64sha256
  handler = "index.handler"
  runtime = "nodejs20.x"
}

Rustの場合

バイナリビルドになるため、環境の差異に注意する必要があります。

cargo lambda build --release --output-format zip
resource "aws_lambda_function" "my_lambda_function" {
  ...
  filename = "bootstrap.zip"
  runtime = "provided.al2023"
}

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/rust-package.html

Discussion