Posts Building S3 Static Website Using AWS CDK
Post
Cancel

Building S3 Static Website Using AWS CDK

Buckle up because we’re about to embark on an exciting journey into the world of website building using AWS CDK. I’ve been bombarded with requests to spill the beans on the code I’ve conjured up, and boy, am I ready to spill! The diagram above illustrates the architecture.

Picture this: we’re in a magical land called TypeScript, armed with the powerful AWS CDK libraries. Our mission? To create a mind-blowing website with an S3 bucket, a CloudFront distribution, and a Route53 record. But hold your horses, my friends! Before we dive in, let’s make sure you’ve already snagged yourself a fancy domain name from the one and only AWS Route 53. Good? Great!

Now, here’s the secret sauce: we’re using the marvelous js-yaml library to grab all the juicy details from a YAML file. Think of it as finding the treasure map that reveals the domain name, subdomain, and hostedZoneID. It’s like being handed a golden ticket to personalize our website our own domain name. The config.yaml below is configured for test.example.com URL.

1
2
3
domain : example.com
subDomain : test
hostedZoneID : Zxxxxxxxxxx

The crux of all the excitement and magic lies within a single file called aws-cdk-static-website-stack.ts. This little gem holds the key to unlocking the wonders of our app.

Setting the Stage

Our code begins with importing the necessary libraries, including AWS CDK modules such as S3, Certificate Manager, CloudFront, Route53, and more.

1
2
3
4
5
6
7
8
9
10
11
12
13
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as route53 from 'aws-cdk-lib/aws-route53';
import * as targets from 'aws-cdk-lib/aws-route53-targets';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
import * as yaml from 'js-yaml'; 
import * as fs from 'fs';

Unveiling the Configurations

Intriguingly, our code taps into the secrets stored within a configuration file called ‘config.yaml’. Using the power of the js-yaml library, we unlock the hidden details of our website’s domain, subdomain, and hosted zone ID. The console then eagerly reveals this vital information to keep us informed and excited about our journey.

1
2
3
4
5
6
7
const config = fs.readFileSync('config.yaml', 'utf8');
const configData : ConfigData = <ConfigData> yaml.load(config);

console.log("configDate.domain  : ############# " , configData.domain);
console.log("configData.Subdomain  : ############# " , configData.subDomain);
console.log("configData.hostedZoneID  : ############# " , configData.hostedZoneID);

Building the Foundation

With the stage set and the configurations at our fingertips, we begin constructing the foundation of our website. Our hero, the S3 bucket, emerges to store the website’s assets securely. Equipped with a custom name, index and error documents, and public read access, the bucket becomes the cornerstone of our dynamic website.

1
2
3
4
5
6
7
8
    const bucket = new s3.Bucket(this, configData.subDomain+'-'+configData.domain, {
      bucketName:  configData.subDomain+'.'+configData.domain,
      websiteIndexDocument: 'index.html',
      websiteErrorDocument: 'error.html',
      publicReadAccess: true,
      removalPolicy: cdk.RemovalPolicy.DESTROY
   
    });

Deploying the Website

But wait, there’s more! In an unexpected twist, we unleash the powerful s3deploy module, allowing us to deploy the website’s static files directly to our heroic S3 bucket. The stage is now set for a stunning display of creativity and innovation.

1
2
3
4
    new s3deploy.BucketDeployment(this, 'MyDeployment', {
      sources: [s3deploy.Source.asset('static-website-files')],
      destinationBucket: bucket,
    });

Charting the Territory

No adventure is complete without navigation, and in our case, we enlist the help of Route53. We connect our website to a hosted zone, a mystical realm where DNS records reside. The correct hosted zone ID and domain name provide us with a clear path to our website’s destination.

1
2
3
4
5
6
    const siteHostedZone = route53.HostedZone.fromHostedZoneAttributes(this,'siteHostedZone',  { 
      hostedZoneId: configData.hostedZoneID, // Provide correct hostedzoneid
      zoneName: configData.domain, 
    
    })

Securing the Journey

To protect our users and establish trust, we acquire an ACM certificate—a shining armor of encryption. With a domain name like ‘*.example.com,’ we wield the power to secure all subdomains, ensuring a safe and seamless experience for our visitors.

1
2
3
4
5
    const certificate = new acm.Certificate(this, 'SiteCertificate', {
      domainName:  '*.'+configData.domain, //'*.configData.domain',
      validation: acm.CertificateValidation.fromDns(siteHostedZone),
    });

Enabling the CloudFront Show

The climax of our journey nears as we unveil the CloudFront distribution—an enchanting force that brings speed and global reach to our website. The CloudFront distribution guards our website’s content, and by setting the viewer protocol policy to redirect to HTTPS, we guarantee a secure connection for all.

1
2
3
4
5
6
7
8
9
    const distribution = new cloudfront.Distribution(this, 'SiteDistribution', {
      defaultBehavior: { origin: new origins.S3Origin(bucket), viewerProtocolPolicy : cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS },
      domainNames: [ configData.subDomain+'.' +  configData.domain],
  
      certificate: certificate,
      
    });

Directing the Spectacle

Our grand finale is orchestrated by none other than Route53, the maestro of DNS management. An A record is created, pointing to our website’s CloudFront distribution, ensuring that visitors arrive at the correct destination. The TTL (Time to Live) of 30 seconds keeps the performance tempo high, promising a smooth and responsive experience.

1
2
3
4
5
6
7
8
    new route53.ARecord(this, 'siteRecord', {
      zone: siteHostedZone,
      target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),
      recordName: configData.subDomain+'.'+ configData.domain,
      ttl : cdk.Duration.seconds(30),

      
    });

Conclusion

Congratulations, brave adventurers! You have successfully navigated the enthralling world of infrastructure-as-code with AWS CDK. Together, we built a dynamic website, unveiled its secrets, deployed its assets, secured its path, and directed its audience. This thrilling journey proves that with the right tools and a dash of imagination, anything is possible. So go forth and create your own captivating website, for the world eagerly awaits your next masterpiece! Find the enchanting code that conjures this captivating website on the GitHub repository: https://github.com/PradeepMeruva/aws-cdk-static-website. It’s a digital treasure trove awaiting your exploration!

This post is licensed under CC BY 4.0 by the author.