【モダンTerraform】意外と便利!? Miscプロバイダーたち(Templateプロバイダー編)
今回は前回の続きとしてMiscプロバイダーの中からTemplateプロバイダーについて扱います。
Templateプロバイダーとは
その名の通りテンプレート機能を提供してくれるプロバイダーです。
以下のリソースとデータソースが含まれます。
分類 | 名称 | 概要 |
---|---|---|
データソース | template_file | 単一のファイル(など)用のテンプレート機能 |
データソース | template_cloudinit_config | cloud-initのconfigファイル用テンプレート機能 |
リソース | template_dir | ディレクトリ内のテンプレートファイルに対し一括処理を行う機能 |
template_dir
だけリソースになっている点が注意ですね。
詳しくは後述しますが、template_dir
のみファイルが作成されるという作用があるためです。
それでは各リソース/データソースについて見ていきます。
template_file
データソース
単一のファイル(など)用のテンプレート機能を提供してくれます。
基本的な使い方
まず次のようなテンプレートを用意しておきます。
#!/bin/bash echo "CONSUL_ADDRESS = ${consul_address}" > /tmp/iplist
このテンプレートを描画するには以下のようにデータソースを定義します。
data "template_file" "init" { # テンプレートとして<モジュールのディレクトリ>/init.tpl を指定 template = "${file("${path.module}/init.tpl")}" # テンプレートに渡す変数の定義 vars { consul_address = "${sakuracloud_server.consul.ipaddress}" } }
あとはこのリソースのrendered
という属性を参照することで描画済み文字列が取得可能です。
# 例: スタートアップスクリプトとして利用する resource sakuracloud_note "init-script" { name = "init" content = "${data.template_file.init.rendered}" # rendered属性から描画済み文字列を参照 } # 例: プロビジョナーに渡す resource sakuracloud_server "server" { name = "server" # 描画した内容をサーバ上の/tmp/init.shに書き込み provisioner "file" { destination = "/tmp/init.sh" content = "${data.template_file.init.rendered}" } }
変数の指定
テンプレートに渡す変数は先程の例の通りvars
ブロックで指定します。
data "template_file" "init" { ... # テンプレートに渡す変数の定義 vars { consul_address = "${sakuracloud_server.consul.ipaddress}" } }
この例ではconsul_address
というキーを用いていますが、任意のキーを複数指定可能です。
vars { ipaddress = "192.2.0.1" nw_mask_len = 24 }
注意点としては値の指定にはリスト型/マップ型を指定できないということです。
# !バリデーションエラーとなります! locals { # リスト型変数 iplist = ["192.2.0.1", "192.2.0.2"] # マップ型変数 rules = { allow = "80,443" deny = "all" } } data "template_file" "init" { template = "foobar" # テンプレートに渡す変数の定義 vars { target_hosts = "${local.iplist}" # リスト型は指定不可 target_rules = "${local.rules}" # マップ型は指定不可 } }
この場合は何らかの形で文字列に変換して渡してあげましょう。
# 文字列にする例 # テンプレートに渡す変数の定義 vars { target_hosts = "${join(",", local.iplist)}" # joinで文字列化 target_rules = "${jsonencode(local.rules)}" # jsonencodeでJSON化 }
テンプレートをインラインで指定
テンプレートはファイルとして用意しておかなくてもtfファイルの中にインライン指定可能です。
data "template_file" "init" { # テンプレートをインライン指定 template = "$${consul_address}:1234" vars { consul_address = "${sakuracloud_server.consul.ipaddress}" } }
この場合、$
をエスケープして$$
として書く必要があることに注意してください。
エスケープを忘れて$
ひとつだけにした場合はテンプレート描画前にterraformが参照の解決をしようとしてしまいます。
使い所
基本的には以下のような場合に利用することになると思います。
- tfファイル中にインラインで書くには長すぎる内容を扱う場合
- 複数のリソースに展開する場合
ちょっと長い程度の文字列なら以下のようにヒアドキュメントで書くことも可能ですので、状況に応じて使い分けましょう。
resource sakuracloud_server "server" { name = "server" # 描画した内容をサーバ上の/tmp/init.shに書き込み provisioner "file" { destination = "/tmp/init.sh" # ちょっと長い程度ならヒアドキュメントで指定もあり content = <<EOL #!/bin/bash echo "CONSUL_ADDRESS = ${sakuracloud_server.consul.ipaddress}" > /tmp/iplist EOL } }
template_cloudinit_config
データソース
template_file
データソースの機能に加え、cloud-initで利用するconfigに特化した機能が追加されたデータソースです。
write-mime-multipartコマンドなどを利用せずともマルチパートなconfigを組み立てることが可能です。
以下のように利用します。
# Render a part using a `template_file` data "template_file" "script" { template = "${file("${path.module}/init.tpl")}" vars { consul_address = "${aws_instance.consul.private_ip}" } } # Render a multi-part cloudinit config making use of the part # above, and other source files data "template_cloudinit_config" "config" { gzip = true base64_encode = true # Setup hello world script to be called by the cloud-config part { filename = "init.cfg" content_type = "text/part-handler" content = "${data.template_file.script.rendered}" } part { content_type = "text/x-shellscript" content = "baz" } part { content_type = "text/x-shellscript" content = "ffbaz" } } # Start an AWS instance with the cloudinit config as user data resource "aws_instance" "web" { ami = "ami-d05e75b8" instance_type = "t2.micro" user_data = "${data.template_cloudinit_config.config.rendered}" }
これを描画すると以下のようになります(gzip/base64エンコードはoffの状態)
Content-Type: multipart/mixed; boundary="MIMEBOUNDARY" MIME-Version: 1.0 --MIMEBOUNDARY Content-Disposition: attachment; filename="init.cfg" Content-Transfer-Encoding: 7bit Content-Type: text/part-handler Mime-Version: 1.0 #!/bin/bash echo "CONSUL_ADDRESS = 192.2.0.1" > /tmp/iplist --MIMEBOUNDARY Content-Transfer-Encoding: 7bit Content-Type: text/x-shellscript Mime-Version: 1.0 baz --MIMEBOUNDARY Content-Transfer-Encoding: 7bit Content-Type: text/x-shellscript Mime-Version: 1.0 ffbaz --MIMEBOUNDARY--
template_file
と比較すると以下の属性が追加で指定可能となっています。
gzip
: 出力をgzip圧縮するか(デフォルトtrue
)base64_encode
: 出力をBASE64エンコードするか(デフォルトtrue
)part
: 各パートの内容(指定可能な属性は以下参照)file_name
: ファイル名content_type
: コンテントタイプcontent
: ボディmerge_type
: マージタイプ(詳細はドキュメントを参照)
user_dataの組み立てに便利ですね。
template_dir
リソース
template_file
データソースのディレクトリ版です。
指定のディレクトリ配下のファイルをテンプレートとみなし、一括して描画してくれます。
実際にファイル(ディレクトリも)が作成されることに注意してください。
使い方は以下の通りです。
resource "template_dir" "config" { source_dir = "${path.cwd}/source" destination_dir = "${path.cwd}/dest" vars { consul_addr = "${var.consul_addr}" } }
この例だとterraform apply
を実行するとsource
ディレクトリ配下の各ファイルをテンプレートとして読み込み、描画したものをdest
ディレクトリ配下に同名のファイルとして作成してくれます。
ちなみにtemplate_dir
リソースはIDにdestination_dir
のコンテンツから作成されたハッシュ値が用いられており、ファイル/ディレクトリの変更やリネーム、削除が行われた場合は次回のapply時に再作成されるようになっています。
template_dir
の注意点
前述の通りこのリソースはIDが宛先ディレクトリのコンテンツから作成されたハッシュとなっているため、ステートを複数人で共有していてもterraformコマンドを実行するマシンに宛先ディレクトリ(と描画後のコンテンツ)が存在しない場合はapply時に再作成が行われることとなります。
例えばステートレスなCIでterraform plan
に差分があったら通知するような仕組みを取っている場合は問題となるケースもありますので注意が必要です。
終わりに
今回はTemplateプロバイダーを紹介しました。
ちょっとしたプロビジョニングをTerraformで行う際の強力な武器となりますので是非使いこなしましょう。
次回はMiscプロバイダーの中からRandomかTLSを扱う予定です。
以上です。Enjoy!!