0
0

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】モジュールでEC2インスタンスを一気に作りたい

0
Last updated at Posted at 2026-07-28

はじめに

「検証環境などで、Terraformを用いて同じような構成のEC2インスタンスを複数台立ち上げたい」というケースはよくあります。
しかし、モジュールを使わずにコードを書こうとすると、コードが冗長で非常に長くなってしまいます。可読性が下がるだけでなく、修正漏れなどのミスの原因にもなりがちです。
そこで本記事では、モジュール(module)を活用してコード本体をシンプルに保ちつつ、複数のEC2を一気に作成する方法を解説します!
モジュール側のコード作成から呼び出し側のコード作成だけでなく、出力(Output)するコツまでまとめてご紹介します。

TL;DR

この記事を読めば、以下のことができるようになります。

  • モジュールを活用してコード本体をシンプルにする方法
  • for_each を用いて、1つのモジュールから複数台のEC2(および付随ネットワーク)を一括作成する方法
  • モジュールの出力(Output)を呼び出し側で集約し、マップ形式で綺麗に表示する方法

前提条件

  • SSH接続用のキーペアがAWS上に作成済みであること

実行環境

  • Terraform: v1.15.5

詳細

1. フォルダ構造

モジュールはmodulesフォルダ配下に作成します。モジュール名は、ec2_instanceとします。
また、今回はステージング環境にEC2インスタンスを作成することを想定して、stgフォルダの下にTerraformのファイル本体を配置しています。

.
├── modules
│   └── ec2_instance
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
└── stg
    ├── main.tf
    ├── outputs.tf
    └── variables.tf

2-1. ファイル説明(modulesフォルダ配下)

./modules/ec2_instance/main.tf

今回は、作成するEC2インスタンスごとに以下を個別で作成する実装としています。

  • VPC
  • サブネット
  • インターネットゲートウェイ
  • ルートテーブル
  • セキュリティグループ

本記事では解説をシンプルにするため、1つのEC2ごとに独立したVPC/Subnetを完全新規作成する構成にしています。
実運用ではVPCを共通化するか、インスタンスごとにvpc_cidrを変更して渡す構成が一般的です。

resource "aws_instance" "my_instance" {
  ami           = var.ami_id
  instance_type = var.instance_type
  subnet_id     = aws_subnet.my_subnet.id
  vpc_security_group_ids = [aws_security_group.my_sg.id]
  associate_public_ip_address = true
  key_name      = var.key_name

  root_block_device {
    volume_size = 8
    volume_type = "gp3"
    tags = {
      Name = "${var.instance_name}-root-volume"
    }
  }

  depends_on = [
    aws_internet_gateway.my_igw,
    aws_security_group.my_sg
  ]

  tags = {
    Name = "${var.instance_name}-instance"
  }
}

resource "aws_vpc" "my_vpc" {
  cidr_block = var.vpc_cidr

  tags = {
    Name = "${var.instance_name}-vpc"
  }
}

resource "aws_subnet" "my_subnet" {
  vpc_id     = aws_vpc.my_vpc.id
  cidr_block = var.subnet_cidr
  map_public_ip_on_launch = true
  availability_zone = "ap-northeast-1a"

  tags = {
    Name = "${var.instance_name}-subnet"
  }
}

resource "aws_internet_gateway" "my_igw" {
  vpc_id = aws_vpc.my_vpc.id

  tags = {
    Name = "${var.instance_name}-igw"
  }
}

resource "aws_route_table" "my_rt" {
  vpc_id = aws_vpc.my_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.my_igw.id
  }

  tags = {
    Name = "${var.instance_name}-rt"
  }
}

resource "aws_route_table_association" "my_rta" {
  subnet_id      = aws_subnet.my_subnet.id
  route_table_id = aws_route_table.my_rt.id
}

resource "aws_security_group" "my_sg" {
  name        = "${var.instance_name}-sg"
  description = "Security group for ${var.instance_name}"
  vpc_id      = aws_vpc.my_vpc.id

  ingress {
    from_port   = 22
    to_port     = 22
    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 = "${var.instance_name}-sg"
  }
}

ポイントは以下の通りです。

  1. EC2インスタンスの作成タイミングを制御するためにdepends_onを使用する

    • リソース間の参照関係(aws_subnet.my_subnet.idなど)があれば通常は自動で順番が考慮されますが、インターネットゲートウェイ経由の通信設定やセキュリティグループの反映が完全に完了してからEC2を作成したい場合、明示的にdepends_onを書くことで作成順序のトラブルを防げます
      depends_on = [
        aws_internet_gateway.my_igw,
        aws_security_group.my_sg
      ]
    

./modules/ec2_instance/variables.tf

今回はシンプルな実装にしたいので、instance_namekey_name以外はデフォルト値を使用します。

variable "ami_id" {
  description = "The AMI to use for the instance."
  type        = string
  default     = "ami-XXXXXXXXXXXXXXXXX"    # 使用するAMI IDを記載
}

variable "instance_type" {
  description = "The type of instance to start."
  type        = string
  default     = "t2.micro"
}

variable "instance_name" {
  description = "Name of the EC2 instance"
  type        = string
}

variable "vpc_cidr" {
  description = "The CIDR block for the VPC."
  type        = string
  default     = "10.0.0.0/16"
}

variable "subnet_cidr" {
  description = "The CIDR block for the subnet."
  type        = string
  default     = "10.0.1.0/24"
}

variable "key_name" {
  description = "Key name for the EC2 instance."
  type        = string
}

./modules/ec2_instance/outputs.tf

出力はシンプルに、インスタンスIDとPublic IPのみとします。
後でも出てきますが注意点としては、以下の通りです

  • モジュール(子モジュール)側で定義したoutputs.tfは、単体ではterraform apply実行時のターミナル画面(Outputs領域)に表示されません
  • 呼び出し元(親モジュール)側のoutputs.tf(今回は./stg/outputs.tf)で、子モジュールの出力値を参照して再定義する必要があります
output "instance_id" {
  description = "The ID of the EC2 instance."
  value       = aws_instance.my_instance.id
}

output "public_ip" {
  description = "The public IP address of the EC2 instance."
  value       = aws_instance.my_instance.public_ip
}

2-2. ファイル説明(stgフォルダ配下)

./stg/main.tf

モジュールを呼び出す側のコード(本体)は、定義したモジュールを指定するだけなので非常にシンプルです。

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

  default_tags {
    tags = {
      Env = var.env
    }
  }
}

module "ec2_instances" {
  source   = "../modules/ec2_instance"
  for_each = toset([
    "terraform-test-1",
    "terraform-test-2"
  ])

  key_name = "test_key"
  instance_name = each.key
}

ポイントは以下の通りです。

  1. default_tagsを使用すると、作成されるすべてのリソースに強制的に指定したタグを付与できる
    • 今回は、タグEnvを付与する
  2. for_eachで作成するリソース名(instance_name)のリストを渡すことで、複数のリソースを作成できる

./stg/variables.tf

変数はシンプルにすべてのリソースに付与する環境名(env)のみを定義します。
ステージング環境を想定しているので、値はstgとしました。

variable "env" {
  description = "Environment for the EC2 instance"
  type        = string
  default     = "stg"
}

./stg/outputs.tf

./modules/ec2_instance/outputs.tfの注意点でも書いたように、モジュールのoutputs.tfの結果を出力するように記載します。

output "instance_ids" {
  description = "A map of instance names to their IDs."
  value       = { for k, v in module.ec2_instances : k => v.instance_id }
}

output "public_ips" {
  description = "A map of instance names to their public IP addresses."
  value       = { for k, v in module.ec2_instances : k => v.public_ip }
}

3. 実行結果

stgフォルダでTerraformを実行します。

  • コマンド

    $ terraform init
    $ terraform plan
    $ terraform apply
    

terraform applyの最後のOutputは以下のようになります。

  • 実行結果
Outputs:

instance_ids = {
  "terraform-test-1" = "i-XXXXXXXXXXXXXXXXX"
  "terraform-test-2" = "i-XXXXXXXXXXXXXXXXX"
}
public_ips = {
  "terraform-test-1" = "XXX.XXX.XXX.XXX"
  "terraform-test-2" = "XXX.XXX.XXX.XXX"
}

さいごに

今回は、Terraformのモジュールを活用して、コードをシンプルに保ちながら複数台のEC2インスタンスを一気に作成する方法をご紹介しました。
モジュール化を取り入れることで、以下のメリットが得られます。

  • コードの再利用性と可読性の向上(本体側は引数を渡すだけでスッキリ)
  • 同じ構成のインスタンス追加が簡単(リストに名前を追加するだけ)
  • 保守性の向上(構成変更が必要な際もモジュール側を一括修正するだけでOK)
    • 変更内容によってはEC2インスタンスなどの「再作成(Destroy & Create)」が発生する場合があります
    • terraform applyの前に、破壊的変更が含まれていないか必ず確認するようにしましょう!

最初はファイルの分割や Output の渡し方に少し戸惑うかもしれませんが、一度作ってしまえば環境構築が劇的に楽になります。ぜひ試してみてください!

参考URL

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?