http://jawsug-cli.doorkeeper.jp/events/13797 でのハンズオン資料です。
このハンズオンでは、CloudFormationを利用してS3上で以下の作業を行なってみます。
- バケットの作成
- バケットの変更 (Webサイトホスティングの設定を追加)
- バケットポリシーの作成
- バケットポリシーの削除
- バケットの削除
CloudFormationの学習が目的なので、最小のテンプレートでバケットを作成し、後からWebサイトホティングの設定をします。
(通常はこの2つの作業内容を1つのテンプレートに記述して1回で実行します。)
更に、バケットポリシーは独立したスタックとして作成します。
最後に各スタックを削除して終了とします。
CloudFormationとしての作業は下記になります。
- スタックの作成 (最低限の項目によるS3バケットの作成)
- スタックの更新 (webサイトホスティングの設定を追加)
- 別のスタックの作成 (バケットポリシー)
- バケットポリシースタックの削除
- バケットスタックの削除
前提条件
- S3へのフルアクセス権限
- 事前作業
=================
0.1. S3バケット名の決定
S3バケット名を決めておいてください。
$ CF_BUCKET_NAME='web-example'
バケット名はS3全体でユニークである必要があります。
0.2. コンテンツ用ディレクトリの作成
webサイトホスティングの動作確認用コンテンツを置くためのディレクトリを作成します。
$ CF_CONTENTS_DIR="${HOME}/tmp/contents"
$ mkdir -p ${CF_CONTENTS_DIR}
0.3. 作業用ディレクトリの作成
テンプレートを作成する作業用ディレクトリを作成します。
$ WORK_DIR="${HOME}/tmp/aws_cf"
$ mkdir -p ${WORK_DIR}
$ cd ${WORK_DIR}
- S3バケット作成
=================
まず最初に、create-stackサブコマンドの実習として、S3のバケットを作成してみます。
1.1. テンプレートの作成 (S3バケット作成)
S3バケットを作成するCloudFormationテンプレートを作成します。
$ FILE_CF_TEMPLATE="${CF_BUCKET_NAME}-bucket.json"
$ CF_RESOURCE_ID='S3Bucket'
$ CF_DESCRIBE='CloudFormation web bucket'
$ cat << EOF > ${FILE_CF_TEMPLATE}
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Resources" : {
"${CF_RESOURCE_ID}" : {
"Type" : "AWS::S3::Bucket",
"Properties" : {
"BucketName" : "${CF_BUCKET_NAME}"
}
}
},
"Description": "${CF_DESCRIBE}"
}
EOF
JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。
$ cat ${FILE_CF_TEMPLATE} | json_verify
JSON is valid
1.2. S3バケットの作成
実際にCloudFormationでS3バケットを作成してみましょう。
最初に、スタック名を決めます。
$ CF_STACK_NAME="${CF_BUCKET_NAME}-bucket" && echo ${CF_STACK_NAME}
web-example-bucket
次に、実際にスタックを実行して、S3バケットを作成します。
$ aws cloudformation create-stack --stack-name ${CF_STACK_NAME} --template-body file://${FILE_CF_TEMPLATE}
{
"StackId": "arn:aws:cloudformation:us-west-1:XXXXXXXXXXXX:stack/web-example-bucket/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}
1.3. スタックの状況確認
list-stacksコマンドで、スタックの状況を確認してみましょう。
$ aws cloudformation list-stacks \
| jq -r --arg stackname ${CF_STACK_NAME} '.StackSummaries[] | select(.StackName == $stackname) | .StackStatus'
CREATE_COMPLETE
StackStatusが'CREATE_COMPLETE'になっていれば作成は完了です。
それ以外が表示されている場合は、下記コマンドでFailedの文字が出ている前後を見て原因を調べます。
$ aws cloudformation describe-stack-events --stack-name ${CF_STACK_NAME}
1.4. S3バケットの確認
実際にS3バケットが存在することを確認しましょう。
$ aws s3 ls | grep ${CF_BUCKET_NAME}
2014-09-09 15:19:34 web-example
1.5. スタックの内容確認
get-templateサブコマンドでスタックの内容を確認します。
$ aws cloudformation get-template --stack-name ${CF_STACK_NAME}
{
"TemplateBody": {
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "CloudFormation web bucket",
"Resources": {
"S3BucketWeb": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": "web-example"
}
}
}
}
}
1.6. バケットのURL取得
作成したバケットのアクセスURL(HTTP)を取得します。
$ CF_BUCKET_URL="${CF_BUCKET_NAME}.s3-website-`aws s3api get-bucket-location --bucket=${CF_BUCKET_NAME} --query LocationConstraint --output text`.amazonaws.com" \
&& echo ${CF_BUCKET_URL}
web-example.s3-website-us-west-1.amazonaws.com
- コンテンツの転送
===================
確認用コンテンツを作成して、バケットに転送します。
2.1. コンテンツの作成
確認用コンテンツをコンテンツ用ディレクトリで作成します。
$ cd ${CF_CONTENTS_DIR}/
$ cat << EOF > ${CF_CONTENTS_DIR}/index.html
<html>
<head>
<title>CF</title>
</head>
<body>
<h1>CF</h1>
</body>
<html>
EOF
2.2. コンテンツの同期
確認用コンテンツをS3バケットに転送します。
$ aws s3 sync . "s3://${CF_BUCKET_NAME}/"
upload: ./index.html to s3://web-example/index.html
2.3. コンテンツの確認
S3バケット上に転送されたことを確認します。
$ aws s3 ls s3://${CF_BUCKET_NAME}/
2014-09-21 19:52:50 78 index.html
- Webサイトホスティングの設定
==============================
update-stackサブコマンドの実習として、S3バケットにWebサイトホスティング設定を反映してみます。
3.1. テンプレートの作成 (S3バケット更新)
最初に、更新用のテンプレートを作成します。
$ FILE_CF_TEMPLATE="${CF_BUCKET_NAME}-bucket-website.json"
$ cd ${WORK_DIR}
$ cat << EOF > ${FILE_CF_TEMPLATE}
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources" : {
"${CF_RESOURCE_ID}" : {
"Type" : "AWS::S3::Bucket",
"Properties" : {
"BucketName" : "${CF_BUCKET_NAME}",
"WebsiteConfiguration": {
"IndexDocument": "index.html",
"ErrorDocument": "error.html"
}
}
}
},
"Description": "${CF_DESCRIBE}"
}
EOF
テンプレートは上書き更新されるようなので、更新前の情報も含めておかないと、記述の無い部分は消えてしまいます。
JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。
$ cat ${FILE_CF_TEMPLATE} | json_verify
JSON is valid
3.2. S3バケットの更新
次に、実際にバケットの設定を更新します。
$ aws cloudformation update-stack --stack-name ${CF_STACK_NAME} --template-body file://${FILE_CF_TEMPLATE}
{
"StackId": "arn:aws:cloudformation:us-west-1:XXXXXXXXXXXX:stack/web-example-bucket/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}
3.3. スタックの状況確認
list-stacksサブコマンドで更新状況を確認します。
$ aws cloudformation list-stacks \
| jq -r --arg stackname ${CF_STACK_NAME} '.StackSummaries[] | select(.StackName == $stackname) | .StackStatus'
UPDATE_COMPLETE
StackStatusが'UPDATE_COMPLETE'になっていれば更新は完了です。
それ以外が表示されている場合は、下記コマンドでFailedの文字が出ている前後を見て原因を調べます。
$ aws cloudformation describe-stack-events --stack-name ${CF_STACK_NAME}
3.4. S3バケットのWebサイトホスティング設定確認
$ aws s3api get-bucket-website --bucket ${CF_BUCKET_NAME}
{
"RedirectAllRequestsTo": {},
"IndexDocument": {
"Suffix": "index.html"
},
"ErrorDocument": {
"Key": "error.html"
},
"RoutingRules": []
}
- S3バケットポリシーの設定
===========================
Webサイトコンテンツとして外部からアクセスできるように、S3バケットポリシーの設定を行います。
ここでは、新規スタックを追加してみます。
4.1. テンプレートの作成 (S3バケットポリシー作成)
まず、CloudFormation用のテンプレートを作成します。
$ FILE_CF_TEMPLATE="${CF_BUCKET_NAME}-bucketpolicy.json"
$ CF_RESOURCE_ID='S3BucketPolicy'
$ CF_DESCRIBE='CloudFormation web bucket policy'
$ cat << EOF > ${FILE_CF_TEMPLATE}
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources" : {
"${CF_RESOURCE_ID}" : {
"Type" : "AWS::S3::BucketPolicy",
"Properties" : {
"Bucket" : "${CF_BUCKET_NAME}",
"PolicyDocument" : {
"Version" : "2012-10-17",
"Statement" : [
{
"Action" : ["s3:GetObject"],
"Effect":"Allow",
"Principal": {
"AWS" : [
"*"
]
},
"Resource":["arn:aws:s3:::${CF_BUCKET_NAME}/*"],
"Sid":"AddPerm"
}
]
}
}
}
},
"Description": "${CF_DESCRIBE}"
}
EOF
JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。
$ cat ${FILE_CF_TEMPLATE} | json_verify
JSON is valid
4.2. S3バケットポリシーの作成
実際にCloudFormationでS3バケットポリシーを作成してみましょう。
まず、スタック名を決めます。
$ CF_STACK_NAME="${CF_BUCKET_NAME}-bucketpolicy" && echo ${CF_STACK_NAME}
web-example-bucketpolicy
次に、実際にスタックを作成します。
$ aws cloudformation create-stack --stack-name ${CF_STACK_NAME} --template-body file://${FILE_CF_TEMPLATE}
{
"StackId": "arn:aws:cloudformation:us-west-1:XXXXXXXXXXXX:stack/web-example-bucketpolicy/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}
4.3. スタックの状況確認
list-stacksコマンドで、スタックの状況を確認してみましょう。
$ aws cloudformation list-stacks \
| jq -r --arg stackname ${CF_STACK_NAME} '.StackSummaries[] | select(.StackName == $stackname) | .StackStatus'
CREATE_COMPLETE
StackStatusが'CREATE_COMPLETE'になっていれば作成は完了です。
それ以外が表示されている場合は、下記コマンドでFailedの文字が出ている前後を見て原因を調べます。
$ aws cloudformation describe-stack-events --stack-name ${CF_STACK_NAME}
4.4. S3バケットポリシーの確認
S3バケットにバケットポリシーが実際に設定されていることを確認してみましょう。
$ aws s3api get-bucket-policy --bucket ${CF_BUCKET_NAME}
{
"Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AddPerm\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":\"s3:GetObject\",\"Resource\":\"arn:aws:s3:::web-example/*\"}]}"
}
- コンテンツへのアクセス
=========================
最後に、ブラウザでS3のコンテンツにアクセスできることを確認してみましょう。
URLは下記のコマンドで確認できます。
echo ${CF_BUCKET_URL}
- 後始末1 (S3バケットポリシーの削除)
====================================
delete-stackサブコマンドの実習として、S3バケットポリシーのスタックを削除してみます。
6.1. スタックの一覧
まず、スタック一覧から削除するスタックのスタック名を確認します。
$ aws cloudformation list-stacks \
| jq '.StackSummaries[] | select(.StackStatus != "DELETE_COMPLETE" )'
6.2. 削除対象の確定
次に、削除するスタック名と、対象となるS3バケット名を特定します。
$ S3_BUCKET_NAME="web-example"
$ CF_STACK_NAME="web-example-bucketpolicy"
6.3. スタックの削除
実際に、スタックを削除してみます。
$ aws cloudformation delete-stack --stack-name ${CF_STACK_NAME}
(戻り値なし)
6.4. スタックのステータス確認
list-stacksサブコマンドで削除状況を確認します。
$ aws cloudformation list-stacks \
| jq -r --arg stackname ${CF_STACK_NAME} '.StackSummaries[] | select(.StackName == $stackname) | .StackStatus'
DELETE_COMPLETE
StackStatusが'DELETE_COMPLETE'になっていれば削除は完了です。
それ以外が表示されている場合は、下記コマンドでFailedの文字が出ている前後を見て原因を調べます。
$ aws cloudformation describe-stack-events --stack-name ${CF_STACK_NAME}
6.5. ポリシーの確認
$ aws s3api get-bucket-policy --bucket ${S3_BUCKET_NAME}
S3のバケットポリシーを別スタックで追加した場合、スタックを削除してもポリシーは変更ないようです。(詳細未確認)
6.6. コンテンツへのアクセス
コンテンツにも、引き続きアクセスできるようです。(詳細未確認)
- 後始末2 (S3バケットの削除)
============================
最後に、後始末としてS3バケットを削除します。
7.1. スタックの一覧
まず、スタック一覧から削除するスタックのスタック名を確認します。
$ aws cloudformation list-stacks \
| jq '.StackSummaries[] | select(.StackStatus != "DELETE_COMPLETE" )'
7.2. 削除対象の確定
次に、削除するスタック名と、対象となるS3バケット名を特定します。
$ S3_BUCKET_NAME='web-example'
$ CF_STACK_NAME='web-example-bucket'
7.3. バケットの確認
バケットは中身が空でないと削除できないので、バケットにファイルがないか確認します。
$ aws s3 ls s3://${S3_BUCKET_NAME}/
2014-09-09 15:19:34 web-example
7.4. バケットの中身削除
S3バケット内にファイルが存在する場合は、削除します。
$ aws s3 rm s3://${S3_BUCKET_NAME}/ --recursive
7.5. スタックの削除
実際に、スタックを削除してみます。
$ aws cloudformation delete-stack --stack-name ${CF_STACK_NAME}
(戻り値なし)
7.6. スタックのステータス確認
list-stacksサブコマンドで削除状況を確認します。
$ aws cloudformation list-stacks \
| jq -r --arg stackname ${CF_STACK_NAME} '.StackSummaries[] | select(.StackName == $stackname) | .StackStatus'
DELETE_COMPLETE
StackStatusが'DELETE_COMPLETE'になっていれば削除は完了です。
それ以外が表示されている場合は、下記コマンドでFailedの文字が出ている前後を見て原因を調べます。
$ aws cloudformation describe-stack-events --stack-name ${CF_STACK_NAME}
7.7. バケットの確認
削除対象のバケットが存在しないことを確認します。
$ aws s3 ls s3://${S3_BUCKET_NAME}/
A client error (NoSuchBucket) occurred when calling the ListObjects operation: The specified bucket does not exist
7.8. スタックの一覧
list-stacksサブコマンドで他に削除するものがないか確認します。
$ aws cloudformation list-stacks \
| jq '.StackSummaries[] | select(.StackStatus != "DELETE_COMPLETE" )'
おわり
この手順で、stackの作成、一覧、更新、削除ができるようになりました。
(あくまでも実習用なので実用的ではないですが;-)