【AWS×Webアプリ】CloudFrontのキャッシュ削除（Terraform）

## 目的
・AWS上の静的Webサイトホスティングを有効にしたS3をCloudFrontで公開。
・S3のコンテンツを更新した際に、CloudFrontのキャッシュ削除を行うLambdaを実装。

## 前提条件
・Terraformを使用してAWS上にリソースを作成する。
・Pythonを使用してLambdaを実装する。
・作成する内容は以下と同様

https://qiita.com/Haruki-N/items/f9e8fb32fd20285b57ad

## TFファイル
```terraform:aws_s3.tf（前回からの追加箇所のみ抜粋）
resource "aws_s3_bucket_notification" "WebBucket_notification" {
  bucket = aws_s3_bucket.WebBucket.id

  lambda_function {
    lambda_function_arn = aws_lambda_function.DeleteCache.arn
    events              = ["s3:ObjectCreated:*","s3:ObjectRemoved:*"]
    filter_suffix       = "index.html"
  }
}
```

```terraform:aws_lambda.tf
data "archive_file" "lambda_delete_cache" {
  type        = "zip"
  source_file = "${path.module}/lambda/delete_cache.py"
  output_path = "${path.module}/lambda_zip/delete_cache.zip"
}

resource "aws_lambda_function" "DeleteCache" {
  filename         = "${path.module}/lambda_zip/delete_cache.zip"
  function_name    = "delete_cache"
  role             = aws_iam_role.delete_cache_lambda_role.arn
  handler          = "delete_cache.lambda_handler"
  runtime          = "python3.11"
  source_code_hash = filebase64sha256("${path.module}/lambda/delete_cache.py")

  environment {
    variables = {
      DISTRIBUTION_ID = aws_cloudfront_distribution.WebBucketDistribution.id
    }
  }
}

resource "aws_lambda_permission" "allow_s3_to_invoke" {
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.DeleteCache.function_name
  principal     = "s3.amazonaws.com"
  source_account = data.aws_caller_identity.self.account_id
  source_arn    = aws_s3_bucket.WebBucket.arn
}
```

```terraform:aws_iam.tf
data "aws_iam_policy_document" "allow_access_to_WebBucket" {
  statement {
    principals {
      type        = "Service"
      identifiers = ["cloudfront.amazonaws.com"]
    }

    effect = "Allow"
    actions = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.WebBucket.arn}/*"]

    condition {
      test     = "StringEquals"
      variable = "aws:SourceArn"
      values   = [aws_cloudfront_distribution.WebBucketDistribution.arn]
    }
  }
}

resource "aws_iam_role" "delete_cache_lambda_role" {
  name = "DeleteCacheLambdaRole"

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

resource "aws_iam_role_policy" "delete_cache_lambda_policy" {
  role = aws_iam_role.delete_cache_lambda_role.id

  policy = jsonencode({
    Statement = [
      {
        Action = ["cloudfront:CreateInvalidation","cloudfront:ListDistributions"]
        Effect = "Allow"
        Resource = "*"
      },
      {
        Action = ["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"]
        Effect = "Allow"
        Resource = ["arn:aws:logs:*:*:*"]
      },
    ]
  })
}
```

◆aws_s3.tf
・WebBucket_notification：WebBucketでindex.htmlが作成・削除された際にLambda関数を呼び出すS3通知

◆aws_lambda.tf
・lambda_delete_cache：Lambda関数用のPythonファイルをZip圧縮
・DeleteCache：Lambda関数定義
　a. source_code_hash：TerraformがLambda関数のソースコードが変更を検出するために使用するハッシュ値
　b. environment：ディストリビューションIDを環境変数として設定
・allow_s3_to_invoke：Lambdaに付与するリソースベースのポリシー

◆aws_iam.tf
・allow_access_to_WebBucket：aws_s3.tfより移管したもの（前回記事より）
・delete_cache_lambda_role：キャッシュ削除Lambda用IAMロール
・delete_cache_lambda_policy：「delete_cache_lambda_role」に付与するポリシー

## Lambda
```delete_cache.py
import os
import boto3

def lambda_handler(event, context): 
    cloudfront_client = boto3.client('cloudfront')
                                     
    distribution_id = os.environ['DISTRIBUTION_ID']
    paths_to_invalidate = ['/index.html']
    
    #CloudFrontのキャッシュ削除
    cloudfront_client.create_invalidation(
        DistributionId=distribution_id,
        InvalidationBatch={
            'Paths': {
                'Quantity': len(paths_to_invalidate),
                'Items': paths_to_invalidate
            },
            'CallerReference': str(context.aws_request_id)
        }
    )

```

## 動作確認
①作成されたS3バケットへ「index.html」をアップロードする
```index.html
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>My Website Home Page</title>
</head>
<body>
  <h1>Welcome to my website(CloudFront)</h1>
  <p>Now hosted on Amazon S3 using Terraform(Before)!</p>
</body>
</html>
```

②Webサイトへアクセス
・正常にアクセスできることを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3637963/d6accb81-92b3-4427-9fd4-de575db05c4c.png)


③S3バケットへ「index.html」をアップロードする（更新）
```index.html
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>My Website Home Page</title>
</head>
<body>
  <h1>Welcome to my website(CloudFront)</h1>
  <p>Now hosted on Amazon S3 using Terraform(After)!</p>
</body>
</html>
```

④ブラウザの更新
・表示される画面が更新され最新のコンテンツが配信されていることを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3637963/d162e64b-78d8-0504-74c2-5f12e078dce1.png)


⑤ログ（CloudWatch）にエラーが出ていないことの確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3637963/872011ce-2de2-f6a7-89aa-d868ff136ab0.png)

## 参考（前回記事）
https://qiita.com/Haruki-N/items/a42ff982c275bc191d72