CloudFront Functions でアクセス元の国によってリダイレクトされるソリューションを試してみた
amazon-cloudfront-functions/redirect-based-on-country at main · aws-samples/amazon-cloudfront-functions · GitHub
上記ソリューションを試してみました。
AWS CLI の実行環境は CloudShell です。
1. AWS CLI のインストール
CloudShell にはプリインストールされているためスキップします。
2. リポジトリのクローン
$ git clone https://github.com/aws-samples/amazon-cloudfront-functions.git
Cloning into 'amazon-cloudfront-functions'...
remote: Enumerating objects: 326, done.
remote: Counting objects: 100% (150/150), done.
remote: Compressing objects: 100% (119/119), done.
remote: Total 326 (delta 71), reused 43 (delta 29), pack-reused 176 (from 2)
Receiving objects: 100% (326/326), 105.58 KiB | 2.78 MiB/s, done.
Resolving deltas: 100% (138/138), done.
3. カレントディレクトリの変更
$ cd amazon-cloudfront-functions/
4. CloudFront Functions の作成
$ aws cloudfront create-function \
--name redirect-based-on-country \
--function-config Comment="redirect-based-on-country",Runtime=cloudfront-js-1.0 \
--function-code fileb://redirect-based-on-country/index.js
レスポンス
{
"Location": "https://cloudfront.amazonaws.com/2020-05-31/function/arn:aws:cloudfront::012345678901:function/redirect-based-on-country",
"ETag": "ETVPDKIKX0DER",
"FunctionSummary": {
"Name": "redirect-based-on-country",
"Status": "UNPUBLISHED",
"FunctionConfig": {
"Comment": "redirect-based-on-country",
"Runtime": "cloudfront-js-1.0"
},
"FunctionMetadata": {
"FunctionARN": "arn:aws:cloudfront::012345678901:function/redirect-based-on-country",
"Stage": "DEVELOPMENT",
"CreatedTime": "2025-01-09T10:36:32.096000+00:00",
"LastModifiedTime": "2025-01-09T10:36:32.096000+00:00"
}
}
}
5. CloudFront Functions のテスト
エラーパターン
$ aws cloudfront test-function \
--name redirect-based-on-country \
--if-match ETVPDKIKX0DER \
--event-object fileb://redirect-based-on-country/test-objects/country-de.json
レスポンス
{
"TestResult": {
"FunctionSummary": {
"Name": "redirect-based-on-country",
"Status": "UNPUBLISHED",
"FunctionConfig": {
"Comment": "redirect-based-on-country",
"Runtime": "cloudfront-js-1.0"
},
"FunctionMetadata": {
"FunctionARN": "arn:aws:cloudfront::012345678901:function/redirect-based-on-country",
"Stage": "DEVELOPMENT",
"CreatedTime": "2025-01-09T10:36:32.096000+00:00",
"LastModifiedTime": "2025-01-09T10:36:32.125000+00:00"
}
},
"ComputeUtilization": "0",
"FunctionExecutionLogs": [],
"FunctionErrorMessage": "The CloudFront function associated with the CloudFront distribution is invalid or could not run. SyntaxError: Token \"async\" not supported in this version in 1",
"FunctionOutput": "{}"
}
}
ここで、上述の通り async
が JavaScript ランタイム 1.0 ではサポートされていない旨のエラーが発生しました。
Use async and await - Amazon CloudFront
You must use JavaScript runtime 2.0 for the following code samples.
いったん CloudFront Functions のコンソール上で async
を削除しましたが、他にも以下の対応方法があります。
- リポジトリをクローンした時点でコードから
async
を削除する -
async
を使用する場合、Runtime
でcloudfront-js-2.0
を指定する
今回のように関数作成後にコードを更新した場合、 --if-match
オプションで指定する ETag
も変わるため、関数更新後に describe-function
コマンドを実行して新しい ETag
を確認する必要があります。
$ aws cloudfront describe-function --name redirect-based-on-country
レスポンス
{
"ETag": "E3UN6WX5RRO2AG",
"FunctionSummary": {
"Name": "redirect-based-on-country",
"Status": "UNPUBLISHED",
"FunctionConfig": {
"Comment": "redirect-based-on-country",
"Runtime": "cloudfront-js-1.0"
},
"FunctionMetadata": {
"FunctionARN": "arn:aws:cloudfront::012345678901:function/redirect-based-on-country",
"Stage": "DEVELOPMENT",
"CreatedTime": "2025-01-09T10:36:32.096000+00:00",
"LastModifiedTime": "2025-01-09T10:40:18.277000+00:00"
}
}
}
成功パターン
再度 test-function
コマンドを実行します。
なお、redirect-based-on-country のソリューションには 2 つのテストパターンがあるので両方試しました。
$ aws cloudfront test-function \
--name redirect-based-on-country \
--if-match E3UN6WX5RRO2AG \
--event-object fileb://redirect-based-on-country/test-objects/country-de.json
レスポンス
{
"TestResult": {
"FunctionSummary": {
"Name": "redirect-based-on-country",
"Status": "UNPUBLISHED",
"FunctionConfig": {
"Comment": "redirect-based-on-country",
"Runtime": "cloudfront-js-1.0"
},
"FunctionMetadata": {
"FunctionARN": "arn:aws:cloudfront::012345678901:function/redirect-based-on-country",
"Stage": "DEVELOPMENT",
"CreatedTime": "2025-01-09T10:36:32.096000+00:00",
"LastModifiedTime": "2025-01-09T10:40:18.277000+00:00"
}
},
"ComputeUtilization": "4",
"FunctionExecutionLogs": [],
"FunctionErrorMessage": "",
"FunctionOutput": "{\"response\":{\"headers\":{\"location\":{\"value\":\"https://www.example.com/de/index.html\"}},\"statusDescription\":\"Found\",\"cookies\":{},\"statusCode\":302}}"
}
}
$ aws cloudfront test-function \
--name redirect-based-on-country \
--if-match E3UN6WX5RRO2AG \
--event-object fileb://redirect-based-on-country/test-objects/country-not-de.json
レスポンス
{
"TestResult": {
"FunctionSummary": {
"Name": "redirect-based-on-country",
"Status": "UNASSOCIATED",
"FunctionConfig": {
"Comment": "redirect-based-on-country",
"Runtime": "cloudfront-js-1.0"
},
"FunctionMetadata": {
"FunctionARN": "arn:aws:cloudfront::012345678901:function/redirect-based-on-country",
"Stage": "DEVELOPMENT",
"CreatedTime": "2025-01-09T10:36:32.096000+00:00",
"LastModifiedTime": "2025-01-09T10:40:18.277000+00:00"
}
},
"ComputeUtilization": "6",
"FunctionExecutionLogs": [],
"FunctionErrorMessage": "",
"FunctionOutput": "{\"request\":{\"headers\":{\"cloudfront-viewer-country\":{\"value\":\"GB\"},\"host\":{\"value\":\"www.example.com\"},\"accept\":{\"value\":\"text/html\"}},\"method\":\"GET\",\"querystring\":{\"test\":{\"value\":\"true\"},\"arg\":{\"value\":\"val1\"}},\"uri\":\"/index.html\",\"cookies\":{\"loggedIn\":{\"value\":\"false\"},\"id\":{\"value\":\"CookeIdValue\"}}}}"
}
}
6. CloudFront Functions の公開
$ aws cloudfront publish-function \
--name redirect-based-on-country \
--if-match E3UN6WX5RRO2AG
レスポンス
{
"FunctionSummary": {
"Name": "redirect-based-on-country",
"Status": "UNASSOCIATED",
"FunctionConfig": {
"Comment": "redirect-based-on-country",
"Runtime": "cloudfront-js-1.0"
},
"FunctionMetadata": {
"FunctionARN": "arn:aws:cloudfront::012345678901:function/redirect-based-on-country",
"Stage": "LIVE",
"CreatedTime": "2025-01-09T10:52:54.031000+00:00",
"LastModifiedTime": "2025-01-09T10:52:54.031000+00:00"
}
}
}
CloudFront ディストリビューションとの関連付け
$ aws cloudfront get-distribution-config \
--id E17MNC66XWD3RJ \
--output json > dist-cfg.json
上記コマンドで出力された dist-cfg.json で CloudFront Functions の紐づけを設定します。
"FunctionAssociations": {
"Quantity": 1,
"Items": [
{
"EventType": "viewer-response",
"FunctionARN": "arn:aws:cloudfront::012345678901:function/redirect-based-on-country"
}
]
}
上記に加えて、ETag
というパラメーターキーを IfMatch
というキーに置換します。
"IfMatch": "E7OXO5EJB76EN",
更新後の dist-cfg.json ファイルで CloudFront ディストリビューションを更新します。
$ aws cloudfront update-distribution \
--id E17MNC66XWD3RJ \
--cli-input-json fileb://dist-cfg.json
レスポンス
{
"ETag": "E1TYLC352WFPP1",
"Distribution": {
"Id": "E17MNC66XWD3RJ",
"ARN": "arn:aws:cloudfront::012345678901:distribution/E17MNC66XWD3RJ",
"Status": "InProgress",
"LastModifiedTime": "2025-01-09T11:09:11.591000+00:00",
"InProgressInvalidationBatches": 0,
"DomainName": "xxx.cloudfront.net",
"ActiveTrustedSigners": {
"Enabled": false,
"Quantity": 0
},
"ActiveTrustedKeyGroups": {
"Enabled": false,
"Quantity": 0
},
"DistributionConfig": {
"CallerReference": "fd568ddc-74d9-434c-979e-0f1e5af96afa",
"Aliases": {
"Quantity": 1,
"Items": [
"xxx"
]
},
"DefaultRootObject": "index.html",
"Origins": {
"Quantity": 1,
"Items": [
{
"Id": "xxx",
"DomainName": "xxx",
"OriginPath": "",
"CustomHeaders": {
"Quantity": 0
},
"S3OriginConfig": {
"OriginAccessIdentity": ""
},
"ConnectionAttempts": 3,
"ConnectionTimeout": 10,
"OriginShield": {
"Enabled": false
},
"OriginAccessControlId": "EW4HGERGH3D5T"
}
]
},
"OriginGroups": {
"Quantity": 0
},
"DefaultCacheBehavior": {
"TargetOriginId": "xxx",
"TrustedSigners": {
"Enabled": false,
"Quantity": 0
},
"TrustedKeyGroups": {
"Enabled": false,
"Quantity": 0
},
"ViewerProtocolPolicy": "allow-all",
"AllowedMethods": {
"Quantity": 2,
"Items": [
"HEAD",
"GET"
],
"CachedMethods": {
"Quantity": 2,
"Items": [
"HEAD",
"GET"
]
}
},
"SmoothStreaming": false,
"Compress": true,
"LambdaFunctionAssociations": {
"Quantity": 0
},
"FunctionAssociations": {
"Quantity": 1,
"Items": [
{
"FunctionARN": "arn:aws:cloudfront::012345678901:function/redirect-based-on-country",
"EventType": "viewer-response"
}
]
},
"FieldLevelEncryptionId": "",
"CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6",
"GrpcConfig": {
"Enabled": false
}
},
"CacheBehaviors": {
"Quantity": 0
},
"CustomErrorResponses": {
"Quantity": 0
},
"Comment": "",
"Logging": {
"Enabled": false,
"IncludeCookies": false,
"Bucket": "",
"Prefix": ""
},
"PriceClass": "PriceClass_All",
"Enabled": true,
"ViewerCertificate": {
"CloudFrontDefaultCertificate": false,
"ACMCertificateArn": "arn:aws:acm:us-east-1:012345678901:certificate/87075377-1557-47e8-8da6-88c234749fab",
"SSLSupportMethod": "sni-only",
"MinimumProtocolVersion": "TLSv1.2_2021",
"Certificate": "arn:aws:acm:us-east-1:012345678901:certificate/87075377-1557-47e8-8da6-88c234749fab",
"CertificateSource": "acm"
},
"Restrictions": {
"GeoRestriction": {
"RestrictionType": "whitelist",
"Quantity": 1,
"Items": [
"JP"
]
}
},
"WebACLId": "",
"HttpVersion": "http2",
"IsIPV6Enabled": true,
"ContinuousDeploymentPolicyId": "",
"Staging": false
},
"AliasICPRecordals": [
{
"CNAME": "012345678901",
"ICPRecordalStatus": "APPROVED"
}
]
}
}
以上で CloudFront Functions を紐づけた CloudFront ディストリビューションのデプロイ完了です。
まとめ
今回は CloudFront Functions でアクセス元の国によってリダイレクトされるソリューションを試してみました。
どなたかの参考になれば幸いです。
Discussion