How to create a website using S3 + Certificate Manager + Cloud Front with CDK ?
Tags: aws, serverless, typescript, javascript
Published: 2021-05-05 00:17:20

#Prerequisites 

Before start we need a few things installed on our computer. 

1) npm
2) NodeJs + TypeScript(2.7 or later)
3) [CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html)

After that we have to set up your aws credentials, to achieve that you can follow this [link](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/setup-credentials.html)

The last one, you should have created a hosted zone in AWS and delegate your domain. 

Well, we can start!!

#Configure SSL certificate

```javascript
const hostedZone = HostedZone.fromLookup(this,"myZone", {
    domainName: "example.com"
 });

const certificate = new acm.Certificate(this, `myCertificate`, {
      domainName: "example.com",
      subjectAlternativeNames: ["www.example.com"],
      validation: acm.CertificateValidation.fromDns(hostedZone),
});

``` 
On the first part we got the hosted zone related to your domain. 
After that we created the SSL certificate using `Certificate Manager`. Adding the key `validation` into the `CM`, `AWS` automatically add the necessary records to validate your domain. 


#Set up bucket

```javascript
const bucket = new Bucket(this, `myBucket`, {
      encryption: BucketEncryption.S3_MANAGED,
      bucketName: `your-bucket-name`,
      cors: [
        {
          allowedMethods: [HttpMethods.GET, HttpMethods.HEAD],
          allowedOrigins: ["*"],
          allowedHeaders: ["*"],
        },
      ],
});
```
In this way we create the bucket. It's recommended to use the domain's name as bucket's name and use a prefix to difference the environments. 

## Set up policy and CDNOAI

In this part, I am going to explain step by step, because It could be a bit confuse.


```javascript
const CDNOAI = new CfnCloudFrontOriginAccessIdentity(
      this, "myCDNOAI", {
        cloudFrontOriginAccessIdentityConfig: {
          comment: "S3 OAI",
        },
      }
);
```

### What is cloud front origin access identity ?


> An Origin Access Identity (OAI) is used for sharing private content via CloudFront. The OAI is a virtual user identity that will be used to give your CF distribution permission to fetch a private object from your origin server (e.g. S3 bucket).
 
```javascript
const policy = new iam.PolicyStatement({
      actions: ["s3:Get*"],
      effect: Effect.ALLOW,
      resources: [bucket.bucketArn, bucket.arnForObjects("*")],
});
```
We created a policy to allow get object on the whole bucket.

 ```javascript
policy.addCanonicalUserPrincipal(CDNOAI.attrS3CanonicalUserId);
```
We add our `CDNOAI` to our policy.

```javascript
const bucketPolicy = new BucketPolicy(this, `myBucketPolicy`, { 
  bucket,
});

bucketPolicy.document.addStatements(policy);

```
The last step of this section is create a bucket policy and attach to it our policy.

## Set up Cloud front

```javascript

const cloudFront = new cloudfront.Distribution(
      this,
      `myCloudFront`,
      {
        defaultBehavior: {
          origin: new origins.S3Origin(bucket),
          allowedMethods: AllowedMethods.ALLOW_GET_HEAD,
          viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        },
        certificate,
        defaultRootObject: "index.html",
        domainNames: ["example.com"],
        enabled: true,
        errorResponses: [
          {
            ttl: Duration.days(1),
            httpStatus: 404,
            responseHttpStatus: 200,
            responsePagePath: "/index.html",
          },
          {
            ttl: Duration.days(1),
            httpStatus: 403,
            responseHttpStatus: 200,
            responsePagePath: "/index.html",
          },
        ],
      }
    );
```
This is a basic cloudfront configuration. 

I want to highlight the `certificate` variable. It is the same certificate that we previously created.

## Create A record

```javascript
new ARecord(this, `myARecord`, {
      zone: hostedZone,
      ttl: Duration.minutes(5),
      target: RecordTarget.fromAlias(new targets.CloudFrontTarget(cloudFront)),
      recordName: "example.com",
    });
```

In this part we are going to create an A record within our hosted zone, and reference it with our cloud front. 




# GITHUB

This is my github, and here you are going to find a full complete example using CDK to create and deploy powerfull webapps!!

https://github.com/skaznowiecki/cdk-s3-cloudfront