CloudFormation で OAI を使った CloudFront + S3 の静的コンテンツ配信インフラを作る

よく訓練されたアップル信者、都元です。年末に Fitbit Alta HR を買いました。 睡眠や心拍がほぼ24時間体制でトラッキングされるって面白いですね。え? アップルウォッうわなにをする

はじめに

さて、S3 + CloudFront で静的コンテンツ配信をしよう、というのはもう今さら言うまでもない鉄板構成だと思います。かつてAWSにおける静的コンテンツ配信パターンカタログ(アンチパターン含む)でご紹介した「横綱」パターンですね。

公開サイトとして、この構成を雑に作る場合

CloudFront を介して配信するということは基本的に公開サイトとして配信するユースケースが多いと思います。

つまり、原則としてはみんな CloudFront を介してコンテンツにアクセスして欲しいのですが、最悪 S3 に直接アクセスしてコンテンツを読まれたとしてもセキュリティ上は問題ない、ということです。

このような要件であれば、S3 の設定を Public にしてしまい、CloudFront からも(どこからでも) 自由にコンテンツを読み出せるように設定してしまうのが楽だと思います。

プライベートコンテンツを配信したい場合

一方で、CloudFront の署名付きURL(下記のリンクを参照)などの機能を使ってプライベートコンテンツを配信したい時は、前述のように S3 バケットを Public にしてしまうわけにはいきません。

このような時は、CloudFront の Origin Access Identity (以下、OAI) という機能を使います。 要するに S3 を公開状態にすることなく、S3 へのアクセスを CloudFront からのリクエストに絞るための仕組みです。詳細は次のエントリーを参照してください。

[CloudFront + S3]特定バケットに特定ディストリビューションのみからアクセスできるよう設定する

要するに次のような作業をします。

  1. S3 バケットを作成
  2. CloudFront の OAI を作成
  3. S3 バケットに、作成した OAI からのアクセスを許可するバケットポリシーを設定
  4. オリジンとして S3 バケットを設定、そしてオリジンアクセスの際にこの OAI を利用するように設定して、CloudFront distribution を作成

CloudFront + S3 with OAI の構成を CloudFormation で。

しかし、この構成の手動構築はそこそこ面倒です。こういう時は、CloudFormation でテンプレート化しておくのが吉でしょう。

ただ、私の古い記憶によると、CloudFormation はステップ 2 の Origin Access Identity 作成をサポートしていませんでした。当時やる気を失い、テンプレートを作らないでいた気がします。カスタムリソースを使うという手もありますが、いや、まぁ……ねぇ…w

しかし、先日あらためて調べてみたところ、いつの間にかサポートがありました。え、これいつの間にリリースされたの! 誰もブログ書いてないんじゃ…。やべえ、ということでいま筆を取っている次第です。

AWS::CloudFront::CloudFrontOriginAccessIdentity リソース

はい、テンプレート置いておきます。

AWSTemplateFormatVersion: 2010-09-09
Description: Static contents distribution using S3 and CloudFront.

Resources:
# S3 bucket contains static contents
AssetsBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
# S3 bucket policy to allow access from CloudFront OAI
AssetsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref AssetsBucket
PolicyDocument:
Statement:
- Action: s3:GetObject
Effect: Allow
Resource: !Sub arn:aws:s3:::${AssetsBucket}/*
Principal:
AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity}

# CloudFront Distribution for contents delivery
AssetsDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- Id: S3Origin
DomainName: !GetAtt AssetsBucket.DomainName
S3OriginConfig:
OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}
Enabled: true
DefaultRootObject: index.html
Comment: !Sub ${AWS::StackName} distribution
DefaultCacheBehavior:
TargetOriginId: S3Origin
ForwardedValues:
QueryString: false
ViewerProtocolPolicy: redirect-to-https
CloudFrontOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Ref AWS::StackName

Outputs:
URL:
Value: !Join [ "", [ "https://", !GetAtt [ AssetsDistribution, DomainName ]]]

ご自由にご利用ください!