AWS: Step-by-Step Guide to Cloudfront Invalidation of S3 assets in CodePipeline using Lambda
Set up a Lambda function and CodePipeline to invalidate Cloudfront cache automatically.
Introduction
I built my website with React TypeScript and deployed it to S3 with Cloudfront via CodePipeine and CodeBuild.
This story will explain how I automated Cloudfront cache invalidation in the CodePipeline so I don’t have to run aws-cli manually to invalidate the cache. I’m sure there are some other ways to accomplish the automation and this is one way to do it (i.e. add the trigger to S3 events for Lambda to pick up the changes and invalidate the Cloudfront cache).
Let’s get started!
#1 Create resources.
First, ensure that you have resources created for CodePipeline. Refer to my Github for my CloudFormation code. If you already have CodePipeline and S3 setup, skip this.
#2 Create a Lambda function.
- Go to Create function page i.e. https://us-east-1.console.aws.amazon.com/lambda/home?region=us-east-1#/create/function. You will notice you have to create an IAM role. You can click Create a new role with basic Lambda permissions and update the IAM policies later with the custom one (in the next step). I named my function invalidate-cloudfront-cache-from-code-pipeline.
- Update the IAM role of the Lambda function. Here is what my IAM role and permission policy look like.
- Add this code to the Lambda function’s Code Source and click deploy.
import json
import boto3
code_pipeline = boto3.client("codepipeline")
cloud_front = boto3.client("cloudfront")
def lambda_handler(event, context):
job_id = event["CodePipeline.job"]["id"]
try:
user_params = json.loads(
event["CodePipeline.job"]
["data"]
["actionConfiguration"]
["configuration"]
["UserParameters"]
)
cloud_front.create_invalidation(
DistributionId=user_params["distributionId"],
InvalidationBatch={
"Paths": {
"Quantity": len(user_params["objectPaths"]),
"Items": user_params["objectPaths"],
},
"CallerReference": event["CodePipeline.job"]["id"],
},
)
except Exception as e:
code_pipeline.put_job_failure_result(
jobId=job_id,
failureDetails={
"type": "JobFailed",
"message": str(e),
},
)
else:
code_pipeline.put_job_success_result(
jobId=job_id,
)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"codepipeline:PutJobFailureResult",
"codepipeline:PutJobSuccessResult",
"cloudfront:CreateInvalidation"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:us-east-1:420504697712:*"
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:us-east-1:XXX:log-group:/aws/lambda/invalidate-cloudfront-cache-from-code-pipeline:*"
}
]
}
- Once the Lambda function is created, it should look something like this.
#3 Add another step to your CodePipeline.
- Navigate to the edit page of your CodePipeline i.e. https://us-east-1.console.aws.amazon.com/codesuite/codepipeline/pipelines/mywebsite-420504697712-CodePipeLine-prod/edit?region=us-east-1 and add a new step for the cache invalidation.
- Update the IAM role for this CodePipeline to have these permissions: lambda:InvokeFunction and lambda:ListFunctions to allow CodePipeline to access Lambda resources.
The End
Run the CodePipeline. That’s it!
🎉🎉Happy building 🎉🎉
I hope you found this guide helpful.
Live life to the fullest,
Stephen S. Lee