DEV Community

Cover image for Automate Building a Unique Domain Hosting Environment with AWS CDK
Yasunori Kirimoto for AWS Community Builders

Posted on

Automate Building a Unique Domain Hosting Environment with AWS CDK

img

I automated building a unique domain hosting environment with AWS CDK πŸŽ‰

In my previous article, "Build a Unique Domain Hosting Environment with Amazon Route 53, AWS WAF, Amazon CloudFront, and Amazon S3" and "Automate Building a Unique Domain Hosting Environment with AWS CloudFormation," I tried to implement the contents of these articles with AWS CDK.

I've made the template available on GitHub, so please use it!

aws-cdk-templates-showcase

/lib/unique-domain-hosting-stack.ts

import { Stack, StackProps, RemovalPolicy, CfnOutput } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as cdk from 'aws-cdk-lib';
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
import * as route53 from 'aws-cdk-lib/aws-route53';
import * as targets from 'aws-cdk-lib/aws-route53-targets';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';

export class UniqueDomainHostingStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    const domainName = this.node.tryGetContext('domainName');
    const {
      accountId,
      region,
    } = new cdk.ScopedAws(this);
    const hostedZoneId = route53.HostedZone.fromLookup(this, 'HostedZoneId', {
      domainName: domainName
    });
    const certificateManagerCertificate = new acm.DnsValidatedCertificate(this, 'CertificateManagerCertificate', {
      domainName: domainName,
      hostedZone: hostedZoneId,
      region: 'us-east-1',
      validation: acm.CertificateValidation.fromDns(),
    });
    const s3Bucket = new s3.Bucket(this, 'S3Bucket', {
      bucketName: `${domainName}-${region}-${accountId}`,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      removalPolicy: RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    });
    const cloudFrontOAI = new cloudfront.OriginAccessIdentity(this, 'CloudFrontOriginAccessIdentityy', {
      comment: 'Unique Domain Hosting Environment',
    })
    const cloudFrontDistribution = new cloudfront.Distribution(this, 'CloudFrontDistribution', {
      domainNames: [domainName],
      defaultBehavior: {
        origin: new origins.S3Origin(s3Bucket, {
          originAccessIdentity: cloudFrontOAI
        }),
        compress: true,
        allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD,
        cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD,
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
      },
      errorResponses: [
        {
          httpStatus: 403,
          responsePagePath: '/index.html',
          responseHttpStatus: 200,
          ttl: cdk.Duration.minutes(0),
        },
        {
          httpStatus: 404,
          responsePagePath: '/index.html',
          responseHttpStatus: 200,
          ttl: cdk.Duration.minutes(0),
        },
      ],
      priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
      enabled: true,
      certificate: certificateManagerCertificate,
      minimumProtocolVersion: cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021,
      httpVersion: cloudfront.HttpVersion.HTTP2,
      defaultRootObject: 'index.html',
      enableIpv6: true,
    })
    new route53.ARecord(this, 'Route53RecordSet', {
      recordName: domainName,
      zone: hostedZoneId,
      target: route53.RecordTarget.fromAlias(
        new targets.CloudFrontTarget(cloudFrontDistribution)
      ),
    });
    new s3deploy.BucketDeployment(this, 'S3BucketDeploy', {
      sources: [s3deploy.Source.asset('./dist')],
      destinationBucket: s3Bucket,
      distribution: cloudFrontDistribution,
      distributionPaths: ['/*'],
    });
    new CfnOutput(this, 'DeployURL', {
      value: `https://${domainName}`,
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

/bin/unique-domain-hosting.ts

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { UniqueDomainHostingStack } from '../lib/unique-domain-hosting-stack';

const app = new cdk.App();
new UniqueDomainHostingStack(app, 'UniqueDomainHostingStack', {
  env: {
    account: process.env.CDK_DEFAULT_ACCOUNT,
    region: process.env.CDK_DEFAULT_REGION
  },
});
Enter fullscreen mode Exit fullscreen mode

cdk.json

{
  "app": "npx ts-node --prefer-ts-exts bin/unique-domain-hosting.ts",
  "watch": {
    "include": [
      "**"
    ],
    "exclude": [
      "README.md",
      "cdk*.json",
      "**/*.d.ts",
      "**/*.js",
      "tsconfig.json",
      "package*.json",
      "yarn.lock",
      "node_modules",
      "test"
    ]
  },
  "context": {
    "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
    "@aws-cdk/core:stackRelativeExports": true,
    "@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
    "@aws-cdk/aws-lambda:recognizeVersionProps": true,
    "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true,
    "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
    "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
    "@aws-cdk/core:target-partitions": [
      "aws",
      "aws-cn"
    ],
    "domainName": "Domain Name Settings"
  }
}
Enter fullscreen mode Exit fullscreen mode

package.json

{
  "name": "unique-domain-hosting",
  "version": "0.1.0",
  "bin": {
    "unique-domain-hosting": "bin/unique-domain-hosting.js"
  },
 "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "test": "jest",
    "cdk": "cdk"
  },
  "keywords": [],
  "author": "Yasunori Kirimoto",
  "license": "ISC",
  "devDependencies": {
    "@types/jest": "^26.0.10",
    "@types/node": "10.17.27",
    "jest": "^26.4.2",
    "ts-jest": "^26.2.0",
    "aws-cdk": "2.15.0",
    "ts-node": "^9.0.0",
    "typescript": "~3.9.7"
  },
  "dependencies": {
    "aws-cdk-lib": "2.15.0",
    "constructs": "^10.0.0",
    "source-map-support": "^0.5.16"
  }
}
Enter fullscreen mode Exit fullscreen mode

Advance Preparation

img

Execution environment

  • node v16.10.0
  • npm v7.24.0

How to build

  1. Deployment of files and updating of configuration files
  2. Auto-deploy unique domain hosting environment

Deployment of Files and Updating of Configuration Files

First, update each file arrangement and configuration file.

Use the "unique-domain-hosting" directory in the "aws-cdk-templates-showcase" repository or copy it to an arbitrary location.
img

Set the unique domain name obtained from Amazon Route 53 in "cdk.json."

"domainName": "Domain Name Settings"
Enter fullscreen mode Exit fullscreen mode

Copy the set of application files you wish to deploy to the "dist" directory.
img

Auto-Deploy Unique Domain Hosting Environment

Finally, we will automatically deploy our unique domain hosting environment.

Install package

npm install
Enter fullscreen mode Exit fullscreen mode

Execute the following command only the first time before deploying. Also, run this command when you change the region.

cdk bootstrap
Enter fullscreen mode Exit fullscreen mode

img

Deploy the project.

cdk deploy
Enter fullscreen mode Exit fullscreen mode

img

Confirm that the stack has been created. You can see that each service is also created automatically.
img

When you access your unique domain, you will see the deployed WebSite.
img

Using AWS CDK makes it possible to automate the construction of various resources, such as your unique domain hosting. In this case, it was possible to create ACM in a fixed region, automatically generate S3 bucket policies for CloudFront, and define the application to be deployed in the code with a small amount of code. Each feature definition needs to be learned from the documentation as well as AWS CloudFormation, but it is a very promising tool, and it’s fun to build.

I hope to automate a lot more in the future!

Related Articles

References

AWS CDK
API Reference

Latest comments (0)