Terraform 入門：Terraformでセキュリティグループをモジュール化して、EC2インスタンスを構築してみた

## はじめに
Terraformではモジュール化を活用することで、セキュリティグループ（SG）などのリソースを**別のファイル**に分離し、管理を容易にできます。

本記事では、セキュリティグループを独立したモジュールとして管理し、`main.tf` から呼び出す方法を解説します。

## ファイル構成
Terraformのモジュールを活用する場合、以下のようなファイル構成になります。

```
terraform-project/
│── main.tf                 # メインのTerraform設定（EC2を定義）
│── variables.tf            # 変数の定義
│── outputs.tf              # 出力変数の定義
│── modules/
│   ├── security_group/      # セキュリティグループのモジュール
│   │   ├── main.tf         # セキュリティグループの定義
│   │   ├── variables.tf    # 変数の定義
│   │   ├── outputs.tf      # 出力変数の定義
```

## `modules/security_group/main.tf`（セキュリティグループの定義）
セキュリティグループをモジュール化し、`modules/security_group/main.tf` に記述します。

```hcl
resource "aws_security_group" "my_sg" {
  name        = "my-security-group"
  description = "Allow SSH and HTTP traffic"
  vpc_id      = var.vpc_id

  # SSH (22) 許可
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # HTTP (80) 許可
  ingress {
    from_port   = 80
    to_port     = 80
    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 = "my-security-group"
  }
}
```

## `modules/security_group/variables.tf`（変数の定義）

モジュールが受け取る変数を定義します。

```hcl
variable "vpc_id" {
  description = "VPC ID for the security group"
  type        = string
}
```

## `modules/security_group/outputs.tf`（出力の定義）

作成したセキュリティグループのIDを外部から取得できるように出力変数を定義します。

```hcl
output "security_group_id" {
  description = "The ID of the security group"
  value       = aws_security_group.my_sg.id
}
```

## `main.tf`（EC2の定義 + モジュールの呼び出し）
メインの `main.tf` から **セキュリティグループのモジュールを呼び出す** ようにします。

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

# セキュリティグループのモジュールを呼び出し
module "security_group" {
  source = "./modules/security_group"
  vpc_id = "vpc-xxxxxxxx"  # 実際のVPC IDに置き換え
}

# EC2インスタンスの定義
resource "aws_instance" "my_ec2" {
  ami             = "ami-072298436ce5cb0c4"
  instance_type   = "t2.micro"
  key_name        = "my-key-pair"
  vpc_security_group_ids = [module.security_group.security_group_id]  # SGのIDを渡す

  tags = {
    Name = "Terraform-EC2"
  }
}
```

## `variables.tf`（変数の定義）

Terraformの変数を定義します。

```hcl
variable "vpc_id" {
  description = "VPC ID for the security group"
  type        = string
}
```

## `outputs.tf`（出力の定義）

Terraformで出力したい情報を定義します。

```hcl
output "ec2_instance_id" {
  description = "ID of the created EC2 instance"
  value       = aws_instance.my_ec2.id
}
```

## Terraformの実行
Terraformを適用するには、以下のコマンドを順番に実行します。

```sh
terraform init    # 初期化
terraform plan    # 設定の確認
terraform apply -auto-approve  # 適用
```

**実際の作成画面**

![Screenshot 2025-03-04 at 22.53.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/9f527524-d21f-4bfa-bfed-7b85c12d957d.png)

**実際のマネコン画面**

![Screenshot 2025-03-04 at 22.54.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/b7418c32-4e05-45a4-a9a8-623cd507e374.png)


## まとめ
- **セキュリティグループをモジュール化**することで、EC2とは**別のファイル**に分けて管理可能
- **`main.tf` ではモジュールを呼び出すだけ**なので、スッキリしたコードになる
- **再利用しやすくなる**（他のプロジェクトでも使い回せる）

この方法なら、今後**他のセキュリティグループやリソースを追加する際も、モジュールごとに整理できる**ので、Terraformの管理が楽になります！

## おまけ：一括で作成するシェルスクリプト

```setup_terraform.sh
#!/bin/bash

# 作成するディレクトリ
PROJECT_DIR="terraform-project"
MODULE_DIR="$PROJECT_DIR/modules/security_group"

# 必要なディレクトリを作成
mkdir -p "$MODULE_DIR"

# main.tf (EC2の定義 + セキュリティグループのモジュールの呼び出し)
cat <<EOF > "$PROJECT_DIR/main.tf"
provider "aws" {
  region = "ap-northeast-1"
}

# セキュリティグループのモジュールを呼び出し
module "security_group" {
  source = "./modules/security_group"
  vpc_id = "vpc-xxxxxxxx"  # 実際のVPC IDに置き換え
}

# EC2インスタンスの定義
resource "aws_instance" "my_ec2" {
  ami             = "ami-072298436ce5cb0c4"
  instance_type   = "t2.micro"
  key_name        = "my-key-pair"
  vpc_security_group_ids = [module.security_group.security_group_id]  # SGのIDを渡す

  tags = {
    Name = "Terraform-EC2"
  }
}
EOF

# variables.tf（変数の定義）
cat <<EOF > "$PROJECT_DIR/variables.tf"
variable "vpc_id" {
  description = "VPC ID for the security group"
  type        = string
}
EOF

# outputs.tf（出力の定義）
cat <<EOF > "$PROJECT_DIR/outputs.tf"
output "ec2_instance_id" {
  description = "ID of the created EC2 instance"
  value       = aws_instance.my_ec2.id
}
EOF

# modules/security_group/main.tf（セキュリティグループの定義）
cat <<EOF > "$MODULE_DIR/main.tf"
resource "aws_security_group" "my_sg" {
  name        = "my-security-group"
  description = "Allow SSH and HTTP traffic"
  vpc_id      = var.vpc_id

  # SSH (22) 許可
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # HTTP (80) 許可
  ingress {
    from_port   = 80
    to_port     = 80
    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 = "my-security-group"
  }
}
EOF

# modules/security_group/variables.tf（変数の定義）
cat <<EOF > "$MODULE_DIR/variables.tf"
variable "vpc_id" {
  description = "VPC ID for the security group"
  type        = string
}
EOF

# modules/security_group/outputs.tf（出力の定義）
cat <<EOF > "$MODULE_DIR/outputs.tf"
output "security_group_id" {
  description = "The ID of the security group"
  value       = aws_security_group.my_sg.id
}
EOF

echo "Terraformのファイル作成が完了しました！"
echo "次のコマンドを実行してTerraformを適用できます:"
echo "cd terraform-project"
echo "terraform init"
echo "terraform plan"
echo "terraform apply -auto-approve"
```