Open17

AWS cdkを使ってS3+Cloudfrontでのstatic website hostingを構築してみる

海老原祐輔海老原祐輔

やりたいこと

  • 本番環境とステージング環境を作成
  • ステージング環境にはlambda@edgeを利用してbasic認証を設定する

構成

  • AWS S3
    • 静的ホスティング機能を利用
    • オリジンアクセスコントロール (OAC) 有効
  • Cloudfront
    • SSL有効
    • 証明書はACMで取得済みとする
    • CNAME設定
  • Lambda@edge
    • STG環境のみ
    • Cloudfrontへのアクセスに対し、basic認証をかける
    • viewer request
海老原祐輔海老原祐輔

CDKの環境を構築する。

cdk toolkitのインストール

npm install -g aws-cdk
❯ cdk --version
2.118.0 (build a40f2ec)

プロジェクトディレクトリの作成

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

以下のような構造のディレクトリが生成される。

❯ tree -I node_modules
.
├── README.md
├── bin
│   ├── cdk.d.ts
│   ├── cdk.js
│   └── cdk.ts
├── cdk.json
├── jest.config.js
├── lib
│   ├── cdk-stack.d.ts
│   ├── cdk-stack.js
│   └── cdk-stack.ts
├── package-lock.json
├── package.json
├── test
│   ├── cdk.test.d.ts
│   ├── cdk.test.js
│   └── cdk.test.ts
└── tsconfig.json

3 directories, 15 files
海老原祐輔海老原祐輔

lib/cdk-stack.ts に書いていけばいいっぽい。

とりあえずS3 bucketとCloudfront distributionを作ってみる。

        const bucket = new s3.Bucket(this, "deployBucket", {
            publicReadAccess: true,
        });

        // cloudfront
        const distribution = new cloudfront.Distribution(this, "Distribution", {
            defaultBehavior: { origin: new origins.S3Origin(bucket) },
            defaultRootObject: "index.html",
            errorResponses: [
                {
                    httpStatus: 404,
                    responseHttpStatus: 200,
                    responsePagePath: "/index.html",
                },
            ],
        });

書いたら、

npm synth

でスタックが生成される。

海老原祐輔海老原祐輔

デプロイしてみる

まずはbootstrapという作業が必要らしい。

cdk bootstrap --profile [PROFILE]

cloudformation Stackが作られる。

そしたらデプロイする。

cdk deploy --profile [PROFILE]

bucketpolicyの作成に失敗。

海老原祐輔海老原祐輔

修正が必要っぽい。

blockPublicAccess: BlockPublicAccess.BLOCK_ACLS, を追加する必要があるらしい。

        const bucket = new s3.Bucket(this, "deployBucket", {
            publicReadAccess: true,
            blockPublicAccess: s3.BlockPublicAccess.BLOCK_ACLS,
        });

修正して cdk deploy

デプロイ中の表示がかっこいい。

デプロイ完了。 ✨ Total time: 253.58s

ちゃんとS3 bucketもできている。

海老原祐輔海老原祐輔

Cloudfront distributionもできている。
S3に手動で index.html をアップロードし、見られることを確認した。

海老原祐輔海老原祐輔

スタック名を変えたいので、一回スタックを削除する。

cdk destroy --profile [PROFILE]

took 3m40

ちゃんといろいろ消えた。

S3だけ消えない(DeletionPolicy: Retainになっている)ので、手動で消す。

海老原祐輔海老原祐輔

スタック名は bin/cdk.ts の方にある。
bin/cdk.ts から lib/cdk-stack.ts を呼び出してるだけ。

海老原祐輔海老原祐輔

複数環境にデプロイしたい。

環境変数で指定するのが楽じゃね?

const stage = process.env.STAGE || "dev";
cdk deploy

STAGE=prod cdk deploy

で分ける感じ。

海老原祐輔海老原祐輔

basic認証を仕掛ける

lambda@edgeを使う。

lambdaをデプロイする

cdkにはLambdaをデプロイする仕組みがあるっぽい。

LambdaをTypeScirptで書いて、CDKでサクッとデプロイする方法|F Lab|Fixel株式会社

Lambda at edgeへの応用はこれ

Lambda@EdgeをCDKで書いてみる

Basic Authの実装はこれを見る

[AWS] CloudFrontでBASIC認証を行う - Lambda@Edge + Node.js - ねこの足跡R

aws_lambda_nodejs を使う方法だと、 cloudfront.experimental.EdgeFunction が使えない?

素直に、us-east-1用にstackを作る。

cf. AWS CDKで別リージョンに基本認証用Lambda@Edgeを作成するスタックをデプロイしてAmazon CloudFrontに設定する - NRIネットコムBlog

crossRegionReferences を利用して、値を受け渡す

cf. AWS CDK(Typescript) で LambdaEdge を使う #TypeScript - Qiita

海老原祐輔海老原祐輔

Lambda@edgeの更新ができない問題

更新すると更新でエラーが出る。

23:20:19 | UPDATE_FAILED | Custom::CrossRegionExportWriter | ExportsWriterapnor...t12334E1B81D43DF3F Received response status [FAILED] from custom resource. Message returned: Error: Exports cannot be updated:

ロールバックもできなくなってしまった。
Cross origin referenceがよくわからない。

Lambda用のarnが変わってしまうのが悪いのかな。

海老原祐輔海老原祐輔

lambda関数に

currentVersionOptions: {  removalPolicy: cdk.RemovalPolicy.RETAIN, },

を指定したが、解決はせず。

海老原祐輔海老原祐輔

CI/CDをする

Github ActionsからCDできるようにする

GithubのOIDCを使い、特定のリポジトリからのS3/Cloudfrontアクセスを許可する。

海老原祐輔海老原祐輔

一旦全てを破壊する。

cdk destroy

ちなみに以下のエラーで一回必ず失敗する。

Lambda was unable to delete arn:aws:lambda:...:1 because it is a replicated function.

Lambda@edgeに割り当てた関数は、Cloudfront distributionを削除してから時間が経つまで削除できないため。

レプリカは通常、数時間以内に削除されます。Lambda@Edge 関数のレプリカを手動で削除することはできません。
Lambda@Edge 関数とレプリカの削除 - Amazon CloudFront