CloudFormationの長所・短所と備忘録的Tips

# はじめに
最近仕事でIaCの一つであるCloudFormation（以下、cfn）を利用した開発を行っていて、その時考えていたことをここにダンプしておきます。実際に触ってみて、IaCは今後のインフラ構築のデファクトになっていくんだろうなあと感じました。例えば、マネジメントコンソールで行ってることのほとんどはcfnで実現できます。また、一度cfnコードを書いてしまえばある程度潰しを効かせながら他の環境もquickに構築できます。（自分はPoC開発の範疇でしか利用していないので、状況によっては活きないかもしれないことは留意ください....）

# CloudFormationとは
>AWS CloudFormation は AWS リソースのモデル化およびセットアップに役立つサービスです。リソース管理に割く時間を減らし、AWS で実行するアプリケーションにより注力できるようになります。使用するすべての AWS リソース (Amazon EC2 インスタンスや Amazon RDS DB インスタンスなど) を記述するテンプレートを作成すれば、CloudFormation がお客様に代わってこれらのリソースのプロビジョニングや設定を受け持ちます。AWS リソースを個別に作成、設計して、それぞれの依存関係を考える必要はありません。CloudFormation がこれを処理します。次のシナリオは CloudFormation がどのように役立つかを示します。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/Welcome.html

# IaC (Infrastructure as Code)とは
手動のプロセスではなく、インフラストラクチャの管理やプロビジョニングをコード管理すること
ex) Terraform, CloudFormation

↓こんな感じの
```
Resources: 
# ------------------------------------------------------------#
#  VPC
# ------------------------------------------------------------#
# VPC Create
  VPC: 
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: !Ref VpcBlock
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: !Sub "${AWS::StackName}-VPC"

# InternetGateway Create
  InternetGateway: 
    Type: AWS::EC2::InternetGateway
    Properties: 
      Tags: 
        - Key: Name
          Value: !Sub "${AWS::StackName}-igw"

# IGW Attach
  InternetGatewayAttachment: 
    Type: AWS::EC2::VPCGatewayAttachment
    Properties: 
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC 

# ------------------------------------------------------------#
#  Subnet
# ------------------------------------------------------------#          
  PrivateSubnet00:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: !Ref PrivateSubnet00Block
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub "${AWS::StackName}-PrivateSubnet00"

  PrivateSubnet01:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1c
      CidrBlock: !Ref PrivateSubnet01Block
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub "${AWS::StackName}-PrivateSubnet01"
```

# メリット
実際に触ってみていいなと思った点は3つ
**1. 自動構築（手作業からの解放）**
**2. スタックとしてリソースの一元管理が可能**
**3. インフラに変更を加える時の労力を低減できる**

1つずつ説明する
## 1. 自動構築（手作業からの解放）
インフラ設計をしながら、色々構築することはとても労力のいる事。特にエンドポイントやRDSの設定を行おうとすると、待ち時間が結構かかったりする（作業途中に15分以上待たないといけないのマジ？）。IaCでインフラ構築を行えば、必要なリソースはすべてファイルに記述されているから、その**ファイルだけ投げておけば後はAWSが勝手にやってくれる**。開発フェーズでリソースを頻繁に消したり、作ったりするのであればこの自動化は開発者体験を大きく向上させる。コード自体も別の用途で使いまわせるのも良い。

また、インフラをコード化する際はサブネットアドレスやセキュリティグループで許可するIP等をあらかじめ記載することができる。
```
# ------------------------------------------------------------#
# CIDR Settings
# ------------------------------------------------------------# 
Parameters:
  VpcBlock:
    Type: String
    Default: 192.168.0.0/20
    Description: The CIDR range for the VPC. This should be a valid private (RFC 1918) CIDR range.

  PrivateSubnet00Block:
    Type: String
    Default: 192.168.0.0/24
    Description: CidrBlock for private subnet 00 within the VPC

  PrivateSubnet01Block:
    Type: String
    Default: 192.168.1.0/24
    Description: CidrBlock for private subnet 01 within the VPC
```
インフラ構築の際にパラメーターシートと睨めっこしながら設定を行うことは個人的にとても退屈だし、ミスが出やすい作業箇所だとも思う。初めから決まっているパラメーターをコードに記載しておけばその工程を全てスキップ出来ることもIaCの魅力だと思う。

## 2. スタックとしてリソースの一元管理が可能
cfnの場合、IaCで作成したリソースは「スタック」という単位でまとめられる。構築したものを消したいときはスタックを消せば、そこに紐づくリソースも全て削除される。AWSだとリソースの消し忘れで想定外の課金が発生するケースもあり（3敗）、まとめて削除してくれるのであればそれに越したことはない。

スタックから個々のコンソールに直接飛べるのも結構便利だったりする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016889/9b66287e-b282-2294-c250-f84050833ec2.png)

余談だが、AWSのリソースを全部消し飛ばしてくれる"aws-nuke"なるものがある。興味があれば是非。

https://github.com/rebuy-de/aws-nuke

https://qiita.com/yu-yama-sra/items/cca952150356c15ec496

## 3. インフラに変更を加える時の労力を低減できる
IaCであれば変更したい箇所や影響範囲が気になればコード検索で調査がしやすい。また、コード化したことによりCI/CDに組み込み、変更をすぐに反映できる点も魅力的。流行りのDevOpsとも高い親和性が期待できる。
手作業だと構築手順書を用意しても作業者によって若干の差異が発生する可能性があるが、IaCならそれが発生しえないためリスク低減にも繋がる。

# デメリット
**1. コードを読み解くのが大変**
**2. AWSのサービス知識とIaCへの理解が必須**

## 1. コードを読み解くのが大変
#### コードがデカい、マジで。
「コード化することで各リソースの依存関係を把握できる」と紹介する記事がネットに転がっている。これは確かに事実だが、数千行のファイルを前にして同じことが言えるだろうか？AWSのベストプラクティスによると、機能ごとにファイルやスタックを分割する方法が推奨されている（VPCやサブネットだけ作るyamlファイル、ec2インスタンスやセキュリティグループを設定するyamlファイル等）が、それでもコードを読み解いて理解するコストがそこそこかかる。（自分はcfnしかまともに触っていないので、スマートに書ける他サービスがあればコメントお願いします！）

**参考**

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/best-practices.html#organizingstacks

ちなみにAWSで公開されてるテンプレートを覗いてみると、基本的なLAMP構成を作るだけでも400行超の記述が必要になる。

**参考**

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/sample-templates-appframeworks-ap-northeast-1.html

## 2. AWSのサービス知識とIaCへの理解が必須
**AWS上で**構築するインフラを**コード**に落とし込むわけだからある意味当然だが、多くの前提知識が要求されるため、IaC周りの業務が属人化してしまう恐れがある。
まぁ、Dockerファイルの書き方を学ぶ程度話ではある。


# Tips集
ここからは書き散らし

### cfnのコードを描くならyamlがおすすめ
cfnではyamlとjsonがサポートされているが、yamlが圧倒的におすすめ。コードが肥大化しがちなIaCを少しでも分かりやすくするにはコメントが必要不可欠。jsonはコメントできないうえ、ネストが深くなると括弧が可読性を死に追いやる。

### !Refは積極的に使おう
cfnには```DependsOn```という特定のリソースが他のリソースに続けて作成されるように指定できるオプションがあるが、他リソースのパラメーターを参照する```!Ref```を使えば勝手に参照エラーが発生しない順番でリソースを立ててくれるので、そちらを使った方がコードがスッキリする

### リソースの命名には!Subを使おう
同一のリソース名はaws上で利用できない制約があり、cfnで色々組み立てている際はエラーの源となることがある。
```
dbsubnet2 already exists in stack arn:aws:cloudformation:ap-northeast-1:aaaaaaaa:stack/stackname/
```
ので、```!Sub```で名前が被らないようにすることを心掛ける。
```
      Tags: 
        - Key: Name
          Value: !Sub "${AWS::StackName}-VPC"
```
### cfnコードの文法チェックにはAWS CLIを使おう
yaml形式で書いていると、ちょっとしたスペースやタブのミスを見逃しがちだから、スタック作成前にAWS CLIの**テンプレート検証**を使用することで手戻りを減らせる。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-validate-template.html

### IAMロールはcfn作成できるけど、KeyPairは作れないよ
残念ながら暗号通信用の秘密鍵はcfnで作成できない。ここはあらかじめ作って、作業者に連携しよう

### cfnといえど、リージョンは跨げない
Tokyoリージョン(ap-northeast-1a)から大阪リージョン(ap-northeast-3a)にリソースを立てようとするとエラーになるので注意
>Resource handler returned message: "Value (ap-northeast-3a) for parameter availabilityZone is invalid. Subnets can currently only be created in the following availability zones: ap-northeast-1a, ap-northeast-1c, ap-northeast-1d. (Service: Ec2, Status Code: 400, Request ID: ...

気が向いたら追記します