前書き
やること
今回は完全に趣味の投稿となります。実用性はほぼ無いと思いますので興味のある方のみご覧ください。
やることは以下です。すべてCloudFormation単体で行います。CDKは使用しません。
- S3バケットを作成。(パブリックアクセスブロックを設定)
- CloudFrontをデプロイしてオリジンに❶で作ったバケットを設定。同時にOAI(Origin Access Identity)を作って設定する。
- ❶で作ったS3バケットのバケットポリシーに、❷で作ったOAIからの接続のみを許可する設定を投入する。
- ❶〜❸で作ったリソースを削除しようとするが、エラーが発生する。
- エラーの原因を特定して、解消してリソースを削除する。
リソース削除エラーへの対処
❹〜❺に書きましたら、エラーが発生してリソースが削除できなくなりました。
原因は以下でした。
要するに、スタックから別のスタックの値をインポート(Fn::ImportValue)しているので、依存関係のようなものが発生して削除できなくなった、というものでした。
こんなトラップ的なエラー(そもそもデプロイの仕方が悪いのかもしれませんが)があるものかと、備忘録をかねて記載したいと思います。
やってみる
S3バケット作成
使用するCloudFormationテンプレートは以下になります。
- S3.yml
---
AWSTemplateFormatVersion: 2010-09-09
Description: "IaC for S3"
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: "formatsushi0"
PublicAccessBlockConfiguration:
BlockPublicAcls: "true"
BlockPublicPolicy: "true"
IgnorePublicAcls: "true"
RestrictPublicBuckets: "true"
Outputs:
NameOfS3Bucket:
Description: "Name of S3 Bucket"
Value: !Ref S3Bucket
Export:
Name: "ExS3BucketName"
簡単に説明します。
“formatsushi0″という名前でS3バケットを作成しています。
パブリックアクセスはすべてブロックしています。
S3バケット名を、”ExS3BucketName”という名前でエクスポートしています。
% aws cloudformation create-stack --template-body file:///Users/atsushi/Document/AWS/CloudFormation/S3.yml --stack-name Stack-S3
上記コマンドでデプロイします。
CloudFrontのデプロイ
テンプレートは以下になります。
- CloudFront.yml
---
AWSTemplateFormatVersion: 2010-09-09
Description: "IaC for CloudFront"
Resources:
OriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Sub
- "OAI-${S3Bucket}"
- S3Bucket: {"Fn::ImportValue": !Sub "ExS3BucketName"}
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Comment: "Deployed by CloudFormation"
HttpVersion: "http2"
Aliases:
- "atsushinotes.work"
ViewerCertificate:
AcmCertificateArn: "arn:aws:acm:us-east-1:XXXXXXXXXXXX:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
MinimumProtocolVersion: "TLSv1.2_2021"
SslSupportMethod: "sni-only"
Origins:
- S3OriginConfig:
OriginAccessIdentity: !Sub
- "origin-access-identity/cloudfront/${OAIID}"
- OAIID: !Ref "OriginAccessIdentity"
DomainName: !Sub
- "${ExS3BucketName}.s3.ap-northeast-1.amazonaws.com"
- ExS3BucketName: {'Fn::ImportValue': !Sub 'ExS3BucketName'}
Id: !Sub
- "${ExS3BucketName}.s3.ap-northeast-1.amazonaws.com"
- ExS3BucketName: {'Fn::ImportValue': !Sub 'ExS3BucketName'}
DefaultCacheBehavior:
TargetOriginId: !Sub
- "${ExS3BucketName}.s3.ap-northeast-1.amazonaws.com"
- ExS3BucketName: {'Fn::ImportValue': !Sub 'ExS3BucketName'}
ViewerProtocolPolicy: allow-all
CachePolicyId: "6xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx6"
Enabled: true
Outputs:
IdOfOAI:
Description: "ID of OAI"
Value: !Ref OriginAccessIdentity
Export:
Name: "ExOAIID"
簡単に説明します。
OAIを”OAI-[S3バケット名]”という名前で作成しています。
代替ドメインを”atsushinotes.work”という名前で作成しています。作成済みのACMををしています。(このドメインは使いません。検証で設定してみただけです)
“sni-only”でレガシークライアントサポートは無効にしています。
オリジンは先ほど作成したS3バケットにしています([S3バケット名].s3.ap-northeast-1.amazonaws.com)。先ほどのS3のスタックからS3バケット名はインポートしています。
キャッシュの動作はマネージドの「CachingOptimized」に設定しました。IDをマネージメントコンソールで確認してコードで指定しています。
OAIのIDを”ExOAIID”という名前でエクスポートしています。
% aws cloudformation create-stack --template-body file:///Users/atsushi/Document/AWS/CloudFormation/CloudFront.yml --stack-name Stack-CloudFront
上記コマンドでデプロイします。
S3バケットポリシーの更新
テンプレートは以下になります。
- S3-2.yml
---
AWSTemplateFormatVersion: 2010-09-09
Description: "IaC for S3"
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: "formatsushi0"
PublicAccessBlockConfiguration:
BlockPublicAcls: "true"
BlockPublicPolicy: "true"
IgnorePublicAcls: "true"
RestrictPublicBuckets: "true"
##### 追記ここから #####
S3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3Bucket
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "OAI"
Action:
- "s3:GetObject"
Effect: "Allow"
Resource: !Join
- ""
- - "arn:aws:s3:::"
- !Ref S3Bucket
- "/*"
Principal:
AWS: !Sub
- "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${IDofOAI}"
- IDofOAI: {"Fn::ImportValue": !Sub "ExOAIID"}
##### 追記ここまで #####
Outputs:
NameOfS3Bucket:
Description: "Name of S3 Bucket"
Value: !Ref S3Bucket
Export:
Name: "ExS3BucketName"
先ほどの”S3.yml”に追記を行っています。
内容は、以下のバケットポリシーを追加するものです。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "OAI",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXXXXXXXXXXXX"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::formatsushi0/*"
}
]
}
“XXXXXXXXXXXXXX”のところには、CloudFrontのスタックからインポートしたOAIのIDが入ります。
% aws cloudformation update-stack --template-body file:///Users/atsushi/Document/AWS/CloudFormation/S3-2.yml --stack-name Stack-S3
上記コマンドでS3のスタックを更新します。
動作確認
S3バケットにHTMLファイルを配置して、CloudFront経由で表示できることを確認します。
“form.html”という自作のファイルをアップロードします。
% aws s3 cp form.html s3://formatsushi0/
upload: ./form.html to s3://formatsushi0/form.html
表示することができました。
後かたづけ
HTMLファイル削除
とりあえず先に、作成したS3バケットの中を空にします。
% aws s3 rb s3://formatsushi0 --force
delete: s3://formatsushi0/form.html
remove_bucket: formatsushi0
CloudFrontスタックを削除しようとすると。。
CloudFrontスタックから削除します。
スタックはすぐに削除できると思っていたのですができませんでした。
以下のコマンドでCloudFrontスタックを削除しようとしました。
% aws cloudformation delete-stack --stack-name Stack-CloudFront
エラーは以下のとおりで、内容は「エクスポートしている値(ここでは”ExOAIID”)がS3スタックに使用されているので削除できません」です。
S3スタックが参照している箇所を実際の値に置き換える
ですので、S3スタック側でCloudFrontスタックの値を参照している箇所を、実際の値に置き換えます。
テンプレートは以下です。
- S3-delete.yml
---
AWSTemplateFormatVersion: 2010-09-09
Description: "IaC for S3"
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: "formatsushi0"
PublicAccessBlockConfiguration:
BlockPublicAcls: "true"
BlockPublicPolicy: "true"
IgnorePublicAcls: "true"
RestrictPublicBuckets: "true"
S3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3Bucket
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "OAI"
Action:
- "s3:GetObject"
Effect: "Allow"
Resource: !Join
- ""
- - "arn:aws:s3:::"
- !Ref S3Bucket
- "/*"
Principal:
##### 変更箇所は下記の一行 #####
AWS: "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXXXXXXXXXXX"
Outputs:
NameOfS3Bucket:
Description: "Name of S3 Bucket"
Value: !Ref S3Bucket
Export:
Name: "ExS3BucketName"
OAIのIDをAWSコンソールで確認して、上記のXXXXXXXXXXXXXの箇所に埋め込んでスタックを更新します。つまり、これでCloudFrontスタックの値を参照することがなくなります。
スタック更新のコマンド。
% aws cloudformation update-stack --template-body file:///Users/atsushi/Document/AWS/CloudFormation/S3-delete.yml --stack-name Stack-S3
CloudFrontスタックの削除のリトライ
S3スタック更新が成功したら、もう一度CloudFrontスタックの削除を試みます。
% aws cloudformation delete-stack --stack-name Stack-CloudFront
削除が始まりました。
S3スタックの削除
最後に、S3スタックを削除します。
% aws cloudformation delete-stack --stack-name Stack-S3
削除が始まりました。
今回の検証で作成したリソースはすべて削除されました。
コメント