dayjournal memo

Total 975 articles!!

Try #085 – AWS CDKで独自ドメインホスティング環境構築を自動化してみた

Yasunori Kirimoto's avatar

img




AWS CDKで独自ドメインホスティング環境構築を自動化してみました



以前の記事「Try #081 – Amazon Route 53とAWS WAFとAmazon CloudFrontとAmazon S3で独自ドメインホスティング環境を構築してみた」や「Try #084 – AWS CloudFormationで独自ドメインホスティング環境構築を自動化してみた」の内容をAWS CDKで実現する方法をためしてみました。


今回の作成したテンプレートをGitHubで公開したのでぜひご利用ください!

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);
    // ホストゾーンID取得
    const hostedZoneId = route53.HostedZone.fromLookup(this, 'HostedZoneId', {
      domainName: domainName
    });
    // Certificate Manager設定
    const certificateManagerCertificate = new acm.DnsValidatedCertificate(this, 'CertificateManagerCertificate', {
      // ドメイン指定
      domainName: domainName,
      // ホストゾーンID指定
      hostedZone: hostedZoneId,
      // リージョン指定 (CloudFront固定)
      region: 'us-east-1',
      // 検証方法指定
      validation: acm.CertificateValidation.fromDns(),
    });
    // S3バケット作成
    const s3Bucket = new s3.Bucket(this, 'S3Bucket', {
      // バケット名指定
      bucketName: `${domainName}-${region}-${accountId}`,
      // ブロックパブリックアクセスをすべてON
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      // リソース削除時にバケット削除
      removalPolicy: RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    });
    // CloudFrontOAI設定
    const cloudFrontOAI = new cloudfront.OriginAccessIdentity(this, 'CloudFrontOriginAccessIdentityy', {
      comment: 'Unique Domain Hosting Environment',
    })
    // CloudFront設定
    const cloudFrontDistribution = new cloudfront.Distribution(this, 'CloudFrontDistribution', {
      // ドメイン指定
      domainNames: [domainName],
      // キャッシュビヘイビア設定
      defaultBehavior: {
        // オリジンID指定
        origin: new origins.S3Origin(s3Bucket, {
          originAccessIdentity: cloudFrontOAI
        }),
        // 自動圧縮有無指定
        compress: true,
        // HTTPメソッド指定
        allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD,
        cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD,
        // ビューアプロトコルポリシー指定
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        // キャッシュポリシー指定 (CachingOptimized)
        cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
      },
      // SPA対応設定
      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,
      // SSL証明書設定
      certificate: certificateManagerCertificate,
      // セキュリティポリシー指定
      minimumProtocolVersion: cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021,
      // HTTPバージョン指定
      httpVersion: cloudfront.HttpVersion.HTTP2,
      // ルートURL指定
      defaultRootObject: 'index.html',
      // IPv6有無指定
      enableIpv6: true,
    })
    // Route53レコード設定
    new route53.ARecord(this, 'Route53RecordSet', {
      // ドメイン指定
      recordName: domainName,
      // ホストゾーンID指定
      zone: hostedZoneId,
      // エイリアスターゲット設定
      target: route53.RecordTarget.fromAlias(
        new targets.CloudFrontTarget(cloudFrontDistribution)
      ),
    });
    // S3バケットにデプロイ
    new s3deploy.BucketDeployment(this, 'S3BucketDeploy', {
      // 対象ディレクトリ指定
      sources: [s3deploy.Source.asset('./dist')],
      // S3バケット指定
      destinationBucket: s3Bucket,
      // CloudFront指定
      distribution: cloudFrontDistribution,
      distributionPaths: ['/*'],
    });
    // デプロイ先URL表示
    new CfnOutput(this, 'DeployURL', {
      value: `https://${domainName}`,
    })
  }
}

/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
  },
});

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": "ドメイン名設定"
  }
}

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


事前準備

img


実行環境

  • node v16.10.0
  • npm v7.24.0

構築の流れ

  1. 各ファイル配置と設定ファイル更新
  2. 独自ドメインホスティング環境を自動デプロイ

各ファイル配置と設定ファイル更新

はじめに、各ファイル配置と設定ファイルを更新します。


aws-cdk-templates-showcase」リポジトリ内の「unique-domain-hosting」ディレクトリをそのまま利用するか任意の場所にコピーします。

img


「cdk.json」にAmazon Route 53で取得した独自ドメイン名を設定します。

"domainName": "ドメイン名設定"

「dist」ディレクトリにデプロイしたいアプリケーションファイル一式をコピーします。

img



独自ドメインホスティング環境を自動デプロイ

最後に、独自ドメインホスティング環境を自動デプロイします。


パッケージをインストールします。

npm install

デプロイ前に初回のみ下記コマンドを実行します。リージョンを変更した時にも実行します。

cdk bootstrap

img


プロジェクトをデプロイします。

cdk deploy

img


スタックが作成されたのを確認。各サービスも自動作成されているのを確認できます。

img


独自ドメインにアクセスするとデプロイしたWebSiteが表示されます。

img



AWS CDKを利用することで、独自ドメインホスティング等のさまざまなリソース構築を自動化することが可能です。今回は、ACMを固定リージョンで作成やCloudFront用のS3バケットポリシー自動生成やデプロイしたいアプリケーションをコード内で定義する等がコード量も少なく実装可能でした。各機能定義は、AWS CloudFormationと同様にドキュメントで学習する必要はありますが、すごく将来性を感じるツールで構築するのが楽しいです。

今後も色々と自動化していければと思います!



AWS CDKについて、他にも記事を書いています。よろしければぜひ。
tags - AWS CDK



book

Q&A