CloudFormation個人メモ：VPC、6つのサブネット、IGW、RT、NAT、ALB、SG、ACM、ECS、RDSのコード化

## はじめに
ここから先は、現在キャッチアップ中のCloudFormationを活用し、自分用のコードを書きながら進めていきます。

:::note alert
**【補足事項】**
***※内容は完全に自分向けとなっています。***
:::

また、過去にTerraformを使って構築した環境を、CloudFormationで再現する形で進めています。

https://qiita.com/free-honda/items/1d0fd904288f16f8c539

このプラットフォーム上に自分専用の備忘録記録を積み重ねていく予定です。**あらかじめご了承いただければ幸いです。**

## 実際にコードで行っていること

前回の続きです。

https://qiita.com/free-honda/items/6a6a254937028ff28e18

https://qiita.com/free-honda/items/a33bf86f1a26a2ee9e6e

https://qiita.com/free-honda/items/dc8024628e04c5118474

https://qiita.com/free-honda/items/6b6b56cf6b7a1f2f4d04

https://qiita.com/free-honda/items/42ffd26646557799b814

- **AWSプロバイダー設定**
  - 東京リージョンでリソースを構築する設定。

- **VPC作成**
  - CIDRブロック範囲 `10.0.0.0/16` のVPCを作成。
  - DNSサポートとDNSホスト名を有効化。

- **パブリックサブネット作成**
  - 2つのパブリックサブネットを異なるアベイラビリティゾーンで作成。
  - インターネットゲートウェイを通じてインターネットにアクセス可能。

- **プライベートサブネット作成**
  - 4つのプライベートサブネットを異なるCIDRブロックとアベイラビリティゾーンで作成。
  - NATゲートウェイを経由してインターネットにアクセス可能。

- **インターネットゲートウェイ設定**
  - パブリックサブネットのインターネットアクセスを提供。

- **ルートテーブルの設定**
  - パブリックサブネット用のルートテーブルを作成し、インターネットゲートウェイをルートに設定。
  - プライベートサブネット用のルートテーブルを作成し、NATゲートウェイをルートに設定。

- **NATゲートウェイ設定**
  - Elastic IPを割り当てたNATゲートウェイを作成。
  - プライベートサブネットのアウトバウンド通信をサポート。

- **サブネットとルートテーブルの関連付け**
  - 各サブネットを対応するルートテーブルに関連付け。

- **セキュリティグループ設定**
  - SSH（22番）とALB（443番）のインバウンド通信を許可。
  - 全アウトバウンドトラフィックを許可。

- **ALBとターゲットグループの設定**
  - 外部公開用のALBを作成し、HTTPリクエストを処理するターゲットグループを設定（ヘルスチェックとスティッキーセッションを有効化）。

- **ECSインスタンスの登録**
  - プライベートサブネット内のECSインスタンスをターゲットグループに登録し、ALBを介してリクエストを転送。

- **ECSクラスターとタスク実行ロールの作成**
  - **ECSクラスター**: 
    ECSサービスを実行するためのクラスターを作成。  
  - **タスク実行ロール**:
    ECRアクセスやログ送信権限を付与するIAMロール。

- **ECSタスク定義の作成**
  - **リソース名**:Fargate用のタスク（コンテナ）設定を定義。  
    - CPU: 256  
    - メモリ: 512  
    - コンテナ: ECRイメージを使用し、ポート1323を開放。  

- **ECSサービスの作成**
  - **リソース名**:ECSクラスター内でタスクを実行するサービスを作成。  
    - ALBとターゲットグループを使用してアクセスを管理。  
    - タスクをFargateで1つ実行（`desired_count = 1`）。

- **ACMによるHTTPS通信の有効化**
  - **リソース名**:ACM (AWS Certificate Manager) を利用して SSL/TLS 証明書を設定し、HTTPS 通信を暗号化。  
    - ユーザーがアクセスするすべてのトラフィックが HTTPS によって保護。  

- **RDSインスタンスの作成**
  - **リソース名**:RDSインスタンスを作成。  
    - MySQL 8.0を使用したシングルAZのRDSインスタンスをプライベートサブネット内にデプロイ。
    - プライベートサブネットを指定して、RDSインスタンスが配置可能なサブネットグループを作成

## 事前準備と補足事項

**1. お名前.comで購入したドメインを使用してSSL証明書を取得する方法**

AWS ACMとRoute53を利用してSSL証明書を取得する手順については、以下の記事をご参照ください：

https://qiita.com/free-honda/items/8fb5124ae36616d64dd1

**2. ALBのAレコードをRoute53のホストゾーンに追加する方法**

Terraformで自動デプロイ後、ALBのAレコードをRoute53に追加する手順については、以下の記事をご参照ください：

https://qiita.com/free-honda/items/1d8810054b556739afaf#route-53-%E3%81%AE%E3%83%9B%E3%82%B9%E3%83%88%E3%82%BE%E3%83%BC%E3%83%B3%E3%81%AB-a-%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E8%BF%BD%E5%8A%A0

## 作ったコードについて

```test.yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: CloudFormation template for creating a VPC, subnets, NAT gateway, and related resources.

# 変数の定義
Parameters:
  ACMCertificate:
    Type: String
    Default: "arn:aws:acm:ap-northeast-1:xxx:certificate/d09e0200-6c2e-49d5-a52f-15a1100f8c2a"
    Description: "ARN of the ACM Certificate"

Resources:
  # VPCの作成
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: "10.0.0.0/16"
      InstanceTenancy: "default"
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-vpc"

  # パブリックサブネット1の作成
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.0.0/20"
      AvailabilityZone: "ap-northeast-1a"
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-subnet-public1-ap-northeast-1a"

  # パブリックサブネット2の作成
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.16.0/20"
      AvailabilityZone: "ap-northeast-1c"
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-subnet-public2-ap-northeast-1c"

  # プライベートサブネット1の作成
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.128.0/20"
      AvailabilityZone: "ap-northeast-1a"
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-subnet-private1-ap-northeast-1a"

  # プライベートサブネット2の作成
  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.144.0/20"
      AvailabilityZone: "ap-northeast-1c"
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-subnet-private2-ap-northeast-1c"

  # プライベートサブネット3の作成
  PrivateSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.160.0/20"
      AvailabilityZone: "ap-northeast-1a"
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-subnet-private3-ap-northeast-1a"

  # プライベートサブネット4の作成
  PrivateSubnet4:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.176.0/20"
      AvailabilityZone: "ap-northeast-1c"
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-subnet-private4-ap-northeast-1c"

  # インターネットゲートウェイの作成
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-igw"

  # VPCへのインターネットゲートウェイのアタッチ
  AttachInternetGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  # パブリックルートテーブルの作成
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-rtb-public"

  # パブリックルートの設定
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway

  # パブリックサブネット1をルートテーブルに関連付け
  PublicRouteTableAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  # パブリックサブネット2をルートテーブルに関連付け
  PublicRouteTableAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

  # NATゲートウェイ用のElastic IPの作成
  NatEIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: "vpc"
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-eip-ap-northeast-1a"

  # NATゲートウェイの作成
  NatGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      SubnetId: !Ref PublicSubnet1
      AllocationId: !GetAtt NatEIP.AllocationId
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-nat-public1-ap-northeast-1a"

  # プライベートルートテーブル1の作成
  PrivateRouteTable1:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-rtb-private1-ap-northeast-1a"

  # プライベートルート1の設定
  PrivateRoute1:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable1
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NatGateway

  # プライベートサブネット1をルートテーブルに関連付け
  PrivateRouteTableAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1
      RouteTableId: !Ref PrivateRouteTable1

  # プライベートルートテーブル2の作成
  PrivateRouteTable2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-rtb-private2-ap-northeast-1c"

  # プライベートルート2の設定
  PrivateRoute2:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable2
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NatGateway

  # プライベートサブネット2をルートテーブルに関連付け
  PrivateRouteTableAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet2
      RouteTableId: !Ref PrivateRouteTable2

  # プライベートルートテーブル3の作成
  PrivateRouteTable3:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-rtb-private3-ap-northeast-1a"

  # プライベートルート3の設定
  PrivateRoute3:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable3
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NatGateway

  # プライベートサブネット3をルートテーブルに関連付け
  PrivateRouteTableAssociation3:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet3
      RouteTableId: !Ref PrivateRouteTable3

  # プライベートルートテーブル4の作成
  PrivateRouteTable4:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: "CloudFormation-vpc-rtb-private4-ap-northeast-1c"

  # プライベートルート4の設定
  PrivateRoute4:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable4
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NatGateway

  # プライベートサブネット4をルートテーブルに関連付け
  PrivateRouteTableAssociation4:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet4
      RouteTableId: !Ref PrivateRouteTable4

  # ECS用セキュリティグループ
  ECSGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for ECS instances"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: 1323
          ToPort: 1323
          SourceSecurityGroupId: !GetAtt PublicSG.GroupId
      SecurityGroupEgress:
        - IpProtocol: "-1"
          FromPort: 0
          ToPort: 0
          CidrIp: "0.0.0.0/0"
      Tags:
        - Key: Name
          Value: "CloudFormation-private-instance-sg"

  # ECSクラスターの作成
  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: "my-ecs-cluster"

  # ECSタスク実行ロールの作成
  ECSExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: "ecs-task-execution-role"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "ecs-tasks.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "ecs-task-execution-policy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "ecr:GetAuthorizationToken"
                  - "ecr:GetDownloadUrlForLayer"
                  - "ecr:BatchGetImage"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                  - "s3:GetObject"
                Resource: "*"

  # ECSタスク定義の作成
  ECSTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: "my-app-task"
      NetworkMode: "awsvpc"
      RequiresCompatibilities:
        - "FARGATE"
      Cpu: "256"
      Memory: "512"
      ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
      ContainerDefinitions:
        - Name: "my-app-repo"
          Image: "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/my-app-repo"
          PortMappings:
            - ContainerPort: 1323
              HostPort: 1323
              Protocol: "tcp"

  # ECSサービスの作成
  ECSService:
    Type: AWS::ECS::Service
    Properties:
      ServiceName: "my-ecs-service"
      Cluster: !Ref ECSCluster
      TaskDefinition: !Ref ECSTaskDefinition
      DesiredCount: 1
      LaunchType: "FARGATE"
      NetworkConfiguration:
        AwsvpcConfiguration:
          Subnets:
            - !Ref PublicSubnet1
          SecurityGroups:
            - !GetAtt ECSGroup.GroupId
          AssignPublicIp: "ENABLED"
      LoadBalancers:
        - TargetGroupArn: !Ref MainTargetGroup
          ContainerName: "my-app-repo"
          ContainerPort: 1323
    DependsOn:
      - HttpsListener # HttpListenerではなく、HttpsListenerを指定

  # パブリック用セキュリティグループ作成
  PublicSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Public security group for ALB"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: 443
          ToPort: 443
          CidrIp: "0.0.0.0/0"
      SecurityGroupEgress:
        - IpProtocol: "-1"
          FromPort: 0
          ToPort: 0
          CidrIp: "0.0.0.0/0"
      Tags:
        - Key: Name
          Value: "CloudFormation-public-sg"

  # アプリケーションロードバランサー(ALB)作成
  MainALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: "cloudformation-main-alb"
      Scheme: "internet-facing"
      Type: "application"
      SecurityGroups:
        - !GetAtt PublicSG.GroupId
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2
      Tags:
        - Key: Name
          Value: "cloudformation-main-alb"

  # ターゲットグループ作成
  MainTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: "cloudformation-main-target-group"
      Port: 1323
      Protocol: "HTTP"
      VpcId: !Ref VPC
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: "/"
      HealthCheckProtocol: "HTTP"
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 3
      UnhealthyThresholdCount: 3
      TargetType: "ip" # ターゲットタイプをIPに設定
      Tags:
        - Key: Name
          Value: "cloudformation-main-target-group"

  # ALBのリスナー作成 (HTTPS)
  HttpsListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref MainALB
      Port: 443
      Protocol: "HTTPS"
      Certificates:
        - CertificateArn: !Ref ACMCertificate
      DefaultActions:
        - Type: "forward"
          TargetGroupArn: !Ref MainTargetGroup

  # RDS用セキュリティグループ
  RDSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for RDS"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: 3306
          ToPort: 3306
          CidrIp: "10.0.0.0/16"
      SecurityGroupEgress:
        - IpProtocol: "-1"
          FromPort: 0
          ToPort: 0
          CidrIp: "0.0.0.0/0"
      Tags:
        - Key: Name
          Value: "CloudFormation-rds-sg"

  # RDS用サブネットグループ
  RDSSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: "Subnet group for RDS"
      SubnetIds:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      Tags:
        - Key: Name
          Value: "CloudFormation-rds-subnet-group"

  # RDSインスタンス
  RDSInstance:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Delete
    Properties:
      AllocatedStorage: "20" # 無料利用枠に合わせたストレージ
      StorageType: "gp2"
      Engine: "mysql"
      EngineVersion: "8.0.39"
      DBInstanceClass: "db.t4g.micro" # Graviton2プロセッサを使用したインスタンス
      MasterUsername: "admin"
      MasterUserPassword: "mypassword123"
      DBParameterGroupName: "default.mysql8.0"
      DBSubnetGroupName: !Ref RDSSubnetGroup
      VPCSecurityGroups:
        - !GetAtt RDSSecurityGroup.GroupId
      PubliclyAccessible: false
      MultiAZ: false
      Tags:
        - Key: Name
          Value: "CloudFormation-rds-instance"
      DeletionProtection: false
```

## まとめ
この記事は、自分用の備忘録としてまとめたものです。その点をご理解いただけると幸いです。

:::note info
***もし、この記事の内容が少しでも参考になれば嬉しく思います。***
:::

今後も同様の内容を継続して投稿していきますので、温かく見守っていただけるとありがたいです。

## おまけ
お名前.comで取得したドメイン名を使用してアクセスしたところ、以下のようにブラウザで正常に表示されることを確認しました。コード化は大成功です！

![Screenshot 2025-01-23 at 12.53.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/b59b1af2-be8a-3799-5b29-580a5d27bbd2.png)


また、今回作成したALBのDNS名でも、以下のように正常に表示されることを確認しています。

![Screenshot 2025-01-23 at 12.53.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/f263dedb-df98-80bb-f3c1-dd3bb0bbcd10.png)


今回追加で実装したRDSインスタンスについても、マネジメントコンソール上で正しく作成されていることを確認しました。

![Screenshot 2025-01-22 at 19.05.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/29aab67c-a297-c76c-7a1e-5402962cd024.png)