Lambda@EdgeはLambdaをCloudFrontのエッジロケーションで実行するもので、CloudFrontで受けたアクセスに対して何かしらの処理を実行するのに安くて便利な方法です。
今回はLambda@Edgeを使ってリダイレクトやフォワードするための方法を紹介します。
リダイレクトやフォワードの為にわざわざApacheサーバーなどを用意しなくてよいですし、Lambdaを使うことで(常時サーバー立ち上げが必要ないので)非常に安く済みます。
手段の一つとして覚えておき活用してもらえたら嬉しく思います。

(クリックすると画像が拡大されます。)
本記事ではLambda@Edgeの基本的な構築の流れを1から解説しています。
既に知っている場合は飛ばしながら読んでください。
アーキテクチャ構築の流れ
さっそく構築手順を紹介していきます。
構築の流れは以下のようになります。
1. CloudFrontを構築
1-1. ダミーオリジンの作成
1-2. CloudFrontの構築
2. Lambda@Edgeを構築
2-1. Lambda@Edge用のIAM作成
2-2. Lambdaの構築
2-3. トリガーの設定
まず紹介する構築の流れではリダイレクト用のLambdaファンクションコードを紹介しますが、
フォワード用のソースコードやパスを任意で変えるためのソースコードなども後から紹介します。
Lambda@Edgeのアーキテクチャ構築の流れは変わらないので、Lambdaファンクションのコードを変えれば実行する処理を変えることができます。
また、Lambda@Edge構築のポイントは以下となります。このポイントだけでも押さえておくとよいと思います。
- Lambda@Edge用のIAMロールを作成し、信頼関係を追加する必要がある
- Lambda@EdgeのLambdaファンクションはバージニア北部リージョンに作成する
- Lambdaファンクションから特定のバージョンを発行してトリガー設定が必要になる
- LambdaのトリガーをCloudFrontにすることでLambda@Edgeとなる
CloudFrontを作成
ダミーオリジンの作成
まずはCloudFrontを構築するためのダミーオリジン用のS3バケットを作成していきます。
CloudFrontは必ずオリジンを設定しなければならないため、オリジン用として空のS3バケットを用意しておけばOKです。
S3コンソール画面の「バケット」から「バケットを作成」をクリックします。

(クリックすると画像が拡大されます。)
「バケット名」だけ入力し「バケットを作成」をクリックします。
バケットの各種設定はデフォルトから変更する必要はありません。

(クリックすると画像が拡大されます。)
CloudFrontの構築
ダミーバケットが作成出来たらCloudFrontの構築を行います。
CloudFrontコンソール画面の「ディストリビューション」から「ディストリビューションを作成」をクリックします。

(クリックすると画像が拡大されます。)
「オリジンドメイン」だけ先ほど作成したダミーバケットを指定し「ディストリビューションを作成」をクリックします。
CloudFrontの各種設定はデフォルトから変更する必要はありません。

(クリックすると画像が拡大されます。)
ここまででLambda@Edgeを実行するCloudFrontが構築できました。
Lambda@Edgeを構築
実行用のCloudFrontができたので、続けてLambda@Edgeを構築していきます。
Lambda@Edge用のIAM作成
Lambda@Edgeでは通常のLambda用IAMロールから「信頼関係」などを変更したIAMロールを使用する必要があります。
まずはLambda@Edge用のIAMロールを作成していきます。
IAMコンソール画面の「ロール」から「ロールを作成」をクリックします。

(クリックすると画像が拡大されます。)
信頼されたエンティティを選択では、「AWSのサービス」から「Lambda」を選択し、「次へ」をクリックします。

(クリックすると画像が拡大されます。)
許可を追加では、検索窓に「lambda」と入力し、「AWSLambdaRole」を選択して「次へ」をクリックします。

(クリックすると画像が拡大されます。)
名前、確認、および作成では、「Role名」を入力して「ロールを作成」をクリックします。

(クリックすると画像が拡大されます。)
このままではデフォルトのLambda用IAMロールと変わらないので、Lambda@Edge用に信頼関係を追加していきます。
IAMコンソール画面の「ロール」から先ほど作成したIAMロールを選択し、「信頼関係」タブの「信頼ポリシーを編集」をクリックします。

(クリックすると画像が拡大されます。)
信頼関係に「edgelambda.amazonaws.com」を追加してください。
追加する位置は以下のオレンジ箇所になります。

(クリックすると画像が拡大されます。)
ここまでで、Lambda@Edgeを実行できるIAMロールはできました。
ここからはCloudWatch Logsにログを出力できるようにさらにIAMポリシーを追加していきます。
IAMコンソール画面の「ポリシー」から「ポリシーを作成」をクリックします。

(クリックすると画像が拡大されます。)
IAMポリシー作成画面ではビジュアルエディタでサービスを「CloudWatch Logs」を選択し、アクションに「CreateLogGroup」、 「CreateLogStream」 、 「PutLogEvents」を追加します。
log-groupとlog-streamの対象を設定する必要があるので、「ARNの追加」から設定します。
log-groupは「Region:すべて」、「Log group name:すべて」とします。
log-streamは「Region:すべて」、「Log group name:すべて」 、「Log stream name:すべて」とします。
設定出来たら「次のステップ:タグ」をクリックします。

(クリックすると画像が拡大されます。)
タグは特に必要ないので「次のステップ:確認」をクリックします。

(クリックすると画像が拡大されます。)
「名前」を任意で入力し「ポリシーの作成」をクリックします。

(クリックすると画像が拡大されます。)
IAMポリシーを作成出来たら、IAMコンソール画面の「ロール」から作成したLambda@Edge用ロールを選択して「許可」の「アクセス許可を追加」から先ほど作成した「ポリシーをアタッチ」します。

(クリックすると画像が拡大されます。)
ここまでで、Lambda@Edgeを実行するためのIAMロール作成が完了しました。
Lambdaの構築
ここまで来たらいよいよLambdaを構築していきます。
Lambda@Edgeを作成するうえでのポイントはLambdaを「バージニア北部リージョンに構築すること」です。
CloudFrontはグローバルリージョンに作成されるのでCloudFrontをトリガーにできる設定できるのはバージニア北部リージョンのLambdaのみです。
Lambdaコンソール画面の「関数」から「バージニア北部リージョン」を選択したうえで「関数の作成」をクリックします。

(クリックすると画像が拡大されます。)
「関数名」を任意で入力し、ランタイムを「Python3.8」にします。
デフォルトの実行ロールの変更から「既存のロールを使用する」を選択し、「作成したIAMロール」を入力して「関数の作成」をクリックします。

(クリックすると画像が拡大されます。)
続いてLambdaファンクションのコードを入力していきます。
今回のコードはリダイレクト用のコードです。フォワードなどのコードは後述で紹介します。
また、オレンジの箇所は任意に変更して使用するとよいです。

(クリックすると画像が拡大されます。)
def lambda_handler(event, context):
#Definition
redirectProtocol = "https"
redirectDomain = "xxxx.yyyyy.com"
request = event['Records'][0]['cf']['request']
response = {
'status': '302',
'statusDescription': 'Found',
'headers': {
'location': [{
'key': 'Location',
'value': redirectProtocol+'://' + redirectDomain + request['uri']
}]
}
}
return response
トリガーの設定
ここまで来たら、後はトリガーとしてCloudFrontを設定すればOKです。
ただし、トリガーをCloudFrontとして設定するためには「特定のバージョンとして発行する必要がある」ことがポイントとなります。
そのため、まずはバージョンの発行から行っていきます。
作成したLambdaファンクションの「バージョン」タブから「新しいバージョンを発行」をクリックします。

(クリックすると画像が拡大されます。)
$LATESTから新しいバージョンを発行します。では、「バージョン名を任意に入力」し「発行」をクリックすればOKです。

(クリックすると画像が拡大されます。)
これで新しいバージョンが発行できたので、あとはCloudFrontをトリガーとして設定していきます。
Lambdaファンクション画面の「バージョン」タブから「発行したバージョンをクリック」し、バージョン毎の画面に移動します。

(クリックすると画像が拡大されます。)
バージョンの画面であることを確認し、「トリガーを追加」をクリックします。

(クリックすると画像が拡大されます。)
トリガーを追加では「CloudFront」を選択し、「先ほど作成したディストリビューションを選択」します。
lambda@Edgeへのデプロイを確認に「チェック」して「追加」をクリックします。

(クリックすると画像が拡大されます。)
以上でLambda@Edgeの構築がすべて完了しました。
これでCloudFrontにアクセスすればリダイレクトされるようになっているはずです。
CloudFrontコンソール画面のディストリビューションで設定したディストリビューションを見るとドメイン名が確認できるので、ドメイン名にアクセスして挙動を確認してみてください。
(ただし、次項の「Lambda@Edge使用の注意点」に注意してみてください。)

(クリックすると画像が拡大されます。)
Lambda@Edge使用の注意点
CloudFrontは世界中のエッジロケーションを使用しています。
このため、CloudFrontの設定変更などはエッジロケーションへの設定伝搬を待つ必要があります。
例えば、Lambda@Edgeを設定してから実際に動作するまでタイムラグがあります。
また、Lambda@Edgeのコードを変更しても実際に適用されるまでタイムラグがあります。
こちらのホワイトペーパーにも以下のように記述があります。
•トリガーを追加するなど、CloudFront ディストリビューションを更新した後で、トリガーで指定した関数が機能する前に、変更がエッジロケーションに伝達されるのを待つ必要があります。
Lambda@Edgeのコード紹介
ここまではリダイレクトするためのコードを紹介していましたが、
URL欄を変更せず表示だけ転送したい場合はフォワード用のコードを使用すれば実現できます。
また、パスを変えるためのサンプルコードもあるので、近い用途のモノを変更して使用してみてください。
Lambdaファンクションのコードを以下で紹介するコードに変更すれば問題ありません。ほかの構築手順はすべて上記で説明した通りです。
Lambda@Edgeでフォワードする
URL欄の内容を変えず表示する中身だけ変える(フォワードする)ためのコードです。
オレンジの箇所を任意に変更して使用します。
def lambda_handler(event, context):
#Definition
redirectDomain = "xxx.yyy.com"
request = event['Records'][0]['cf']['request']
request['headers']['host'][0]['value'] = redirectDomain
return request

(クリックすると画像が拡大されます。)
Lambda@Edgeでサブディレクトリを削除してリダイレクトする
以下のようにサブディレクトリを一つ削除してリダイレクトするコードです。
オレンジの箇所を任意に変更して使用します。

(クリックすると画像が拡大されます。)
def lambda_handler(event, context):
#Definition
redirectDomain = "xxxx.yyyyy.com"
request = event['Records'][0]['cf']['request']
subDirArray = request['uri'].split("/")
del subDirArray[0:2]
redirectSubDir = ""
for val in subDirArray:
redirectSubDir = redirectSubDir + "/" + val
response = {
'status': '302',
'statusDescription': 'Found',
'headers': {
'location': [{
'key': 'Location',
'value': redirectDomain + redirectSubDir
}]
}
}
return response

(クリックすると画像が拡大されます。)
Lambda@Edgeでサブディレクトリを追加してフォワードする
以下のようにサブディレクトリを一つ追加してフォワードするコードです。
オレンジの箇所を任意に変更して使用します。

(クリックすると画像が拡大されます。)
def lambda_handler(event, context):
#Definition
redirectDomain = "xxx.yyy.com"
subDir = "zzzz"
request = event['Records'][0]['cf']['request']
request['headers']['host'][0]['value'] = redirectDomain
request['uri'] = "/" + subDir + request['uri']
return request

(クリックすると画像が拡大されます。)
以上です。本記事が参考になったという方、似たようなAWSの使い方ハンズオンを以下のサイトにまとめています。他のAWSサービスの使い方の参考に覗いてみてください。
