frp_configuration_and_commands

TOML / Bash

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    null = {
      source  = "hashicorp/null"
      version = "~> 3.0"
    }
    http = {
      source  = "hashicorp/http"
      version = "~> 3.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

variable "aws_region" {
  default = "ap-northeast-1"
}

# ==========================================
# 0. 自分のグローバルIPを自動取得
# ==========================================
data "http" "my_ip" {
  url = "http://ipv4.icanhazip.com"
}

locals {
  my_cidr = "${chomp(data.http.my_ip.response_body)}/32"
}

# ==========================================
# 1. ネットワーク(VPC / Subnet / IGW)
# ==========================================
resource "aws_vpc" "main" {
  cidr_block           = "192.168.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
}

resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.main.id
}

resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "192.168.1.0/24"
  map_public_ip_on_launch = true
  availability_zone       = "ap-northeast-1a"
}

resource "aws_route_table" "rt" {
  vpc_id = aws_vpc.main.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }
}

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

# 安全に絞られたセキュリティグループ
resource "aws_security_group" "strict_sg" {
  name        = "strict_sg_for_testing"
  vpc_id      = aws_vpc.main.id

  # 22番:自宅IP(直接アクセス用) と AWSコンソール(EC2 Instance Connect用) の両方を許可
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [
      local.my_cidr,
      "3.112.23.0/29"
    ]
  }
  
  # 80番:自宅IPからのみアクセスを許可
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = [local.my_cidr]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# ==========================================
# 2. ECR と 手元でのカスタムコンテナビルド
# ==========================================
resource "aws_ecr_repository" "custom_apache" {
  name                 = "my-custom-apache"
  image_tag_mutability = "MUTABLE"
  force_delete         = true
}

resource "null_resource" "docker_build_and_push" {
  depends_on = [aws_ecr_repository.custom_apache]

  provisioner "local-exec" {
    # <<-EOF でインデントを許容し、内部では echo を使って安全にファイルを作成
    command = <<-EOF
      mkdir -p ./custom-container
      echo "hello world!!" > ./custom-container/index.html
      
      echo "FROM httpd:latest" > ./custom-container/Dockerfile
      echo "COPY index.html /usr/local/apache2/htdocs/" >> ./custom-container/Dockerfile

      aws ecr get-login-password --region ${var.aws_region} | docker login --username AWS --password-stdin ${aws_ecr_repository.custom_apache.repository_url}
      docker build -t custom-apache ./custom-container/
      docker tag custom-apache:latest ${aws_ecr_repository.custom_apache.repository_url}:latest
      docker push ${aws_ecr_repository.custom_apache.repository_url}:latest
    EOF
  }
}

# ==========================================
# 3. IAM (EC2上のECSエージェント用権限)
# ==========================================
resource "aws_iam_role" "ecs_instance_role" {
  name = "ecs-instance-role-testing"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{ Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "ec2.amazonaws.com" } }]
  })
}

resource "aws_iam_role_policy_attachment" "ecs_instance_role_attach" {
  role       = aws_iam_role.ecs_instance_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}

resource "aws_iam_instance_profile" "ecs_instance_profile" {
  name = "ecs-instance-profile-testing"
  role = aws_iam_role.ecs_instance_role.name
}

# ==========================================
# 4. ECS クラスター
# ==========================================
resource "aws_ecs_cluster" "main" {
  name = "my-ecs-cluster"
}

# ==========================================
# 5. EC2 インスタンス(Ubuntu 24.04 + IP固定)
# ==========================================
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
  }
}

resource "aws_instance" "ecs_host" {
  ami                  = data.aws_ami.ubuntu.id
  instance_type        = "t3.small"
  subnet_id            = aws_subnet.public.id
  private_ip           = "192.168.1.10"
  iam_instance_profile = aws_iam_instance_profile.ecs_instance_profile.name
  security_groups      = [aws_security_group.strict_sg.id]

  user_data = <<-EOF
              #!/bin/bash
              apt-get update -y
              apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release
              mkdir -p /etc/apt/keyrings
              curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
              echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
              apt-get update -y
              apt-get install -y docker-ce docker-ce-cli containerd.io
              mkdir -p /etc/ecs /var/log/ecs /var/lib/ecs/data
              echo "ECS_CLUSTER=${aws_ecs_cluster.main.name}" >> /etc/ecs/ecs.config
              docker run --name ecs-agent \
                --detach=true \
                --restart=on-failure:10 \
                --volume=/var/run:/var/run \
                --volume=/var/log/ecs/:/log \
                --volume=/var/lib/ecs/data:/data \
                --volume=/etc/ecs:/etc/ecs \
                --net=host \
                --env-file=/etc/ecs/ecs.config \
                amazon/amazon-ecs-agent:latest
              EOF

  tags = { Name = "ecs-ubuntu-host" }
}

# ==========================================
# 6. ECS タスク定義 & サービス
# ==========================================
resource "aws_ecs_task_definition" "custom_apache" {
  family                   = "custom-apache-task"
  requires_compatibilities = ["EC2"]
  network_mode             = "bridge"
  cpu                      = "256"
  memory                   = "256"

  container_definitions = jsonencode([{
    name      = "custom-apache-container"
    image     = "${aws_ecr_repository.custom_apache.repository_url}:latest"
    cpu       = 256
    memory    = 256
    essential = true
    portMappings = [{
      containerPort = 80
      hostPort      = 80
    }]
  }])
}

resource "aws_ecs_service" "custom_apache" {
  name            = "custom-apache-service"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.custom_apache.arn
  desired_count   = 1
  launch_type     = "EC2"

  depends_on = [null_resource.docker_build_and_push, aws_instance.ecs_host]
}

# ==========================================
# 7. 出力情報
# ==========================================
output "access_url" {
  value       = "http://${aws_instance.ecs_host.public_ip}"
  description = "ApacheへのアクセスURL"
}