はじめに

S3上のコンテンツをCloudFrontを使用し配信している。

参照:独自ドメインを使用してS3上の静的ウェブサイトをRoute53を使用し公開する

また、githubへpushした際、CodePipeLineを使用し自動build、自動deployは対応済

参照:GithubへのpushをトリガーとしてCode PipelineでJekyllをビルドしてS3にデプロイする

S3の更新をトリガーとするとLambdaが必要以上に実行されてしまう

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

その為、CodePileLineの処理の一環として、Lambdaを実行する

パイプライン処理は、CodeCommitにPushしたソースをCodePipelineでS3にデプロイ後、AWS Lambdaを呼び出しLambda FunctionでInvalidatioinとInvalidationのステータスチェックを行い、Invalidationを実行する。

手順

IAM ロールの作成

IAM コンソールから新しいロールを作成する

ロールの作成を選択

信頼されたエンティティの種類を選択

AWSサービス選択

ユースケースの選択

Lambdaを選択

次のステップを選択

ポリシーの作成を選択

権限内容は以下の JSON で指定する

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "codepipeline:PutJobFailureResult",
                "codepipeline:PutJobSuccessResult",
                "cloudfront:CreateInvalidation"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:CreateLogGroup",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}

タグは任意で必要であれば作成する

名前も任意で自分のわかりやすい名称をつける

例:lambda_cloud_front_invalidation

先ほど作成したポリシーを検索し、選択

次のステップを選択

タグは任意で必要であれば作成する

名前も任意で自分のわかりやすい名称をつける

例:lambda_cloud_front_invalidation

ポリシーと区別する必要もないと考えたので同じ名前

Lambda 関数の作成

Lambda コンソールから新しい関数を作成する

一から作成を選択

関数名は任意で入力

ランタイムで使用する言語は「Python 3.8」、実行ロールは既存のロールから「先ほど作成した IAM ロール」を指定する

Lambda関数は以下のように記載する

from __future__ import print_function

import boto3
import json
import time

cp = boto3.client('codepipeline')
cf = boto3.client('cloudfront')

# codepipelineへ成功jobを返答する
def put_job_success(job_id):
    cp.put_job_success_result(jobId=job_id)

# codepipelineへ失敗jobを返答する
def put_job_failure(job_id, err):
    message = 'Function exception: ' + str(err)
    cp.put_job_failure_result(
        jobId=job_id,
        failureDetails={
            'type': 'JobFailed',
            'message': message
        }
    )

# lambdaのmain関数
def lambda_handler(event, context):
    try:
        job_id = event['CodePipeline.job']['id']
        job_data = event['CodePipeline.job']['data']
    
        user_parameters = json.loads(
                job_data['actionConfiguration']['configuration']['UserParameters']
            )
    
        pipeline_name = user_parameters['PipelineName']
        distribution_id = user_parameters['DistributionId']
        
        res = cf.create_invalidation(
            DistributionId=distribution_id,
            InvalidationBatch={
            'Paths': {
                'Quantity': 1,
                'Items': ['/*'],
            },
            'CallerReference': str(time.time())
            }
        )
        put_job_success(job_id)
    
    except Exception as err:
        put_job_failure(job_id, err)
        
    return "Complete."

CodePipeLineの設定

CodePipeLineから呼び出すための設定を行う

Deployを実行した後に呼び出しを行うため、最下段のステージを追加するを選択

アクション名(任意)、アクションプロバイダはAWS Lambda、リージョン(任意)、関数名は先ほど作成したLambda関数名、ユーザパラメータはLambdaに渡す引数

ユーザパラメータとしてこのPipeLineの名称と更新するCloudFrontのDistributionIdをjson形式にて記載する

{
    "PipelineName": "PipeLineの名称",
    "DistributionId": "更新するCloudFrontのDistributionId"
}

おわりに

問題なく実行はされた。これで自動化完了。

PipeLine処理がいつ完了したか取得するため、SNSを用いてSlackへ通知させるようにしたい。

参考