Automating CloudFront Cache Invalidation with Lambda: CodePipeline and S3 Events approach
Published: 2024-05-07 12:49:46

Are you a cloud Architect or Cloud Engineer or DevOps that setup S3 backed by AWS Cloudfront, and you have been struggling with Invalidating the cache anytime there is a new feature upload to S3? This task is not just tasking but can be so insignificant.

**In this blog, we will be discussing two ways of achieving our goal.**

1. Invalidate the cache by triggering a lambda using S3 event triggers.
2. Invalidate the cache by triggering a lambda using CodePipeline.


**Prerequisites**
You should already have a website setup on S3 and configured to use the CloudFront distribution. If not, click [here](https://medium.com/aws-in-plain-english/automate-static-web-application-deployment-on-aws-cloud-92c006ce032c) to learn.

For approach 2,

Invalidate the cloudfront cache using CodePipeline, you must already have CodePipeline setup for your frontend deployment. If not, click [here](https://medium.com/aws-in-plain-english/preventing-unauthorized-access-to-your-s3-buckets-using-cloudfront-oai-c019490aae81) to learn.
  
Approach 1: Invalidate the cache by triggering a lambda using S3 event triggers
Step 1: Create a lambda function
Select Python as your runtime environment and a new execution role for your lambda.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hl7r79mxjehmzjrhlch0.png)

Use the below lambda code and **replace it with your distribution ID** and object path to be invalidated.

```
import json
import boto3

cloud_front = boto3.client("cloudfront")

def lambda_handler(event, context):
    # Extract information from S3 event
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = event['Records'][0]['s3']['object']['key']

    # Specify the CloudFront distribution ID
    distribution_id = "your_cloudfront_distribution_id"

    # Specify the object path to be invalidated (e.g., '/*' to invalidate all)
    object_paths = ["/*"]

    try:
        # Create CloudFront invalidation
        cloud_front.create_invalidation(
            DistributionId=distribution_id,
            InvalidationBatch={
                'Paths': {
                    'Quantity': len(object_paths),
                    'Items': object_paths,
                },
                'CallerReference': str(hash(key)),  # You can use a different reference based on your needs
            }
        )
    except Exception as e:
        print(f"Error: {e}")
        raise e
```

Step 2: Assign required permissions
Go to your lambda IAM role and attach an inline policy for lambda to invalidate cache. Replace **ACCOUNT_ID** and **DISTRIBUTION_ID** with your values.

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "cloudfront:CreateInvalidation",
            "Resource": "arn:aws:cloudfront::ACCOUNT_ID:distribution/DISTRIBUTION_ID"
        }
    ]
}
```

Step 3: Configure S3 event trigger
Navigate to your S3 bucket > Properties and create event notifications.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/win10aac98b20unmzmzg.png)

Select your lambda function as the destination and click save.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rdrydlo2birlioxhm1u4.png)



**Approach 2**:

Invalidate the cache by triggering a lambda using CodePipeline.
In this approach, the steps are pretty similar, but instead of S3 event notifications, our lambda function will be triggered by CodePipeline itself.

Step 1: Create Lambda function
Give a relevant name, select Python as a runtime environment, and select Create a new role.



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4wwt572vk12a7vvf23tc.png)

Paste the below code in your lambda function and **deploy** it. There is no need to change anything in the code.

```
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,
        )
```

Step 2: Assign required permissions
Go to your lambda IAM role and attach an inline policy for lambda to invalidate cache. Replace ACCOUNT_ID and DISTRIBUTION_ID with your values.

```
{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Sid": "VisualEditor0",
   "Effect": "Allow",
   "Action": [
    "logs:CreateLogGroup",
    "cloudfront:CreateInvalidation"
   ],
   "Resource": [
    "arn:aws:logs:us-east-1:ACCOUNT_ID:*",
    "arn:aws:cloudfront::ACCOUNT_ID:distribution/DISTRIBUTION_ID"
   ]
  },
  {
   "Sid": "VisualEditor1",
   "Effect": "Allow",
   "Action": [
    "codepipeline:PutJobFailureResult",
    "codepipeline:PutJobSuccessResult"
   ],
   "Resource": "*"
  },
  {
   "Sid": "VisualEditor2",
   "Effect": "Allow",
   "Action": [
    "logs:CreateLogStream",
    "logs:PutLogEvents"
   ],
   "Resource": "arn:aws:lambda:us-east-1:ACCOUNT_ID:function:InvalidationCachePipeline:*"
  }
 ]
}
```

Step 3: Configure CodePipeline
In order to automate the cache invalidation process, we will be adding a new stage to our code pipeline. Navigate to your frontend pipeline and click on Edit button.

Click on “Add stage” to add a new stage after deploying content to Amazon S3.

Name the stage “Cache-Invalidate” and add an action group.

Specify action name of your choice
Action Provider: AWS Lambda
Region: Your region
Input Artifacts: Select buildartifact
Function Name: Select your function
User Parameters: Specify your distribution ID and object path to be invalidated.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/357h3o6i6l2q7r3s9ybo.png)

```
{"distributionId": "1234567890","objectPaths":["/*"]}
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fu9ym631636yxc1gnafk.png)

Click done, and save your pipeline. Now, it’s time it's done, whenever the pipeline is triggered the Cloudfront will invalidate once the deployment is done.

Hurray😍 our workflow is working as expected and our lambda function created new invalidation for our cloudfront distribution.




#DevOps #AWS #CloudEngineer #Automation #Cloud #SRE #SolutionsArchitect