AWS CloudFormation入門：VPCとEC2インスタンスの自動構築ガイド

# はじめに
最近、Web、メール、DNSサーバをVPCを分けて構築する機会がありました。毎回、同じ構成の環境をコンソール上でVPCから作成してくのが手間と感じました。そこで、ワンクリックでEC2の起動までできればラクだと思い、CloudFormationを使ってテンプレートを作成することにしました。

# チュートリアル：ステップバイステップガイド

**事前準備**

まず、AWSアカウントの設定やCLIのインストールなど、必要な前提条件を整えます。


**ステップ1：VPCの作成**

CloudFormationテンプレートを使ってVPCを作成する手順を詳しく説明します。
以下は、テンプレートの一部です。

```yaml
Resources:
  VPC:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
        - Key: Name
          Value: MyVPC
```

**ステップ2：サブネットとインターネットゲートウェイの設定**

次に、必要なネットワーク設定を行います。

```yaml
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId: !Ref VPC
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: MyPubSub

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: MyIGW
```

**ステップ3：インターネットゲートウェイのアタッチ**

インターネットゲートウェイをVPCにアタッチするための設定を行います。

```yaml
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
```

**ステップ4：ルートテーブルとルーティング設定**

ルートテーブルにルーティング設定し、サブネットに関連付ける設定を行います。

```yaml
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: MyPubRT"

  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway

  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable
```

**ステップ5：セキュリティグループの作成**

これから作成するEC2インスタンスにのセキュリティを確保するためにセキュリティグループの設定を行います。

```yaml
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH and HTTP
      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
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: MySG
```

**ステップ6：EC2インスタンスの作成**

最後にAMIとキーペアを指定してEC2インスタンスを起動し、作成したセキュリティグループをアタッチ

```yaml
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro
      ImageId: ami-0d0150aa305b7226d
      KeyName: my-Key
      VpcId: !Ref VPC
      SubnetId: !Ref PublicSubnet
      SecurityGroupIds:
        - !Ref InstanceSecurityGroup
      Tags:
        - Key: Name
          Value: MyEC2
```

# やってみた：実際の体験

CloudFormationを使ってVPCからEC2インスタンスを起動する過程で遭遇した問題点とその解決方法を紹介します。

**問題点1：セキュリティグループ名を指定できない**

セキュリティグループを作成するとき、セキュリティグループ名を指定するには`SecurityGroupName`プロパティを使用します。ただし、このプロパティは**デフォルトのVPC**でのみ使用可能で、新たに作成するVPCでは使用できません。

**解決方法1：TagsプロパティでNameタグを指定する**

`Name`タグを使用することで、セキュリティグループにわかりやすい名前をつけることができます。
マネジメントコンソール上で作成する場合は、セキュリティグループ名は指定することが可能です。

```yaml
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:

      ### 中略

      Tags:
        - Key: Name
          Value: MySG
```

**問題点2：キーペア指定でエラーが出る**

EC2インスタンスにSSH接続するために必要なキーペアは存在しているものを指定しないとエラーになります。

**解決方法2：マネジメントコンソールでキーペアを事前に作成する**

マネジメントコンソールで事前にキーペアを作成することで、テンプレート内でキーペアを指定できるようになります。

```yaml
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
    
      KeyName: my-Key
    
      ### 中略

```

# テンプレート全体

最終的に完成したテンプレートは以下となります。(設定内容についてはコメントしています。)

```yaml
AWSTemplateFormatVersion: "2010-09-09"  # テンプレートのバージョンを指定(基本バージョン)
Description: "Create VPC, Subnet, IGW, RouteTable, EC2" # このテンプレートの説明

# Resourceセクションで実際に作成するリソースのパラメータを定義
Resources:
  VPC:  # CIDRブロック"10.0.0.0/16"でVPCを作成
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
        - Key: Name
          Value: MyVPC

  PublicSubnet: # CIDRブロック"10.0.0.0/24", 上記のVPCを指定, パブリックIPの割り当てを有効にしてパブリックサブネットを作成
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId: !Ref VPC
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: MyPubSub

  InternetGateway:  # インターネットゲートウェイを作成
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: MyIGW

  AttachGateway:  # 作成したインターネットゲートウェイをVPCにアタッチ
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  PublicRouteTable: # ルートテーブルを作成
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: MyPubRT"

  PublicRoute:  # 作成したルートテーブルに"宛先：0.0.0.0/0, 送信先:：IGW"を指定
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway

  PublicSubnetRouteTableAssociation:  # ルートテーブルを作成したサブネットに関連付け
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable

  InstanceSecurityGroup:  # 作成したVPCに"tcp/22, tcp/80"のインバウンドルールを許可したセキュリティグループを作成
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH and HTTP
      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
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: MySG

  EC2Instance:  # AMIとキーペアを指定して、作成したVPCとサブネット内にEC2インスタンスを起動, 作成したセキュリティグループをアタッチ
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro
      ImageId: ami-0d0150aa305b7226d
      KeyName: my-Key
      VpcId: !Ref VPC
      SubnetId: !Ref PublicSubnet
      SecurityGroupIds:
        - !Ref InstanceSecurityGroup
      Tags:
        - Key: Name
          Value: MyEC2
```


# カスタマイズ・応用例
テンプレートのカスタマイズ方法や応用例を紹介します。

**1. Parameters, Outputsセクションの追加**

- Parametersセクション：テンプレートのカスタマイズ可能な入力値を定義します。

```yaml
Parameters:
  HANDSON:
    Type: String
    Default: "HANDS-ON"
  VPCCIDR:
    Type: String
    Default: "10.0.0.0/16"
  PublicSubnetCIDR:
    Type: String
    Default: "10.0.0.0/24"
```

このように定義することで、以下のように`Resources`セクションや後述する`Outputs`セクションで共通で利用する変数として記述できます。

```yaml
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCIDR # ここ
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Sub "${HANDSON}-VPC"  # ここ
```

- Outputsセクション：スタック(テンプレートから作成されるリソースのまとまり)作成後に取得可能な情報（例えば、リソースIDやエンドポイントURLなど）を定義します。

```yaml
Outputs:
  VPCID:
    Value: !Ref VPC
    Export:
      Name: !Sub "${HANDSON}-VPC-ID"
```

記述の順番は、Parameters -> Resources -> Outputsとなります。


**2. 追加のセキュリティグループルールを設定する**

今回のテンプレートではWebサーバを想定したセキュリティグループとなりますが、メールサーバやDNSサーバなど、目的によって許可するインバウンドルールが異なるため、カスタマイズが必要となります。

- メールサーバ
```yaml
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH and HTTP
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp # SMTP(メール送信用ポート)
          FromPort: 25
          ToPort: 25
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp # IMAP(メール受信用ポート)
          FromPort: 143
          ToPort: 143
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp # POP3(メール受信用ポート)
          FromPort: 110
          ToPort: 100
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 587 # サブミッションポート(SMTP認証用ポート)
          ToPort: 587
          CidrIp: 0.0.0.0/0  
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: MySG
```

- DNSサーバ
```yaml
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH and HTTP
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp # DNS(TCP)
          FromPort: 53
          ToPort: 53
          CidrIp: 0.0.0.0/0
        - IpProtocol: udp # DNS(UDP)
          FromPort: 53
          ToPort: 53
          CidrIp: 0.0.0.0/0
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: MySG
```


# まとめ
今回のチュートリアルでは、CloudFormationを使用してVPCからEC2インスタンスを起動するまでの手順を詳しく解説しました。以下は、学んだ内容のまとめとCloudFormationを使用するメリットです。

**学んだ内容のまとめ**

1. VPCの作成: CloudFormationテンプレートを使用して、VPCを効率的に作成する方法を学びました。これにより、ネットワークの基盤を簡単に構築できるようになりました。
2. サブネットとインターネットゲートウェイの設定: 必要なネットワークコンポーネントを設定し、VPC内のリソースがインターネットにアクセスできるようにする方法を習得しました。
3. セキュリティグループの設定: EC2インスタンスのセキュリティを確保するために、必要なセキュリティグループを作成し、適切なルールを設定する方法を学びました。
4. EC2インスタンスの起動: CloudFormationテンプレートを使用して、EC2インスタンスを起動し、必要な設定を行う手順を学びました。

**CloudFormationを使用するメリット**

1. 一貫性と再現性: CloudFormationテンプレートを使用することで、インフラストラクチャを一貫して再現することができます。これにより、異なる環境間での設定の違いによる問題を防ぐことができます。
2. 自動化: 手動での設定作業を省き、インフラの構築を自動化できます。これにより、時間と労力を大幅に節約できます。
3. バージョン管理: テンプレートはコードとして管理できるため、Gitなどのバージョン管理システムを使用して変更履歴を追跡し、以前のバージョンに戻すことが容易になります。
4. スケーラビリティ: テンプレートを使用することで、同じインフラ構成を簡単にスケールアウトできます。新しいリソースを迅速に追加することが可能です。
5. 運用コストの削減: 自動化されたテンプレートにより、人的ミスが減少し、運用コストが削減されます。効率的なインフラ運用が可能になります。


CloudFormationを活用することで、インフラの構築と管理が効率化され、運用の信頼性とスピードが向上します。このチュートリアルを通じて得た知識を活用し、より高度なAWSインフラストラクチャの構築に挑戦してみてください。