Terraformにおいて例えば、A,B,Cという種類のサーバの属性がありそれぞれモジュールで定義されていて、それらに属するサーバが複数あると行った場合、何も考えずに書くと以下のようなコードになると思う。
locals { servers_a = { node-a-1 = "192.168.0.1", } servers_b = { node-b-1 = "192.168.1.1", node-b-2 = "192.168.1.2", } servers_c = { node-c-1 = "192.168.2.1", node-c-2 = "192.168.2.2", node-c-3 = "192.168.2.3", } } module "server_a" { source = "module/server_a" for_each = local.servers_a name = each.key ipaddress = each.value } module "server_b" { source = "module/server_b" for_each = local.servers_b name = each.key ipaddress = each.value } module "server_c" { source = "module/server_c" for_each = local.servers_c name = each.key ipaddress = each.value }
ここでWorkspaceを考慮する必要が出てきた場合、読み替えが必要になり以下のように分岐を入れることになる。 以下の例ではproductionとそれ以外で分岐を入れている。
locals { servers_a = terraform.workspace == 'production' ? { node-a-1 = "192.168.0.1", } : { test-node-a-1 = "192.168.10.1", } servers_b = terraform.workspace == 'production' ? { node-b-1 = "192.168.1.1", node-b-2 = "192.168.1.2", } : { test-node-b-1 = "192.168.11.1", } servers_c = terraform.workspace == 'production' ? { node-c-1 = "192.168.2.1", node-c-2 = "192.168.2.2", node-c-3 = "192.168.2.3", } : {} } # 以下moduleの定義は同じなので省略 #
分岐が入りかなり見通しが悪いコードになってしまった…。 ここにさらにWorkspaceが増えたり、サーバの種類やノードが増えてしまうとズラズラとファイルが長くなっていきつらい感じになってくる…。 module単位で別のファイルに切り出すという方法もあるが、localsの定義で分岐をしているのでここをDRYに書きたくなってくる。
Terraformではyamldecode/jsondecodeという関数が用意されていて、これを使うとlocalsをスッキリ書くことができる。
以下は environtment/<worspace>/<サーバの種類>.yaml
なファイルを読み込むように変えたコードである。
locals { servers_a = yamldecode(file(format("./environment/%s/servers_a.yaml", terraform.workspace))) servers_b = yamldecode(file(format("./environment/%s/servers_b.yaml", terraform.workspace))) servers_c = yamldecode(file(format("./environment/%s/servers_c.yaml", terraform.workspace))) } # 以下moduleの定義は同じなので省略 #
YAMLファイルは以下のような感じ
# ./environment/production/servers_a.yaml node-a-1: 192.168.0.1 # ./environment/production/servers_b.yaml node-b-1: 192.168.1.1 node-b-2: 192.168.1.2 # ./environment/production/servers_c.yaml node-c-1: 192.168.2.1 node-c-2: 192.168.2.2 node-c-3: 192.168.2.3 # ./environment/default/servers_a.yaml test-node-a-1: 192.168.10.1 # ./environment/default/servers_b.yaml test-node-b-1: 192.168.11.1 # ./environment/default/servers_c.yaml # yamldecodeやfor_eachがエラーになるので空のハッシュを定義しておく {}
YAMLファイルにサーバの情報を外だしすることでコードがスッキリしたと思う。 しかしながら、モジュールごとに変数を定義しないといけなかったり、./environment/default/servers_c.yamlのように空のハッシュを定義するファイルを設置しなくてはならなくてめんどくさい…。 もう少しいい感じにできないかと悩んでいたところ、以下のサイトにたどりつきfileset関数とfor文を使う方法を知った*1。
これらを使うと以下のように書ける。
locals { servers = { for f in fileset("", format("./environment/%s/*.yaml", terraform.workspace)): replace(basename(f), "/\\.yaml/", "") => yamldecode(file(f)) } } module "server_a" { source = "module/server_a" for_each = try(local.servers["server_a"], {}) name = each.key ipaddress = each.value } module "server_b" { source = "module/server_b" for_each = try(local.servers["server_b"], {}) name = each.key ipaddress = each.value } module "server_c" { source = "module/server_c" for_each = try(local.servers["server_c"], {}) name = each.key ipaddress = each.value }
YAMLファイルは前項と同じなので省略。 ./environment/default/servers_c.yaml を削除してもエラーにはならないはず。 最初のコードに比べるとかなりスッキリしたし、コードとデータを分離できて管理しやすくなったと思う。 最高〜
*1:この記事でやりかったこととは問題点が違うがfileset関数とfor文を使うというアイディアが参考になった