DEV Community

loading...
Viyet

Host Static website using AWS CDK for Terraform and CloudFront: Part 2

thakkaryash94 profile image Yash Thakkar Updated on ・2 min read

In part 1, we saw how we can host our website using S3. In this part, we will see how we can configure AWS CloudFront to serve our S3 bucket objects as website. In case, if you have not checked out the Part 1, please read this first.

Let's setup CloudFront distribution using AWS CDK of Terraform. We need to create CloudFront Origin Access Identity(OAI), which we will use for CloudFront and S3 bucket policy.

import { CloudfrontOriginAccessIdentity } from './.gen/providers/aws';

/*
 * Create am Origin Access Identity
 * Doc link: https://aws.amazon.com/premiumsupport/knowledge-center/cloudfront-access-to-amazon-s3/
 * Tutorial link: https://aws.amazon.com/premiumsupport/knowledge-center/cloudfront-access-to-amazon-s3/
 */
const cloudfrontOriginAccessIdentity = new CloudfrontOriginAccessIdentity(this, 'aws_cloudfront_origin_access_identity', {
  comment: 's3-cloudfront-cdk-example'
})
Enter fullscreen mode Exit fullscreen mode

Now, we need to set few required parameters for the CloudFront configuration.

  • dependsOn: Bucket is required to setup CloudFront.
  • defaultRootObject: index.html is our default file that we need to serve.
  • customErrorResponse: We can setup custom rules/response for errors like 400, 404, 500, 501 etc.
  • origin:
    • originId: unique id (should be same as targetOriginId)
    • domainName: S3 bucket as domain (eg. thakkaryash94-cdk-dev.s3.amazonaws.com)
  • defaultCacheBehavior:
    • targetOriginId: unique id (should be same as originId)
  • restrictions: We want our website be to accessible from everywhere, so set it to none.
  • viewerCertificate: We can use CloudFront default certificate and can also add custom ACM certificate, IAM certificate etc.
import { CloudfrontDistribution } from './.gen/providers/aws';

const originId = `S3-${BUCKET_NAME}`;

const cloudFrontDistribution = new CloudfrontDistribution(this, `aws_cloudfront_${BUCKET_NAME}`, {
  enabled: true,
  dependsOn: [bucket],
  defaultRootObject: 'index.html',
  customErrorResponse: [{
    errorCode: 404,
    responseCode: 200,
    responsePagePath: '/index.html'
  }],
  origin: [{
    originId: originId,
    domainName: bucket.bucketDomainName,
    s3OriginConfig: [{
      originAccessIdentity: cloudfrontOriginAccessIdentity.cloudfrontAccessIdentityPath
    }]
  }],
  defaultCacheBehavior: [{
    allowedMethods: ['GET', 'HEAD'],
    cachedMethods: ['GET', 'HEAD'],
    forwardedValues: [{
      cookies: [{ forward: 'none' }],
      queryString: false
    }],
    targetOriginId: originId,
    viewerProtocolPolicy: 'allow-all'
  }],
  restrictions: [{
    geoRestriction: [{
      restrictionType: 'none'
    }]
  }],
  viewerCertificate: [{
    cloudfrontDefaultCertificate: true
  }],
});
Enter fullscreen mode Exit fullscreen mode

Previously, our bucket was public, it means anyone can access bucket objects using bucket website URL. Now, we have configured CloudFront to serve our website, so it's time to block that access. With this, our website will be only accessible by CloudFront URL only. No-one will be able to access bucket objects using S3 website URL.

bucket.acl = 'private'            // Set bucket ACL as private
bucket.website = []               // Disable website hosting feature
bucket.policy = `{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${cloudfrontOriginAccessIdentity.id}"
      },
      "Action": [
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::${BUCKET_NAME}/*"
      ]
    }
  ]
}`
Enter fullscreen mode Exit fullscreen mode

Last, we will print the CloudFront url, which will serve our s3 objects as a website.

// Output the cloudfront url to access the website
new TerraformOutput(this, 'cloudfront_website_endpoint', {
  description: 'CloudFront URL',
  value: `https://${cloudFrontDistribution.domainName}`
});
Enter fullscreen mode Exit fullscreen mode

Now, we follow the same process to deploy the changes as per the Part 1. After successfull deployment, CloudFront public URL will be printed on the console and we will be able to access our website with default https certificate.

So this is how, we can setup CloudFront with AWS S3 using AWS CDK for Terraform.

GitHub logo thakkaryash94 / terraform-cdk-react-example

Host react website using terraform CDK on AWS S3

Host Static website using AWS CDK for Terraform

This repo contains the code for DEV.to blog

Links:

Discussion

pic
Editor guide
Collapse
shotlom profile image
Sholto Maud

interesting articles - I get access denied on my url
Could you perhaps test your repo, because I think you may need to comment out the "Output the bucket url to access the website" section which fails when I try.

Collapse
thakkaryash94 profile image
Yash Thakkar Author

Yes, we need to comment out the bucket access url because we are not serving s3 bucket as a url anymore. That's why we printing CloudFront URL. I kept it for the part 1, but for part 2, we need to comment out the s3 bucket url and can only access using CloudFront URL.

Collapse
shotlom profile image
Sholto Maud

I get access denied on my url - how did you make sure your Cloudfront can have access to the S3? Also, do you Route53 for your dns?

Thread Thread
thakkaryash94 profile image
Yash Thakkar Author

We have setup CloudFront Origin Access Identity(OAI) and also updated the S3 bucket permission as below.
{
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${cloudfrontOriginAccessIdentity.id}"
}

So with this, only CloudFront will be able to access the bucket using OAI.

Collapse