AWS S3更新時にLambdaでCloudFrontのInvalidationを自動実行

Vue.js で作ったウェブアプリは、静的サイトとして AWS S3 + CloudFront からホスティングできます。

非常にコスパのいいオススメの設計なのですが、S3 内の本番ファイルを更新した場合、**CloudFront のエッジサーバ上に残っているキャッシュのせいで、その更新は即座に反映されません。**

CloudFront のコンソールから「Invalidation」を手動で実行すればそのキャッシュを削除できます。

今回は、その **Invalidation 手動実行の部分を AWS Lambda を使って自動化するやり方**を整理します。

# 前提

静的サイト（特に Vue.js）を S3 + CloudFront でホストしているケースを前提としています。

# Lambda を使った Invalidation の自動化手順

## IAM ロールの作成

まず Lambda 関数に適用する IAM ロールの準備です。

[IAM コンソール](https://console.aws.amazon.com/iam/home?region=us-east-1#/users)から新しいロールを作成します。ロールを使用する権限は「**Lambda**」、権限内容は以下の JSON で指定します。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
        "Effect": "Allow",
        "Action": [
            "cloudfront:CreateInvalidation"
        ],
        "Resource": [
            "*"
        ]
    }
  ]
}
```

*「[Automatic Cloudfront invalidation with Amazon Lambda](https://blog.miguelangelnieto.net/posts/Automatic_Cloudfront_invalidation_with_Amazon_Lambda.html)」より引用*

ロール名は「lambda_cloud_front_invalidation」など分かりやすい適当な名前にしましょう。


## Lambda 関数の作成

続いて Lambda 関数の作成です。

### 関数の作成

[Lambda コンソール](https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#/functions)から新しい関数を作成します。今回、ランタイムで使用する言語は「**Python 2.7**」、実行ロールは既存のロールから「**先ほど作成した IAM ロール**」を指定します。

### トリガーの追加

関数のトリガーに静的サイトをホストしている S3 を追加します。イベントタイプは「**すべてのオブジェクト作成イベント**」を選択します。

### 関数コードの設定

デフォルトの設定されている関数コード `lambda_function.py` を、以下の内容で更新します。

```python:lambda_function.py
from __future__ import print_function

import boto3
import time

def lambda_handler(event, context):
    path = "/dist"
    for items in event["Records"]:
        if path in items["s3"]["object"]["key"]:
            client = boto3.client('cloudfront')
            invalidation = client.create_invalidation(DistributionId='YOUR_DISTRIBUTION_ID',
                InvalidationBatch={
                    'Paths': {
                        'Quantity': 1,
                        'Items': [path + '/*']
                },
                'CallerReference': str(time.time())
            })
            break
```

コード内容は、S3 の `/dist` ディレクトリ内（Vue.js のビルドしたコード）の更新を検知して CloudFront の Invalidation を走らせるというものです。

`YOUR_DISTRIBUTION_ID` の部分は、ご自身の の Distribution ID に置き換えてください。

## 稼働テスト

Lambda 関数が意図しているように動くかテストします。

S3 の対象ディレクトリ（今回は `/dist`）を更新し、[Lambda コンソール](https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#/functions)の「モニタリング> Error count and Success rate」で処理が成功したか確認できます。

<img width="1069" alt="スクリーンショット 2019-12-07 17.43.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138789/58208eec-f5e4-9478-4353-76a4f47bc1cb.png">

さらに新しくブラウザを開いて、変更内容が反映されていればOKです。

# 請求アラーム設定もお忘れなく

意図しない高額請求が発生しないように [CloudWatch のコンソール](https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#) にて、請求アラームを設定しておきましょう。

# Reference

* [Automatic Cloudfront invalidation with Amazon Lambda](https://blog.miguelangelnieto.net/posts/Automatic_Cloudfront_invalidation_with_Amazon_Lambda.html)