You have a static website in an S3 bucket. You want to put CloudFront in front of that bucket site, for caching, SSL with a custom domain, etc. You want to use CloudFormation because Infrastructure as Code for the win, and artisanally hand-crafted systems for the lose.
In short, this is the magic you want in your CloudFormation template, with thanks to Eric Hammond and Ryan Brown:
SiteBucket:
Type: "AWS::S3::Bucket"
# Snip
Mappings:
RegionToS3DomainSuffix:
"us-east-1":
suffix: "s3-website-us-east-1.amazonaws.com"
"eu-central-1":
suffix: "s3-website.eu-central-1.amazonaws.com"
# I've not investigated other regions...
CF:
Type: "AWS::CloudFront::Distribution"
Properties:
DistributionConfig:
Origins:
-
DomainName:
"Fn::Join":
- "."
-
- !Ref SiteBucket
- !FindInMap [RegionToS3DomainSuffix, !Ref "AWS::Region", suffix]
Yes that’s an ugly hand-crafted Join
.
Yes that’s an ugly hand-crafted Mapping
to account for different regions having different bucket website URL conventions.
However, it works.
In an ideal world, AWS::S3::Bucket
s would have a WebsiteDomain
attribute so you could !GetAttr SiteBucket.WebsiteDomain
.
Alas at time of writing (October 2016) they don’t.
AWS::S3::Bucket.WebsiteURL
exists but it returns a URL, with leading http://
and trailing /
.
Stripping these within a CloudFormation template is beyond me.
As far as I can tell, icky region-specific substitutions are where it’s at for now!
Note: Originally I’d proposed a range of hardcoded Fn::Sub
s that only worked for a subset of regions.
Thanks to Eric Hammond for pointing out how it could be improved with Mappings