Automatically Invalidate AWS CloudFront Cache When Updating Files in an S3 Bucket with CloudFormation

Posted by Brian Porter on April 14, 2023

This blog post will guide you through the process of creating a CloudFormation stack that automatically invalidates CloudFront cache when you upload updated files to an associated S3 bucket. We will achieve this by using an AWS Lambda function that gets triggered by S3 bucket notifications.

Prerequisites

  1. AWS CLI installed and configured with the necessary permissions to create and update CloudFormation stacks and other resources.
  2. An existing S3 bucket and CloudFront distribution created using CloudFormation. In this example, we’re using the stack name webclient-assets.

Update your CloudFormation template

Update your existing CloudFormation template to include the necessary resources for the Lambda function, S3 Bucket Notification, and Lambda Permission.

Here is the updated CloudFormation template including the new resources:

[...]
# Add the following resources to your existing CloudFormation template

# Lambda function
InvalidateCloudFrontFunction:
  Type: AWS::Lambda::Function
  Properties:
    FunctionName: InvalidateCloudFront
    Handler: index.lambda_handler
    Role: !GetAtt LambdaExecutionRole.Arn
    Runtime: python3.8
    Code:
      S3Bucket: REPLACE_WITH_YOUR_S3_BUCKET_NAME
      S3Key: lambda_function.zip

# S3 bucket notification configuration
ContentBucketNotification:
  Type: AWS::S3::BucketNotificationConfiguration
  Properties:
    Bucket: !Ref WebsiteBucket
    NotificationConfiguration:
      LambdaConfigurations:
        - Event: s3:ObjectCreated:*
          Function: !GetAtt InvalidateCloudFrontFunction.Arn

# Lambda execution role
LambdaExecutionRole:
  Type: AWS::IAM::Role
  Properties:
    AssumeRolePolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Effect: Allow
          Principal:
            Service:
              - lambda.amazonaws.com
          Action:
            - sts:AssumeRole
    Policies:
      - PolicyName: LambdaExecutionPolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
              Resource: 'arn:aws:logs:*:*:*'
            - Effect: Allow
              Action:
                - cloudfront:CreateInvalidation
              Resource: '*'

# Lambda invoke permission
LambdaInvokePermission:
  Type: AWS::Lambda::Permission
  Properties:
    Action: lambda:InvokeFunction
    FunctionName: !Ref InvalidateCloudFrontFunction
    Principal: s3.amazonaws.com
    SourceAccount: !Ref 'AWS::AccountId'
    SourceArn: !Sub 'arn:aws:s3:::${WebsiteBucket}'

Package and upload your Lambda function code

Create a file named ```index.py`` with the following Python code for the Lambda function:

import boto3
import uuid

def lambda_handler(event, context):
    distribution_id = 'REPLACE_WITH_YOUR_CLOUDFRONT_DISTRIBUTION_ID'

    cloudfront_client = boto3.client('cloudfront')
    caller_reference = str(uuid.uuid4()) # Using a UUID as CallerReference

    response = cloudfront_client.create_invalidation(
        DistributionId=distribution_id,
        InvalidationBatch={
            'Paths': {
                'Quantity': 1,
                'Items': ['/*']
            },
            'CallerReference': caller_reference
        }
    )

    return {
        'statusCode': 200,
        'body': json.dumps(response)
    }

Create a .zip archive containing the index.py file. Name the archive ```lambda_function.zip``.

Update your CloudFormation stack

Use the AWS CLI to update your CloudFormation stack with the updated template:

aws cloudformation update-stack \
  --stack-name webclient-assets \
  --template-body file://path/to/your/updated_template.yaml \
  --capabilities CAPABILITY_NAMED_IAM

Replace path/to/your/updated_template.yaml with the path to your updated CloudFormation template file.

Conclusion

Now, whenever you update the content in your S3 bucket, the associated CloudFront cache will be automatically invalidated. This will ensure that your users always see the latest content on your website.

If you need to make further changes to your CloudFormation stack or Lambda function, simply update the necessary files and repeat the steps to update the stack.

Happy coding!

If you made it this far, you may as well follow me on LinkedIn: Follow Brian Porter