dev ドメイン用のブログ環境を AWS で作成する
概要
dev ドメインを取得したので、AWS でブログホスティングする環境を構築します。 一般的に、静的なサイトの場合、S3 の Website ホスティングで構築すれば良いですが、HTTPS に対応するためには前段に CloudFront を配置する必要があります。dev ドメインではウェブブラウザで HSTS プリロードされているため HTTPS が必須となります(S3 だけでは運用できません) 本記事では、CloudFront + S3 環境を一撃(ほんとは二撃)で作成する CFn を備忘録として紹介します。
前提
- 運用したい dev ドメインを Route53 でホスティングしている環境を想定しています。
テンプレート
以下、テンプレートとなります。注意点としては、
- CloudFornt は us-east-1 でしか作成できないので、テンプレート内の全てのリソースは us-east-1 となる
- ACM で証明書をリクエストしているので、検証レコードをマネジメントコンソールより追加する必要がある
作成するリソースは大体次です。
Lambda@Ege は CloudFront がサブパスの hoge/
を hoge/index.html
に保管しないために作成しています。
- CloudFront
- S3
- ACM 証明書
- Route53 のレコード
- Lambda@Edge
パラメータ
ValidationDomain
には、Route53 のホストゾーンのドメインを指定する(例:konoui.dev
)DomainName
には、Web サイトのドメインを指定する(例:www.konoui.dev
)AlternativeDomainName
には、DomainName
とは別に運用したい代替ドメインを指定する(例:konoui.dev
)
AWSTemplateFormatVersion: 2010-09-09
Description: Static contents distribution using S3 and CloudFront.
Parameters:
ValidationDomain:
Description: The domain name which you want to validate
Type: String
DomainName:
Description: FQDN of your web sites
Type: String
AlternativeDomainName:
Description: FQDN of your web sites
Type: String
Resources:
ACMCertificate:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Ref DomainName
SubjectAlternativeNames:
- !Ref AlternativeDomainName
DomainValidationOptions:
- DomainName: !Ref DomainName
ValidationDomain: !Ref ValidationDomain
ValidationMethod: DNS
Tags:
- Key: Name
Value: !Ref DomainName
AssetsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref DomainName
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}"
ReservedBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref AlternativeDomainName
AssetsDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Aliases:
- !Ref DomainName
- !Ref AlternativeDomainName
Origins:
- Id: S3Origin
DomainName: !GetAtt AssetsBucket.DomainName
S3OriginConfig:
OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}"
ViewerCertificate:
SslSupportMethod: sni-only
AcmCertificateArn: !Ref ACMCertificate
Enabled: true
HttpVersion: http2
# see https://aws.amazon.com/jp/cloudfront/pricing/
PriceClass: PriceClass_200
DefaultRootObject: index.html
Comment: !Sub "${AWS::StackName} Distribution"
DefaultCacheBehavior:
TargetOriginId: S3Origin
ForwardedValues:
QueryString: false
ViewerProtocolPolicy: redirect-to-https
LambdaFunctionAssociations:
- EventType: origin-request
LambdaFunctionARN: !Ref OriginRequestLambdaVersion
Tags:
- Key: Name
Value: AWS::StackName
CloudFrontOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Sub "${AWS::StackName} Origin Access Identity"
OriginRequestLambda:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
'use strict';
const path = require('path');
exports.handler = (event, context, callback) => {
var request = event.Records[0].cf.request;
if (!path.extname(request.uri)) {
// Rewrite URL
request.uri = request.uri.replace(/\/?$/, '\/index.html');
}
return callback(null, request);
};
Handler: index.handler
MemorySize: 128
Role: !GetAtt OriginRequestLambdaExecutionRole.Arn
Runtime: nodejs8.10
Tags:
- Key: Domain
Value: !Ref DomainName
OriginRequestLambdaVersion:
Type: AWS::Lambda::Version
Properties:
FunctionName: !GetAtt OriginRequestLambda.Arn
OriginRequestLambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- edgelambda.amazonaws.com
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
AssetsDNSRecords:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneName: !Sub "${ValidationDomain}."
RecordSets:
- Name: !Sub "${DomainName}."
Type: A
AliasTarget:
# see Amazon CloudFront https://docs.aws.amazon.com/ja_jp/general/latest/gr/rande.html
HostedZoneId: Z2FDTNDATAQYW2
DNSName: !GetAtt AssetsDistribution.DomainName
- Name: !Sub "${AlternativeDomainName}."
Type: A
AliasTarget:
HostedZoneId: Z2FDTNDATAQYW2
DNSName: !GetAtt AssetsDistribution.DomainName
Outputs:
URL:
Value: !Sub "https://${DomainName}"
デプロイ
aws cloudformation deploy --stack-name <スタック名> --template <テンプレート名> --capabilities CAPABILITY_IAM --region us-east-1
上記デプロイだけではリソースの作成は完了しません。ACM の証明書は DNS 検証としているため、ACM コンソールより DNS 検証ための DNS レコードを当該ホストゾーンに追加する必要があります。 DNS 検証終了後、CFn にて処理が継続し CloudFront 等が作成されます。