この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
やりたいこと
S3 に置いてある JSON ファイルを CloudFront で配信し、CloudFrontを経由してのみアクセスできるように設定します。
使うもの
- Typescript
- AWS CDK
AWS リソースを作成するのに AWS CDK を使い言語はTypescript で記述します。
本記事で利用した環境は以下の通りです。
tsc -v
Version 3.7.4
cdk version
1.57.0 (build 2ccfc50)
AWS リソースの作成
早速 AWS CDK を使って AWS リソースを作成しましょう。必要なリソースは S3 バケットと CloudFront です。
雛形の作成
からっぽのディレクトリを作成して、そこに CDK の雛形を作成します。
$ mkdir cdk
$ cd cdk
$ cdk init --language typescript
モジュールの追加
S3 と CloudFront のモジュールを追加します。S3 と CloudFront に加え IAM も利用するのでインストールしておきます。
$ npm install @aws-cdk/aws-cloudfront @aws-cdk/aws-s3 @aws-cdk/aws-iam
Watch Mode を ON にする
Typescript は Javascript へトランスパイルしてからデプロイする必要があるので Typescript コンパイラの Watch Mode を ON にしておくことをお勧めします。
シンタックスエラーがある場合リアルタイムで教えてくれるので便利です。
$ tsc -w
AWS リソースの定義
lib/cdk-stack.ts
にリソースを定義します。
S3 Bucket & Policy
バケットと CloudFrontOriginAccessIdentity を定義してそのあとに Policy を設定します。
cdk-stack.ts
// Create Bucket
const myBucket = new s3.Bucket(this, "my-bucket");
// Create OriginAccessIdentity
const oai = new cloudfront.OriginAccessIdentity(this, "my-oai");
// Create Policy and attach to mybucket
const myBucketPolicy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["s3:GetObject"],
principals: [
new iam.CanonicalUserPrincipal(
oai.cloudFrontOriginAccessIdentityS3CanonicalUserId
),
],
resources: [myBucket.bucketArn + "/*"],
});
myBucket.addToResourcePolicy(myBucketPolicy);
principals
に設定したアクセス元からのみに S3 バケットのGetObject
権限を渡しています。上記のポリシーを設定することで、S3 バケットのオブジェクトは CloudFront を介してのみアクセスできるようになります。
ポリシーの中にOrigin
という単語がよく出てきますが、Origin = CloudFrontを介してアクセスしたいリソース
です。
参考:オリジンアクセスアイデンティティを使用して Amazon S3 コンテンツへのアクセスを制限する
CloudFront WebDistribution
次にCloudFrontのWebDistributionの設定をします。
cdk-stack.ts
// Create CloudFront WebDistribution
new cloudfront.CloudFrontWebDistribution(this, "WebsiteDistribution", {
viewerCertificate: {
aliases: [],
props: {
cloudFrontDefaultCertificate: true,
},
},
priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
originConfigs: [
{
s3OriginSource: {
s3BucketSource: myBucket,
originAccessIdentity: oai,
},
behaviors: [
{
isDefaultBehavior: true,
minTtl: cdk.Duration.seconds(0),
maxTtl: cdk.Duration.days(365),
defaultTtl: cdk.Duration.days(1),
pathPattern: "my-contents/*",
},
],
},
],
errorConfigurations: [
{
errorCode: 403,
responsePagePath: "/index.html",
responseCode: 200,
errorCachingMinTtl: 0,
},
{
errorCode: 404,
responsePagePath: "/index.html",
responseCode: 200,
errorCachingMinTtl: 0,
},
],
});
originConfigs
s3OriginSource
: アクセスしたいリソースの格納先にS3を利用する場合はバケットを指定しますbehaviors
:pathPattern
にmy-contents/*
を指定しています。これでバケットのmy-contents/
ディレクトリ下の全てのリソースへアクセスを許可する設定になります
errorConfigurations
許可したパス以外へアクセスがあった際のルーティング設定です。今回はindex.html
へリダイレクトされるよう設定します。
CDK 全文
lib/cdk-stack.ts
cdk-stack.ts
import * as cloudfront from "@aws-cdk/aws-cloudfront";
import * as iam from "@aws-cdk/aws-iam";
import * as s3 from "@aws-cdk/aws-s3";
import * as cdk from "@aws-cdk/core";
export class CdkStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Create Bucket
const myBucket = new s3.Bucket(this, "my-bucket");
// Create OriginAccessIdentity
const oai = new cloudfront.OriginAccessIdentity(this, "my-oai");
// Create Policy and attach to mybucket
const myBucketPolicy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["s3:GetObject"],
principals: [
new iam.CanonicalUserPrincipal(
oai.cloudFrontOriginAccessIdentityS3CanonicalUserId
),
],
resources: [myBucket.bucketArn + "/*"],
});
myBucket.addToResourcePolicy(myBucketPolicy);
// Create CloudFront WebDistribution
new cloudfront.CloudFrontWebDistribution(this, "WebsiteDistribution", {
viewerCertificate: {
aliases: [],
props: {
cloudFrontDefaultCertificate: true,
},
},
priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
originConfigs: [
{
s3OriginSource: {
s3BucketSource: myBucket,
originAccessIdentity: oai,
},
behaviors: [
{
isDefaultBehavior: true,
minTtl: cdk.Duration.seconds(0),
maxTtl: cdk.Duration.days(365),
defaultTtl: cdk.Duration.days(1),
pathPattern: "my-contents/*",
},
],
},
],
errorConfigurations: [
{
errorCode: 403,
responsePagePath: "/index.html",
responseCode: 200,
errorCachingMinTtl: 0,
},
{
errorCode: 404,
responsePagePath: "/index.html",
responseCode: 200,
errorCachingMinTtl: 0,
},
],
});
}
}
上記をデプロイして動作確認をしてみましょう。
cdk deploy
動作確認
CloudFormationのリソースからS3バケットが作成されているのが確認できたらpathPattern
とS3の構成を合わせるためにS3バケットのCreate Folder
からmy-contents
というディレクトリを作成します。
以下のファイルを作成したパスへ置いて、CloudFrontからアクセスできるかを確認します。 URLはCloudFrontのマネジメントコンソールから見ることができます。
questions.json
questions.json
{
"survey1": {
"question": "すきなどうぶつをおしえてね",
"options": [
"うさぎさん",
"ぞうさん",
"きりんさん",
"いるかさん",
"ねこさん"
]
},
"survey2": {
"question": "すきなフルーツをおしえてね",
"options": ["もも", "いちご", "すいか", "パイナップル", "ぶどう"]
}
}
閲覧することができました。
バケットへ直接アクセスするとどうなるでしょう
アクセスできないようになっています。
指定していないパスへアクセスするとどうなるでしょうか
index.html
へリダイレクトされているのが確認できます。(index.htmlが存在しないのでNoSuchKeyになっています)