概要
S3バケットをオリジンとしてCloudFrontで静的Webリソースを配信、というのは典型的なAWSのサーバレス構成の一つかと思いますが、それをサクッと作成したい時のための自分用CloudFormationテンプレートを公開したものです。
数年前に個人的に作成して使っていたものが、内容が少々古くなっていたので更新(※)するついでに自身にも他の方にも参照しやすいようにQiita記事として公開してみました。
※更新した部分
「少々古くなっていて更新した」という部分は以下の2点です(古いものは公開していないので比較対象がありませんが)。
- キャッシュポリシーなどを管理ポリシーに変更
- S3バケットへのアクセスをCloudFront経由に制限する設定をOAIからOACに変更
それぞれの更新内容については後述します。
CloudFormationテンプレート全体
「CloudFormationスタック名+AWSアカウントID」の名前のS3バケットと、それをオリジンとするCloudFrontディストリビューションが作成されるだけのテンプレートです。
こちらのGitHubからダウンロードもできます。
AWSTemplateFormatVersion: '2010-09-09'
Description: Sample template for S3Origin CloudFront
Parameters:
CloudFrontPriceClass:
Type: String
Default: PriceClass_200
AllowedValues: [PriceClass_100, PriceClass_200, PriceClass_All]
Resources:
OriginS3Bucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
BucketName: !Sub ${AWS::StackName}-${AWS::AccountId}
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
OriginS3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref OriginS3Bucket
PolicyDocument:
Statement:
- Action: s3:GetObject
Effect: Allow
Resource: !Sub arn:${AWS::Partition}:s3:::${OriginS3Bucket}/*
Principal:
Service: cloudfront.amazonaws.com
Condition:
StringEquals:
AWS:SourceArn: !Sub arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
- Action: s3:ListBucket
Effect: Allow
Resource: !Sub arn:${AWS::Partition}:s3:::${OriginS3Bucket}
Principal:
Service: cloudfront.amazonaws.com
Condition:
StringEquals:
AWS:SourceArn: !Sub arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Comment: !Sub 'Created by ${AWS::StackName}'
DefaultCacheBehavior:
TargetOriginId: mainS3Origin
ForwardedValues:
QueryString: false
Cookies:
Forward: 'none'
ViewerProtocolPolicy: redirect-to-https
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized
OriginRequestPolicyId: acba4595-bd28-49b8-b9fe-13317c0390fa # UserAgentRefererHeaders
Compress: true
DefaultRootObject: index.html
Enabled: true
PriceClass: !Ref CloudFrontPriceClass
Origins:
- DomainName: !Sub ${OriginS3Bucket}.s3.amazonaws.com
Id: mainS3Origin
S3OriginConfig:
OriginAccessIdentity: ''
OriginAccessControlId: !GetAtt OriginAccessControl.Id
OriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: !Sub 'OAC of ${OriginS3Bucket}. Created by ${AWS::StackName}'
Name: !Sub 'OAC-${OriginS3Bucket}'
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
Outputs:
OriginS3BucketName:
Value: !Ref OriginS3Bucket
Export:
Name: !Sub ${AWS::StackName}-OriginS3Bucket
CloudfrontDomainName:
Value: !GetAtt CloudFrontDistribution.DomainName
Export:
Name: !Sub ${AWS::StackName}-CloudfrontDomainName
テンプレートからコマンドでリソースを構築する場合は、下記のようなコマンド実行になります。
ここではスタック名を「sample-s3cf」としています。作成されるS3バケット名に使われます。
aws cloudformation deploy --stack-name sample-s3cf --template-file ./tmpl_s3cf_202303.yaml
更新した部分1つめについて
1つめの更新ポイントである「キャッシュポリシーなどを管理ポリシーに変更」についての補足説明です。
2020年7月頃からCloudFrontのキャッシュとオリジンリクエストの設定がポリシーとして外だしできるようになり、AWSマネージドな管理ポリシーも提供されるようになっていました。
なので、管理ポリシーを使用するようにテンプレートを改善しました。
テンプレートの59~60行目にある下記の設定が該当の部分です。
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized
OriginRequestPolicyId: acba4595-bd28-49b8-b9fe-13317c0390fa # UserAgentRefererHeaders
ここでは CachePolicyとOriginRequestPolicyに、それぞれ「CachingOptimized」「UserAgentRefererHeaders」の管理ポリシーを設定しています。
各管理ポリシーのID値については、下記の公式ガイドに載っています。
更新した部分2つめについて
2つめの更新ポイントでである「S3バケットへのアクセスをCloudFront経由に制限する設定をOAIからOACに変更」についての補足説明です。
以前(2022年8月頃まで)はOAC(OriginAccessControl)ではなくOAI(OriginAccessIdentity)を使って、オリジンとなるS3バケットのアクセスをCloudFront経由に制限する設定を行っていました。
OACの登場により、OAIではなくOACの利用が推奨されるようになったのでテンプレートを改善しました。
下記がテンプレートの関連する部分です。
OriginS3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref OriginS3Bucket
PolicyDocument:
Statement:
- Action: s3:GetObject
Effect: Allow
Resource: !Sub arn:${AWS::Partition}:s3:::${OriginS3Bucket}/*
Principal:
Service: cloudfront.amazonaws.com
Condition:
StringEquals:
AWS:SourceArn: !Sub arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
- Action: s3:ListBucket
Effect: Allow
Resource: !Sub arn:${AWS::Partition}:s3:::${OriginS3Bucket}
Principal:
Service: cloudfront.amazonaws.com
Condition:
StringEquals:
AWS:SourceArn: !Sub arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
※ ↑で「s3:GetObject」だけでなく「s3:ListBucket」の設定も行っているのは、存在しないファイルのURLが参照された場合に403(Access Denied)ではなく404(Not Found)のステータコードのレスポンスを返したいためです(ListBucketの設定がないと403になります)。
Origins:
- DomainName: !Sub ${OriginS3Bucket}.s3.amazonaws.com
Id: mainS3Origin
S3OriginConfig:
OriginAccessIdentity: ''
OriginAccessControlId: !GetAtt OriginAccessControl.Id
OriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: !Sub 'OAC of ${OriginS3Bucket}. Created by ${AWS::StackName}'
Name: !Sub 'OAC-${OriginS3Bucket}'
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
※現状では、使わないとしてもテンプレート上は"OriginAccessIdentity"の設定が必須になっているようで、エラー回避のための空文字を設定しています。
備考(おまけ)
別記事でCodePipelineを使用して、gitリポジトリ(CodeCommit)へのpushをトリガーにSPA(React)のビルドとS3バケットへのデプロイを自動化するための構成用のCloudFormationテンプレートを↓の記事で公開しています。
SPA(React)のビルドとデプロイを自動化するAWS CodePipeline用のCloudFormationテンプレート
当記事で作成できるS3+CloudFrontと、↑の記事で作成できるCodePipelineを組み合わせることで、SPAの公開およびビルドとデプロイの自動化を行える構成が簡単に構築できます。
ご参考になれば幸いです。