ローカルtfstateファイルの危険性

Terraformerのこんにちは。皆様地表の整地にいそしんでおりますでしょうか。

さて、Terraformの話題です。TerraformはいわゆるIaCツールで、AWSで言うならばCloudFormationにあたります。個人的にはループやら変数やらの書き方と関数回りの書き方がCloudFormationよりもずっと手になじむのでこちらを使用しています。

Ref::とかfn::とか言われてもねぇ…わかるんだけど、こちとら手続き型LL言語ばかりここ最近使ってるわけで、いまいち手になじまないのよ。

さて、そんなTerraformですが、こちら実行して構築した環境情報はtfstateというファイルに保存されます。で、このファイルには現時点のTerraformの認識しているすべての情報が詰め込まれています。

つまり、パスワードを記述して構築した、とか、構築結果に基づいてパスワードが発行された、とか全部入っているんです。

当たり前ですが、ふつうにgitignoreの対象です。うっかりでもgitにpushしてはいけません。

 

さて、そんなファイルですので、基本的にはいくつかのパターンでそれを保護します。デフォルトだとローカルに作成されてしまいますので。まあそのへんのやり方はいろんな人が書いてくれています。まあうちは素直にプライベートのS3でバージョニング管理に放り込みます。

うっかり間違えて別プロジェクトのtfstateを別プロジェクトにオーバーライトしても戻せるので(そんなんやるわけねぇだろとか思うかもしれませんが、Terraformの初期設定用のファイルなんてコピって使いまわすので修正忘れとか普通に起きます)。

で、まあそんなtfstateファイルなんですが、さくっと小さなシステムだったりするといちいちその辺の設定するのもめんどくさくなって、「あとで修正してmigrateすればいいや」みたいなノリで書き始めることが(私は)たまにあります。

そして、そんな場合に限って、うっかりとかなんか他事でごそごそやってて、とか、一年前のそれをmigrateし忘れてて、とかまあ言い訳自体はいろいろありつつも、要するにただのボケやらかしてtfstateを消してしまうことがあります。そう、git cloneすりゃいいんだからこのディレクトリもういらねぇよな!とか。

gitにはないんですよ、tfstateは。

 

まあ、そんなこんなでtfstateを消す、なんて間抜けな行為はまれによくある、というやつです。めったにないけど起きるときは起きる。やらかすときはやらかす。やらかしちゃった人アドベントカレンダー見て「そんなんするわけねぇだろ」っておもうくらいにはやらかす。

皆さんはないですか。私は何度かあります。そのたびにうぎゃああってなりつつ、tfstateをs3にmigrateし忘れた自分をぶん殴りたくなります。

そして、たいていの場合ここで多くの人が絶望感にまみれながらググります。「tfstate 削除」とかで。そして、「再作成がもっとも手っ取り早いよばーか」と書かれているのを見て、目の前のすでに運用開始しているリソースを再作成とかできるわけねぇだろ畜生…!ってなる全世界3人くらいのやらかしちゃった人、こんにちは。

(ここまで壮大な前置き)

 

さて、今回はそんなtfstate消しちゃった人のための、tfstateをこっそり直そう。のコーナーです。え、余計なお世話だって? いいじゃないですか、そんな日もありますよ。

まずやるべきことは、terraform applyです(ぉぃ)。少なくともどれくらいのリソースがその消しちゃった中に存在するのかは知っておきましょう。もちろんdry-runです。

これが、まあ200ちょい、くらいなら頑張ればイケます。大丈夫です。600とかあったらもう絶望してください。200でもだいぶダルい。というか600とかになる前に分割しろマジで(自戒)。

さて、やることは単純で一個ずつimportしていきましょうってだけなんですが、そもそもimportはtfstateがないと動作しません。つまり、まっさらのリソース書かれていないtfstateが必要なわけです。

なんか適当なディレクトリ切って、terraform {} や provider {}なんかを記述しただけの、resourceを含まないterraformコードを作成します。

そして、その環境でterraform initして、terraform applyします。

はい、これでまっさらのtfstateが手に入ります。当然ですが、tfenvなんか使っている人は.terraform-versionも持ってきて、同一バージョンで作りましょう。そして、このtfstateをやらかしたディレクトリに持ち込みます。

さて、ふつうはこれでimportが効き始めますので、あとは頑張ってね、となるんですが、ここでimportが効かないケースがあります。例えば、local_fileプロバイダでファイル作ってたりする場合ですね。

明確に、これなんかはapplyした後じゃねぇとだめだって怒られるので、問題のないリソースならばterraform apply –targetで部分実行噛ませましょう。

はい、あとは地道な作業です。tfファイルを上から下まで眺めていき、リソース定義やモジュールを見るたびにぺちぺちimportしていきます。厄介なのはmoduleで、moduleのコードを追っかけないとimportターゲットがわかりませんので、地味ぃに地味ぃに頑張る必要があります。

無事、全部importが終わったらplanして、差分見つけて、ずれがあったらコードか環境のどっちかを直します。たいていの場合、tfstate同様にtfvarsファイルとかもgitには送り込んでいないことが多く、その場合も泣きながらvariablesの初期値を調整してplanが文句言わなくなるまで頑張ります。

ちなみにこんなことしなくても、コードを生成するだけなら既存環境に接続させてリテラル値満載の出来の悪いべた書きコードなら自動生成はできますが、そもそもIaCでTerraformで書いているような人がそんなべたコード書いているとは到底思えません。

for文でぶん回し、for_eachでブン回し、dynamicでブン回し、大量のvariablesとlocalsで汎用性を持たせたコードのはずです。そう、可読性とかどこ行きましたか。みたいなコードに違いないのです。

リファクタリングするたびにめまいがするのです。

なんか最近べた書きコードのほうが正義なような気がしてきたりもしますが、きっと気のせいです。

なお、どう考えてもUUIDでランダム識別子作ってるとかは喪失するので、生成したuuidをインポートします。じゃないと新規生成されたUUID識別子とかランダム文字列によって軒並みリソース作り直すぜ!とかえげつないこと言われるので、そういうのも全部インポートしてください。

また、local_fileで作成した秘密鍵ファイルとかがあったら、どう考えてもgithubにpushされているとは思えないので、そんなもんは顧客に送信した時の記録とかから復元してください。それもなかったらどうせ秘密鍵ですから顧客がなくしたときには再生成しましょう。自分の手元に残っている必要はありません。というか他人の秘密鍵を手元に残しておくんじゃない。というか人の秘密鍵を他人が作るなwwwwww

 

このように、とっても頑張れば一応tfstateの復元は可能です。これを復元というのかどうかは知りませんが、現環境を正としてコード側にバックポートするという動きですね。

ええ、現環境を宣言状態に戻そうとしてたときにtfstateが消えた、という場合は、一応上記手順でやってやれなくはないんですが、import漏れとかを検出がものすごく困難になるので、皆様やらかす際には少なくとも現実行環境を正、と言える状態のときにしましょう。

importブロックでやるのは、tfstateうっかりさんのときには逆にやりにくいです。なんかでっかい環境をIaC化するときとかにはいいんでしょうけど、既存コードに新たに組み込むにはちょっと向いてないかと。だってtfはもうあるんだし。tfstateだけ書いてほしいんだしw

ただ、注意点としてこれで復旧する場合はなるべく集中的に一気にやってしまいましょう。日をあけたりすると、ぶっちゃけどこまでやったかすらわかりにくくなるうえ、AWS側の仕様とかがちょっと変わって、とかが起きると下手打つと目を覆うような事態になります。ダルイうえに地味な作業ですが、やらかしたのは自分です、覚悟決めてやりましょう。

これで全世界で3人くらい救われるといいなぁ…。

本日の結論:素直にTerraform Enterpriseでも契約しとけよ。