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

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

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

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

https://qiita.com/free-honda/items/825709ed952bf47027c3

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

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

前回の続きです。

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

- **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サービスを実行するためのクラスターを作成。  
  - **タスク実行ロール**: `aws_iam_role.ecs_task_execution_role`  
    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 によって保護。  

## 事前準備と補足事項

**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
```

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

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

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

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

![Screenshot 2025-01-23 at 12.33.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/f7c22c7b-7459-4fcd-26c1-5c8519f8f615.png)


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

![Screenshot 2025-01-23 at 12.31.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/1824f7ce-49f6-3d72-ea08-8f5a3d1600b5.png)