LoginSignup
1
0

CloudFront + Lambda@Edgeを利用して、user-agentに応じて異なるwebページを表示させる

Last updated at Posted at 2024-01-21

CloudFront + S3でウェブページを表示する仕組みは以前作ったことがありますが、
AWS上の仕組みを使ってスマートフォンとPCからのアクセスで、異なるwebページを表示させることってできるのかな?」と思ったので、実践してみました。

目標

・スマートフォンからのアクセス時は「スマートフォン専用サイト」
・PCからのアクセス時は「PC専用サイト」を表示させる

条件

・user-agentに「iPhone」「Android」が含まれている場合にスマートフォン、含まれていない場合はPCからのアクセスと判断する
・表示させるのはS3に配置したhtmlファイル

構成

image.png

流れ

①S3に2つのhtmlファイルを配置する
②Lambda@Edgeを作成する
③CloudFrontのディストリビューションを作成する
④S3に権限を付与する
⑤Lambda実行ロールに権限を付与する
⑥CloudFrontのディストリビューションにLambda@Edgeを紐づける

作業

①S3に2つのhtmlファイルを配置する

1-1. S3バケット「example-user-agent-site」を作成する

なお、セキュリティ観点からアクセスは「非公開のバケットとオブジェクト」とし、「パブリックアクセスはすべてブロック」で設定する。
image.png

1-2. ファイルを配置する

構成は以下となる

-example-user-agent-site/
 ├ pc.html
 └ smartphone.html

なお、配置するファイルはuser-agentが表示されるようなものとしました。
※こちらの記事を参考に記載しております
ユーザーエージェントを取得する(navigator.userAgent)

pc.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PC用画面</title>
</head>
<body>

<h1>PC専用画面</h1>

<p id="msg">これはPC専用の画面です。user-agentに「iPhone」もしくは「Andrond」を含まない場合のみ表示されます</p>

<script>
let element = document.getElementById('msg');
element.insertAdjacentHTML('afterend', '<p>' + navigator.userAgent + '</p>');
</script>


</body>
</html>
smartphone.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PC用画面</title>
</head>
<body>

<h1>スマートフォン専用画面</h1>

<p id="msg">これはスマートフォン専用の画面です。user-agentに「iPhone」もしくは「Andrond」を含む場合のみ表示されます</p>

<script>
let element = document.getElementById('msg');
element.insertAdjacentHTML('afterend', '<p>' + navigator.userAgent + '</p>');
</script>


</body>
</html>

②Lambda@Edgeを作成する

2-1.Lambdaを作成する

まずは通常のLambdaを作成します
注意点としては、Lambdaはリージョンを「米国東部 (バージニア北部) us-east-1」で作成する必要があります。

項目 設定値
関数名 check-userAgent
ランタイム Node.js 20.x
アーキテクチャ x86_64
実行ロール 新規作成

2-2.コード記載する

ユーザーエージェントにiphoneとandroidが含まれている場合はスマートフォン用、含まない場合はPC版のURLとする記載をする。
※注意点として、既存のファイル名は「index.mjs」となっているが、形式がmjsだとエラーとなる。ファイルは拡張子を変更して「index.js」とする。

index.js
'use strict';

exports.handler = async (event) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    // ユーザーエージェントに基づいて異なるS3オブジェクトにリダイレクトする
    const userAgent = headers['user-agent'] ? headers['user-agent'][0].value.toLowerCase() : '';

    if (userAgent.includes('iphone') || userAgent.includes('android')) {
        request.uri = '/smartphone.html';
    } else {
        request.uri = '/pc.html';
    }

    return request;
};

2-3.バージョンを作成する

CloudFrontで利用するためにはバージョンを作成する必要がある。今回は単純に「1」を作成する
関数の ARNは arn:aws:lambda:us-east-1:{アカウント番号}:function:check-userAgent:1となる
※ARNはこの後利用するので控えておく

③CloudFrontのディストリビューションを作成する

3-1.オリジンアクセス(OAI)を作成する

項目 設定値
名前 example-user-agent-site
説明 For S3 "example-user-agent-site"
署名動作 署名リクエスト
認証ヘッダーを上書きしない チェックなし
オリジンタイプ S3

3-2.ディストリビューションを作成

以下の設定をする

・オリジン

項目 設定値
オリジンドメイン S3
(例:example-user-agent-site.s3.ap-northeast-1.amazonaws.com)
オリジンパス -
名前 example-user-agent-site
オリジンアクセス Origin access control settings
Origin access control 手順3-1で作成したOAI(例:example-user-agent-site)
カスタムヘッダーを追加 -
オリジンシールドを有効にする いいえ

・デフォルトのキャッシュビヘイビア

項目 設定値
パスパターン デフォルト (*)
オブジェクトを自動的に圧縮 Yes
ビューワープロトコルポリシー HTTP and HTTPS
許可された HTTP メソッド GET, HEAD
ビューワーのアクセスを制限する No
キャッシュキーとオリジンリクエスト Cache policy and origin request policy
キャッシュポリシー CachingOptimized
オリジンリクエストポリシー -
レスポンスヘッダーポリシー -

・関数の関連付け ※後で再設定する

対象 関数タイプ 関数 ARN/名前 本文を含める
ビューワーリクエスト 関連付けなし - -
ビューワーレスポンス 関連付けなし - -
オリジンリクエスト 関連付けなし - -
オリジンレスポンス 関連付けなし - -

これ以降の設定はご自身の環境に合わせて設定いただいて問題ありません。
作成後にバケットポリシーに関するポップアップが表示されるので、表示されたポリシーをコピーしておく
image.png

④S3に権限を付与する

4-1. S3にバケットポリシーを追記する

手順3-2で控えておいたポリシーをS3のバケットポリシーに記載する

{
        "Version": "2008-10-17",
        "Id": "PolicyForCloudFrontPrivateContent",
        "Statement": [
            {
                "Sid": "AllowCloudFrontServicePrincipal",
                "Effect": "Allow",
                "Principal": {
                    "Service": "cloudfront.amazonaws.com"
                },
                "Action": "s3:GetObject",
                "Resource": "arn:aws:s3:::example-user-agent-site/*",
                "Condition": {
                    "StringEquals": {
                      "AWS:SourceArn": "arn:aws:cloudfront::{アカウント番号}:distribution/{ディストリビューション番号}"
                    }
                }
            }
        ]
      }

⑤Lambda実行ロールに権限を付与する

5-1. Lambda実行ロールに信頼関係を記載する

手順2で作成したLambdaの実行ロール (例:check-userAgent-role-aqs8vip)に、信頼関係を追記する
※これを入れることでLamnda@EdgeとしてCloudFrontからの呼び出しが可能になる

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "lambda.amazonaws.com",
                    "edgelambda.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

5-2. Lambda実行ロールにインラインポリシーを追加する

例:check-userAgent-policy

{
	"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:{アカウント番号}:function:check-userAgent:1"
		},
		{
			"Effect": "Allow",
			"Action": [
				"cloudfront:UpdateDistribution"
			],
			"Resource": "arn:aws:cloudfront::{アカウント番号}:distribution/{ディストリビューション番号}"
		}
	]
}

ちなみにこの段階で直接ファイルパスを指定するとアクセスが可能である
例:https://{ディストリビューションドメイン名}/pc.html
image.png

例:https://{ディストリビューションドメイン名}/smartphone.html
image.png

→この時点だとLambda@Edgeを紐づけていないので特に振り分けはされない

⑥CloudFrontのディストリビューションにLambda@Edgeを紐づける

6-1. 手順③で作成したCloudFrontディストリビューションのビヘイビアにLambda@Edgeを紐づける

デフォルトのビヘイビアの編集をクリックし、関数の関連付けを実施する
・関数の関連付け

対象 関数タイプ 関数 ARN/名前 本文を含める
ビューワーリクエスト 関連付けなし Lambda@Edge 関数ARN:バージョン
(例:arn:aws:lambda:us-east-1:{アカウント番号}:function:check-userAgent:1)
ビューワーレスポンス 関連付けなし - -
オリジンリクエスト 関連付けなし - -
オリジンレスポンス 関連付けなし - -

6-2. アクセス確認する

CloudFrontのディストリビューションドメイン名(https://xxxxx..cloudfront.net)に対して、PCとスマートフォンからアクセスして挙動を確認する

★PCからアクセスする
image.png

★iPhoneからアクセスする
image.png

きちんと振り分けられていることがわかりますね!

まとめ

CloudFront と Lambda@Edgeを組み合わせることでいろんなことができそう。
またLambda@Edge利用時にいろいろと引っかかった部分もあったので、別途記事にする。

参考文献

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0