👏

AWS CDK で CloudFront + S3 環境を構築してみよう!

に公開

はじめに

AWS リソース(CloudFront や S3)をアプリと同じように管理したいと思い「CloudFormation より簡単!?AWS CDK で楽々 IaC 実践」を参考に CDK で CloudFront + S3 環境を構築してみました。

対象読者

  • AWS CDK の導入を検討している方
  • CloudFormation を利用しているが、より柔軟な管理をしたい方

AWS CDK の基本概念

この記事を参考にしてください。
https://zenn.dev/gnc_tech/articles/d01c65b1912fd4#aws-cdk-の基本概念

さっそく環境構築から

以降は Windows11 向けの解説です。その他の OS 利用者はダウンロード先やコマンドは必要に応じて読み替えて実行してください。

ステップ1:Node.js のインストール

https://nodejs.org/

> node -v && npm -v
v20.16.0
10.8.1

ステップ2:AWS CLIのインストール

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html

> aws --version
aws-cli/2.15.15 Python/3.11.6 Windows/10 exe/AMD64 prompt/off

ステップ3:AWS認証情報の設定

3-1. IAMユーザーの作成とアクセスキーの設定

  1. AWSマネジメントコンソールにログイン
  2. IAMサービスにアクセス
    IAM

  1. 「ユーザー」→「ユーザーを追加」をクリック
    ユーザーを追加

  1. ユーザー名欄に任意のユーザー名を入力し、「次へ」をクリック
    ユーザー名

  1. 「ポリシーを直接アタッチする」を選択し、許可ポリシーで「AdministratorAccess」を検索してチェックし「次へ」をクリック
    既存のポリシーを直接アタッチ
  1. 「ユーザーの作成」をクリック
    ユーザーの作成

  1. 作成したユーザーを選択して、「セキュリティ認証情報」タブの「アクセスキーを作成」をクリック
    アクセスキーの作成

  1. ユースケースから「コマンドラインインターフェイス (CLI)」を選択して、下部の確認マークをチェックして「次へ」をクリック
    ユースケース

  1. 「アクセスキーを作成」をクリック
    アクセスキーを作成

  1. アクセスキーが作成されました!

3-2. AWS CLIの設定

ターミナルで下記コマンドを実行し、認証情報を設定します

> aws configure
  • AWS Access Key ID: 先ほど作成したアクセスキー
  • AWS Secret Access Key: 先ほど作成したシークレットアクセスキー
  • Default region name: リージョン指定 東京 (ap-northeast-1) にしました。
  • Default output format: 出力結果の形式 初期値の json にしました。

ステップ4:CDK のインストール

> npm install -g aws-cdk
> cdk --version
2.1100.1 (build db19110)

ステップ5:CDK環境の初期化(ブートストラップ)

> cdk bootstrap aws://ACCOUNT-NUMBER/REGION

ACCOUNT-NUMBERREGION は、自分のAWSアカウント番号とリージョンに置き換えてください。

成功すると以下の通り CloudFormation で「CDKToolkit」という名前のスタックが作成されます。
ブートストラップ

ステップ6:CDK プロジェクトの作成

1. 作業ディレクトリの作成

my-cdk-app というディレクトリを作成しています。
お好きな名前に変更してください。

> mkdir my-cdk-app && cd my-cdk-app
my-cdk-app>

2. CDK プロジェクトの作成

my-cdk-app> cdk init app --language=typescript

※成功すると my-cdk-app 内にいろいろファイルが作られます。


テンプレートの作成

以下 my-cdk-app で作成した場合の例です。該当の ts ファイルを修正してください。
必要に応じて読み替えて変更してください。

スタック(Stack)

my-cdk-app/lib/my-cdk-app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import {
  aws_s3 as s3,
  aws_cloudfront as cloudfront,
  aws_cloudfront_origins as cloudfront_origins,
 } from 'aws-cdk-lib';

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

    const s3Bucket = new s3.Bucket(this, 'WebSiteBucket', {
      bucketName: `s3-bucket-${cdk.Aws.ACCOUNT_ID}-${cdk.Aws.REGION}`,
      autoDeleteObjects: true,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      // S3バケットのパブリックアクセスを拒否(デフォルト)
      publicReadAccess: false,
      websiteIndexDocument: 'index.html',
      // S3へのパブリックアクセスを全て拒否(デフォルト)
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
    });

    const oac = new cloudfront.S3OriginAccessControl(this, 'OAC', {
      originAccessControlName: `${id}-oac`,
      // 認証の設定
      // SIGV4:認証プロトコル(現状唯一)
      // ALWAYS/NO_OVERRIDE:CloudFrontが認証するリクエストの選定
      signing: cloudfront.Signing.SIGV4_ALWAYS,
    });

    const distribution = new cloudfront.Distribution(this, 'Distribution', {
      // CloudFrontディストリビューションのデフォルト動作を各種設定
      defaultBehavior: {
        // オリジンの指定
        origin: cloudfront_origins.S3BucketOrigin.withOriginAccessControl(
          s3Bucket,{
            originAccessLevels: [cloudfront.AccessLevel.READ],
          },
        ),          
      },
      // S3からではなくCloudFrontから配信するのでこちらでルートオブジェクトを指定
      defaultRootObject: 'index.html',
    });

    const url = new cdk.CfnOutput(this, 'URL', {
      value: distribution.domainName,
      description: 'Website URL'
    });
  }
}

アプリ(App)

my-cdk-app/bin/my-cdk-app.ts
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { S3CloudfrontStack } from '../lib/my-cdk-app-stack';

const app = new cdk.App();
new S3CloudfrontStack(app, 'S3CloudfrontStack', {
});

デプロイ

my-cdk-app> cdk deploy

時間がかかりますので気長に待ちましょう!

成功すると以下の通り CloudFormation で「S3CloudfrontStack」という名前のスタックが作成されます。
deploy

S3 バケットの確認と index.html の作成

  1. AWSマネジメントコンソールで S3 サービスにアクセスし「s3-bucket-XXXXX-リージョン」のバケットが作成されていることを確認します。

  2. そのバケットに index.html をアップロードします。

index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>TEST</title>
    </head>
    <body>
        <h1>TEST</h1>
        <p>テストページです。</p>
    </body>
</html>

動作確認

OAC の確認

  1. AWSマネジメントコンソールで S3 サービスにアクセスし、対象バケットの「プロパティ」→「静的ウェブサイトホスティング」から「バケットウェブサイトエンドポイント」をコピーします。
    バケットウェブサイトエンドポイント

  2. ご利用のブラウザで「バケットウェブサイトエンドポイント」にアクセスすると「403 Forbidden」が表示されます。(成功)
    403


CloudFront 経由でアクセス

  1. AWSマネジメントコンソールで CloudFront サービスにアクセスし、対象ディストリビューションの「ディストリビューションドメイン名」をコピーします。
    ディストリビューションドメイン名

  2. ご利用のブラウザで「ディストリビューションドメイン名」にアクセスすると作成したテストページが表示されます。(成功)
    テストページ


片付け

作成したリソースは cdk destroy で削除可能です。
S3 バケット内にファイルがあってもリソースは削除されました。

my-cdk-app> cdk destroy

まとめ

環境構築にてこずりましたが、先人たちの知恵に助けられました。
(参考サイトの方々、ありがとうございました。🙇‍♂️)
不要になった際、コマンド1つで削除できるのは、やっぱり便利ですね!
また yaml とは違い、条件分岐やループを活用できるそうなので、今後チャレンジしてみようと思います。
この記事が、誰かのお役に立てば幸いです。

参考サイト

株式会社グローバルネットコア 有志コミュニティ(β)

Discussion