はじめに
社内R&Dの一環として、AWS CDKを用いてCloudFront + S3の最小構成で静的Webサイトを構築しました。
目的は以下です。
- IaC(Infrastructure as Code)による構築手順の理解
- 作成→削除のライフサイクル検証
システム構成
以下が今回構築した最小構成のアーキテクチャです。
開発環境
- OS:Windows 11
- エディタ:Visual Studio Code
- AWS CLI:v2
- AWS CDK:v2
CDKプロジェクト作成
1.作業用ディレクトリの作成
任意の作業場所で、以下のコマンドを実行します。
mkdir cdk-cloudfront-s3-sample
cd cdk-cloudfront-s3-sample
※ ディレクトリ名は任意ですが、
本記事ではcdk-cloudfront-s3-sampleとします。
2.TypeScriptテンプレートでCDKプロジェクト作成
以下のコマンドを実行します。
cdk init app --language typescript
このコマンドにより、CDKアプリケーションの雛形が生成され、
依存関係も自動的にインストールされます。
3.スタック作成
cdk init app --language typescript で生成された
lib/cdk-cloudfront-s3-sample-stack.ts を編集し、CloudFront + S3 の構成を実装します。
lib/cdk-cloudfront-s3-sample-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 s3deploy from 'aws-cdk-lib/aws-s3-deployment';
export class CdkCloudfrontS3SampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// S3バケットを作成
const bucket = new s3.Bucket(this, 'DemoSiteBucket', {
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, // バケットの公開アクセスを全てブロック
encryption: s3.BucketEncryption.S3_MANAGED, // S3管理の暗号化を有効化
removalPolicy: cdk.RemovalPolicy.DESTROY, // スタック削除時にバケットも削除
autoDeleteObjects: true, // バケット内オブジェクトも自動削除
});
// S3は非公開のまま、CloudFrontからのみ署名付きリクエストでアクセスさせるため
// OAC(Origin Access Control)を使用する
const s3Origin = origins.S3BucketOrigin.withOriginAccessControl(bucket);
// CloudFrontディストリビューションを作成し、S3バケットをオリジンとして設定
const dist = new cloudfront.Distribution(this, 'DemoDistribution', {
defaultBehavior: {
origin: s3Origin,
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
},
defaultRootObject: 'index.html',
});
// S3バケットにローカルの静的サイトコンテンツをデプロイ
new s3deploy.BucketDeployment(this, 'DeployDemo', {
sources: [s3deploy.Source.asset('./website-demo')], // ローカルのwebsite-demoディレクトリをソースとして指定
destinationBucket: bucket, // 配置先のS3バケットを指定
distribution: dist, // CloudFrontディストリビューションを指定して、デプロイ後にキャッシュを無効化
distributionPaths: ['/*'], // CloudFrontの全パスのキャッシュを無効化
});
// CloudFrontのURLを出力
new cdk.CfnOutput(this, 'CloudFrontUrl', {
value: `https://${dist.distributionDomainName}`,
description: 'CloudFront distribution URL',
});
}
}
OAC(Origin Access Control)
OACはCloudFrontがSigV4署名付きリクエストでS3へアクセスする仕組みです。
S3は公開せず、特定Distributionからのみアクセス可能にできます。
sources: [s3deploy.Source.asset('./website-demo')]
デモページとして、website-demo/index.html を用意しています。
BucketDeployment により、このディレクトリ配下の静的ファイルがS3へアップロードされます。
4.静的コンテンツの準備
CloudFront で配信するための、最小構成の静的コンテンツを用意します。
CDKプロジェクト(cdk-cloudfront-s3-sample)のルート直下にwebsite-demo ディレクトリを作成し、 その配下に index.html を作成します。
mkdir website-demo
website-demo/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CDK Demo</title>
</head>
<body>
<h1>Hello, CloudFront + S3!</h1>
</body>
</html>
5.binファイル設定
bin配下のtsファイル(bin/cdk-cloudfront-s3-sample.ts)がアプリケーションのエントリポイントになります。
ここでスタックを読み込み、AWS CLI の設定に基づいてデプロイ対象の環境(account / region)を指定します。
以下のように編集してください。
bin/cdk-cloudfront-s3-sample.ts コード全体
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { CdkCloudfrontS3SampleStack } from '../lib/cdk-cloudfront-s3-sample-stack';
const app = new cdk.App();
new CdkCloudfrontS3SampleStack(app, 'CdkCloudfrontS3SampleStack', {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
});
検証
1.デプロイ
以下のコマンドでスタックをデプロイします。
cdk deploy CdkCloudfrontS3SampleStack --profile <プロファイル名>
※ --profile で指定した AWS CLI プロファイルに、利用するアカウントおよびリージョンが設定されている前提です。
確認プロンプトについて
cdk deploy および cdk destroy 実行時には、以下のような確認プロンプトが表示される場合があります。
... (y/n)
処理はこの入力待ちで一時停止するため、続行する場合は y を入力してください。
デプロイ完了後、CloudFrontのURLがログに出力されます。
Outputs:
CdkCloudfrontS3SampleStack.CloudFrontUrl = https://xxxxx.cloudfront.net
このURLにアクセスすることで、デプロイした静的サイトを確認できます。
必要に応じて、以下のコマンドでも出力値を確認できます。
aws cloudformation describe-stacks --stack-name CdkCloudfrontS3SampleStack --query "Stacks[0].Outputs" --output table --region <リージョン名> --profile <プロファイル名>
検証ポイント
CloudFrontのURLから正常にデモページが表示されることを確認しました。
S3直アクセスが拒否されることを確認
S3に配置された index.html を、S3のオブジェクトURLから直接取得します。
その結果、403 AccessDenied となり、S3が非公開であることを確認できます。
curl -i https://<bucket>.s3.ap-northeast-1.amazonaws.com/index.html
# HTTP/1.1 403 Forbidden
# ~
# <Error><Code>AccessDenied</Code>~
上記の <bucket> には、実際に作成されたS3バケット名を指定します。
S3バケット名の確認方法
バケット名は、AWSコンソールから確認できます。
- AWSコンソール → S3 → CDKで作成された対象バケットを選択
※ S3バケット名は、スタック名およびリソースの論理IDを元に、一意な名前として自動生成されます。
2.再デプロイ
index.html を修正後、再度 cdk deploy を実行します。
検証ポイント
CloudFrontのURLは変更されないことを確認しました。
3.削除
以下のコマンドでスタックを削除します。
cdk destroy CdkCloudfrontS3SampleStack --profile <プロファイル名>
削除後、以下のコマンドでスタックが存在しないことを確認
aws cloudformation describe-stacks --stack-name CdkCloudfrontS3SampleStack --region <リージョン名> --profile <プロファイル名>
検証ポイント
削除済みの場合、以下のようなエラーが表示されます。
An error occurred (ValidationError) when calling the DescribeStacks operation: Stack with id CdkCloudfrontS3SampleStack does not exist
このエラーは、スタックが正常に削除されていることを示しています。
4.削除後の再デプロイ
再度デプロイを実行。
検証ポイント
削除前とは異なるURLが発行されていることを確認しました。
今回の検証で分かったこと
- AWS CDKを使うことで、インフラをコードで定義し、コマンド実行により環境を構築できる。
- OACを利用することで、S3を公開せずCloudFront経由のみでアクセス可能な構成にできる。
- destroyしない限りCloudFrontのURLは変わらず、削除後の再デプロイでは新しいURLが発行される。