Cloudfront Invalidation from Cross Account CodePipeline

Subhas Patil
3 min readAug 14, 2021

--

I’m using Amazon CloudFront to serve objects stored in Amazon Simple Storage Service (Amazon S3). The S3 bucket contains the nodejs application files which are built and deployed via CodePipeline located in different AWS account from Cloudfront.
In large organizations, there are multiple AWS accounts in Landing Zone architecture where the codepipeline for all the environments are setup in a dedicated aws account, commonly in Shared Services account.

Lets say we have Codepipeline in Account A and
Cloudfront, S3 bucket in Account B.

Problem Statement:
When the Codepipeline deploys the code to S3 and updates the existing files, CloudFront distribution still serves the previous versions of those files because by default, CloudFront caches a response from Amazon S3 for 24 hours (Default TTL of 86,400 seconds). If your request lands at an edge location that served the Amazon S3 response within 24 hours, then CloudFront uses the cached response even if you updated the content in Amazon S3.

The challenge is to invalidate the cloudfront files located in Account B from Codepipeline in Account A.

Solution:
Create a Lambda python function in Account A, attach a role to this lambda which will assume a role from Account B and perform the invalidation.
Create an action at the end of CodePipeline stage with Invoke Lambda action.

Steps involved:

  1. Create a role in Account B which will be used by the lambda role from Account A.
    Role Name: CloudfrontAssumeRole
    Policy Attached: CloudFrontFullAccess
    Trust Relationship Policy JSON:

Note: You can allow specific roles from Account A by inserting the role arn in the Principal section above. But here Iam allowing any roles from Account A to assume this role.

2. Create an assume role policy in Account A.
Policy name: CloudfrontInvalidation

3. Create a policy for CodePipeline to put success status when Codepipeline Invokes the lambda function.
Policy name: CodePipelineLambdaExecPolicy

4. Create a policy in Account A with permissions to invoke lambda function.

5. Create a role in Account A and attach the above 3 policies.
Role name: AWSCloudfrontInvalidation
Policies attached:
- AWSLambdaBasicExecutionRole
- CodePipelineLambdaExecPolicy
- CloudfrontInvalidation

6. Create a lambda function and attach the above role to it.
Pass the Cloudfront Distribution ID in the lambda code along with the Account B ID.

import boto3
import time
import json
import logging
def lambda_handler(event, context):sts_connection = boto3.client('sts')
acct_b = sts_connection.assume_role(
RoleArn="arn:aws:iam::{Account B ID}:role/CloudfrontAssumeRole",
RoleSessionName="cross_acct_lambda"
)

ACCESS_KEY = acct_b['Credentials']['AccessKeyId']
SECRET_KEY = acct_b['Credentials']['SecretAccessKey']
SESSION_TOKEN = acct_b['Credentials']['SessionToken']
client = boto3.client(
'cloudfront',
aws_access_key_id=ACCESS_KEY,
aws_secret_access_key=SECRET_KEY,
aws_session_token=SESSION_TOKEN,
)

response = client.create_invalidation(
DistributionId='CLOUDFRONT_DISTRIBUTION_ID_HERE',
InvalidationBatch={
'Paths': {
'Quantity': 1,
'Items': [
'/*',
]
},
'CallerReference': str(time.time()).replace(".", "")
}
)
invalidation_id = response['Invalidation']['Id']

print("Invalidation created successfully with Id: " + invalidation_id)

logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.debug(json.dumps(event))

codepipeline = boto3.client('codepipeline')
job_id = event['CodePipeline.job']['id']

try:
#raise ValueError('This message will appear in the CodePipeline UI.')
logger.info('Success!')
response = codepipeline.put_job_success_result(jobId=job_id)
logger.debug(response)
except Exception as error:
logger.exception(error)
response = codepipeline.put_job_failure_result(
jobId=job_id,
failureDetails={
'type': 'JobFailed',
'message': f'{error.__class__.__name__}: {str(error)}'
}
)
logger.debug(response)

7. Create an action AWS Lambda in the codepipeline and select the above created Lambda function.

Conclusion:
Now we can invalidate cloudfront cache from cross account codepipeline thus making it a complete CI/CD pipeline.

--

--

Subhas Patil
Subhas Patil

Written by Subhas Patil

Devops Engineer - Prodt Consulting Services, AWS Certified Solutions Architect | Terraform Certified

No responses yet