Edited at

はじめてのAWS CloudFormationチュートリアル


本記事の対象者


  • AWS初心者の方


  • AWS初心者向けのチュートリアル、ハンズオンなどでマネジメントコンソール画面を用いてシンプルな構成を構築した経験のある方


  • 上記をマネジメントコンソールではなく、ファイルを使って構築することに関心のある方



本記事で行うこと

AWSのサービスのひとつであるCloudFormation用のテンプレートファイルを作成し、パブリックサーバー、プライベートサーバーを持つシンプルな構成を構築していきます。


テンプレートファイルとは

CloudFormationは、テンプレートファイルというものを読み込ませることでAWSのリソースを一括して構築してくれます。

テンプレートファイルの形式はJSONかYAMLを選択できますが、今回はYAMLで記述することにします。


エディタ

私は普段JetBrainsのIDEを使用しているのですが、CloudFormationのプラグインが存在するので今回導入しました。

プラグインを導入することで、リソースタイプやプロパティを補完してくれるようになります(リソースタイプやプロパティがそもそもどういったものなのかについては後述)。

スクリーンショット 2019-10-04 18.20.50.png

スクリーンショット 2019-10-04 18.23.09.png

また、2つ目のスクリーンショットではPropetiesにエラー(赤い下線)が表示されていますが、これは各AWSリソースタイプに対して必須のプロパティが記述されているかどうかをチェックしてくれています。


1. テンプレートファイルの作成

では、ここからテンプレートファイルの作成に入ります。


1.1. テンプレートフォーマットバージョンの指定

まず、テンプレートのフォーマットバージョンを指定します。

指定可能な値は2010-09-09のみです。

AWSTemplateFormatVersion: 2010-09-09

なお、この行の記述は任意なので、無かったとしてもCloudFormationはエラーにはなりません。


1.2. リソースの記述方法

ここからVPC等、各種のAWSリソースを作成するための記述を行なっていきます。

リソースの記述方法は以下になります。

Resources:

<Logical ID>:
Type: <Resource type>
Properties:
<Set of properties>


Logical ID

Logical IDは、そのリソースに付ける名前です。

同一テンプレートファイル内で、他と被らない一意なものにする必要があります。


Resource Type

Resource Typeの部分には、作成するAWSリソースの種類を指定します。

具体的にはリソースタイプ識別子というものを指定します。

リソースタイプ識別子は、


  • service-provider::service-name::data-type-name

という形式になっていて、例えば


  • VPCだったら、AWS::EC2::VPC

  • サブネットだったらAWS::EC2::Subnet

となっています。

一覧は以下から確認できます。


Set of propeties

Set of propetiesの部分には、各リソースのプロパティを記述します。

リソースの種類に応じて必須のプロパティと任意のプロパティがあります。


1.3. VPCの定義

まず、はじめにVPCを定義します。

# 最終行に以下を追加

Resources:
CfVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
-
Key: Name
Value: CfVpc


マネジメントコンソールの場合

上記は、マネジメントコンソールのVPCの作成において、下記の指定でVPCを作成するのと同等です。

項目
設定内容

名前タグ
CfVpc

IPv4 CIDRブロック
10.0.0.0/16

IPv6 CIDRブロック
IPv6 CIDRブロックなし

テナンシー
デフォルト


2. テンプレートファイルを使って実際にAWSリソースを作成してみる

まだVPCしか定義はしていませんが、いったんこの段階でCloudFormationを使ってAWSリソースを作成してみます。


2.1. スタックの作成

CloudFormationでは、テンプレートファイルで定義されたAWSリソース群をスタックという単位で管理します。

テンプレートファイルを使ってAWSリソースを作成するには、CloudFormationの画面でまずスタックの作成を選択します。

ap-northeast-1.console.aws.amazon.com_cloudformation_home_region=ap-northeast-1 (1).png


2.2. テンプレートの指定

テンプレートの準備完了 > テンプレートのファイルのアップロード > ファイルの選択 を選び、作成したテンプレートファイルをアップロードしたら、次へを押します。

ap-northeast-1.console.aws.amazon.com_cloudformation_home_region=ap-northeast-1 (4).png


2.3. スタックの詳細を指定

スタックの名前を決めます。ここではtrainingとしておきます。

パラメータは本記事では使わないので、そのまま次へを押します。

ap-northeast-1.console.aws.amazon.com_cloudformation_home_region=ap-northeast-1 (5).png


2.4. スタックオプションの設定/レビュー

続いて、スタックオプションの設定画面が表示されるので、そのまま次へを押します。

最後に、レビュー画面(確認画面)が表示されるので、スタックの作成 を押します。


2.5. 各リソースの作成状況の確認

スタックの作成を押すと、スタックの詳細画面のイベントタブが表示された状態になります。

画面下部のイベント欄に、リソースの作成状況が順次表示されていきます(丸矢印ボタンで更新できます)。

緑色のCREATE_COMPLETEが表示されれば、そのリソースは作成完了です。

最終的にスタック名(今回の場合であればtraining)で、CREATE_COMPLETEが表示されれば全てのリソースの作成が正常に完了したことになります。

ap-northeast-1.console.aws.amazon.com_cloudformation_home_region=ap-northeast-1 (6).png


2.6. スタックの削除

スタック画面か、もしくはスタックの詳細画面画面上部の削除を押し、さらに確認のダイアログで表示されたスタックの削除を押すと、このスタックに紐付く全てのリソースが一括削除されます。


3. テンプレートファイル作成の続き

CloudFormationでのリソースの作成方法はわかったので、テンプレートファイルの作成を再開します。

本記事では割愛しますが、この先でリソースを追記したテンプレートファイルは、追記の都度CloudFormationの画面でアップロードして、実際にリソースを作成できるか確認してみても良いかと思います。(リソースを作成した後は、適宜スタックごと削除してください)。


3.1. サブネットの定義

VPCの次は、サブネットを2つ定義します。

1つ目はパブリックサブネット、2つ目はプライベートサブネットとします。

# 最終行に以下を追加

CfPublicSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.5.0/24
MapPublicIpOnLaunch: true
VpcId: !Ref CfVPC
AvailabilityZone: ap-northeast-1a
Tags:
- Key: Name
Value: CfPublicSubnet
CfPrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.10.0/24
MapPublicIpOnLaunch: false
VpcId: !Ref CfVPC
AvailabilityZone: ap-northeast-1c
Tags:
- Key: Name
Value: CfPrivateSubnet


マネジメントコンソールの場合

1つ目のパブリックサブネットの内容については、マネジメントコンソールの各画面で以下を指定した場合と同等です(2つ目のプライベートサブネットについては割愛)。


サブネットの作成画面

項目
設定内容

名前タグ
CfPublicSubnet

VPC
CIDRが10.0.0.0/16である、作成済みのVPC

アベイラビリティゾーン
ap-northeast-1a

IPv4 CIDR ブロック
10.0.5.0/24


アクション > 自動割り当てIP設定

項目
設定内容

パブリックIPv4アドレスの自動割り当てを有効にする
チェックを入れる


MapPublicIpOnLaunchについて

YAML内のMapPublicIpOnLaunchは、パブリックIPアドレスの自動割り当てに該当します。


MapPublicIpOnLaunch: true



!Refについて

YAML内の!RefについてはCloudFormationで使用できる組み込み関数のひとつです。


Resources:

CfVPC:
# 略
CfPublicSubnet:
Type: AWS::EC2::Subnet
Properties:
# 略
VpcId: !Ref CfVPC

!Ref リソースの論理IDとすることで、そのリソースの物理IDが返ります。

マネジメントコンソール画面で順次リソースを作成していく場合と異なり、CloudFormationで一括してリソースを定義する場合には、このサブネットが属するVPCの物理IDがまだ決まっていませんので、このように!Ref関数を使います。


!Refによるリソース作成順序への影響

また、!Refを使用することで、参照先のリソースが先に作成されるようになります。

例えば、


Resources:

CfPublicSubnet:
Type: AWS::EC2::Subnet
Properties:
# 略
VpcId: !Ref CfVPC
# 略
CfVPC:
Type: AWS::EC2::VPC
# 略

のようにテンプレートファイル中でのリソース定義順序が


  1. サブネット

  2. VPC

の順だったとしても、サブネットがVPCを!Refで参照していると、リソースの作成順序は


  1. VPC

  2. サブネット

の順となります。


3.2. インターネットゲートウェイの定義とVPCへのアタッチ

続いて、インターネットゲートウェイを定義し、VPCへアタッチします。

# 最終行に以下を追加

CfInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: CfInternetGateway
CfAttachCfInternetGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId : !Ref CfInternetGateway
VpcId: !Ref CfVPC


  • まずTypeAWS::EC2::InternetGatewayを指定し、インターネットゲートウェイを定義します。



  • 次にTypeAWS::EC2::VPCGatewayAttachmentを指定し、インターネットゲートウェイをVPCにアタッチします。



    • InternetGatewayIdVpcIdで、どのインターネットゲートウェイをどのVPCにアタッチするのか指定しています。




マネジメントコンソールの場合

上記は、マネジメントコンソールにおいて、下記の指定でインターネットゲートウェイを作成するのと同等です。


インターネットゲートウェイの作成画面

項目
設定内容

名前タグ
CfInternetGateway


アクション > VPCにアタッチ の画面

項目
設定内容

VPC
CIDRが10.0.0.0/16である、作成済みのVPC


3.3. パブリックサブネット用ルートテーブルの定義およびルートの定義

# 最終行に以下を追加

CfRouteTableForPublicSubnet:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref CfVPC
Tags:
- Key: Name
Value: CfRouteTableForPublicSubnet
CfRouteForPublicSubnet:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref CfRouteTableForPublicSubnet
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref CfInternetGateway
CfAssocciateRouteTableForPublicSubnet:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref CfRouteTableForPublicSubnet
SubnetId: !Ref CfPublicSubnet


  • まずTypeAWS::EC2::RouteTableを指定し、ルートテーブルを作成しています。



  • 次にTypeAWS::EC2::Routeを指定し、ルートを追加しています。



    • DestinationCidrBlock(送信先)を、0.0.0.0/0(フルオープン)としています。

    • ターゲット(GatewayId)に、定義済みのインターネットゲートウェイ(!Ref CfInternetGateway)を指定しています。




  • 続いてTypeAWS::EC2::SubnetRouteTableAssociationを指定し、ルートテーブルをサブネットに関連付けしています。



    • RouteTableIdSubnetIdで、どのルートテーブルをどのサブネットに関連付けするのか指定しています。




マネジメントコンソールの場合

マネジメントコンソールの場合、下記の指定でルートテーブルを作成するのと同等です。


ルートテーブルの作成画面

項目
設定内容

名前タグ
CfRouteTableForPublicSubnet

VPC
CIDRが10.0.0.0/16である、作成済みのVPC


アクション > ルートの編集 > ルートの追加 の画面

項目
設定内容

送信先
0.0.0.0/0

ターゲット
名前タグ(Name)がCfInternetGatewayであるインターネットゲートウェイ


アクション > サブネットの関連付けの編集 の画面


  • 名前タグ(Name)がCfPublicSubnetであるサブネットを選択して、保存


3.4. パブリックサーバー用セキュリティグループの定義

# 最終行に以下を追加

CfSecurityGroupForPublicServer:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: CfSecurityGroupForPublicServer
GroupDescription: CfSecurityGroupForPublicServer
VpcId: !Ref CfVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: CfSecurityGroupForPublicServer


  • セキュリティグループを作成するため、TypeAWS::EC2::SecurityGroupを指定します。



  • インバウンドルールは、SecurityGroupIngressプロパティに指定します。


    • ここでは、SSH(22), HTTP(80), HTTPS(443)でのアクセスを許可しています。




マネジメントコンソールの場合

マネジメントコンソールの場合、下記の指定でセキュリティグループを作成するのと同等です。


セキュリティグループの作成画面

項目
設定内容

セキュリティグループ名
CfSecurityGroupForPublicServer

説明
CfSecurityGroupForPublicServer

VPC
CIDRが10.0.0.0/16である、作成済みのVPC


アクション > インバウンドのルールの編集画面

以下のルールを設定

タイプ
プロトコル
ポート範囲
ソース

SSH
TCP
22
0.0.0.0/0

HTTP
TCP
80
0.0.0.0/0

HTTPS
TCP
443
0.0.0.0/0


3.5. パブリックサーバーの定義

# 最終行に以下を追加

CfPublicServer:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0ff21806645c5e492
InstanceType: t2.micro
KeyName: cf-key-pair
# SubnetId: !Ref CfPublicSubnet
# SecurityGroupIds:
# - !Ref CfSecurityGroupForPublicServer
NetworkInterfaces:
- SubnetId: !Ref CfPublicSubnet
GroupSet:
- !Ref CfSecurityGroupForPublicServer
AssociatePublicIpAddress: true
DeviceIndex : 0
Tags:
- Key: Name
Value: CfPublicServer


  • EC2を作成するため、TypeAWS::EC2::Instanceを指定します。



  • ImageIdでAMI(Amazonマシンイメージ)を指定します。


    • ここでは、Amazon Linux 2(x86)のAMIを指定しています。

    • 具体的にどんなIDを指定するかについてですが、AMI IDはマネジメントコンソールのEC2作成画面の途中に表示されているので、今回は事前にそちらを確認して指定しています。




  • KeyNameにはキーペア名を指定します。ここでは事前にマネジメントコンソール画面で作成しておいたキーペア名を指定しています。


    • なお、キーペアは、マネジメントコンソールのEC2 > キーペア > キーペアの作成で作成できます。




自動割り当てパブリックIPの設定を有効にする場合の注意点


#      SubnetId: !Ref CfPublicSubnet

# SecurityGroupIds:
# - !Ref CfSecurityGroupForPublicServer

SubnetIdSecurityGroupがコメントアウトしてありますが、自動割り当てパブリックIPを有効にしないのであれば、コメントアウト前の記述で特に問題ありません。

今回のように自動割り当てパブリックIPを有効にする場合は、以下のようにNetworkInterfacesプロパティ内のSubnetプロパティとGroupSetプロパティに、サブネットとセキュリティグループそれぞれを記述する必要があります。


     NetworkInterfaces:

- SubnetId: !Ref CfPublicSubnet
GroupSet:
- !Ref CfSecurityGroupForPublicServer
AssociatePublicIpAddress: true
DeviceIndex : 0

もし、上記のように記述しなかった場合、以下のエラーになります。


Network interfaces and an instance-level subnet ID/security groups may not be specified on the same request.(ネットワークインターフェイスとインスタンスレベルのサブネットID/セキュリティグループは、同じリクエストで指定できません)


自動割り当てパブリックIPの有効化そのものはAssociatePublicIpAddresstrueとすることで設定できます。

また、NetworkInterfacesプロパティ内の配列の各要素(つまり個々のNetworkInterface)には、DeviceIndexプロパティが必須となっているので、これも指定しています。

なお、DeviceIndexが無いと、


Property DeviceIndex cannot be empty


というエラーになります


3.6. Elastic IPの定義

パブリックサーバーのパブリックIPアドレスについて、起動のたびに変更されることが無いよう、Elastic IPを定義し、パブリックサーバーに関連付けします。

# 最終行に以下を追加

CfElasticIpForPublicServer:
Type: AWS::EC2::EIP
Properties:
InstanceId: !Ref CfPublicServer


  • Elastic IPを作成するため、TypeAWS::EC2::EIPを指定します。


  • このElastic IPをパブリックサーバーに関連付けするため、InstanceIdにパブリックサーバーを指定します。



3.7. プライベートサーバー用セキュリティグループの定義

# 最終行に以下を追加

CfSecurityGroupForPrivateServer:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: CfSecurityGroupForPrivateServer
GroupDescription: CfSecurityGroupForPrivateServer
VpcId: !Ref CfVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId: !Ref CfSecurityGroupForPublicServer
Tags:
- Key: Name
Value: CfSecurityGroupForPrivateServer



  • インバウンドルールを、SecurityGroupIngressプロパティに指定します。


    • パブリックサーバーからのみプライベートサーバーにアクセスできるよう、ソース(SourceSecurityGroupId)には、パブリックサーバー用のセキュリティグループを指定しています。




マネジメントコンソールの場合

マネジメントコンソールの場合、下記の指定でインバウンドのルールを編集するのと同等です。


アクション > インバウンドのルールの編集画面

以下のルールを設定

タイプ
プロトコル
ポート範囲
ソース

SSH
TCP
22
パブリックサーバー用セキュリティグループのグループID


3.8. プライベートサーバーの定義

# 最終行に以下を追加

CfPrivateServer:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0ff21806645c5e492
InstanceType: t2.micro
KeyName: cf-key-pair
SubnetId: !Ref CfPrivateSubnet
SecurityGroupIds:
- !Ref CfSecurityGroupForPrivateServer
Tags:
- Key: Name
Value: CfPrivateServer


  • SubnetIdでプライベートサブネットを、SecurityGroupIdsでプライベートサーバー用セキュリティグループを指定しています。


  • パブリックサーバーの場合は、自動割り当てパブリックIPアドレスを有効にする関係で、サブネットとセキュリティグループを以下のようにNetworkInterfacesプロパティの下に指定する必要がありましたが、今回はそのようにする必要はありません。



     NetworkInterfaces:

- SubnetId: !Ref CfPublicSubnet
GroupSet:
- !Ref CfSecurityGroupForPublicServer
AssociatePublicIpAddress: true
DeviceIndex : 0


4. もう一度テンプレートファイルを使ってAWSリソースを作成する

ここまで作成してきたテンプレートファイルを使って、実際にAWSリソースを作成してみます。

CloudFormation上の操作の流れは、2. テンプレートファイルを使って実際にAWSリソースを作成してみると同じです。

スタックの作成開始から完了までにかかる時間は、1分半ほど。

☕️でも飲んで待ちましょう。


最後に

以上で、本記事でのCloudFormationによるパブリックサーバー/プライベートサーバーの構築は完了です。

パラメータなど、CloudFormationの機能をまだまだ活用はできていないので、もし第二弾的な記事を書くことがあればその辺りにも触れたいと思います。

また、TypeScriptやPythonのコードでCloudFormationのテンプレートファイルを生成できる、AWS CDKについても今後記事を書ければと思います。


参考