TT Lab
はじめる
学ぶ 学習パス コース

Infrastructure as Code

金曜の夜に手で直したルールを月曜の apply が消そうとした

TT Labで続きを見る

目標

コードの外で生じた変更を、plan -refresh-onlyで別に読み、そのまま適用すると消えるものを、プランのJSONから見つけたうえで、吸収・元に戻す・除外の3つの解消を自分で行い、毎日回す検知スクリプトを作ります。

なぜ重要なのか

宣言的なツールは、コードを真実として扱うので、コンソールで直した正しい修正も、次の適用で元に戻します。そのため、ドリフトを見つけたら、まず何が消えるかを読んで、吸収するのか元に戻すのかを、人が決める必要があります。ignore_changesは、すべてのドリフトを隠してくれるスイッチではなく、プロバイダーが読まない属性の変更は、プランにそもそも現れません。この限界を知っていてはじめて、検知の結果が空のときに、「一致している」と「見えていない」を区別できます。

ステップ

  1. /root/iac-drift/main.tfに、rules変数(デフォルト値["allow 443"])、ルールごとに1行ずつout/firewall.rulesに書くlocal_file.fw、out/motd.txtにmaintenance window: sun 02:00を1行書くlocal_file.motdを置いて、init・applyしてください。
  2. 障害対応中に、誰かが/root/iac-drift/out/firewall.rulesの末尾に、allow 8443を1行、手で追加しました(自分で追加してください)。コードには触れずに、tofu plan -refresh-onlyで、外で生じた変更だけを見た出力を、/root/iac-drift/drift.txtに保存してください。
  3. コードを変更せずに、tofu plan -out=/root/iac-drift/monday.tfplanでプランを保存してください(適用しません)。そのプランがfirewall.rulesに書く内容と、現在のファイルを比較して、適用すると消える行だけを、/root/iac-drift/lost.txtに書いてください。
  4. 緊急の修正が正しかったと判断しました。rulesのデフォルト値を["allow 443", "allow 8443"]に変更して、applyしてください。そのあと、firewall.rulesは2行で、プランはきれいでなければなりません。
  5. 今度は、誰かがout/motd.txtをmaintenance window: noneに変更しました(自分で変更してください)。これは誤った変更です。元に戻す前に、tofu plan -refresh-onlyの出力を/root/iac-drift/revert-drift.txtに保存し、コードはそのままにして、applyして、お知らせをコードの内容に戻してください。
  6. main.tfに、out/cache.confにcache v1を書き、lifecycle { ignore_changes = [content] }がかかったlocal_file.cacheを追加して、applyしてください。(a)ファイルを手でcache tamperedに変更したあと、tofu planの出力を/root/iac-drift/ignore.txtに保存して、applyで元に戻します。(b)コードの内容をcache v2に変更して、プランがきれいかを見ます(適用しません)。(c)chmod 600 out/firewall.rulesのあと、tofu plan -detailed-exitcodeの終了コードを、/root/iac-drift/perm.txtにperm=<코드>として書いてください(プレースホルダーはコードです)。
  7. /root/iac-drift/detect.sh <작업디렉터리>を作成してください。plan -refresh-onlyをプランファイルとして保存して、外で変更されたリソースのアドレスを集め、ドリフトがなければOKと0、あればDRIFT <주소들(공백 구분, 정렬)>を1行と2、失敗したらERRORと1で終了します(プレースホルダーは、順に作業ディレクトリと、空白区切りで並べ替えたアドレスの一覧です)。作業ディレクトリにファイルを残しません。採点ツールは、/root/iac-driftのコピーで、ファイルを変えながら確認します。

参考

ベースライン: ファイアウォールルールとお知らせをコードで適用する

/root/iac-drift/main.tfに、rules変数(デフォルト値["allow 443"])、ルールごとに1行ずつout/firewall.rulesに書くlocal_file.fw、out/motd.txtにmaintenance window: sun 02:00を1行書くlocal_file.motdを置いて、init・applyしてください。

for式でルールごとに改行を付けて、joinでつなげればよいです。適用したあと、プランが空であることが、ベースラインです。

金曜の夜の緊急修正を、プランで見る

障害対応中に、誰かが/root/iac-drift/out/firewall.rulesの末尾に、allow 8443を1行、手で追加しました(自分で追加してください)。コードには触れずに、tofu plan -refresh-onlyで、外で生じた変更だけを見た出力を、/root/iac-drift/drift.txtに保存してください。

-refresh-onlyは、コードの変更を計画せず、実際と状態の差だけを表示します。local_fileプロバイダーは、ファイルの内容が記録と違うと、そのファイルを「削除された」と報告します。属性1つが変わったようには見えないという点を、出力で確認してください。

月曜日にそのままapplyすると、何が消えるか

コードを変更せずに、tofu plan -out=/root/iac-drift/monday.tfplanでプランを保存してください(適用しません)。そのプランがfirewall.rulesに書く内容と、現在のファイルを比較して、適用すると消える行だけを、/root/iac-drift/lost.txtに書いてください。

プランのJSONのresource_changesで、local_file.fwのchange.after.contentが、適用後のファイルの内容です。現在のファイルにだけある行が、消える行です。commやgrep -vxF -fで、行単位の差を求められます。

正しい修正だった: コードに吸収する

緊急の修正が正しかったと判断しました。rulesのデフォルト値を["allow 443", "allow 8443"]に変更して、applyしてください。そのあと、firewall.rulesは2行で、プランはきれいでなければなりません。

吸収は、コードを現実に合わせることです。このプロバイダーは、ファイルを「削除された」と見なしたため、コードを直したあとのプランも、更新ではなく作成として出ます。書く内容が現在のファイルと同じなので、結果は同じです。

誤った修正だった: コードどおりに元に戻す

今度は、誰かがout/motd.txtをmaintenance window: noneに変更しました(自分で変更してください)。これは誤った変更です。元に戻す前に、tofu plan -refresh-onlyの出力を/root/iac-drift/revert-drift.txtに保存し、コードはそのままにして、applyして、お知らせをコードの内容に戻してください。

元に戻すことは、現実をコードに合わせることです。元に戻す前に、なぜそのような変更があったのかを確認することが、手順の一部です。ここでは、driftの出力を残すことで、その痕跡を残します。

ignore_changesが防ぐもの、防げないもの、まったく見えないものを確かめる

main.tfに、out/cache.confにcache v1を書き、lifecycle { ignore_changes = [content] }がかかったlocal_file.cacheを追加して、applyしてください。(a)ファイルを手でcache tamperedに変更したあと、tofu planの出力を/root/iac-drift/ignore.txtに保存して、applyで元に戻します。(b)コードの内容をcache v2に変更して、プランがきれいかを見ます(適用しません)。(c)chmod 600 out/firewall.rulesのあと、tofu plan -detailed-exitcodeの終了コードを、/root/iac-drift/perm.txtにperm=<코드>として書いてください(プレースホルダーはコードです)。

ignore_changesは、「設定に書かれた値が変わっても、更新のプランを立てない」という意味です。プロバイダーが実物を読んでいて、なくなったと判断することは、防げません。そして、プロバイダーが読まない属性の変更は、ドリフトとして検出されません。

毎朝回るドリフト検知を作る

/root/iac-drift/detect.sh <작업디렉터리>を作成してください。plan -refresh-onlyをプランファイルとして保存して、外で変更されたリソースのアドレスを集め、ドリフトがなければOKと0、あればDRIFT <주소들(공백 구분, 정렬)>を1行と2、失敗したらERRORと1で終了します(プレースホルダーは、順に作業ディレクトリと、空白区切りで並べ替えたアドレスの一覧です)。作業ディレクトリにファイルを残しません。採点ツールは、/root/iac-driftのコピーで、ファイルを変えながら確認します。

プランのJSONには、resource_changes以外にもresource_driftがあります。外で生じた変更が、そこに別に入ります。-detailed-exitcodeは、-refresh-onlyと一緒に使えます。