Help us understand the problem. What is going on with this article?

[JAWS-UG CLI] CloudFormation:#1 はじめてのCloudFormation (S3webサイトホスティングの作成から削除まで)

More than 5 years have passed since last update.

http://jawsug-cli.doorkeeper.jp/events/13797 でのハンズオン資料です。

このハンズオンでは、CloudFormationを利用してS3上で以下の作業を行なってみます。

  • バケットの作成
  • バケットの変更 (Webサイトホスティングの設定を追加)
  • バケットポリシーの作成
  • バケットポリシーの削除
  • バケットの削除

CloudFormationの学習が目的なので、最小のテンプレートでバケットを作成し、後からWebサイトホティングの設定をします。
(通常はこの2つの作業内容を1つのテンプレートに記述して1回で実行します。)
更に、バケットポリシーは独立したスタックとして作成します。
最後に各スタックを削除して終了とします。

CloudFormationとしての作業は下記になります。

  • スタックの作成 (最低限の項目によるS3バケットの作成)
  • スタックの更新 (webサイトホスティングの設定を追加)
  • 別のスタックの作成 (バケットポリシー)
  • バケットポリシースタックの削除
  • バケットスタックの削除

前提条件

  • S3へのフルアクセス権限

0. 事前作業

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}

1. 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. コンテンツの転送

確認用コンテンツを作成して、バケットに転送します。

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

3. 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": []
   }

4. 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/*\"}]}"
   }

5. コンテンツへのアクセス

最後に、ブラウザでS3のコンテンツにアクセスできることを確認してみましょう。

URLは下記のコマンドで確認できます。

コマンド
   echo ${CF_BUCKET_URL}

6. 後始末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. コンテンツへのアクセス

コンテンツにも、引き続きアクセスできるようです。(詳細未確認)

7. 後始末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の作成、一覧、更新、削除ができるようになりました。
(あくまでも実習用なので実用的ではないですが;-)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away