先日社内でIaCについて少し喋った記録

## はじめに
先日ランチタイムテックトークというお昼にラジオ感覚で技術の話を聞こうという社内のイベントで「IaCについて適当に喋る」と簡単な内容ではあったものの意外と好評だったので、記事にもまとめようと思います
ここ2年くらい案件でCloudFormationとTerraformに触ったので、個人的な所感をまとめたものです

## IaCとは
**IaC**とは**I**nfrastructure **a**s **C**odeのことで、**インフラ構成をコードで管理**することです

### IaCのメリット
+ 再現性
    →同じコードを使うと、同じリソースが作成される
+ 手作業の減少
    →人的ミスが減り、自動化できる
+ バージョン管理ができる
    →変更履歴を追うことができ、戻すのも楽

### IaCのデメリット
+ 学習コストが高い
+ 1回しか作らない場合はコンソールから作った方が早い

### 代表的なIaCサービス
+ AWS CloudFormation
    ・提供元：AWS
    ・対応クラウド：AWS
    ・記述言語：JSON、YAML
:::note info
分岐やループのような組み込み関数が少なくわかりやすい
一方で、記述量が増えてしまう
:::
+ AWS CDK
    ・提供元：AWS
    ・対応クラウド：AWS
    ・記述言語：TypeScript、Python、C#など
:::note info
CloudFormationをいい感じに動かしてくれる
元々開発している人なら慣れた言語で扱える
記述量が少なくなる(らしい)
:::
+ Terraform
    ・提供元：HashiCorp
    ・対応クラウド：AWS、GoogleCloud、Azureなど
    ・記述言語：HCL (Hashicorp Configuration Language)
:::note info
対応しているクラウドが多いためマルチクラウドも可能
記述言語が専用で特に学習コストが高い
GoogleCloudは公式ドキュメントにもIaCツールとしてTerraformが使用されている
:::

### IaCを使う時に考慮したいこと
+ モジュール化
    管理しやすい単位でリソースを分割し1ファイルとして管理する
    1ファイルが大きくなりすぎることを防ぐ
    別のファイルから呼び出すこともできる
    例）S3を1つ作るCloudFormationのテンプレートを子スタックとして、親スタックから呼び出す
+ CI/CD
    CloudFormationとGithubを連携したり、Githubアクションを使用して特定のブランチにプッシュされるのをトリガーとすることで、デプロイを自動化できる
+ セキュリティ
    Githubなどのコード管理ツールで管理することが多く、SecretManagerなどを利用しリポジトリに機微情報が載らないように注意が必要

## 実際に動かしてみた
今回は社内イベントの中でCloudFormationを実際に動かしてみました
手順としては
1. テンプレートを用意する
    ※社内イベントで使ったテンプレートを後ろに載せておきます
1. AWSコンソールにログインする
1. CloudFormation用のIAMロールを作成する
    リソースを作るのはCloudFormationなので、必要な権限を与えます
    後述するテンプレートだとEC2のフルアクセスとssmの読み取りで動きます
    ※実際のシステムで使う場合は最小権限を意識しましょう
1. CloudFormationのスタックを作成する
    作成したテンプレートとIAMロールを指定してスタックを作成します
    パラメータはこの時に入力します
1. 作成されたリソースを確認する
1. （リソースを削除する）
    検証で作成した場合は余計なコストが発生しないように作成したリソースを削除します
    基本的にはスタックを削除すると、スタックが管理しているリソースは消えますが、削除ポリシーで保持の設定をしていたり、IAMロールに削除権限が無いとリソースが残ってしまうので注意

今回の社内イベントの中で実際にCloudFormationを動かしてみた時のテンプレートを載せておきます
作成したリソースはVPC関連とEC2です

```rb
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Create an EC2 instance in a public subnet within the free tier'

Parameters:
  SystemName:
    Type: String
    Default: MySystem
    Description: 'System name'
  EnvironmentName:
    Type: String
    Default: Dev
    Description: 'Environment name'

Resources:
  MyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${EnvironmentName}-VPC"

  MyInternetGateway:
    Type: AWS::EC2::InternetGateway

  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref MyVPC
      InternetGatewayId: !Ref MyInternetGateway

  MyPublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MyVPC
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${EnvironmentName}-PublicSubnet"

  MyRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVPC
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${EnvironmentName}-RouteTable"

  MyRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref MyRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref MyInternetGateway

  MySubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref MyPublicSubnet
      RouteTableId: !Ref MyRouteTable

  MySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH and HTTP
      VpcId: !Ref MyVPC
      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
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${EnvironmentName}-SecurityGroup"

  MyInstance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro
      ImageId: !Sub "{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}"
      SubnetId: !Ref MyPublicSubnet
      SecurityGroupIds:
        - !Ref MySecurityGroup
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${EnvironmentName}-EC2"
```

## さいごに
かなり基本的な内容だったので日頃からIaCを触っている人には少し退屈な内容だったかもしれないですが、これからIaCを触ってみようと思っている人やIaCって聞いたことあるくらいの人が興味を持つきっかけになってたら嬉しいです