[登壇レポート]「やさしいTerraform Module入門」というタイトルで登壇しました

「JAWS-UG東京 ランチタイムLT会 #7」の登壇資料と当日喋れなかったことを追加した記事です。
2024.01.28

はじめに

先日、「JAWS-UG東京 ランチタイムLT会 #7」にて登壇させて頂きました。
初LTで緊張しましたが、とてもいい経験になりました。

今回の資料は図がメインだったので、このブログで文字情報を追加したいと思います。
また、時間の都合上スライドに差し込めなかったページを追加しています。

資料

登壇の資料はこちらです。

この資料の対象者

この資料はTerraform moduleの初心者向けの資料です。
今回のLTは私がTerraformを触り始めた頃に躓いたことを共有したい!という思いから登壇させて頂きました。
Terraformを始めたけどmoduleがよく分からない!という方はぜひ参考にして頂けると嬉しいです。

moduleのベストプラクティスや、設計思想的なことは話していません。

初心者の悩み

Terraformでmodule構成を始めた時、何度terraform planを実行してもエラーが出る日々が続きました。
とくに分からなかったのは

  • maint.tfの書き方
  • 変数の定義方法
  • outputs.tfで出力した値の利用方法
  • moduleディレクトリの構成

この辺りでしょうか。
本記事では上記のような悩みを、図を交えて説明しています。

まずは簡単な構成から

難しい構成を理解するには、まずは簡単な構成から理解することが重要だと思います。
ということで最初は一番簡単なmain.tfで管理する構成です。

これは簡単ですね。
変数も、リソースもmain.tfで管理します。
ちなみに、ここで書いているrootはプロジェクトのルートディレクトリという意味です。

次にmain.tfから変数のみを別のファイルで管理する構成です。

変数はvariables.tfで定義した値をmain.tfで「var.変数名」と書くことで呼び出すことができます。

次に、main.tfを複数のファイルに分けることも可能です。
リソースが増えてくると、リソース毎にファイルを分けることで管理しやすくなったり、コードの可読性が上がります。

ファイルが分かれる場合、ファイル間で値の参照が発生します。
例えば、EC2を作成する際にインスタンスを配置するサブネットのidが必要になります。
moduleを使わない構成では、ファイルがすべて同じ階層にあるので簡単に参照できます。

ここまでがmoduleを使わない構成です。

module構成

では、ここからmoduleを使った構成を見ていきましょう。

全体像

まずは今回使用する全体像です。

moduleを使う構成としてはかなりシンプルな構成です。
しかし、これだけパッと出されてもよく分かりませんよね。
ということで大きく2つの構成要素に分けてみましょう。

プロジェクトのルートディレクトリとmoduleのディレクトリに分けられます。
まずはルートディレクトリを見てみましょう。

今回の構成ではルートディレクトリにmain.tfとvariables.tfがあります。
他にもprovider.tfなどをルートディレクトリに置くこともありますが、今回は必要最小限ということで除いています。
まずは必要なことをmain.tfに記載して、慣れてきたらファイルを分けましょう。

それぞれの役割は

  • main.tf → moduleの管理
  • variables.tf → 変数の管理

です。
次にmoduleディレクトリを見てみましょう。

moduleディレクトリはmoduleという単位でリソースを分割します。
例えばnetworkモジュールでネットワーク関連のリソースを、
ec2モジュールでec2インスタンス関連のリソースを管理するといった具合です。

networkモジュールを使って、さらにmoduleの中身がどうなっているのか見てみましょう。

ファイル構成はmodule構成を使わない時の構成と似ています。   主な構成要素は

  • network.tf → リソースを定義
  • variables.tf → 変数を定義
  • outputs.tf → リソースの情報を出力

outputs.tfはnetwork.tfで定義したリソースを別のmoduleに渡す役割をしています。   また後ほど出てくるので、今はなんとなくの理解で大丈夫です。

ここが難しい

ここまででmoduleの構成がなんとなく見えてきました。 次に、module構成を作る際に個人的にここ分かりにくいな〜という部分を紹介します。

main.tfってどう書けばいいのか分かりにくいですよね
variables.tfってなんで複数あるんでしょう
outputs.tfで出力した値はどうやって使うんでしょう

分からないことがいっぱいですね。
この疑問は変数の定義、参照方法が分かれば解決します。
ということで、まずはmain.tfから見ていきましょう。

main.tfって何を書くの

main.tfではmoduleを管理しています。
全体像はこんな感じです。

スライドではmodule毎に色分けをしています。

構成としては
source = "moduleのパス"
という形でmoduleのディレクトリがあるパスを指定します。

その下に、sourceで指定したmoduleで使用したい変数を記載します。
ここで記載する変数はvariables.tfで定義した変数です。

main.tfの構成が見えてきました。
では、実際に変数を呼び出す一連の流れを見ていきましょう。

moduleで変数を呼び出してみよう

今回はルートディレクトリのvariables.tfで定義したvpc_cidrをnetwork.tfで使用するまでの流れです。

流れは

  • ルートディレクトリのvariables.tfで変数を定義する
  • main.tfで変数をvpc_cidrに代入する
  • moduleのvariables.tfで変数を受け取る

です。

最後にnetwork.tfで変数を使用するのは今まで通り、変数を呼び出すだけで使用できます。

ルートのvariables.tfで変数の中身を定義しているので、module側のvariables.tfでは変数の中身を定義する必要はありません。

もちろん、module側のvariables.tfで変数の中身を定義することも可能です。
その場合はルート側で変数を定義する必要はありません。

outputs.tfってどうやって使うの

各moduleの中にoutputs.tfファイルがありますが、このファイルで出力した値を他のmoduleで使用する流れを見てみましょう。

moduleを使わない構成では他のリソースの値が簡単に参照できました。
これは全てのファイルが同一階層だったからです。
しかし、module構成ではファイルが配置されているディレクトリが違います。
そこで、値の受け渡しには一工夫必要になります。

一度main.tfに戻ります。

ec2のmodule部分に
public = module.network.public_id
という一行があります。
outputs.tfで出力した値はこのようにmodule.モジュール名.出力時の名前とすることで
main.tfを経由して他のmoduleで使用できます。

では実際にoutputs.tfで出力した値を別のmoduleで使用する流れを見てみましょう。

module間で値を渡そう

EC2を作成する際、インスタンスを配置するサブネットのidが必要です。
そこで、今回はnetwork.tfで定義したpublic_idをec2.tfで使用するまでの流れを見てみましょう。

まずはnetworkモジュール側で値を出力します。

  • network.tfでパブリックサブネットを定義
  • outputs.tfでパブリックサブネットのidを出力

次にmoduleで使えるよう変数を受け渡します。

  • outputs.tfで出力した値をmain.tfで変数に代入
  • EC2モジュールのvariables.tfで変数を受け取る

最後にec2.tfで変数を使用するのは変数を参照するだけです。

ファイルを行ったり来たりするのでややこしいですね。

terraform.tfvars

時間の都合上LTでは喋れませんでしたが、terraform.tfvarsも個人的には最初困惑しました。
terraform.tfvarsはクレデンシャルなど公開したくない変数を扱うことができるファイルです。
(環境ごとに変数を分ける際にも活躍しますが、今回は環境分離については触れていないので割愛します)

つまりvariables.tfには書きたくない変数を扱うための変数定義ファイルです。
公開してもよいvariables.tfと、
公開したくないterraform.tfvarsで変数定義を分けることで
GitHubなどにファイルを上げる際、誤って機密情報を上げてしまうことを防ぐことができます。

それではterraform.tfvarsで定義した変数を使う流れを見ていきましょう。
まずはterraform.tfvarsの場所です。

今回の環境ではterraform.tfvarsはルートディレクトリに配置します。
terraform.tfvarsで定義した変数をvariables.tfで受け取ります。

variables.tfで変数を受け取った後の流れはmoduleで変数を呼び出してみよう と同じ要領です。
分かってしまえば簡単ですが、同じ階層に同じような機能のファイルがあるのは混乱しますね。

まとめ

今回はterraformのmodule構成を考える上で、なんかここよく分からないよな〜という部分を図に起こしてみました。
自分の中でもなんとなく理解してるという状態で使っていましたが、改めて図に起こしてみると依存関係が理解できてスッキリしました。
この記事が誰かの入門の手助けになれば幸いです。