見出し画像

【AWS CDK】CloudFrontとS3で静的ホスティングサイト(SPA)をサッと作る

はじめに

AWSの勉強会や新人教育の講師を担当していますが、講義中に急遽ハンズオン環境が必要になったり、解説のための環境が必要になるケースがあります。そんな場面で、素早くサッとS3静的ホスティングサイト(SPA)を用意するための記事となります。個人的なメモとして、対処法を書き留めておきたいと思います。

個人的備忘が強いためコードの注釈などは薄いですが、
とりあえずCDKで動くものを体験したいという方に有用ではないかと思います。

構成

AWS CDKを使って以下のようなS3静的ホスティングサイトを作ります。

CDK準備

CDKのバージョンは2です。cdk bootstrap済みを想定しています。

$ mkdir teststatic && cd teststatic
$ cdk init app --language typescript

AWS CDK (Typescript)

/lib/teststatic-stack.ts

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
import * as path from 'path';

export class TeststaticStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // 静的ホスティング用S3バケットを作成
    const bucket = new s3.Bucket(this, 'SampleBucket', {
      versioned: true,
      publicReadAccess: false,
    });
    
    // CloudFront Origin Access Identity (OAI) を作成
    const cloudfrontOai = new cloudfront.OriginAccessIdentity(this, 'CloudFrontOAI');
  
    // 静的ホスティング用S3バケットに対して必要なアクセスポリシーを作成
    const bucketPolicy = new s3.BucketPolicy(this, 'WebsiteBucketPolicy', {
      bucket: bucket,
    });
    bucketPolicy.document.addStatements(
      new iam.PolicyStatement({
        actions: ['s3:GetObject'],
        effect: iam.Effect.ALLOW,
        principals: [new iam.CanonicalUserPrincipal(cloudfrontOai.cloudFrontOriginAccessIdentityS3CanonicalUserId)],
        resources: [`${bucket.bucketArn}/*`],
      })
    );

    // 静的ホスティング用S3バケットにOAIのアクセスを許可
    bucket.grantRead(cloudfrontOai);

    // CloudFrontディストリビューションを作成
    const distribution = new cloudfront.Distribution(this, 'WebsiteDistribution', {
      defaultBehavior: {
        origin: new origins.S3Origin(bucket, {
          originAccessIdentity: cloudfrontOai,
        }),        
      },
      defaultRootObject: 'index.html',
      enableLogging: true, // ログ出力設定
      logBucket: new s3.Bucket(this, 'LogBucket', {
        objectOwnership: s3.ObjectOwnership.OBJECT_WRITER,
      }),
      logFilePrefix: 'distribution-access-logs/',
      logIncludesCookies: true,
    });    

    // index.htmlを静的ホスティング用S3バケットにアップロード
    const indexHtmlPath = path.resolve(__dirname, './source/index.html');
    new s3deploy.BucketDeployment(this, 'DeployIndexHtml', {
      sources: [s3deploy.Source.asset(path.dirname(indexHtmlPath))],
      destinationBucket: bucket,
      destinationKeyPrefix: '/',
    });

    // CloudFrontディストリビューションのDNS名を出力
    new cdk.CfnOutput(this, 'DistributionURL', {
      value: distribution.distributionDomainName,
    });
  }
}

構成のポイント

いくつかポイントをあげると、CloudFront - S3間はOAIを利用してます。
本来なら、OACが推奨されるのですが、
執筆時点(2023/06/18)でOACはL1コンストラクトしかないため、L2利用ができるOAIの設定にしています。
さらに、CloudFrontの標準ログが有効化されてます。その標準ログ用のS3バケットも作成されます。
「index.html」ファイルを、lib/source/配下に用意します。
簡素ですが、、以下のような感じで適宜。

<html>
Hello World!
</html>

デプロイ

$ cdk synth
$ cdk deploy


まとめ

今回は、短時間でサッと静的Webホスティング環境(SPA)を作るということにフォーカスしています。 学習用やサンドボックスとして使用するため、用が済んだら全てcdk destroyで消してしまう想定です。
CDKを少し離れると忘れてしまうので、個人的な備忘のため書き残したいと思います。
本番環境用として使うにはさらにセキュリティ面、認証やWAFの実装、CICDなど追加検討する必要があります。

この記事が気に入ったらサポートをしてみませんか?