Using AWS to Host a Static Website - Part 6

Using AWS to Host a Static Website - Part 6

Table of Contents

In part 5 I created the SSL certificates to use with the static website. In this post, I am going to deploy Amazon CloudFront for the CDN portion. Before I do that, I am going to upload the static HTML files that will be rendered.

Using the AWSCLI to Upload Content

The content can be whatever but for ease I have provided the HTML files I am using here.

This assumes that the files to be uploaded are available locally. In this example they are in the directory static-files.

aws s3 sync ./static-files/ s3://S3_BUCKET_NAME --delete --cache-control max-age=3153600 --region us-east-1

So breaking this down.

  1. aws s3 sync: This command synchronizes the contents between a source and destination location. It recursively copies new and updated files
  2. ./static-files/: This is the source directory on your local machine containing the files you want to sync
  3. s3://S3_BUCKET_NAME: This is the destination - an S3 bucket where the files will be synced to. This was defined earlier in the series of posts
  4. --delete: This flag is important - it will remove files from the destination (S3 bucket) that don’t exist in the source directory. This ensures the destination exactly matches the source
  5. --cache-control max-age=3153600: This sets the Cache-Control header for all uploaded files to have a maximum age of 3,153,600 seconds (approximately 36.5 days). This tells browsers and other clients how long they should cache these files
  6. --region us-east-1: Specifies the AWS region where the S3 bucket is located (US East 1 in this case)

The Sequence

  1. Create the Amazon S3 bucket policy
  2. Create the Amazon CloudFront Origin Access Control (OAC)
  3. Create the Amazon CloudFront Distribution

The CloudFormation Template

This CloudFormation template is available here.

AWSTemplateFormatVersion: "2010-09-09"
Description: "CloudFront distribution with existing S3 bucket as origin"

Parameters:
  ExistingBucketName:
    Type: String
    Description: "Name of the existing S3 bucket to use as origin"
  DomainName:
    Type: String
    Description: "The apex domain name (e.g., example.com)"
  AcmCertificateArn:
    Type: String
    Description: "ARN of ACM certificate (must be in us-east-1)"
  TagValue:
    Type: String
    Description: "The tag value for the distribution"

Resources:
  CloudFrontOriginAccessControl:
    Type: "AWS::CloudFront::OriginAccessControl"
    Properties:
      OriginAccessControlConfig:
        Name: !Sub "${AWS::StackName}-OAC"
        Description: "Origin Access Control for S3"
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4

  CloudFrontDistribution:
    Type: "AWS::CloudFront::Distribution"
    Properties:
      DistributionConfig:
        Enabled: true
        PriceClass: PriceClass_100 # US, Canada, Europe (not Middle East) Edge Locations
        DefaultRootObject: index.html
        Aliases:
          - !Ref DomainName
          - !Sub "www.${DomainName}"
        ViewerCertificate:
          AcmCertificateArn: !Ref AcmCertificateArn
          MinimumProtocolVersion: TLSv1.2_2021
          SslSupportMethod: sni-only
        Origins:
          - DomainName: !Sub "${ExistingBucketName}.s3.${AWS::Region}.amazonaws.com"
            Id: S3Origin
            S3OriginConfig:
              OriginAccessIdentity: ""
            OriginAccessControlId: !GetAtt CloudFrontOriginAccessControl.Id
        DefaultCacheBehavior:
          TargetOriginId: S3Origin
          ViewerProtocolPolicy: redirect-to-https
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # Managed-CachingOptimized
          OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf # Managed-CORS-S3Origin
      Tags:
        - Key: workload
          Value: !Ref TagValue

  S3BucketPolicy:
    Type: "AWS::S3::BucketPolicy"
    Properties:
      Bucket: !Ref ExistingBucketName
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: "AllowCloudFrontServicePrincipalReadOnly"
            Effect: Allow
            Principal:
              Service: "cloudfront.amazonaws.com"
            Action: "s3:GetObject"
            Resource: !Sub "arn:${AWS::Partition}:s3:::${ExistingBucketName}/*"
            Condition:
              StringEquals:
                "AWS:SourceArn": !Sub "arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}"

Outputs:
  DistributionDomainName:
    Description: "CloudFront Distribution Domain Name"
    Value: !GetAtt CloudFrontDistribution.DomainName

The parameter file.

[
    { 
        "ParameterKey": "ExistingBucketName", 
        "ParameterValue": "INSERT_BUCKET_NAME" 
    },
    { 
        "ParameterKey": "DomainName", 
        "ParameterValue": "INSERT_DOMAIN_NAME" 
    },
    { 
        "ParameterKey": "AcmCertificateArn", 
        "ParameterValue": "INSERT_CERTIFICATE_ARN" 
    },
    { 
        "ParameterKey": "TagValue", 
        "ParameterValue": "INSERT_DOMAIN_NAME" 
    }
]

Deploying the CloudFormation Template

aws cloudformation create-stack --stack-name STACKNAME --template-body file://cloufront-distribution.yaml \
--parameters file://parameters.json --region us-east-1 

The STACKNAME can be whatever you choose but I recommend it is meaningful.

Conclusion

A CloudFormation template and a parameter file is a repeatable way of setting up CDN using Amazon CloudFront. In the next post I will walk through creating the appropriate DNS records.


Using AWS to Host a Static Website - Part 7

Related Posts

Preparing for AWS re:Invent 2024

Preparing for AWS re:Invent 2024

Its getting close to that time of year again. This will be my third time at re:Invent and I am expecting it to be the same physical and mental workout that it has been in the past.

Read More
Using AWS to Host a Static Website - Part 1

Using AWS to Host a Static Website - Part 1

For years I have been uses various platforms to host a static website and one of the easiest ways, for me, has been to use AWS services to do so. At the core of it, there is Amazon S3 which is cheap and easy to configure as a static website. You can add several other AWS services to this to provide a scalable and robust solution for hosting a static website.

Read More
Using AWS to Host a Static Website - Part 7

Using AWS to Host a Static Website - Part 7

In part 6 I created the CDN distribution using Amazon CloudFront which tied the SSL certificates, HTTP to HTTPS redirection and the static content on the Amazon S3 bucket together. In this post, I am creating the Amazon Route 53 alias records to point at the distribution that I created.

Read More