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"
}