tl;dr
個人的に Lambda のデプロイは Serverless Framework 一択のつもりだったけど, Terraform で CloudFront を作っておいて, Lambda@Edge だけ Serverless Framework でデプロイするというのは違うかな...と思って Terraform でデプロイすることにしたのでメモしておきます.
尚, Lambda@Edge の用途としては, サイトのサブディレクトリで Directory Index を実現したいと思います.
Lambda 関数を書き散らす
以下のような超シンプルな Lambda 関数を書きます.
'use strict'; exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const current_uri = request.uri; let replace_uri = ''; if (current_uri != '/index.html') { replace_uri = current_uri.replace(/\/$|$/, '\/index.html'); } else { replace_uri = current_uri; } request.uri = replace_uri; return callback(null, request); };
この関数を適当なディレクトリを作成して index.js
というファイル名で保存します. 今回は,
lambda-at-edge/src/index.js
というパスとファイル名で保存しています.
尚, この Lambda 関数が適用されると, 以下のようなリクエストに対して, サブディレクトリ配下の /index.html
にパスを書き換えます.
https://example.com/foo -> https://example.com/foo/index.html https://example.com/foo/ -> https://example.com/foo/index.html
また, ルートの場合には, CloudFront の Default Root Object の設定がよしなに捌いてくれます.
https://example.com -> https://example.com/index.html https://example.com/ -> https://example.com/index.html
Lambda@Edge を設定する
必要なリソースとしては以下の通り.
- IAM Role (ポリシーはマネージドポリシーの
arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
を付与) - Lambda ファンクション
これらデプロイする Terraform コードのサンプルは以下の通り. 尚, Terraform のバージョンは 0.12.x を利用していて, 且つ, Terraform workspace で開発環境, 本番環境を分けて apply することを想定しています.
provider "aws" { region = "us-east-1" alias = "virginia" } resource "aws_iam_role" "lambda-edge" { name = "lambda-edge-${terraform.workspace}" assume_role_policy = data.aws_iam_policy_document.lambda-assume-role.json } data "aws_iam_policy_document" "lambda-assume-role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = [ "lambda.amazonaws.com", "edgelambda.amazonaws.com" ] } } } resource "aws_iam_role_policy_attachment" "lambda-edge-basic-role" { role = aws_iam_role.lambda-edge.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" } data "archive_file" "lambda-edge-function" { type = "zip" source_dir = "lambda-at-edge/src" output_path = "lambda-at-edge/dst/index_access.zip" } resource "aws_lambda_function" "lambda-edge-function" { provider = aws.virginia filename = data.archive_file.lambda-edge-function.output_path function_name = "subdir-redirect-function-${terraform.workspace}" role = aws_iam_role.lambda-edge.arn handler = "index.handler" source_code_hash = data.archive_file.lambda-edge-function.output_base64sha256 runtime = "nodejs12.x" publish = true memory_size = 128 timeout = 3 }
- Lambda@Edge はバージニアリージョン (
us-east-1
) にデプロイする必要がある為,providor
の指定でaws.virginia
を指定 source_code_hash
はソースコードの変更を検知する為, アーカイブの Base64 を SHA256 でハッシュした値を設定しているpublish = true
にすることで関数にバージョンが付与される, Lambda@Edge の関数にはバージョン番号が付与されている必要がある
CloudFront ビヘイビアに Lambda@Edge を設定
あとは, CloudFrontビヘイビアに Lambda@Edge を設定します.
resource "aws_cloudfront_distribution" "aws_cloudfront_distribution" { ... 略 ... default_cache_behavior { ... 略 ... lambda_function_association { event_type = "origin-request" lambda_arn = aws_lambda_function.lambda-edge-function.qualified_arn include_body = false } } ... 略 ... }
上記の例では, デフォルトのキャッシュビヘイビアに設定しています.
あとは, terraform apply
で CloudFront に Lambda@Edge の設定が行われます.
参考
以下を参考にさせて頂きました. ありがとうございました.