Lambda@EdgeによるCloudFront+S3 のDefault Object設定

1月 6, 2021AWS,CloudFront,Lambda,S3

概要

  • CloudFront + Amazon S3 構成の課題に、デフォルトのオブジェクト設定の対応があります。今回は、Lambda@Edge を使用して、デフォルトのオブジェクト設定を解決する方法をご紹介します。
  • Lambda@Edgeは、AWS Lambdaの拡張機能で、CloudFront が配信するコンテンツをカスタマイズする関数を実行できるサービスです。Lambda@Edgeの詳細は、AWSドキュメントを参照。

 

CloudFront+S3 構成の課題

  • CloudFrontは、ディストリビューションのURL へアクセスする際に、"index.html"の指定を省略できません。S3の静的ウェブサイトホスティングではデフォルトで"index.html"を返す動作を行いますが、CloudFrontのデフォルト設定の場合は、"index.html" を省略できずオブジェクトの名前まで指定が必要です。
  • 以下は、CloudFrontのデフォルト設定の動作です。

 

  • 上記課題の対処として、ルートURL については、ディストリビューション設定の Default Root Object に"index.html" を指定することで回避可能です。

 

  • ブラウザなどからCoudFront のルートURL にアクセスし、コンテンツが表示されるように変わったことを確認します。

  • ただし、上記に紹介した、ディストリビューション設定の Default Root ObjectはCoudFront のルートURL(例: http://d2nzmh0f3jp7c9.cloudfront.net/)にのみ機能し、サブディレクトリ(例: http://d2nzmh0f3jp7c9.cloudfront.net/subdir/)には機能しません。
  • そして、ユーザーがアクセスするすべてのURLの最後に “index.html" を付加する制約は、ユーザーエクスペリエンスとして良くありません。

 

Lambda@EdgeによるCloudFront+S3 のDefault Root Object設定

  • 次は、CloudFrontのサブディレクトリについてもデフォルトオブジェクトを設定する方法を記載します。Lambda@Edge を使用することで、リクエストの URLが / で終わっている場合に末尾に index.html を追加することができます。
  • 今回使用するLambda@Edge のコードは、AWS公式ドキュメントに記載されているスクリプトとなります。

 

前提条件

  • CloudFront + Amazon S3 構成を準備してください。ディレクトリ “subdir" に、"index.html" を準備します。なお、CloudFront + Amazon S3 構成を構築する際のポイントは、下記記事を参照ください。

 

IAM ロールの準備

  • Lambda@Edge に使用するIAMロール、ポリシーを準備します。
  • 先ず、IAMポリシーを作成します。エディタにて、後述のJSON を貼りつけます。
    • “111111111111" をAWSアカウント番号に置き換えてください。
    • “cf-replace-defaultindex" はLambdaの関数名となります。
    • “E123456789ABCD"は、CloudFront のディストリビューションID に置き換えてください。

 

  • JSONは、下記を利用ください。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:CreateServiceLinkedRole"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "lambda:GetFunction",
                "lambda:EnableReplication"
            ],
            "Resource": [
                "arn:aws:lambda:us-east-1:111111111111:function:cf-replace-defaultindex"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "cloudfront:UpdateDistribution"
            ],
            "Resource": [
                "arn:aws:cloudfront::111111111111:distribution/E123456789ABCD"
            ]
        }
    ]
}

 

  • 次に、IAMロールを作成します。ロール作成時に、先ほど作成したポリシーおよび AWS管理ポリシーの “AWSLambdaBasicExecutionRole" をアタッチします。
  • IAMロールを作成後、信頼関係の編集をします。

 

  • 信頼関係に、"lambda.amazonaws.com" の他に、"edgelambda.amazonaws.com" を追加します。以下のJSON が利用できます。
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "lambda.amazonaws.com",
          "edgelambda.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

 

Lambda関数の作成

  • Lambda@Edge の関数を作成します。Lambdaのコンソールを開き、リージョンに “バージニア北部" を選択します。
  • 関数名に、先ほどIAMポリシーに指定した名前を入力します。
  • ランタイムに、"Node.js 12.x" を選択します。
  • 実行ロールは、先ほど作成したIAMロールを選択します。

 

  • Lambdaのコードは、AWS公式ドキュメントに紹介されているスクリプトを使用します。スクリプトを貼りつけ、[デプロイ] を押します。
'use strict';
exports.handler = (event, context, callback) => {
    
    // Extract the request from the CloudFront event that is sent to Lambda@Edge 
    var request = event.Records[0].cf.request;

    // Extract the URI from the request
    var olduri = request.uri;

    // Match any '/' that occurs at the end of a URI. Replace it with a default index
    var newuri = olduri.replace(/\/$/, '\/index.html');
    
    // Log the URI as received by CloudFront and the new URI to be used to fetch from origin
    console.log("Old URI: " + olduri);
    console.log("New URI: " + newuri);
    
    // Replace the received URI with the URI that includes the index page
    request.uri = newuri;
    
    // Return to CloudFront
    return callback(null, request);

};
  • 次に、[トリガーを追加] を押し、トリガーを設定します。トリガーに “CloudFront" を選択し、[Lambda@Edge へのデプロイ] のボタンを押します。

 

  • 下記の画面が表れます。
    • ディストリビューションに、Lambda@EdgeをデプロイするCloudFront のディストリビューションID を指定します。
    • キャッシュ動作に、Lambda@Edgeと関連付ける対象の CloudFront のパスを指定します。ここで指定する値は、Behaviors のPath Pattern と一致します。特にBehavior を設定しなければ、デフォルトは"*" となります。
    • リッスンする CloudFront イベントに、"オリジンリクエスト" を選択します。

 

 

  • CloudFront のディストリビューションID, ロールの作成に問題なければ、下記のようにLambda関数が作成されます。

 

  • CloudFront で確認すると、Behavior の対象パスに下記の設定が追加されたことが分かります。

 

 

動作確認

  • あらかじめ、S3 にディレクトリ subdir を作成し、テスト用の index.html を配置します。

 

  • ブラウザを使用し、URL に CoudFront のURL + “subdir/" を付加して、アクセスします。(リクエストの URLが / で終わっていることが条件になります)
  • 以下の通り、無事にLambda@Edeg を使用して、デフォルトオブジェクトの設定ができました。

 

Lambda@Edge のログ

  • 今回、Lambda は"バージニア北部"のリージョンに配置しましたが、Lambda@Edge のログは実行された地域のリージョンにログが出力されます。例えば、東京リージョンのエッジで実行されれば、東京リージョンのCloudWatch Logsにログが出力されます。

 

 

参考資料

AWS,CloudFront,Lambda,S3

Posted by takaaki