一覧CL-14-2 · Infrastructure as Code

Terraformモジュールとリソースアドレス

モジュールの共通定義、呼出しごとの入力、stateのアドレス、実ファイルの対応を説明します。

説明

Terraformのモジュールは、入力変数から資源を宣言し、出力値を返す単位です。同じモジュールを複数回使うときは、それぞれの資源アドレスと実体の名前を分ける必要があります。for_eachのキーは管理対象の識別にも使われます。

frontとadminの2サービスに同じ子モジュールを使い、名前とポートが異なる設定ファイルを作ります。一方だけの入力を変更した後、adminの定義を外して削除対象を確認します。

モジュールの共通定義、呼出しごとの入力、stateのアドレス、実ファイルの対応を説明します。

1共通定義と呼出し側を読む

子モジュールはnameとportを受け取り、名前ごとにファイルを作ります。呼出し側のservicesマップはfront=8080、admin=9090です。

cp modules-main.tf.sample main.tfモジュールを使う定義へ切り替える cat modules/service/main.tf子モジュールの入力と出力を読む cat main.tf呼出し側のマップを読む

同じ定義を2回コピーする代わりに、for_eachでキーと値を渡しています。出力pathは呼出し側から取得できます。

2モジュールを初期化して計画する

新しいモジュール参照をinitで読み込み、計画を保存します。

terraform initローカルの子モジュールを読み込む -input=false -no-color > init.log terraform plan2回の呼出しを計画する -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan展開後の資源アドレスを読む

module.service["admin"]とmodule.service["front"]の別々のアドレスでcreateされます。子モジュール内の資源名が同じでも、呼出し元のアドレスが異なります。

3実ファイルへ反映する

計画を適用し、出力値と実ファイルを確認します。

terraform apply2サービス分を作る -input=false -no-color plan.bin > apply.log terraform output -json pathsモジュール出力を取得する cat front.conf admin.conf入力に対応する実ファイルを読む

出力はそれぞれのファイルを指し、内容はname=front/port=8080とname=admin/port=9090です。

4frontだけを変更する

servicesのfrontを8081へ変え、adminは9090のままにします。

python3 -cfrontだけの入力値を変える 'import json; json.dump({"services":{"front":8081,"admin":9090}},open("terraform.tfvars.json","w"))' terraform plan呼出しごとの差分を保存する -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan変更対象のアドレスを読む

adminはno-op、frontだけが置換対象です。共通モジュールを使っていても、入力が変わった呼出しを識別できます。

5一方だけの変更を確認する

保存した計画を適用して両ファイルを読みます。

terraform applyfrontの変更を適用する -input=false -no-color plan.bin > apply.log cat front.conf admin.conf両方の実ファイルを比較する

frontは8081、adminは9090です。変更する必要のない側の値は保持されています。

6マップからadminを外す

servicesにfrontだけを残します。キーの削除は、そのキーで管理する資源の削除計画になります。

python3 -cadminのキーを外す 'import json; json.dump({"services":{"front":8081}},open("terraform.tfvars.json","w"))' terraform planキー削除の影響を計画する -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan削除する資源を確認する

adminはdelete、frontはno-opです。表示名を変えるつもりでキーを消すと、資源の削除になり得ます。

7削除後の管理対象と実体

計画どおりadminを削除します。

terraform apply確認した削除計画を適用する -input=false -no-color plan.bin > apply.log terraform state list残った管理アドレスを読む python3 -c実ファイルの有無を確認する 'from pathlib import Path; print("front="+str(Path("front.conf").exists())+" admin="+str(Path("admin.conf").exists()))'

stateにはfrontだけが残り、admin.confもなくなります。モジュール定義から実資源まで変更範囲が一致しました。

まとめ

モジュールの入力ごとに異なる資源を管理できます。

for_eachのキーは資源アドレスの一部です。

キー削除は単なる表示変更ではなく、削除計画になることがあります。

操作履歴と解説を振り返れます。

Terraform 1.17.0 / hashicorp/local 2.5.3 / local backend

1
cp modules-main.tf.sample main.tf cat modules/service/main.tf cat main.tf
terraform {
  required_providers {
    local = { source = "hashicorp/local", version = "2.5.3" }
  }
}
variable "name" { type = string }
variable "port" { type = number }
resource "local_file" "config" {
  filename = "${path.root}/${var.name}.conf"
  content = "name=${var.name}\nport=${var.port}\n"
  file_permission = "0600"
}
output "path" { value = local_file.config.filename }
terraform {
  required_providers {
    local = { source = "hashicorp/local", version = "2.5.3" }
  }
}
variable "services" {
  type = map(number)
  default = { front = 8080, admin = 9090 }
}
module "service" {
  for_each = var.services
  source = "./modules/service"
  name = each.key
  port = each.value
}
output "paths" { value = { for key, item in module.service : key => item.path } }
2
terraform init -input=false -no-color > init.log terraform plan -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan
module.service["admin"].local_file.config create
module.service["front"].local_file.config create
3
terraform apply -input=false -no-color plan.bin > apply.log terraform output -json paths cat front.conf admin.conf
{"admin":"./admin.conf","front":"./front.conf"}
name=front
port=8080
name=admin
port=9090
4
python3 -c 'import json; json.dump({"services":{"front":8081,"admin":9090}},open("terraform.tfvars.json","w"))' terraform plan -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan
module.service["admin"].local_file.config no-op
module.service["front"].local_file.config delete,create
5
terraform apply -input=false -no-color plan.bin > apply.log cat front.conf admin.conf
name=front
port=8081
name=admin
port=9090
6
python3 -c 'import json; json.dump({"services":{"front":8081}},open("terraform.tfvars.json","w"))' terraform plan -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan
module.service["admin"].local_file.config delete
module.service["front"].local_file.config no-op
7
terraform apply -input=false -no-color plan.bin > apply.log terraform state list python3 -c 'from pathlib import Path; print("front="+str(Path("front.conf").exists())+" admin="+str(Path("admin.conf").exists()))'
module.service["front"].local_file.config
front=True admin=False

演習完了