aws-sdk-php-laravelを使って、静的ホスティングしている環境のリダイレクトを自由に設定してみる
前置き
S3+CloudFrontで静的サイトを配信している。
この環境とは別に管理画面+APIサーバーとしてのLaravelが動いている環境がある。
管理画面側から静的ホスティングしている環境にリダイレクト設定をしたい!
ざっくり要件はこんな感じでした。
今まではEC2を使っていたので、リダイレクトやBasic認証はhtaccessでやっていた。
さあ静的ホスティングの場合はどうしよう。となって個人的に色々検討した結果、これがベストかなと思った方法がCloudFront Functionsを使う方法でした。
他に検討した方法
方法 | デメリット |
---|---|
S3でリダイレクト | 正規表現が使えない |
ALBを使ってリダイレクト | 設定するルールが多くなって複雑。100ルールまでしか対応できない。そもそもリダイレクトのためだけにALBを使いたく無い |
Lambda@Edge | 特になし?CloudFront Functionsより高機能 |
以前はLambda@Edgeでやるケースが多かったみたいです。がCloudFront Functionsだけで可能ならこちらの方がシンプルだと思います。
Lambda@Edgeより前段で実行されるので、シンプルな機能はCloudFront Functionsで複雑な機能はLambda@Edgeという使い分けや組み合わせができるのも良いですね。
本題
今回はLaravelからAWS SDKを使ってCloudFront Functionsを更新してみました。
流れはこんな感じ
- DBに登録してあるリダイレクト条件とリダイレクト先のpathを取得
- Laravelのviewを元にCloudFrontFunctionsで実行させるコードを作る
- AWS SDKを使ってCloudFrontFunctionsを更新
aws-sdk-php-laravelのインストール
{
"require": {
"aws/aws-sdk-php-laravel": "^3.0"
}
}
composer update
AWS Service Providerを追加
'providers' => [
~~~~
/*
* Package Service Providers...
*/
Aws\Laravel\AwsServiceProvider::class, // ←追加
~~~
],
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Arr' => Illuminate\Support\Arr::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
'AWS' => Aws\Laravel\AwsFacade::class, // ←追加
]
AWS アクセスキー等の設定
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=
Commandを作成
CLIで実行できるようにCommandクラスを作成します。
php artisan make:command CloudFrontSetting
実行すると、app/Console/Commands
配下に作成される。
viewを作成
CloudFront Functionsで実行されるコードの雛形になるviewを作成します。
function handler(event) {
var request = event.request;
var headers = request.headers;
var uri = request.uri;
var patterns = @json($settings);
// リダイレクト
for(var i = 0; i < patterns.length; i++) {
var el = patterns[i];
if(uri.match(el.condition)){
var response = {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: { location: { value: 'https://[host-name]' + el.to } }
};
return response;
}
}
return request;
}
重要なのはこちら
var patterns = @json($settings);
Laravel側から配列をJavaScriptで使えるように@json
を使っています。
その後、配列のデータをループで回して正規表現に一致したら、リダイレクトさせています。
配列の中身はこんな感じを想定。
[
[
'condition' => '^\/doc$|\/doc\/'
'to' => '/document'
],
[
'condition' => '^\/img$|\/img\/'
'to' => '/images'
],
]
ちなみにCloudFrontFunctionではconstやletでの変数定義ができずエラーになるので注意してください。
forEachなども使えないので、ループ処理はfor文で対応する。
CloudFrontFunctionsを作成する
作成はAWS CLIからでもコンソールからでもできます。
作成した関数名を環境変数に設定します。
CLOUD_FRONT_FUNCTION=TestFunction
CloudFrontSettingコマンドを修正する
先ほど作ったCloudFrontSettingコマンドを変更していきます。
<?php
namespace App\Console\Commands;
use App\Models\Redirect;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\App;
class CloudFrontSetting extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'cloudfront:setting';
/**
* The console command description.
*
* @var string
*/
protected $description = 'CloudFrontの設定';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
// パッケージ呼び出し
$cloudfront = App::make('aws')->createClient('CloudFront');
// 現在の設定を取得
$current_setting = $cloudfront->describeFunction([
'Name' => env('CLOUD_FRONT_FUNCTION'),
]);
// リダイレクト設定を取得
$settings = Redirect::select(['condition','to'])->whereActive()->get();
// CloudFrontFunctionの設定を更新
$cloudfront->updateFunction([
'FunctionCode' => View('cloudfront_function',compact('settings')),
'FunctionConfig' => [
'Comment' => 'CloudFront環境ごとの設定',
'Runtime' => 'cloudfront-js-1.0',
],
'IfMatch' => $current_setting['ETag'],
'Name' => env('CLOUD_FRONT_FUNCTION'),
]);
}
}
はじめにAWS SDKのクライアントを呼び出します。
$cloudfront = App::make('aws')->createClient('CloudFront');
CloudFront Functionsの更新にはETag
(現在のバージョン)が必要なのでdescribeFunction
を使い取得します。
$settings = Redirect::select(['condition','to'])->whereActive()->get();
Modelからリダイレクトの設定を取得します。DBからの取得についての詳細は割愛。
updateFunction
のFunctionCode
に動かすコードを指定します。今回は先ほど作ったviewを使います。
function handler(event) {
var request = event.request;
var headers = request.headers;
var uri = request.uri;
var patterns = [{"condition":"^\\\/doc$|\\\/doc\\\/","to":"\/document"},{"condition":"^\\\/img$|\\\/img\\\/","to":"\/images"}];
// リダイレクト
for(var i = 0; i < patterns.length; i++) {
var el = patterns[i];
if(uri.match(el.condition)){
var response = {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: { location: { value: 'https://[host-name]' + el.to } }
};
return response;
}
}
return request;
}
最終的にCloudFront Functionsに更新されるのはこんな感じ。
AWS SDKの詳細
まとめ
これで静的ホスティングしている環境にリダイレクト処理をかけれました。
同じく静的ホスティングする時に悩ましいstg環境のBasic認証問題もCloudFront Functions使えば簡単に実装できます。
今回は管理画面からリダイレクトを変更したりする要件だったので、DBに保存できるようにLaravelで実装しました。
ですが単純にリダイレクトやBasic認証かけるだけならかけるだけならTerraformを使ったりAWS CLI使ったりが便利そうです。
Discussion