CloudFormationでVPCNetworkを作る


# この記事

以前、VPC作ってEC2にひとまずWebサーバ用意するまで、という記事を書きましたが
https://qiita.com/shinon_uk/items/04e86115c7dd96018ec1

こういった基本的なVPC+PublicSubnet+Routetable...のセットをCFnで作るものを
ずっと記事連携させたかったので書きます。

対象は、いつもマネジメントコンソールで個別に構築してるけど
AWSのDevOpsとかインフラ自動化とか、手を伸ばしてみようかなーという方になると思います。
コードを短くしたいので、EC2+SecuriyGroupは別回にして、構築作業はGUIから行います。

#やること

今回は「あとはEC2を立てるだけ」の状態にしたいので
以下最低限のリソースをCloudFormationで作成します。

・VPC
・Subnet（Public + Private）
・RouteTable(Route含む)
・InternetGateway

#知る

###CloudFormationとは
どんなAWSサービスでも、大体Blackbeltを見れば用途は理解できます。
※下記資料：Published on Nov 28, 2018
https://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-online-seminar-aws-cloudformation?ref=https://aws.amazon.com/jp/blogs/news/webinar-bb-cfn-update-2018/

・JSON/YAMLのテキストファイルで、あらゆるリージョンのAWSリソースをプロビジョニングできる
対応リソース：https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html

・プロビジョニングしたAWSリソースの変更/削除が可能で、ソフトウェアに対して行うのと同じように、AWSインフラを管理できる

・CloudFormation自体の料金は発生しない、作成したリソース分のみ


###CloudFormationのセクション

抜粋して記載します。

- AWSTemplateFormatVersion
フォーマットのバージョンを指定する。2020/02現時点では、 たった一つのバージョン2010-09-09が最新である。

- Description
作成するテンプレートの説明を記載する。

- Parameters
カスタマイズが必要なパラメータを、CFn流すときに渡すためのもの。例えば、作成する環境名、ドメイン名など。

- Resources
必須である、リソース記載部分。テンプレートで説明できる最も小さなブロックである。

- Outputs
テンプレートを作成した際に値を返す物を定義する。

尚、テンプレートはJSON、YAMLで定義できる。YAMLが使用可能ということは、例えば**IAM RoleをYAMLで定義することも可能**というメリットもある。
以降、YAMLで記載



#作る

###ドキュメント
AWSが公開しているブループリントを見てもいいんですが、
ドキュメントを参照して行きます。
例えば、VPCリソース
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc.html
各プロパティの必須項目が

```
必須: はい
```
となっている場合は、記載が必要です。
あとは例を見て作成。

```
1 Resources:
2   VPC:
3     Type: AWS::EC2::VPC
4     Properties:
5       CidrBlock: !Ref VpcCidr
6       InstanceTenancy: default
7       EnableDnsSupport: true
8       EnableDnsHostnames: true
9       Tags:
10        - Key: Name
11          Value: !Sub '${AppName}-${Environment}-vpc'
```

2行目、リソースIDは一意であれば自由に決められます。
3行目、Typeでリソースを実際に指定しています。
あとは該当するリソース（Type）のドキュメントを見ながら作成するイメージですね。（先述）

また、１１行目のValueに !Sub がありますが、組込関数で同一コード内のIDを参照しています。
！Sub, !Ref は参照によく使う組み込み関数です。

他にも組み込み関数はたくさんありますが、必要になったら使う、などでいいと思います。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html


####何はともかくやってみる

###テンプレート

作成したテンプレートは下記です。
適宜日本語コメントも入れています。

```
AWSTemplateFormatVersion: '2010-09-09'
Description: 'VPC Template'

#このセクションはCFn流す際に与えるパラメータの定義
Parameters:

#サービス名など
  AppName:
    Description:  Please specify the application name.
    Type:         String
    Default:      "example"

#環境名
  Environment:
    Description:    Please specify the target environment.
    Type:           String
    Default:        "dev"
    AllowedValues:
      - prod
      - stg
      - dev

#VPCのIP指定
  VpcCidr:
    Description: Please specify the VPC IP
    Type: String
    Default: '10.0.0.0/16'
    AllowedPattern: '((\d{1,3})\.){3}\d{1,3}/\d{1,2}'

#各SubnetのIP指定
  Public1SubnetCidr: 
    Description: Please specify the Subnet IP for Public1
    Type: String
    Default: '10.0.0.0/20'

  Private1SubnetCidr: 
    Description: Please specify the Subnet IP for Public1
    Type: String
    Default: '10.0.64.0/20'

```




リソース定義

```
Resources:

  #VPCを定義
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      #Parameters参照
      CidrBlock: !Ref VpcCidr 
      InstanceTenancy: default
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          #複数のParameters参照して繋げて作成
          #defaultの値で指定だと example-dev-vpc というValueになる
          Value: !Sub '${AppName}-${Environment}-vpc'

  #IGWを定義
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${AppName}-${Environment}-igw'

#IGWをVPCにアタッチ
#マネジメントコンソールでやっている作業を定義しているだけ
  VPCInternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      #アタッチしたいVPCを参照
      VpcId: !Ref 'VPC'
      InternetGatewayId: !Ref 'InternetGateway'


#Subnetを定義する

  #Public Subnetを定義
  SubnetPublic1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref Public1SubnetCidr
      AvailabilityZone: !Select [ 0, !GetAZs ]
      MapPublicIpOnLaunch: true
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Name
          Value: !Sub '${AppName}-${Environment}-Public1'

  #Private Subnetを定義
  SubnetPrivate1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref Private1SubnetCidr
      AvailabilityZone: !Select [ 0, !GetAZs ]
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Name
          Value: !Sub '${AppName}-${Environment}-Private1'


#RouteTableを定義

  #Route Table for Publicを定義
  RouteTablePublic:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Name
          Value: !Sub '${AppName}-${Environment}-rt-public'

  #Route Table for Privateを定義
  RouteTablePrivate:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Name
          Value: !Sub '${AppName}-${Environment}-rt-private'


#RouteTableにRouteを定義する

  #Route Table for PublicにRouteを1つ定義
  RoutePublic:
    Type: AWS::EC2::Route
    DependsOn: VPCInternetGatewayAttachment
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      RouteTableId: !Ref 'RouteTablePublic'
      GatewayId: !Ref 'InternetGateway'


#各Subnetを、各RouteTableに関連づける

  #Route Table for PublicにPublic Subnetを関連づける
  SubnetRouteTableAssociationPublic1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref 'RouteTablePublic'
      SubnetId: !Ref 'SubnetPublic1'

  #Route Table for PrivateにPrivate Subnetを関連づける
  SubnetRouteTableAssociationPrivate1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref 'RouteTablePrivate'
      SubnetId: !Ref 'SubnetPrivate1'
```

最後に、Outputセクションで値を出力しておきます。
今回は使わないんですが、例えば他のテンプレートに値を渡したい時に使用したりします。
その話はスタック構成含めて別記事で出します。

```
Outputs:

  VpcId:
    Description: VPC ID
    Value: !Ref 'VPC'
    Export:
      Name: !Sub '${AWS::StackName}-VpcId'

  SubnetPublic1:
    Description: Public subnet 1
    Value: !Ref 'SubnetPublic1'
    Export:
      Name: !Sub '${AWS::StackName}-PublicSubnet1'

  SubnetPrivate1:
    Description: Private subnet １
    Value: !Ref 'SubnetPrivate1'
    Export:
      Name: !Sub '${AWS::StackName}-PrivateSubnet1'
```

###実際に流す作業

テンプレート（コード）はローカルに保存してもS3でも、ひとまずは大丈夫です。
以下はアップロードするパターン。

①みんな大好きマネジメントコンソールで、CloudFormationを開く。

<img width="321" alt="0304q1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406764/d4116189-13ff-b297-81bb-bef548ee9bd2.png">


②スタックの作成へ

<img width="1237" alt="0304q2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406764/aab009a8-9020-4856-2253-560eb52d9582.png">
↓
<img width="1254" alt="0304q3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406764/581220be-4ca5-8a63-db86-b093764af344.png">

↓
直接アップロードなのでこっち（S3にファイルは保存されます。）
<img width="1211" alt="0304q4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406764/1d6eb220-9548-9c60-53f1-4915bc1668ec.png">

ファイルを指定して次に進みます。
この時点で文法エラーなどあれば、画面上にエラー出力してくれます。修正。
（大文字小文字とか自分あるある）

③パラメータ指定

始めはdefaultで指定した値が入っています。
テンプレート内で扱う名前がある場合は、アプリ名やサービス名などをここで指定します。

<img width="1202" alt="0304q5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406764/e8944cf6-7feb-77e1-0978-499965ae382d.png">

↓
次へを押していく。

<img width="1181" alt="0304q6.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406764/2b8b44de-37d7-f8a0-c7f5-18db0314fc76.png">

↓
最後にパラメータで指定した値を確認

<img width="1145" alt="0304q7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406764/a5e85f0a-4b46-06e0-e771-8894a976b893.png">

↓
スタックの作成を押して、流します。

<img width="1276" alt="0304q8.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406764/555ed899-5b2c-b21d-15e1-c7049673a8ac.png">

CREATE IN PROGRESSになれば作成が始まっています。
仮に問題があって一つでも作成ができなかった場合は、デフォルトでロールバックされます。
※設定は作成時に設定変更可能

<img width="1274" alt="0304q9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406764/6f417efa-abc8-c476-9840-e990d8241a29.png">

↓
右上のボタンを押して更新できます。
最終的に、左のスタック画面で以下のように表示されれば完成です。

<img width="1282" alt="0304q10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406764/12ea1551-161d-5730-3b48-5c260bfb826e.png">

リソースタブからもみることができますが、作成したリソースを確認してください。

各リソースの変更は、各サービスの変更画面から可能ですが、
テンプレートを修正して更新する事もできます。

削除する際は、スタックの削除を押せば、テンプレートのリソースが一括で削除されるのでとても便利です。

Github
https://github.com/shinon-uk/CloudFormation/tree/master/vpc-sample


#最後に

久しぶりの記事になりました。

まだまだCloudFormationでやれることはあるので、

後日またスタック構成にしたCLIベース構築の話を記載する予定です。