Terraform で2重ループをやってみた

はじめに

データ戦略室のaranです
今回で2回目の登場になります
月日は早いもので、入社して1年が過ぎました

私ごとですが
去年までは出社していたのですが、寒過ぎて朝起きれず
年末から座りっぱなしのリモートワークの日々を過ごし、節々の痛いと闘っています

不健康まっしぐらなので、最近は1時間弱の散歩を日課としています

今回のネタ

今回は、ゆるーいTipsです
最近、Terraformを使う機会が増えてきたこともあり
for文の2重ループを試してみました。

ググるとTerraformで2重ループは使えないとか
setproduct を使えば、2重ループっぽいことができるよってブログを見つけましたが
for文の2重ループはできるようです
(そんなこと知っているよ!!って内容かもしれませんが、ご了承を)

お試しの構成

私は、データ基盤及びデータパイプラインの構築を担当していて
各種バッチは、主にGCP上で動かしており
IaCツールは、Terraformを使っています

www.terraform.io

ワークフローツールにCloud Workflowsを採用しており
cloud.google.com

今回の2重ループを試した部分は、Cloud Workflowsへ引数を渡す
Cloud Schedulerのパラメータの生成部分で、利用してみました

cloud.google.com

試したいと思った用途

生成したいパラメータで制御するのは、各テーブルをTransform(データ変換)するバッチの起動有無です

BigQueryにデータロードするバッチと、ロードしたデータを変換するバッチがあり
BigQueryにデータロードするバッチができたら、テーブルを作成するファイルを作成(追加)し
データ変換するバッチができたら、フラグを有効にしてパラメータに追記することをやりたいためです

ファイル構成

今回のお試しで、必要とするファイル構成は以下になります。

main.tf
bg/<media>/native/table.json
<media>は各媒体(google, yahoo, line等)

main.tf にて、 bg/<media>/native/table.json をロードします

module "xxx_gcp_workflow" {
  source = "./modules/gcp_workflow"

  ※cloud workflow作成に必要なパラメータをモジュール引数として渡す
  .
  .
  args   = ※2重ループで各媒体 × table.jsonをロードして、パラメータを生成したい

媒体別のテーブルを定義したJSONファイル(bg/<media>/native/table.json)の内容は以下の通りです。
今回、必要とするのは2項目(テーブル名とフラグ)になりますが
BigQueryのテーブル生成に必要なパラメータを管理しています。

[
  {
    "name": "テーブル名",
    "description": "テーブル名の説明",
    "friendly_name": "テーブルのわかりやすい名前",
    "partition_field": "パーティション項目",
    .
    .
    .
    "do_transform": True(データ変換バッチの実行有無)
  },
  {
    "name": "テーブル名",
    "description": "テーブル名の説明",
    "friendly_name": "テーブルのわかりやすい名前",
    "partition_field": "パーティション項目",
    .
    .
    .
    "do_transform": false(データ変換バッチの実行有無)
  },
   .
   .
   .
]

サンプル

単純に for文を2つ記述すると構文エラーになるのですが、以下のように括弧を利用すると 今回の要件を満たす2重ループを実現できました

[
    for media in var.medias :
        ([
            for table in jsondecode(file("bg/${media}/native/tables.json")) :
                "{\"media\": \"${media}\",\"table\": \"${table["name"]}\"}"
                if table["do_transform"]
        ])
]

実際にコマンド引数を生成したソースは、以下の通りです
2重ループで生成できた配列を1つの文字列にするため、joinとflattenを組み合わせています

locals {
  medias = ["media1", "media2", "media3", "media4"]
}

args = "[${join(", ", flatten([for media in local.medias :
    ([for table in jsondecode(file("bg/${media}/native/tables.json")) :
      "{\"param1\": \"${media}\",\"param2\": \"${table["name"]}\"}"
      if table["do_transform"]
    ])
  ]))}]"

argsをデータ整形すると、以下のようになります
(実際の出力結果は1行です)

[
  {
    "param1": "media1",
    "param2": "table1"
  },
  {
    "param1": "media1",
    "param2": "table2"
  },
  {
    "param1": "media1",
    "param2": "table3"
  },
  {
    "param1": "media2",
    "param2": "table1"
  },
  {
    "param1": "media2",
    "param2": "table3"
  },
  {
    "param1": "media3",
    "param2": "table2"
  },
  {
    "param1": "media4",
    "param2": "table1"
  },
  {
    "param1": "media4",
    "param2": "table2"
  },
  {
    "param1": "media4",
    "param2": "table4"
  }
]

if table["do_transform"] によって
do_transform 項目が true のテーブルのみ、出力することができました

さいごに

ググったブログの内容を鵜呑みにせず、できる手段がないかを探してみるが大切って
改めて認識できました