Automate Building a Unique Domain Hosting Environment with AWS CDK
Tags: cdk, awscdk, route53, aws
Published: 2022-03-21 10:17:19

![img](https://day-journal.com/memo/images/try-085_00.png)

### I automated building a unique domain hosting environment with AWS CDK :tada:  

<br>

In my previous article, "[Build a Unique Domain Hosting Environment with Amazon Route 53, AWS WAF, Amazon CloudFront, and Amazon S3](https://dev.to/aws-builders/build-a-unique-domain-hosting-environment-with-amazon-route-53-aws-waf-amazon-cloudfront-and-amazon-s3-5eip)" and "[Automate Building a Unique Domain Hosting Environment with AWS CloudFormation](https://dev.to/aws-builders/automate-building-a-unique-domain-hosting-environment-with-aws-cloudformation-4ehl)," I tried to implement the contents of these articles with AWS CDK.

<br>

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

[aws-cdk-templates-showcase](https://github.com/dayjournal/aws-cdk-templates-showcase)

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

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

#### /bin/unique-domain-hosting.ts

```typescript
#!/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
  },
});
```

#### cdk.json

```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"
  }
}
```

#### package.json

```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"
  }
}
```

<br>

## Advance Preparation

- Installation and configuration of [AWS CLI](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html)
- [AWS CDK #001 - TypeScript environment setup](https://day-journal.com/memo/aws-cdk-001)
- Get a unique domain using Amazon Route 53

![img](https://day-journal.com/memo/images/try-085_07.png)

<br>

Execution environment

- node v16.10.0
- npm v7.24.0

<br>

## How to build

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

<br>

## 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](https://github.com/dayjournal/aws-cdk-templates-showcase)" repository or copy it to an arbitrary location.
![img](https://day-journal.com/memo/images/try-085_08.png)

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

```json
"domainName": "Domain Name Settings"
```

Copy the set of application files you wish to deploy to the "dist" directory.
![img](https://day-journal.com/memo/images/try-085_09.png)

<br>

## Auto-Deploy Unique Domain Hosting Environment

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

Install package

```sh
npm install
```

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

```sh
cdk bootstrap
```

![img](https://day-journal.com/memo/images/try-085_01.png)

Deploy the project.

```sh
cdk deploy
```

![img](https://day-journal.com/memo/images/try-085_02.png)

Confirm that the stack has been created. You can see that each service is also created automatically.
![img](https://day-journal.com/memo/images/try-085_03.png)

When you access your unique domain, you will see the deployed WebSite.
![img](https://day-journal.com/memo/images/try-085_04.png)

<br>

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!

<br>

Related Articles
{% link https://dev.to/aws-builders/automate-building-a-unique-domain-hosting-environment-with-aws-cloudformation-4ehl %}
{% link https://dev.to/aws-builders/build-a-unique-domain-hosting-environment-with-aws-amplify-3jl0 %}
{% link https://dev.to/aws-builders/build-a-unique-domain-hosting-environment-with-amazon-route-53-aws-waf-amazon-cloudfront-and-amazon-s3-5eip %}

<br>

References

[AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/home.html)
[API Reference](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-construct-library.html)