Skip to content

Instantly share code, notes, and snippets.

@NV
Last active March 1, 2024 11:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save NV/8d905ed2ac583dad5649c9ad1a0cc169 to your computer and use it in GitHub Desktop.
Save NV/8d905ed2ac583dad5649c9ad1a0cc169 to your computer and use it in GitHub Desktop.
AWS script to sync files with Amazon s3 and invalidate CloudFront paths that have been synced. I can't believe the AWS CLI doesn't do that in one command!

AWS s3 sync and CloudFront invalidation script

This is a scrappy script I put together to invalidate AWS CloudFront paths that have been uploaded to AWS S3.

Problem

If you host a static website on Amazon S3 and CloudFront, you need to create a CloudFront cache invalidation every time you upload files to Amazon S3.

For example, when I run aws s3 sync website/ s3://n12v.com, it may only upload website/index.html. Now I need to invalidate /index.html on CloudFront.

AWS CLI doesn't provide an easy way to do that.

How to use

Expected directory structure: my-project/website/.

Usage:

my-project $ ./sync.rb
#!/usr/bin/env ruby
DOMAIN = 'n12v.com' # replace with yours
PROFILE = 'nikita' # replace with yours
# Sync with S3
aws_s3_sync = "aws s3 sync --profile #{PROFILE} --delete --acl 'public-read' --exclude '*.DS_Store*' website/ s3://#{DOMAIN}"
puts aws_s3_sync
output = `#{aws_s3_sync}`
puts "-> " + output
# Invalidate CloudFront cache with the file paths that changed
file_paths = []
output.lines.each { |line|
# TODO: Handle "delete: s3://..."
m = line.match(%r@upload\: ([^ ]+) to s3\://@)
if m && m.captures
file_paths.push(m.captures[0])
end
}
if file_paths.size == 0
exit(0)
end
file_paths = file_paths.map {|p|
str = p.sub(%r@^website@, '') # replace with yours
'"' + str + '"'
}
aws_cloudfront_list_distributions = "aws cloudfront list-distributions --profile #{PROFILE} --query 'DistributionList.Items[?contains(Aliases.Items,`#{DOMAIN}`)].Id' --output text"
puts aws_cloudfront_list_distributions
CF_DISTRO_ID = `#{aws_cloudfront_list_distributions}`.strip
puts "-> " + CF_DISTRO_ID
paths_str = file_paths.join(' ')
aws_cloudfront_create_invalidation = "aws cloudfront create-invalidation --profile #{PROFILE} --distribution-id #{CF_DISTRO_ID} --paths #{paths_str}"
puts(aws_cloudfront_create_invalidation)
system(aws_cloudfront_create_invalidation)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment