Host Static website using AWS CDK for Terraform and CloudFront: Part 2
Tags: aws, devops, terraform, cloudfront
Published: 2020-08-07 13:21:44

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. 

```typescript
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'
})
```

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.

```typescript
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
  }],
});
```

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.

```typescript
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}/*"
      ]
    }
  ]
}`
```

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

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

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 thakkaryash94/terraform-cdk-react-example %}

#### Links:

- [Terraform AWS Prorvider CloudFront Docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution)
- [Terraform CDK React Example GitHub](https://github.com/thakkaryash94/terraform-cdk-react-example)