import datetime import os import sys import boto3 from botocore.exceptions import ClientError APP_NAME = os.environ.get('APP_NAME') ENV = os.environ.get('ENV') ITEMS_TO_INVALIDATE = os.environ.get( 'ITEMS_TO_INVALIDATE', '/index.html').split(',') def create_invalidation(client, distribution, items): """ Create an invalidation for a Cloudfront distribution Args: client (obj): a boto3 Cloudfront client distribution (str): a Cloudfront distribution ID Returns: dict: an invalidation """ try: response = client.create_invalidation( DistributionId=distribution, InvalidationBatch={ 'Paths': { 'Quantity': len(items), 'Items': items }, 'CallerReference': datetime.datetime.now().strftime( '%Y%m%d%H%M%S') } ) except ClientError as error: print(error.response['Error']['Message']) raise SystemExit(f'Error creating invalidation for {distribution}') return response['Invalidation'] def find_distributions_to_invalidate(client): """ Find distributions to invalidate based on ENV, with which the distribution description/comment should start, and APP_NAME, which should match the origin path of one or more origins in a given distribution Args: client (obj): a boto3 Cloudfront client Returns: Response: a dict of label information. """ distributions_to_invalidate = [] response = client.list_distributions() distributions = response['DistributionList']['Items'] for distribution in distributions: origin_paths = [origin['OriginPath'] for origin in distribution[ 'Origins']['Items']] if any(APP_NAME in origin_path for origin_path in origin_paths) and \ distribution['Comment'].startswith(ENV): distributions_to_invalidate.append(distribution['Id']) if distributions_to_invalidate: print('Distributions to invalidate: {}'.format( distributions_to_invalidate)) return distributions_to_invalidate else: print('No distributions found for {}'.format(APP_NAME)) sys.exit(0) def main(): assert APP_NAME, 'Environment variable APP_NAME must be set' assert ENV, 'Environment variable ENV must be set' client = boto3.client('cloudfront') for index, item in enumerate(ITEMS_TO_INVALIDATE): # Paths must start with a '/' if not item.startswith('/'): ITEMS_TO_INVALIDATE[index] = '/' + item print('Items to invalidate:{}'.format(ITEMS_TO_INVALIDATE)) invalidation_distributions = find_distributions_to_invalidate(client) for distribution in invalidation_distributions: invalidation = create_invalidation( client, distribution, ITEMS_TO_INVALIDATE) print('Invalidation created for {}; ID: {}, Status: {}'.format( distribution, invalidation['Id'], invalidation['Status'])) if __name__ == "__main__": main()