ようへいの日々精進XP

よかろうもん

Terraform で Lambda@Edge を設定する個人的なテンプレート

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
}

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 の設定が行われます.

参考

以下を参考にさせて頂きました. ありがとうございました.

docs.aws.amazon.com

qiita.com

S3 Cloudfront Lambda Edgeでサブディレクトリへアクセス | STAY FREE