★ 初級

03. EC2 とコンピュート

AWS の代表サービス EC2(Elastic Compute Cloud)。仮想マシンを 1 台立てるだけのシンプル例から、AMI を最新で取得する/起動スクリプトを流す/IAM ロールを付ける、までを順に。

最小例

resource "aws_instance" "web" {
  ami           = "ami-0c4a35bf6c1f8c39d"   # AMI ID(リージョン固有)
  instance_type = "t3.micro"

  tags = {
    Name = "web"
  }
}

これだけで EC2 1 台が起動します。ただしデフォルト VPC の中、デフォルト SG という不便な構成。実用には次のように VPC や SG を指定します。

resource "aws_instance" "web" {
  ami                    = data.aws_ami.al2023.id
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.public["a"].id
  vpc_security_group_ids = [aws_security_group.web.id]
  key_name               = aws_key_pair.deployer.key_name
  iam_instance_profile   = aws_iam_instance_profile.ec2.name

  tags = { Name = "web" }
}

AMI を最新で取得(data ソース)

AMI ID をコードに固定で書くと、月が変わると古い AMI のまま起動し続けて、セキュリティパッチが当たりません。data "aws_ami" で「最新の Amazon Linux 2023」を毎回引いてくるのが定石。

data "aws_ami" "al2023" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.al2023.id
  instance_type = "t3.micro"
  # ...
}
terraform plan 時の挙動 data ソースは plan のたびに最新を引いてくるので、AMI が更新されると差分が出ます。「AMI が更新されたから再作成しろ」と Terraform が言ってきます。これを避けたい時は lifecycle.ignore_changes = [ami] を使う。

user_data で起動時スクリプト

EC2 起動時に 1 度だけ流すシェルスクリプト。

resource "aws_instance" "web" {
  ami           = data.aws_ami.al2023.id
  instance_type = "t3.micro"

  user_data = <<-EOT
    #!/bin/bash
    dnf update -y
    dnf install -y nginx
    systemctl enable --now nginx
    echo "Hello from $(hostname)" > /usr/share/nginx/html/index.html
  EOT

  user_data_replace_on_change = true   # user_data を変えたら EC2 を作り直す
}

テンプレ化するなら templatefile():

# templates/web-init.sh.tpl
# #!/bin/bash
# echo "Hello, ${name}!" > /tmp/hello.txt

resource "aws_instance" "web" {
  user_data = templatefile("${path.module}/templates/web-init.sh.tpl", {
    name = var.environment
  })
}

IAM ロールを付ける

EC2 から AWS API を叩くには IAM ロール を「インスタンスプロファイル」経由で attach します。詳細は 05 章。最小はこれ:

resource "aws_iam_role" "ec2" {
  name = "ec2-web"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = { Service = "ec2.amazonaws.com" }
      Action = "sts:AssumeRole"
    }]
  })
}

resource "aws_iam_role_policy_attachment" "ssm" {
  role       = aws_iam_role.ec2.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_instance_profile" "ec2" {
  name = "ec2-web"
  role = aws_iam_role.ec2.name
}

resource "aws_instance" "web" {
  # ...
  iam_instance_profile = aws_iam_instance_profile.ec2.name
}
SSM 経由で SSH レス運用 上の AmazonSSMManagedInstanceCore を付けると、SSH キーや 22 番ポート開放なしで aws ssm start-session --target i-xxx で入れるようになります。22 番を開けない がベストプラクティス。

ディスク設定(root_block_device)

resource "aws_instance" "web" {
  ami           = data.aws_ami.al2023.id
  instance_type = "t3.medium"

  root_block_device {
    volume_size           = 30          # GB
    volume_type           = "gp3"        # 推奨(gp2 より安く速い)
    iops                  = 3000
    throughput            = 125
    encrypted             = true
    delete_on_termination = true
  }

  ebs_block_device {
    device_name = "/dev/sdf"
    volume_size = 100
    volume_type = "gp3"
    encrypted   = true
  }
}

複数台を一括(for_each)

locals {
  servers = {
    api    = { type = "t3.small", subnet = "a" }
    worker = { type = "t3.micro", subnet = "c" }
    cron   = { type = "t3.nano",  subnet = "a" }
  }
}

resource "aws_instance" "fleet" {
  for_each = local.servers

  ami           = data.aws_ami.al2023.id
  instance_type = each.value.type
  subnet_id     = aws_subnet.private[each.value.subnet].id

  vpc_security_group_ids = [aws_security_group.app.id]
  iam_instance_profile   = aws_iam_instance_profile.ec2.name

  tags = { Name = each.key }
}

# 参照
output "api_private_ip" {
  value = aws_instance.fleet["api"].private_ip
}

EC2 以外の選択肢

用途EC2 ではなく
Web アプリ/APILambda + API Gateway または ECS Fargate
定常稼働のサービスECS Fargate(インスタンス管理が要らない)
Auto Scaling したいバッチaws_launch_template + aws_autoscaling_group
SSH せずに作業AWS Cloud9 / SSM Session Manager

「サーバの OS を自由にいじりたい」「特殊なソフトウェアを動かす」場合だけ EC2、それ以外は Lambda か ECS Fargate を第一候補 にするのが 2026 年のセオリー。