モジュール
この講座では、Terraformのモジュール機能について学びます。
- モジュールの概念とメリット(再利用性・可読性・保守性)
- モジュールのディレクトリ構成
- 入力変数と出力値の定義
- モジュールの呼び出し方法
1. 概要
1.1 モジュールとは
プログラミングにおいて、よく使う処理を「関数」として切り出すのと同じように、Terraformではよく使うリソースのセットを「モジュール」という単位で切り出して管理します。
例えば、「Webサーバと、それに紐づくセキュリティグループ」のセットをモジュール化しておけば、あとはそのモジュールを呼び出すだけで、同じ構成のサーバセットを簡単に何個でも作ることができます。
1.2 メリット
モジュールのメリットを説明します。
まず一つ目は「再利用性」です。一度書いたコードを、複数のプロジェクトや環境(Dev/Prodなど)で使い回すことができます。特に近年は、テストや本番で環境を分離することが一般的ですが、その場合の管理にモジュールが有効活用できます。
二つ目は「可読性」です。main.tf が巨大にならず、スッキリして読みやすくなります。また、特定の機能ごとにモジュールを分けることで、機能に絞ってコードを確認できるようになります。
三つ目は「標準化」です。「会社推奨のセキュリティ設定」などをモジュールに埋め込んでおけば、誰が作っても安全な構成にすることができます。
1.3 ファイル構成のイメージ
モジュールを使う場合、一般的には下記の3つのファイルを作成します。それを、ルートディレクトリにある main.tf から呼び出します。
ルートディレクトリ
├── main.tf # (呼び出し元) ここからモジュールを使う
└── modules/
└── web_server/ # (モジュール本体) 部品としてのコード
├── main.tf
├── variables.tf
└── outputs.tf
今回は、modules というフォルダの中に web_server というフォルダを作り、そこにモジュールを定義します。 一般的にモジュールを作る時は、main.tf、variables.tf、outputs.tf の3つセットで管理します。
一般的にモジュールを作る時は、main.tf、variables.tf、outputs.tf を作成して管理します。
main.tfには、モジュールの中で作成するリソース(EC2やVPCなど)の実体を定義します。variables.tfには、モジュールに引き渡す「引数(variables)」を定義します。これにより、呼び出し元から設定値を変更できるようになります。outputs.tfは、モジュールで作成されたリソースの情報(IDやIPアドレスなど)を外部に「出力(return)」します。他のモジュールでその値を利用したい時に必要になります。
1.4 呼び出し方
モジュールを利用する(呼び出す)際は、module ブロックを使用します。 必須なのは source(モジュールの場所)だけで、あとはそのモジュールが必要とする設定値(変数)を渡します。これは variables で指定した内容が該当します。
module "任意の名前" {
source = "./modules/モジュール名"
# モジュール内の変数に値を渡す
変数名 = "値"
}
2. 実際に使ってみる
実際にディレクトリ(フォルダ)を作成して、モジュールが動く仕組みを体験してみましょう。今回は、web_server というモジュールを作って、それを利用する例を説明します。
2.1 ディレクトリの作成
任意の場所に作業ディレクトリを作成し、その中にmodulesフォルダを作成します。Visual Studio Codeのエクスプローラーで作業ディレクトリを右クリックし、「新しいフォルダー」を選択してmodulesという名前で作成してください。
(作業ディレクトリ)
└── modules ← このフォルダを作成
さらに、作成したmodulesフォルダの中にweb_serverフォルダを作成します。modulesフォルダを右クリックし、「新しいフォルダー」を選択してweb_serverという名前で作成してください。
(作業ディレクトリ)
└── modules
└── web_server ← このフォルダを作成
2.2 モジュールファイルの作成
main.tfの作成
作成したmodules/web_server/の中に、main.tfというファイルを作成します。Visual Studio Codeのエクスプローラーでweb_serverフォルダを右クリックし、「新しいファイル」を選択してmain.tfという名前で作成してください。
(作業ディレクトリ)
└── modules
└── web_server
└── main.tf ← このファイルを作成
作成したファイルに以下の内容を記述して保存します。
resource "aws_s3_bucket" "this" {
bucket = var.name
tags = {
Module = "True"
}
}
variables.tfの作成
同じmodules/web_server/フォルダにvariables.tfファイルを作成します。web_serverフォルダを右クリックし、「新しいファイル」を選択してvariables.tfという名前で作成してください。
(作業ディレクトリ)
└── modules
└── web_server
├── main.tf
└── variables.tf ← このファイルを作成
作成したファイルに以下の内容を記述して保存します。外部から渡す引数としてnameを定義します。
variable "name" {
description = "バケット名を受け取る変数"
type = string
}
2.3 ルートファイルの作成(呼び出し)
作業ディレクトリ直下に main.tf を作成します。Terraformはterraform initを実行したディレクトリ直下の.tfファイルを読み込むため、このファイルが必要です。modules/配下のファイルだけでは、Terraformはリソース定義を認識できません。
(作業ディレクトリ)
├── main.tf ← このファイルを作成
└── modules
└── web_server
├── main.tf
└── variables.tf
main.tfに以下の内容を記述します。
provider "aws" {
region = "ap-northeast-1"
}
# モジュールを使ってバケットを作成
module "my_web_server" {
# 作成したディレクトリを指定
source = "./modules/web_server"
# モジュール内の variable "name" に値を渡す
name = "tf-module-test-bucket-12345"
}
2.4 初期化
ここがポイントです。 新しいモジュールを追加した場合、Terraformに「モジュールを読み込む」作業をさせる必要があります。 以下のコマンドを実行してください。
terraform init
成功すると Initializing modules... と表示され、モジュールの準備が整います。
| ⚠️ 「Module not installed」エラーが出る場合 |
|---|
モジュールを追加した後に terraform init を実行し忘れると、「Module not installed」や「Error: Module not found」というエラーが発生します。新しいモジュールを追加したら、必ず terraform init を実行してください。 |
2.5 作成の実行
terraform apply
実行プランを確認すると、module.my_web_server.aws_s3_bucket.this という名前でリソースが作成されることがわかります。yes で作成してください。
2.6 リソースの削除
確認が終わったら、作成したリソースを削除しておきましょう。
terraform destroy
3. 環境を分けてみる
ここまでのハンズオンで、モジュールを使ってS3バケットを1つ作成しました。しかし、モジュールの本当の便利さは同じモジュールを使い回せるところにあります。
実際の開発では、「テスト環境」と「本番環境」のように、同じ構成のインフラを複数用意することがよくあります。例えば、新しい機能を開発するときは、まずテスト環境で動作確認をしてから本番環境にリリースします。このとき、テスト環境と本番環境のインフラ構成は基本的に同じですが、リソース名やサイズなどの設定値だけが異なります。
モジュールを使わない場合、同じコードをコピーして名前だけ書き換えることになり、修正が必要になったときに両方を変更しなければなりません。モジュールを使えば、コードは1つだけ管理し、呼び出す時に渡す値を変えるだけで複数の環境を作れます。
3.1 VPCを環境ごとに分ける例
例えば、VPCモジュールにnameとvpc_cidrを変数として定義しておけば、テスト環境と本番環境でVPC名とIPアドレス範囲を分けて作成できます。
# テスト環境
module "test_vpc" {
source = "./modules/vpc"
name = "test-vpc"
vpc_cidr = "10.0.0.0/16"
}
# 本番環境
module "prod_vpc" {
source = "./modules/vpc"
name = "prod-vpc"
vpc_cidr = "10.1.0.0/16"
}
テスト環境と本番環境で同じ構成のVPCが作られますが、名前はtest-vpcとprod-vpc、CIDRブロックは10.0.0.0/16と10.1.0.0/16のように異なる値が設定されます。VPCモジュールの中身(サブネットの分割方法やルートテーブルの設定など)は共通のコードが使われるため、構成のばらつきが起きません。
3.2 環境をディレクトリで分けて管理する
ここまでの例では1つのmain.tfの中でテスト用と本番用のモジュールを呼び出していましたが、実務ではさらに進んで、環境ごとにディレクトリを分ける構成が一般的です。
プロジェクト/
├── modules/
│ └── vpc/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── environments/
├── test/ ← テスト環境用
│ └── main.tf
└── prod/ ← 本番環境用
└── main.tf
environments/test/main.tfとenvironments/prod/main.tfはどちらも同じmodules/vpcを呼び出しますが、渡す値が異なります。テスト環境を操作したいときはenvironments/test/ディレクトリに移動してterraform applyを実行し、本番環境を操作したいときはenvironments/prod/に移動して実行します。
📝 なぜ modules/ 自体をルートモジュールにしないのか |
|---|
モジュールは「他から呼び出されて使われること」を前提に設計されたコードです。source 引数で指定されて初めて機能するため、modules/vpc/ に直接移動して terraform apply しても正しく動きません。ルートモジュール(terraform apply を実行するディレクトリ)は、必ずモジュールを呼び出す側である必要があります。そのため、本講座では environments/test/ や environments/prod/ をルートモジュールとし、そこから modules/vpc/ を呼び出す構成を採用しています。 |
さらに、テスト環境と本番環境でAWSアカウント自体を分けることも実務ではよく行われます。テスト環境の操作ミスが本番環境に影響しないよう、AWSアカウントを完全に分離するためです。この場合、environments/test/で作業するときはテスト用のAWSアカウントの認証情報を、environments/prod/で作業するときは本番用のAWSアカウントの認証情報を使うことで、同じモジュールを別々のアカウントにデプロイできます。
| 💡 ポイント |
|---|
| 環境のディレクトリ分割やアカウント分離は、ここでは無理に試す必要はありません。「モジュールを使うと、こういう構成ができるんだな」という参考程度に把握しておいてください。後のハンズオン「TerraformでWebアプリをデプロイしよう」で、この構成を実際に体験します。 |
3.3 リソースの削除
確認が終わったら、作成したリソースを削除しておきましょう。
terraform destroy
4. まとめ
この講座では、Terraformのモジュールについて学びました。
- モジュールは、再利用可能なTerraformコードのパッケージ
sourceでモジュールの場所を指定する(ローカルパス、レジストリなど)- モジュール内のvariableに値を渡すことでカスタマイズできる
- 新しいモジュールを追加した場合は
terraform initで初期化が必要 - 同じモジュールを複数回呼び出すことで、テスト環境・本番環境のようにリソース名だけが異なる構成を簡単に作れる
- モジュールを使うことでコードの再利用性と保守性が向上する