セキュリティルールを三か所にコピペして、prod だけ漏れた
目標
すでに運用中のコピペの環境2つを、movedで置き換えなしにモジュール呼び出しに変え、モジュールをgitタグで固定して、devだけが新しいバージョンを使うようにしたあと、変数の値が入ってくる経路の優先順位と、モジュール境界での入力の検証を確認します。
なぜ重要なのか
コピペした環境は、1か所を直すたびに、残りを直し忘れる機会を生みます。だからといって、すでに適用されたコードをモジュールに移しながら、リソースのアドレスが変わると、ツールはそれを削除して作り直そうとするので、リファクタリングは、状態に移動を知らせる作業と一緒に行う必要があります。モジュールにバージョンがないと、モジュールを直した瞬間に、すべての環境が一緒に変わって、「無関係な環境が壊れる」が起きます。値が複数の経路で入ってくる規則を知らないと、実験して残したファイル1つが、本番の値を静かに変えます。
ステップ
/root/iac-mod/dev/main.tfと/root/iac-mod/prod/main.tfに、同じ形のリソースを2つ置いてください。local_file.configは、out/app.confにname=api、env=<환경>、replicas=<수>の3行(dev 1、prod 3)、local_file.bannerは、out/banner.txtにapi (<환경>)を1行です(プレースホルダーは、順に環境と件数です)。2つのディレクトリとも、init・applyしてください。/root/iac-mod/modules/web/main.tfに、入力env(文字列)・replicas(数)、同じ2つのリソース、出力config_pathを置いたモジュールを作成してください。devのmain.tfは、replicas変数(デフォルト1)を置いて、module "web"をsource = "../modules/web"で呼び出し、movedブロック2つで、古いアドレスをmodule.web.local_file.config・module.web.local_file.bannerに移してください。initしたあと、プランに作成・削除・置き換えがなく、そのままapplyします。/root/iac-mod/prod/main.tfも、同じモジュールを呼び出すように変更してください(replicasのデフォルト3、envprod、movedを2つ)。プランに作成・削除・置き換えがなく、applyします。これで、ルールを直す場所は、モジュール1か所です。/root/iac-mod/modules/webをgitリポジトリにして、現在の内容をコミットし、タグv1.0.0を付けてください。dev・prodのsourceをgit::file:///root/iac-mod/modules/web?ref=v1.0.0に変更して、tofu init -upgradeしたあと、2つのプランがどちらもきれいでなければなりません。- モジュールに2つのことを追加して、コミットし、タグ
v2.0.0を付けてください。configの内容の末尾にowner=platformを1行、そして、replicasが1以上10以下かを検査するvalidationです。devだけをref=v2.0.0に上げて、init・applyし、prodはv1.0.0に残して、プランがきれいでなければなりません。 - devで、
replicasの値をいろいろな経路で入れてみながら、echo var.replicas | tofu consoleで実際の値を確認して、/root/iac-mod/precedence.txtに書いてください。すべての場合に、環境変数TF_VAR_replicas=9があります。env_only(ファイルなし)、with_tfvars(terraform.tfvarsに2)、with_auto(ここにa.auto.tfvarsの3・b.auto.tfvarsの4を追加)、with_var_file(ここに-var-file=ops.tfvars、値6)、with_var(-var-file=ops.tfvars -var replicas=5の順)、var_then_file(-var replicas=5 -var-file=ops.tfvarsの順)の6行を、이름=값の形式で書きます(韓国語で、順に「名前」「値」を意味する語です)。終わったら、a・b.auto.tfvarsとops.tfvarsを削除して、terraform.tfvars(2)だけを残してapplyしてください。 - devで、
tofu plan -var replicas=0を実行して、出力(エラーを含む)を/root/iac-mod/invalid.txtに保存してください。採点ツールは、devのコピーで0と11が拒否され、prod(v1.0.0、検査なし)では0が通過するかを確認します。devとprodのプランは、きれいなまま残しておきます。
参考
- Podには、OpenTofu 1.9.0、localプロバイダーのミラー、gitがあるので、インターネットなしで動きます。モジュールのリポジトリは、
/root/iac-mod/modules/webのローカルgitリポジトリです。 - モジュールのsourceやrefを変更したら、
tofu init(バージョンを上げるときは-upgrade)をやり直す必要があります。 - このPodのgitには、ユーザー名・メールアドレスが設定されていません。コミットするときは、
git -c user.name=… -c user.email=… commitのように渡してください。 - よくある間違い: モジュールの中でpath.moduleでファイルのパスを決めて、movedのあとに置き換えが付くこと。よくある間違い: 優先順位の実験のファイル(*.auto.tfvars)を残しておくこと。
- Refactoring(moved)・Module Sources(git, ref)・Input Variables(優先順位・validation)・Terraform: Input Variables
コピペした2つの環境を、先に適用しておく
/root/iac-mod/dev/main.tfと/root/iac-mod/prod/main.tfに、同じ形のリソースを2つ置いてください。local_file.configは、out/app.confにname=api、env=<환경>、replicas=<수>の3行(dev 1、prod 3)、local_file.bannerは、out/banner.txtにapi (<환경>)を1行です(プレースホルダーは、順に環境と件数です)。2つのディレクトリとも、init・applyしてください。
このステップは、「コピペしたコードがすでに運用中」という出発点を作ることです。2つのファイルの違いが、環境名とレプリカ数だけであることを確認しておいてください。それがモジュールの入力になります。
devをモジュール呼び出しに変えて、何も再作成しない
/root/iac-mod/modules/web/main.tfに、入力env(文字列)・replicas(数)、同じ2つのリソース、出力config_pathを置いたモジュールを作成してください。devのmain.tfは、replicas変数(デフォルト1)を置いて、module "web"をsource = "../modules/web"で呼び出し、movedブロック2つで、古いアドレスをmodule.web.local_file.config・module.web.local_file.bannerに移してください。initしたあと、プランに作成・削除・置き換えがなく、そのままapplyします。
モジュール内のpath.moduleは、モジュールのディレクトリです。ファイルが呼び出し側の環境ディレクトリの下にある必要があるので、path.rootを使います。このパスが古い値と1文字でも違うと、移動のあとに置き換えが続きます。モジュールを追加・変更したら、initをやり直す必要があります。
prodも同じモジュールに移す
/root/iac-mod/prod/main.tfも、同じモジュールを呼び出すように変更してください(replicasのデフォルト3、env prod、movedを2つ)。プランに作成・削除・置き換えがなく、applyします。これで、ルールを直す場所は、モジュール1か所です。
devと同じ手順です。違いは入力値だけでなければなりません。モジュールの中にenv == "prod"のような分岐を入れたくなったら、その値は引数として受け取るべきだというサインです。
モジュールにバージョンを付けて、2つの環境をそのバージョンに固定する
/root/iac-mod/modules/webをgitリポジトリにして、現在の内容をコミットし、タグv1.0.0を付けてください。dev・prodのsourceをgit::file:///root/iac-mod/modules/web?ref=v1.0.0に変更して、tofu init -upgradeしたあと、2つのプランがどちらもきれいでなければなりません。
ローカルパスのsourceはバージョンがないので、モジュールを直した瞬間に、呼び出すすべての環境が一緒に変わります。gitアドレスのrefでタグを固定すると、initがその時点のコピーを.terraform/modulesに取得しておきます。このPodのgitにはユーザー情報がないので、git -c user.name=... -c user.email=...でコミットしてください。
新しいバージョンはdevで先に、prodはそのまま
モジュールに2つのことを追加して、コミットし、タグv2.0.0を付けてください。configの内容の末尾にowner=platformを1行、そして、replicasが1以上10以下かを検査するvalidationです。devだけをref=v2.0.0に上げて、init・applyし、prodはv1.0.0に残して、プランがきれいでなければなりません。
タグを固定したので、モジュールのリポジトリのmainが変わっても、prodは影響を受けません。devのプランで、configがどう変わるか(内容の変更なので置き換え)を確認して、適用してください。
同じ変数に値が複数の場所から入ってくると、どれが勝つか
devで、replicasの値をいろいろな経路で入れてみながら、echo var.replicas | tofu consoleで実際の値を確認して、/root/iac-mod/precedence.txtに書いてください。すべての場合に、環境変数TF_VAR_replicas=9があります。env_only(ファイルなし)、with_tfvars(terraform.tfvarsに2)、with_auto(ここにa.auto.tfvarsの3・b.auto.tfvarsの4を追加)、with_var_file(ここに-var-file=ops.tfvars、値6)、with_var(-var-file=ops.tfvars -var replicas=5の順)、var_then_file(-var replicas=5 -var-file=ops.tfvarsの順)の6行を、이름=값の形式で書きます(韓国語で、順に「名前」「値」を意味する語です)。終わったら、a・b.auto.tfvarsとops.tfvarsを削除して、terraform.tfvars(2)だけを残してapplyしてください。
優先順位は、「あとに読んだものが勝つ」と覚えればよいです。環境変数が最初、次にterraform.tfvars、auto.tfvarsはファイル名の順、コマンドラインの-varと-var-fileは、書いた順に読まれます。実験したファイルを残しておくと、次の人の適用値が静かに変わります。
モジュールの境界で、誤った値を防ぐ
devで、tofu plan -var replicas=0を実行して、出力(エラーを含む)を/root/iac-mod/invalid.txtに保存してください。採点ツールは、devのコピーで0と11が拒否され、prod(v1.0.0、検査なし)では0が通過するかを確認します。devとprodのプランは、きれいなまま残しておきます。
validationは、モジュールの入力変数に付いているので、ルートからどの経路で値を入れても、モジュールの境界で防がれます。同じ入力がprodでは通過するのは、v1に検査がないからで、それがバージョン固定の両面です。