20
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

はじめに

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

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

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

書こうと思ったきっかけ

AWS環境を手動で構築する際、設定ミスや作業の手間が発生しやすい と感じました。

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

実際のAWS構成図

Screenshot 2025-02-21 at 7.29.34.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
  }
}
20
25
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?