Terraform個人メモ：AWS上でFargateベースのECSアプリケーションを構築するTerraform備忘録

## はじめに
AWS環境をTerraformで構築することで、**インフラの管理をコード化** し、**再現性や運用の効率を向上** させることができます。  

:::note info
本記事では、**VPCやECS、RDSなどの主要リソースをTerraformで構築する方法** について詳しく解説します。
:::

Terraformを活用してAWS環境を効率的に管理したい方の参考になれば幸いです。

## 書こうと思ったきっかけ
AWS環境を手動で構築する際、**設定ミスや作業の手間が発生しやすい** と感じました。  

Terraformを使うことで、**インフラの構築・管理がシンプル** になり、**スケールや変更管理も容易** になります。  

**実際のAWS構成図**

![Screenshot 2025-02-21 at 7.29.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/14c68cef-7863-4742-97de-dc012a8dfd49.png)

自身の学習や備忘録として、Terraformを使ったAWS環境構築の手順を整理し、**共有するために本記事を書くことにしました**。

## 全体概要

このTerraform設定は **AWS上でFargateベースのECSアプリケーションを構築** するためのものです。主な構成要素として **VPC・サブネット、ECSクラスター、RDS（MySQL）、ALB、CloudFront、S3、Route 53** などが含まれています。

### 1. ネットワーク構成

- **VPC**（10.0.0.0/16）
- **パブリックサブネット**（ap-northeast-1a, ap-northeast-1c）
- **プライベートサブネット**（ap-northeast-1a, ap-northeast-1c）
- **NATゲートウェイ**（プライベートサブネットがインターネットにアクセスするため）
- **インターネットゲートウェイ（IGW）**（パブリックサブネット用）
- **ルートテーブル**
  - パブリックサブネット → IGW経由でインターネットアクセス可能
  - プライベートサブネット → NATゲートウェイ経由でインターネットアクセス可能

### 2. セキュリティグループ

- **ECSアプリ用セキュリティグループ**
  - ポート **1323**（アプリ通信）をALBからの通信のみ許可
  - Outboundはすべて許可
- **ALB用セキュリティグループ**
  - ポート **443**（HTTPS）を全インターネットから許可
  - Outboundはすべて許可
- **RDS用セキュリティグループ**
  - ポート **3306**（MySQL）をECSタスクのみ許可

### 3. ECS（Fargate）

- **ECSクラスター**（`my-ecs-cluster`）
- **ECSタスク定義**
  - **`my-app-repo`（アプリ本体）**
    - Golang製アプリ（`/app/migrate_app` → `/app/server`）
    - MySQL接続設定（Secrets Managerで管理）
    - ポート **1323**
  - **Datadog Agent**
    - メトリクス収集・ログ管理
  - **cws-instrumentation-init**
    - セキュリティ強化用
- **ECSサービス**
  - **プライベートサブネット** に配置
  - **ALB経由でリクエスト受信**
  - **Fargateで動作**
  - **NATゲートウェイ経由でインターネットアクセス**

### 4. ロードバランサー（ALB）

- **アプリケーションロードバランサー（ALB）**
  - **パブリックサブネット**に配置
  - **HTTPS（443番ポート）でリクエストを受信**
  - **ターゲットグループ経由でECSタスクに転送**
  - **ACM証明書でSSL/TLS対応**

### 5. データベース（RDS）

- **Amazon RDS（MySQL 8.0）**
  - `db.t4g.micro` インスタンス
  - **パブリックアクセスなし**（**プライベートサブネット内**）
  - **パスワードは Secrets Manager で管理**
  - **ECSタスクのみアクセス可能**

### 6. ログ管理

- **CloudWatch Logs**
  - **ECSタスクのログは `/ecs/datadog` に保存**

### 7. CloudFront + S3

- **S3バケット**
  - **CloudFront経由のみアクセス許可**
  - **ウェブホスティング有効（`index.html`）**
- **CloudFront**
  - **S3バケットをオリジン** とするCDN
  - **HTTPS通信を強制（リダイレクト）**
  - **Route 53（`honda333.blog`）と連携**

### まとめ

このTerraform構成は **AWS上でスケーラブルなWebアプリ環境を自動構築** するためのものです。

- **VPC & サブネット構成** → **ECS + RDS + ALB のための環境を用意**
- **ECS（Fargate）** → **アプリをコンテナ実行**
- **ALB（HTTPS対応）** → **外部からのアクセスをECSへ転送**
- **CloudFront + S3** → **静的コンテンツ配信**
- **Secrets Manager & CloudWatch Logs** → **セキュリティ & 運用管理強化**

この構成により、 **フルマネージドなアプリケーション基盤** を構築できます。

## おまけ：実際に完成したコード

```main.tf
# AWSプロバイダーの設定（東京リージョン）
provider "aws" {
  region = "ap-northeast-1"
}

# ACM 証明書の ARN（外部入力用）
variable "acm_certificate_arn" {
  default     = "arn:aws:acm:xxx"
  description = "ARN of the ACM Certificate"
}

# VPC の定義
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "Terraform-vpc"
  }
}

# パブリックサブネット（AZ: ap-northeast-1a）
resource "aws_subnet" "public1" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.0.0/20"
  availability_zone = "ap-northeast-1a"

  tags = {
    Name = "Terraform-subnet-public1-ap-northeast-1a"
  }
}

# パブリックサブネット（AZ: ap-northeast-1c）
resource "aws_subnet" "public2" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.16.0/20"
  availability_zone = "ap-northeast-1c"

  tags = {
    Name = "Terraform-subnet-public2-ap-northeast-1c"
  }
}

# プライベートサブネット（AZ: ap-northeast-1a）
resource "aws_subnet" "private1" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.128.0/20"
  availability_zone = "ap-northeast-1a"

  tags = {
    Name = "Terraform-subnet-private1-ap-northeast-1a"
  }
}

# プライベートサブネット（AZ: ap-northeast-1c）
resource "aws_subnet" "private2" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.144.0/20"
  availability_zone = "ap-northeast-1c"

  tags = {
    Name = "Terraform-subnet-private2-ap-northeast-1c"
  }
}

# プライベートサブネット（AZ: ap-northeast-1a, 別のCIDRブロック）
resource "aws_subnet" "private3" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.160.0/20"
  availability_zone = "ap-northeast-1a"

  tags = {
    Name = "Terraform-subnet-private3-ap-northeast-1a"
  }
}

# プライベートサブネット（AZ: ap-northeast-1c, 別のCIDRブロック）
resource "aws_subnet" "private4" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.176.0/20"
  availability_zone = "ap-northeast-1c"

  tags = {
    Name = "Terraform-subnet-private4-ap-northeast-1c"
  }
}

# インターネットゲートウェイ（IGW）の定義（VPC のインターネット接続用）
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "Terraform-igw"
  }
}

# パブリックルートテーブルの定義（パブリックサブネット用のルートテーブル）
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "Terraform-rtb-public"
  }
}

# インターネットアクセス用のデフォルトルート（0.0.0.0/0 → IGW）
resource "aws_route" "public_internet_access" {
  route_table_id         = aws_route_table.public.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.main.id
}

# パブリックサブネットをルートテーブルに関連付け
resource "aws_route_table_association" "public1" {
  subnet_id      = aws_subnet.public1.id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "public2" {
  subnet_id      = aws_subnet.public2.id
  route_table_id = aws_route_table.public.id
}

# NAT ゲートウェイ用の Elastic IP（プライベートサブネットのインターネットアクセス用）
resource "aws_eip" "nat" {
  tags = {
    Name = "Terraform-eip-ap-northeast-1a"
  }
}

# NAT ゲートウェイの定義（プライベートサブネットがインターネットにアクセスできるようにする）
resource "aws_nat_gateway" "public1" {
  subnet_id     = aws_subnet.public1.id
  allocation_id = aws_eip.nat.id

  tags = {
    Name = "Terraform-nat-public1-ap-northeast-1a"
  }
}

# プライベートルートテーブル（AZ: ap-northeast-1a）
resource "aws_route_table" "private1" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "Terraform-rtb-private1-ap-northeast-1a"
  }
}

# プライベートルートの定義（NAT ゲートウェイ経由でインターネットアクセス）
resource "aws_route" "private1_nat_access" {
  route_table_id         = aws_route_table.private1.id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = aws_nat_gateway.public1.id
}

# プライベートサブネットをルートテーブルに関連付け
resource "aws_route_table_association" "private1" {
  subnet_id      = aws_subnet.private1.id
  route_table_id = aws_route_table.private1.id
}

# プライベートルートテーブル（AZ: ap-northeast-1c）
resource "aws_route_table" "private2" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "Terraform-rtb-private2-ap-northeast-1c"
  }
}

# プライベートルートの定義（NAT ゲートウェイ経由でインターネットアクセス）
resource "aws_route" "private2_nat_access" {
  route_table_id         = aws_route_table.private2.id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = aws_nat_gateway.public1.id
}

# プライベートサブネットをルートテーブルに関連付け
resource "aws_route_table_association" "private2" {
  subnet_id      = aws_subnet.private2.id
  route_table_id = aws_route_table.private2.id
}

# プライベートルートテーブル（AZ: ap-northeast-1a, 別のサブネット用）
resource "aws_route_table" "private3" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "Terraform-rtb-private3-ap-northeast-1a"
  }
}

# プライベートルート（AZ: ap-northeast-1a, NAT ゲートウェイ経由でインターネットアクセス）
resource "aws_route" "private3_nat_access" {
  route_table_id         = aws_route_table.private3.id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = aws_nat_gateway.public1.id
}

# プライベートサブネットをルートテーブルに関連付け（AZ: ap-northeast-1a）
resource "aws_route_table_association" "private3" {
  subnet_id      = aws_subnet.private3.id
  route_table_id = aws_route_table.private3.id
}

# プライベートルートテーブル（AZ: ap-northeast-1c, 別のサブネット用）
resource "aws_route_table" "private4" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "Terraform-rtb-private4-ap-northeast-1c"
  }
}

# プライベートルート（AZ: ap-northeast-1c, NAT ゲートウェイ経由でインターネットアクセス）
resource "aws_route" "private4_nat_access" {
  route_table_id         = aws_route_table.private4.id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = aws_nat_gateway.public1.id
}

# プライベートサブネットをルートテーブルに関連付け（AZ: ap-northeast-1c）
resource "aws_route_table_association" "private4" {
  subnet_id      = aws_subnet.private4.id
  route_table_id = aws_route_table.private4.id
}

# ECS 用のセキュリティグループ（アプリケーションの通信を制御）
resource "aws_security_group" "ecs_sg" {
  vpc_id = aws_vpc.main.id

  # ALB からのリクエストを許可（ポート 1323）
  ingress {
    from_port       = 1323
    to_port         = 1323
    protocol        = "tcp"
    security_groups = [aws_security_group.public_sg.id]
  }

  # すべてのアウトバウンドトラフィックを許可
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "Terraform-private-instance-sg"
  }
}

# CloudWatch Logs グループの作成
resource "aws_cloudwatch_log_group" "ecs_logs" {
  name              = "/ecs/datadog"
  retention_in_days = 7  # ログの保持期間（必要に応じて変更）
}

# ECS クラスターの定義（Fargate でアプリを実行）
resource "aws_ecs_cluster" "main" {
  name = "my-ecs-cluster"
}

# ECS タスク実行ロールの定義（ECS タスクが必要な AWS サービスにアクセスするための IAM ロール）
resource "aws_iam_role" "ecs_task_execution_role" {
  name = "ecs-task-execution-role"

  # ECS タスクがこのロールを引き受けるためのポリシー
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = { Service = "ecs-tasks.amazonaws.com" }
      Action    = "sts:AssumeRole"
    }]
  })

  # タスク実行に必要な AWS サービスへのアクセス許可
  inline_policy {
    name = "ecs-task-execution-policy"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Effect   = "Allow"
          Action   = [
            "ecr:GetAuthorizationToken",
            "ecr:GetDownloadUrlForLayer",
            "ecr:BatchGetImage",
            "logs:CreateLogStream",
            "logs:PutLogEvents",
            "s3:GetObject"
          ]
          Resource = "*"
        }
      ]
    })
  }
}

# ECS Exec に必要な SSM 権限を付与
resource "aws_iam_role_policy_attachment" "ecs_ssm_policy" {
  role       = aws_iam_role.ecs_task_execution_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

# ECS タスク実行ロールの基本ポリシー
resource "aws_iam_role_policy_attachment" "ecs_task_execution_policy" {
  role       = aws_iam_role.ecs_task_execution_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

# ECS タスク定義（Fargate で動作するコンテナの定義）
resource "aws_ecs_task_definition" "main" {
  family                   = "Datadog"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = "256"
  memory                   = "512"
  execution_role_arn       = aws_iam_role.ecs_task_execution_role.arn
  task_role_arn            = aws_iam_role.ecs_task_execution_role.arn  # <-- 追加

  volume {
    name = "cws-instrumentation-volume"
  }

  container_definitions = jsonencode([
    {
      name       = "cws-instrumentation-init"
      image      = "datadog/cws-instrumentation:latest"
      essential  = false
      user       = "0"
      command    = [
        "/cws-instrumentation",
        "setup",
        "--cws-volume-mount",
        "/cws-instrumentation-volume"
      ]
      mountPoints = [
        {
          sourceVolume  = "cws-instrumentation-volume"
          containerPath = "/cws-instrumentation-volume"
          readOnly      = false
        }
      ]
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = "/ecs/datadog"
          awslogs-region        = "ap-northeast-1"
          awslogs-stream-prefix = "cws-instrumentation-init"
        }
      }
    },
    {
      name      = "datadog-agent"
      image     = "datadog/agent:latest"
      essential = true
      environment = [
        {
          name  = "DD_API_KEY"
          value = "xxx"
        },
        {
          name  = "DD_SITE"
          value = "ap1.datadoghq.com"
        },
        {
          name  = "ECS_FARGATE"
          value = "true"
        },
        # メトリクス収集（CPU・メモリ）を有効化
        {
          name  = "DD_CONTAINER_METRICS_ENABLED"
          value = "true"
        },
        # Process Monitoring を完全に無効化
        {
          name  = "DD_PROCESS_AGENT_ENABLED"
          value = "false"
        },
        {
          name  = "DD_PROCESS_CONFIG_ENABLED"
          value = "false"
        },
        {
          name  = "DD_ORCHESTRATOR_EXPLORER_ENABLED"
          value = "false"
        },
        {
          name  = "DD_SYSTEM_PROBE_ENABLED"
          value = "false"
        },
        # APM, ログ, セキュリティ機能も無効化
        {
          name  = "DD_LOGS_ENABLED"
          value = "false"
        },
        {
          name  = "DD_APM_ENABLED"
          value = "false"
        },
        {
          name  = "DD_TRACE_ENABLED"
          value = "false"
        },
        {
          name  = "DD_SECURITY_AGENT_ENABLED"
          value = "false"
        },
        {
          name  = "DD_RUNTIME_SECURITY_CONFIG_ENABLED"
          value = "false"
        },
        {
          name  = "DD_RUNTIME_SECURITY_CONFIG_EBPFLESS_ENABLED"
          value = "false"
        }
      ]
      healthCheck = {
        command     = ["CMD-SHELL", "/probe.sh"]
        interval    = 30
        timeout     = 5
        retries     = 2
        startPeriod = 60
      }
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = "/ecs/datadog"
          awslogs-region        = "ap-northeast-1"
          awslogs-stream-prefix = "datadog-agent"
        }
      }
    },
    {
      name  = "my-app-repo"
      image = "xxx"
      entryPoint = ["/bin/sh", "-c", "/app/migrate_app && /app/server"]

      environment = [
        # MySQL 環境変数
        {
          "name": "GO_ENV",
          "value": "prod"
        },
        {
          "name": "MYSQL_ROOT_PASSWORD",
          "value": "root"
        },        
        {
          "name": "MYSQL_USER",
          "value": "myuser"
        },
        {
          "name": "MYSQL_PW",
          "value": "${jsondecode(data.aws_secretsmanager_secret_version.rds_password_v5.secret_string)["password"]}"
        },
        {
          "name": "MYSQL_HOST",
          "value": "${aws_db_instance.rds_instance.address}"
        },
        {
          "name": "MYSQL_PORT",
          "value": "3306"
        },
        {
          "name": "MYSQL_DB",
          "value": "mydatabase"
        },
        # CORS 設定
        {
          "name": "CORS_ALLOWED_ORIGIN",
          "value": "https://honda333.blog"
        },
        # phpMyAdmin 環境変数
        {
          "name": "PMA_ARBITRARY",
          "value": "1"
        },
        {
          "name": "PMA_USER",
          "value": "myuser"
        },
        {
          "name": "PMA_PASSWORD",
          "value": "root"
        },
        {
          "name": "PMA_HOST",
          "value": "dev-mysql"
        },
        {
          "name": "PMA_PORT",
          "value": "3306"
        },

        # Cognito 環境変数
        {
          "name": "COGNITO_USER_POOL_ID",
          "value": "xxx"
        },
        {
          "name": "COGNITO_CLIENT_ID",
          "value": "xxx"
        },
        {
          "name": "AWS_REGION",
          "value": "ap-northeast-1"
        },

        # OpenAI APIキー
        {
          "name": "OPENAI_API_KEY",
          "value": "xxx"
        }
      ]

      mountPoints = [
        {
          sourceVolume  = "cws-instrumentation-volume"
          containerPath = "/cws-instrumentation-volume"
          readOnly      = true
        }
      ]
      linuxParameters = {
        capabilities = {
          add = ["SYS_PTRACE"]
        }
      }
      dependsOn = [
        {
          containerName = "datadog-agent"
          condition     = "HEALTHY"
        },
        {
          containerName = "cws-instrumentation-init"
          condition     = "SUCCESS"
        }
      ]
      portMappings = [
        {
          containerPort = 1323
          hostPort      = 1323
          protocol      = "tcp"
        }
      ]
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = "/ecs/datadog"
          awslogs-region        = "ap-northeast-1"
          awslogs-stream-prefix = "my-app-repo"
        }
      }
    }
  ])
}

# ECS サービスの定義（Fargate で実行し、プライベートサブネットに配置）
resource "aws_ecs_service" "main" {
  name            = "my-ecs-service"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.main.arn
  desired_count   = 1
  launch_type     = "FARGATE"
  enable_execute_command = true  # <-- 追加

  # ネットワーク設定（プライベートサブネットで実行し、パブリック IP を付与しない）
  network_configuration {
    subnets         = [aws_subnet.private1.id, aws_subnet.private2.id]
    security_groups = [aws_security_group.ecs_sg.id]
    assign_public_ip = false # パブリック IP を付与せず、NAT 経由で通信
  }

  # ALB との接続設定（ECS タスクを ALB に登録）
  load_balancer {
    target_group_arn = aws_lb_target_group.main_target_group.arn
    container_name   = "my-app-repo"
    container_port   = 1323
  }
}

# パブリック（ALB）用セキュリティグループの定義（HTTPS トラフィックを許可）
resource "aws_security_group" "public_sg" {
  vpc_id = aws_vpc.main.id

  # インバウンドルール: HTTPS (443) の受信を全インターネットから許可
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # アウトバウンドルール: すべての通信を許可
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "Terraform-public-sg"
  }
}

# アプリケーションロードバランサー（ALB）の定義（パブリックアクセス可能な ALB）
resource "aws_lb" "main_alb" {
  name               = "terraform-main-alb"
  internal           = false # パブリック ALB
  load_balancer_type = "application"
  security_groups    = [aws_security_group.public_sg.id] # ALB に適用するセキュリティグループ
  subnets            = [aws_subnet.public1.id, aws_subnet.public2.id] # ALB の配置サブネット

  enable_deletion_protection = false # 削除保護を無効化

  tags = {
    Name = "terraform-main-alb"
  }
}

# ターゲットグループの定義（ALB がリクエストを転送する先）
resource "aws_lb_target_group" "main_target_group" {
  name     = "terraform-main-target-group"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.main.id
  target_type = "ip" # Fargate の場合は 'ip' に設定

  # ヘルスチェックの設定（ALB が正常なターゲットを判断する）
  health_check {
    interval            = 30
    path                = "/"
    protocol            = "HTTP"
    timeout             = 5
    healthy_threshold   = 3
    unhealthy_threshold = 3
  }

  # セッション維持設定（ALB の負荷分散時にセッションを保持）
  stickiness {
    type            = "lb_cookie"
    enabled         = true
    cookie_duration = 86400
  }

  tags = {
    Name = "terraform-main-target-group"
  }
}

# ALB のリスナー設定（HTTPS リクエストをターゲットグループに転送）
resource "aws_lb_listener" "http_listener" {
  load_balancer_arn = aws_lb.main_alb.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08"
  
  certificate_arn = var.acm_certificate_arn # SSL 証明書の ARN を指定

  # ターゲットグループへのリクエスト転送設定
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.main_target_group.arn
  }
}

# RDS 用セキュリティグループ（データベースへのアクセスを制御）
resource "aws_security_group" "rds_sg" {
  vpc_id = aws_vpc.main.id

  # アウトバウンドルール: すべての通信を許可
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "Terraform-rds-sg"
  }
}

# RDS のインバウンドルールを ECS のセキュリティグループ（ecs_sg）のみに制限
resource "aws_security_group_rule" "rds_allow_ecs" {
  type                     = "ingress"
  from_port                = 3306
  to_port                  = 3306
  protocol                 = "tcp"
  security_group_id        = aws_security_group.rds_sg.id
  source_security_group_id = aws_security_group.ecs_sg.id  # ← ECSのセキュリティグループのみ許可
  description              = "Allow ECS to access RDS"
}

# RDS 用サブネットグループの定義（RDS を配置するサブネットを指定）
resource "aws_db_subnet_group" "rds_subnet_group" {
  name       = "terraform-rds-subnet-group"
  subnet_ids = [aws_subnet.private3.id, aws_subnet.private4.id]

  tags = {
    Name = "Terraform-rds-subnet-group"
  }
}

# Secrets Manager を使用して RDS のパスワードを管理
resource "aws_secretsmanager_secret" "rds_password_v5" {
  name = "rds_password_v5"
}

# Secrets Manager に保存する RDS のパスワード（初期設定）
resource "aws_secretsmanager_secret_version" "rds_password_version_v5" {
  secret_id     = aws_secretsmanager_secret.rds_password_v5.id
  secret_string = jsonencode({ password = "mypassword123" })
}

# Secrets Manager から RDS のパスワードを取得
data "aws_secretsmanager_secret_version" "rds_password_v5" {
  secret_id  = aws_secretsmanager_secret.rds_password_v5.arn
  depends_on = [aws_secretsmanager_secret_version.rds_password_version_v5]
}

# RDS インスタンスの定義（MySQL 8.0 を使用）
resource "aws_db_instance" "rds_instance" {
  allocated_storage    = 20
  storage_type         = "gp2"
  engine               = "mysql"
  engine_version       = "8.0.39"
  instance_class       = "db.t4g.micro"
  username             = "myuser"  # 変更
  password             = jsondecode(data.aws_secretsmanager_secret_version.rds_password_v5.secret_string)["password"]
  db_name              = "mydatabase"  # ← ここを追加
  db_subnet_group_name    = aws_db_subnet_group.rds_subnet_group.name
  vpc_security_group_ids  = [aws_security_group.rds_sg.id]

  publicly_accessible = false
  multi_az            = false

  tags = {
    Name = "Terraform-rds-instance"
  }

  # 削除時の最終スナップショット作成をスキップ
  skip_final_snapshot = true
}

# S3 バケットの定義（CloudFront 経由でアクセスするためのバケット）
resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-cloudfront-bucket-tokyo"
  acl    = "private" # バケットは非公開に設定（CloudFront からのアクセスのみ許可）

  tags = {
    Name        = "MyS3Bucket"
    Environment = "Production"
  }
}

# S3 バケットのウェブホスティング設定（index.html と error.html を指定）
resource "aws_s3_bucket_website_configuration" "my_bucket_website" {
  bucket = aws_s3_bucket.my_bucket.id

  index_document {
    suffix = "index.html"
  }

  error_document {
    key = "error.html"
  }
}

# CloudFront 用オリジンアクセスアイデンティティ（OAI）の定義（S3 へのセキュアなアクセスを許可）
resource "aws_cloudfront_origin_access_identity" "my_oai" {
  comment = "OAI for S3 bucket"
}

# S3 バケットポリシーの設定（CloudFront OAI のみ S3 へのアクセスを許可）
resource "aws_s3_bucket_policy" "my_bucket_policy" {
  bucket = aws_s3_bucket.my_bucket.id

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect    = "Allow",
        Principal = {
          AWS = aws_cloudfront_origin_access_identity.my_oai.iam_arn
        },
        Action   = "s3:GetObject",
        Resource = "${aws_s3_bucket.my_bucket.arn}/*"
      }
    ]
  })
}

# 簡易的な HTML ファイル（index.html）の作成
resource "local_file" "index_html" {
  content  = "<html><body><h1>React in S3</h1></body></html>"
  filename = "index.html"
}

# index.html を S3 バケットにアップロード
resource "aws_s3_object" "index_file" {
  bucket       = aws_s3_bucket.my_bucket.id
  key          = "index.html"
  source       = local_file.index_html.filename
  content_type = "text/html"
  acl          = "private" # オブジェクトは非公開（CloudFront 経由でのみアクセス可能）
}

# CloudFront ディストリビューションの定義（S3 バケットをオリジンとする）
resource "aws_cloudfront_distribution" "my_distribution" {
  origin {
    domain_name = aws_s3_bucket.my_bucket.bucket_regional_domain_name
    origin_id   = "S3-my-cloudfront-bucket"
    origin_path = "/client"  # 🔹 ここを追加

    s3_origin_config {
      origin_access_identity = aws_cloudfront_origin_access_identity.my_oai.cloudfront_access_identity_path
    }
  }

  enabled             = true
  is_ipv6_enabled     = true
  comment             = "CloudFront Distribution for S3 bucket in Tokyo region"
  default_root_object = "index.html"

  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "S3-my-cloudfront-bucket"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  price_class = "PriceClass_100"

  viewer_certificate {
    acm_certificate_arn      = "arn:aws:acm:us-east-1:xxx"
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }

  aliases = ["honda333.blog"]

  tags = {
    Name        = "MyCloudFrontDistribution"
    Environment = "Production"
  }
}

# Route 53 に CloudFront へのエイリアスレコードを追加
resource "aws_route53_record" "cloudfront" {
  zone_id = "xxx"
  name    = "xxx.blog"
  type    = "A"

  alias {
    name                   = aws_cloudfront_distribution.my_distribution.domain_name
    zone_id                = aws_cloudfront_distribution.my_distribution.hosted_zone_id
    evaluate_target_health = false
  }
}
```