AWS再入門2018 AWS CloudFormation編
こんにちは。池田です。ついにEcho Dotが届きました。注文から中1日で届いたのは感動です。でもEcho Plusが欲しいのです(Echo Dotはホワイトモデルを購入しました)。 開封の儀は家族全員揃って行いました。
早速[日本語Alexa] 発話の揺れをカスタムスロットで吸収する〜より自然に会話できるスキル作成のために〜を参考にスキルを作ったり、音楽を再生してもらったり、ピカチュウを呼び出したりしてみました。楽しかったです。 Echo Plusを購入できたら照明のOFF/ONとかもやってみたいです。
- AWS再入門2018 バックアップとディザスタリカバリ編
- AWS再入門2018 Identity and Access Management(IAM)編
- AWS再入門2018 Amazon VPC(Virtual Private Cloud)編
- AWS再入門2018 Amazon Route 53(DNS)編
- AWS再入門2018 リレーショナルデータベース編
- AWS再入門2018 非リレーショナルデータベース編
- AWS再入門2018 AWS Lambda編
- AWS再入門2018 セキュリティ編
はじめに
AWS再入門2018シリーズ第九弾です。今回はAWS CloudFormationについて、実際にテンプレートを作成しながらその基本操作やお作法について整理していくことにします。
もくじ
概要
- AWSより提供されているアプリケーション管理サービスの1つ
- ほぼ全てのAWSリソースの構築、設定が行える
- 対応した言語で記述されたコードでインフラの構築が可能
- JSONとYAMLに対応
- GUIによる設計ツールとしてAWS CloudFormation Designerが提供されている
- ローカルまたはS3に作成したコードを保存しておけば繰り返し利用が可能
- 複数プロジェクト/部門間で同一のインフラ基盤が容易に構築できる
- 利用可能なサンプルテンプレートも多く公開されている
やってみた
取り敢えずやってみよう。ということで、まずは以下の構成を作成できるテンプレートを用意してみようと思います。
- VPCをひとつ
- 単一AZにPublic SubnetとPrivate Subnetをひとつずつ
- VPCにInternet Gatewayをアタッチ
いきなりゼロからテンプレートを書くのではなく、公開されているサンプルテンプレートに近い構成となったものが無いか探した結果、「Amazon VPC における単一の Amazon EC2」テンプレートを利用することにしました。
不要な部分の削除と整形など
まず、今回の目的ではEC2の構築は不要ですので、該当する記述(サンプルの6行目から187行目まで)をバッサリと削除しました。
次に、残りのコードからEC2向けの記述(残ったコードの75行目から151行目と161行目から286行目)も削除しました。
このままだと、Private Subnetが作られないので、Subnetのセクションを複製して追加しました。
仕上げに}
の数を確認、Description
の内容を簡潔に変更、無駄な改行を減らしたりリソース名を変更したものが以下となります。
{ "AWSTemplateFormatVersion" : "2010-09-09", "Description" : "TEST template.", "Resources" : { "VPC" : { "Type" : "AWS::EC2::VPC", "Properties" : { "CidrBlock" : "10.0.0.0/16", "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ] } }, "PublicSubnet" : { "Type" : "AWS::EC2::Subnet", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "CidrBlock" : "10.0.0.0/24", "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ] } }, "PrivateSubnet" : { "Type" : "AWS::EC2::Subnet", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "CidrBlock" : "10.0.1.0/24", "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ] } }, "InternetGateway" : { "Type" : "AWS::EC2::InternetGateway", "Properties" : { "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ] } }, "AttachGateway" : { "Type" : "AWS::EC2::VPCGatewayAttachment", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "InternetGatewayId" : { "Ref" : "InternetGateway" } } }, "RouteTable" : { "Type" : "AWS::EC2::RouteTable", "Properties" : { "VpcId" : {"Ref" : "VPC"}, "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ] } }, "Route" : { "Type" : "AWS::EC2::Route", "DependsOn" : "AttachGateway", "Properties" : { "RouteTableId" : { "Ref" : "RouteTable" }, "DestinationCidrBlock" : "0.0.0.0/0", "GatewayId" : { "Ref" : "InternetGateway" } } }, "SubnetRouteTableAssociation" : { "Type" : "AWS::EC2::SubnetRouteTableAssociation", "Properties" : { "SubnetId" : { "Ref" : "PublicSubnet" }, "RouteTableId" : { "Ref" : "RouteTable" } } }, "NetworkAcl" : { "Type" : "AWS::EC2::NetworkAcl", "Properties" : { "VpcId" : {"Ref" : "VPC"}, "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} } ] } }, "SubnetNetworkAclAssociation" : { "Type" : "AWS::EC2::SubnetNetworkAclAssociation", "Properties" : { "SubnetId" : { "Ref" : "PublicSubnet" }, "NetworkAclId" : { "Ref" : "NetworkAcl" } } } } }
構文のチェック
これで想定しているリソースが自動で作成されるようになりましたので、AWS CLIでテンプレートの検証をしてみます。ファイル名はCFn-test.json
としました。
$ aws cloudformation validate-template --template-body file://CFn-test.json { "Description": "TEST template.", "Parameters": [] }
AWS CLIでのチェックでは問題がない事がわかりましたが、せっかくなのでAWS CloudFormation Designerも利用してみたいと思います。 AWS CloudFormation Designerを開き、画面下部のテンプレートタブに先ほどのJSONコードを貼り付けます。 画面右上のエリアにリフレッシュを促すメッセージが表示されますので、リフレッシュボタンをクリックします。 VPCの中にPublic SubnetとPrivate Subnetが、それぞれひとつずつ配置されるということが視覚的にわかりやすく表示されました。
構築してみる
さて、ここまでで作成したテンプレートは、構文にミスもなく想定したリソースが配置されそうだ。ということが確認できましたので、実際に作成してみることにします。
CloudFormationの管理コンソールに移動して「新しいスタックの作成」を選択します。
ローカルに保存しておいたCFn-test.json
ファイルを使用するので「テンプレートをAmazon S3にアップロード」を選択します。
詳細の指定画面に変遷しますので、作成するスタックの名前を入力して進めます。今回は「TEST01」としました。
次のオプション選択画面では特に何もせずデフォルト値のままとしました。
作成内容の確認画面で間違いがなければ「作成」ボタンをクリックします。
上記画面が表示されたら少し待ちます。
「状況」が「CREATE_COMPLETE」になれば作成完了です。
作成されたものを確認する
スタックの作成は成功しましたので、確認のためVPCダッシュボードへ移動します。 VPCとサブネットが作成されています。
物足りない...
さすがにこれで終わりにするのは物足りなさすぎるので、まずはテンプレートをJSONからYAMLに書き換えてみます。
JSON形式からYAML形式へ
そもそも、今回のテンプレートをJSON形式で作成したのは拝借したサンプルテンプレートがJSON形式だったからです。そのため、編集作業の中で{}
の数を確かめたりする必要がありました。記述ミスを減らすためにも、より可読性の高いYAML形式の方が、筆者のようにコーディングに不慣れな場合はありがたいです。
とはいえ、ゼロからYAML形式に編集していくのも大変なので「俺が思いつくものは既に誰かが作っている」の理論で探してみることにしました。
はい、ありました。[Ruby][小ネタ] ワンライナーで JSON を YAML に変換する
素晴らしい世界ですね。
早速実行します。
$ ruby -ryaml -rjson -e 'puts YAML.dump(JSON.parse(STDIN.read))' < CFn-test.json > test.yaml $ aws cloudformation validate-template --template-body file://test.yaml { "Description": "TEST template.", "Parameters": [] }
JSONファイルで行った時と同様に、AWS CloudFormation Designerで確かめてみます。 当たり前ではありますが、同じ構成図になりました。 では、実際に作成します。先にJSONファイルで作成したスタックを削除してから実行します。
2018/02/06追記 はてブのコメントにて教えていただきました。気づいていませんでした。ありがとうございます。 https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/working-with-templates-cfn-designer-json-editor.html
削除処理が終わったら、YAML形式に変換したテンプレートを選択してスタックを作成し、JSON形式のテンプレートで作成した時と同じ結果になるかを確かめます。 スタック名は「YAML-TEST」としました。
まだ物足りない
単にJSON形式とYAML形式で同じ構成のスタックを作成しただけで、満足してしまう訳にもいきません。せっかくYAML形式でテンプレートが用意できたのですから、もう少しYAMLファイルと触れ合うことにします。 東京リージョンにAZが追加されたのですから、複数AZにSubnetを配置させることにします。
複数AZにSubnetを配置させる
さて、複数のAZにSubnetを分散配置するにはどうしたら良いのでしょうか。迷ったら検索ですね。
ありました。こちらの【アップデート】CloudFormationに事前にリソースがどう変更されるのかを確認できる「Change Sets」が追加されました!で使われているFn::Select
とFn::GetAZs
です。
--- AWSTemplateFormatVersion: '2010-09-09' Description: TEST template. Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 Tags: - Key: Application Value: Ref: AWS::StackId PublicSubnet1a: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPC CidrBlock: 10.0.0.0/24 AvailabilityZone: Fn::Select: - '0' - Fn::GetAZs: Ref: AWS::Region Tags: - Key: Application Value: Ref: AWS::StackId PrivateSubnet1a: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPC CidrBlock: 10.0.10.0/24 AvailabilityZone: Fn::Select: - '0' - Fn::GetAZs: Ref: AWS::Region Tags: - Key: Application Value: Ref: AWS::StackId PublicSubnet1c: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPC CidrBlock: 10.0.1.0/24 AvailabilityZone: Fn::Select: - '1' - Fn::GetAZs: Ref: AWS::Region Tags: - Key: Application Value: Ref: AWS::StackId PrivateSubnet1c: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPC CidrBlock: 10.0.20.0/24 AvailabilityZone: Fn::Select: - '1' - Fn::GetAZs: Ref: AWS::Region Tags: - Key: Application Value: Ref: AWS::StackId PublicSubnet1d: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPC CidrBlock: 10.0.3.0/24 AvailabilityZone: Fn::Select: - '2' - Fn::GetAZs: Ref: AWS::Region Tags: - Key: Application Value: Ref: AWS::StackId PrivateSubnet1d: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPC CidrBlock: 10.0.30.0/24 AvailabilityZone: Fn::Select: - '2' - Fn::GetAZs: Ref: AWS::Region Tags: - Key: Application Value: Ref: AWS::StackId InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Application Value: Ref: AWS::StackId AttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: Ref: VPC InternetGatewayId: Ref: InternetGateway RouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: Ref: VPC Tags: - Key: Application Value: Ref: AWS::StackId Route: Type: AWS::EC2::Route DependsOn: AttachGateway Properties: RouteTableId: Ref: RouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: Ref: InternetGateway SubnetRouteTableAssociation1a: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: Ref: PublicSubnet1a RouteTableId: Ref: RouteTable SubnetRouteTableAssociation1c: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: Ref: PublicSubnet1c RouteTableId: Ref: RouteTable SubnetRouteTableAssociation1d: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: Ref: PublicSubnet1d RouteTableId: Ref: RouteTable NetworkAcl: Type: AWS::EC2::NetworkAcl Properties: VpcId: Ref: VPC Tags: - Key: Application Value: Ref: AWS::StackId SubnetNetworkAclAssociation1a: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: SubnetId: Ref: PublicSubnet1a NetworkAclId: Ref: NetworkAcl SubnetNetworkAclAssociation1c: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: SubnetId: Ref: PublicSubnet1c NetworkAclId: Ref: NetworkAcl SubnetNetworkAclAssociation1d: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: SubnetId: Ref: PublicSubnet1d NetworkAclId: Ref: NetworkAcl
テンプレートを作成したら構文チェックをします。
$ aws cloudformation validate-template --template-body file://test2.yaml { "Description": "TEST template.", "Parameters": [] }
では、既存のスタックを削除してtest2.yaml
でスタックの作成を行います。
TEST-3AZというスタック名にしました。
期待通り3つのAZにそれぞれPublic SubnetとPrivate Subnetがひとつずつ配置されました。
まとめ
AWS CloudFormationの基本となるテンプレートの記述と使用方法から、複数AZにSubnetを作成するところまでを実践してみました。 今回作成したテンプレートをベースとしてEC2やRDSの配置や、S3など他のAWSリソースと連携させる方法についてはまた別の機会にしようと思います。この記事がこれからAWSを触れてみようとしている方や、AWS認定試験に挑戦しようと考えている方にとって僅かでもお役に立てば幸いです。