CloudFormationは、ネットの解説記事を読むよりも、実際に自分でテンプレートを書いてみて試行錯誤を重ねたほうが理解が早い気がします(解説記事で完成形のテンプレートを見せられて心が折れた人は、僕も含めて少なくないのではないでしょうか)。
ただ、テンプレートの記述に敷居の高さを感じている人は多いと思いますので、本記事では「はじめてCloudFormationを触る人がつまづきやすい点」をピンポイントに解説したいと思います。
なお、本記事ではテンプレートの記述にはYAML形式を採用しています。
#はじめての方へ
CloudFormationへの慣れ方
これからCloudFormationに入門しようという時に、いきなりテンプレートの完成形を読んで理解するのは心理的なハードルが高いかと思います。
そこで最初は「VPCを作ること」を目標にして、VPCができたら次はサブネット、次はインターネットゲートウェイ、次はルートテーブルやセキュリティグループ…というように、ミニマムな目標からはじめてインクリメンタルに目標ラインを上げていくことで挫折を避けられるのではないかと勝手に思っています。
目安となるように、目標を三段階に設定してみました。
- VPCだけ定義してみる。
- サブネットの定義を追加してみる。組込み関数
!Ref
を使ってみる。 - インターネットゲートウェイ、ルートテーブル、セキュリティグループ等を追加し、EC2インスタンスの足場を構築する。
ステップ3まで辿り着けば、あとは公式リファレンスを見ながら自走できるんじゃないかと思ってます。
…と偉そうに書いてますが、僕も今日はじめてCloudFormationを触って、1日かけてなんとかステップ3まで辿り着いた人間です(2020年5月31日記)。
インデントについて
YAML形式ではタブインデントは使用できません。スペースインデントのみです。VS Codeなどタブ入力をスペースに変換してくれるエディタで記述するのが賢明かもしれません。
ミニマムなサンプル
はじめてテンプレートファイルを触る人のために、ミニマムなテンプレートのサンプルを載せておきます。
見ての通り、VPCだけ定義したサンプルです。YAML形式の読み方は、JSON形式と見比べればなんとなく理解できるかと思います。
AWSTemplateFormatVersion: '2010-09-09'
Resources:
MyVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.1.0.0/16
Tags:
- Key: Name
Value: MyVpc
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"MyVpc": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.1.0.0/16",
"Tags": [
{ "Key": "Name", "Value": "MyVpc" }
]
}
}
}
}
リソース定義の記述パターン
リソースの定義は、以下のパターンで記述します。Resources
セクション内に、リソースを列挙して記述します。
「リソースの論理名」など、日本語の部分はプレースホルダです。
Resources:
リソースの論理名:
Type: リソースタイプ
Properties:
リソースプロパティ名: 値
リソースプロパティ名: 値
リソースの論理名:
Type: リソースタイプ
Properties:
リソースプロパティ名: 値
リソースプロパティ名: 値
# (以下、繰り返し)
- 「リソースの論理名」とはリソースの識別子です(例「
MyVpc
」)。 - 「リソースタイプ」とはリソースの型のようなものです(例「
AWS::S3::EC2
」)。 - 「リソースプロパティ」とは、例えばVPCのCIDRブロックなどです。
試行錯誤を回避するためのメモ(共通編)
組込み関数「!Ref」について
公式ドキュメントの解説にはこう書いてあります。
組み込み関数 Ref は、指定したパラメータまたはリソースの値を返します。
・パラメータの論理名を指定すると、それはパラメータの値を返します。
・リソースの論理名を指定すると、それはそのリソースを参照するために通常使用できる値を返します (物理 ID)。
箇条書きのうち前者はともかく、後者が何を言っているか。
・リソースの論理名を指定すると、それはそのリソースを参照するために通常使用できる値を返します (物理 ID)。
これは例えば!Ref
の引数にリソースの論理名を指定すると、!Ref リソースの論理名
の部分が物理IDに展開される、ということを言っています。
VpcId: !Ref MyVpc
VpcId: vpc-053ffba26196d40c0
- 「リソースの論理名」とは、先述のサンプルでいうと
MyVpc
にあたるものです。 - 「物理ID」が何を指すかはコンテキストに依存しますが、リソースがVPCの場合は
VPC ID
(マネージメントコンソールでいうと以下の赤枠の値)に展開されます。つまり、vpc-053ffba26196d40c0
のようなIDに展開されます。
物理IDはリソースを構築するまでどんなIDが採番されるか分からないですし、既存の物理IDをハードコードするのはあり得ないので、こういう組込み関数が存在するのですね。
試行錯誤を回避するためのメモ(VPC編)
セキュリティグループのインバウンド・アウトバウンドに該当するプロパティは?
- インバウンド …
SecurityGroupIngress
- アウトバウンド …
SecurityGroupEgress
です。あまり聞きなれない用語ですね。
セキュリティグループのルールで「すべてのトラフィック」を定義するには?
MySecurityGroup1:
Type: AWS::EC2::SecurityGroup
Properties:
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
IpProtocol: -1
と記述してリソースを作成すると、マネージメントコンソール上のエントリの内容は以下になります。
ソース or 送信先にセキュリティグループを指定するには?
MySecurityGroup2:
Type: AWS::EC2::SecurityGroup
Properties:
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref MySecurityGroup1
IpProtocol: -1
マネージメントコンソール上のエントリの内容は以下になります。
FromPort、ToPortって?
SecurityGroupEgress
、SecurityGroupIngress
のFromPort
、ToPort
はポート番号を範囲指定するプロパティです。
ソースポートやデスティネーションポートの指定ではありません。
例えば「TCPの80番ポート」を指定する場合、以下のようにFromPort
とToPort
に同じポート番号(80
)を指定します。
MySecurityGroup1:
Type: AWS::EC2::SecurityGroup
Properties:
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
FromPort: 80
ToPort: 80
マネージメントコンソール上のエントリの内容は以下になります。
自動生成されるDHCPオプションセットのリソース定義
VPC作成時に自動生成されるDHCPオプションセットですが、そのリソース定義は以下です。DomainName
の値はリージョン.compute.internal
になります。自動生成されるけど使用しない、でもName
タグだけはつけておきたい...という時に使えるサンプルです。
MyDhcpOptions:
Type: AWS::EC2::DHCPOptions
Properties:
DomainName: ap-northeast-1.compute.internal
DomainNameServers:
- AmazonProvidedDNS
MyVPCDHCPOptionsAssociation:
Type: AWS::EC2::VPCDHCPOptionsAssociation
Properties:
DhcpOptionsId: !Ref MyDhcpOptions
VpcId: !Ref MyVpc
この先は?
本記事は育っていく記事なので、また気づきがあれば随時追加していきます。
参考ドキュメント
- AWS CloudFormation テンプレートリファレンス
- AWS リソースおよびプロパティタイプのリファレンス … テンプレートでリソースを定義する際は、このリファレンスからサンプルをコピペしてカスタマイズする、というのが常套ですかね。ドキュメントを読むついでに各プロパティにも目を通しておくと、AWS認定試験の勉強にもなりそうです。