Terragruntのパス指定を簡単にしよう!
Terragruntのパス指定、めんどくさくないですか?
ぱっと調べて、AIに聞いたけどいいのがなさそう。
下記のような相対パス指定がめんどくさすぎる。
locals {
}
terraform {
source = "../../../../modules/schema_obj/stage"
}
dependency "s3" {
config_path = "../../../../../../../../AWS/envs/dev/s3"
}
dependency "storage_integration" {
config_path = "../../../../../account_obj/storage_integration"
}
ならパスをyamlにしてしまえばいいじゃないかということでスクリプトを作成。rootにおいて実行すると下記のようにrootからの相対パスをまとめたyamlができる。
module:
iam_role: AWS/modules/iam_role
・・・
terragrunt:
iam_role: AWS/envs/dev/iam_role
・・・
上記出力結果はrootで実行し、同じパスに保存されているはずなので下記のように呼び出せる。
便利!
呼ばれる側
locals {
root_folder = dirname(find_in_parent_folders("folders.yaml"))
relative_path_from_route = yamldecode(file(find_in_parent_folders("folders.yaml")))
}
呼ぶ側
include {
path = find_in_parent_folders("root.hcl")
}
locals {
ns_vars = yamldecode(file(find_in_parent_folders("namespace_vars.yaml")))
parent = read_terragrunt_config(find_in_parent_folders("root.hcl")).locals
}
terraform {
source = "${local.parent.root_folder}/${local.parent.relative_path_from_route.module.s3_bucket}"
}
inputs = {
bucket_name = local.ns_vars.S3.bucket_name
environment = local.ns_vars.S3.environment
s3_objects = [
for obj in local.ns_vars.S3.objects : {
bucket_name = local.ns_vars.S3.bucket_name
key = obj.key
source = lookup(obj, "source", null) != null ? "${local.parent.root_folder}/${obj.source}" : null
}
]
}
懸念点
yamlのキーを末端のフォルダ名にしてるからこれが一意じゃないとどうなるかわかんないけどね!まあどのリソースかわかんなくなるし多分一意でしょヘーキヘーキ。
yamlの更新忘れそう?それは...そうね...
実行時間が考慮されてない?...それは...そうね...
../s3
で呼べる奴が
"${local.parent.root_folder}\\${local.parent.relative_path_from_route.terragrunt.s3}"
になる。だめかもしれん。
chatgpt君と格闘して作ったスクリプト
import os
import yaml
root_path = os.path.dirname(os.path.abspath(file))
module は単純に格納
modules = {}
terragrunt は env ごとに格納
terragrunts = {"dev": {}, "prod": {}}
for dirpath, dirnames, filenames in os.walk(root_path):
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
folder_name = os.path.basename(dirpath)
rel_path = os.path.relpath(dirpath, root_path)
# パス区切りを / に統一
rel_path = rel_path.replace(os.sep, "/")
# module 側: main.tf がある場合のみ
if "main.tf" in filenames:
modules[folder_name] = rel_path
# terragrunt 側: terragrunt.hcl がある場合のみ
if "terragrunt.hcl" in filenames:
env_key = None
for key in ["dev", "env", "prod"]:
if key in rel_path.split("/"): # os.sep → "/" に変更
env_key = key
break
if env_key:
terragrunts[env_key][folder_name] = rel_path
else:
terragrunts.setdefault("others", {})[folder_name] = rel_path
value(パス)でソート
def sort_dict_by_value(d):
sorted_dict = {}
for k, v in sorted(d.items(), key=lambda x: x[1] if not isinstance(x[1], dict) else ""):
if isinstance(v, dict):
sorted_dict[k] = sort_dict_by_value(v)
else:
sorted_dict[k] = v
return sorted_dict
modules_sorted = sort_dict_by_value(modules)
terragrunts_sorted = sort_dict_by_value(terragrunts)
output = {
"module": modules_sorted,
"terragrunt": terragrunts_sorted
}
yaml_path = os.path.join(root_path, "folders.yaml")
with open(yaml_path, "w", encoding="utf-8") as f:
yaml.dump(output, f, allow_unicode=True, sort_keys=False)
print(f"YAML出力完了: {yaml_path}")
ちなみにrootのフォルダを特定するだけならこれとかあるぞ!たぶんGitリポジトリで管理するだろうからこっちのほうがいいかもな!
Discussion