はじめに
前回に引き続きTerraformをさらにいい感じに使えるTerragrunt。
今回はそのTerragruntでTerraformのRemote Stateをいい感じにDRYに管理する方法をご紹介します。
便利なRemote Stateだが…
Terraformにはインフラの状態(state)をリモートに保存しておけるようにremote stateという便利な機能があります。
保存先として主流なのは、おそらくs3やconsulになるのですが、リモートに保存しておくことで、複数人でインフラをTerraformで扱ったり(要ロック)、ローカルに保存してたstateが消えたりしてもリモートにあるから大丈夫になります。
要はインフラの状態管理が今よりもっといい感じになるのです。
ですが、これには致命的な問題があって、terraformファイルで記述するのはbackendというブロックになるのですが、ここでインタポレーション(変数を使うこと)が効かないのです。
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "frontend-app/terraform.tfstate"
region = "us-east-1"
encrypt = true
lock_table = "my-lock-table"
}
}
上のブロックで記述したものは再利用される項目も多々あるのですが、ここで変数を使えなかったり、共通化できないのは結構な痛手なんですよね。
├── backend-app
│ └── main.tf
├── frontend-app
│ └── main.tf
├── mysql
│ └── main.tf
└── vpc
└── main.tf
例えば上のような構成だった場合。main.tfのbackendブロックに何回も同じ記述を書かないといけないのです。
これはDRYじゃないですしバグを作る要因にもなります。
そこで、Terragruntではremote_stateというブロックを使用することができます。
Terragruntのremote_stateそしてinclude
上の問題をTerragruntでどう解決するのかと言うと、まずはmain.tfにプレースホルダを記載しておきます(これは必須)。
terraform {
# ↓この中身はTerragruntが埋める
backend "s3" {}
}
中身は空っぽです。
この中身をTerragruntが埋めてくれる感じです。ディレクトリ構造としては以下のようになります。
├── terraform.tfvars
├── backend-app
│ ├── main.tf
│ └── terraform.tfvars
├── frontend-app
│ ├── main.tf
│ └── terraform.tfvars
├── mysql
│ ├── main.tf
│ └── terraform.tfvars
└── vpc
├── main.tf
└── terraform.tfvars
親ディレクトリに共通利用するterraform.tfvarsがあり、モジュール固有の設定についてはそれぞれサブディレクトリにterraform.tfvarsを配置します。
そして、親ディレクトリのterraform.tfvarsには以下のように記述します。
terragrunt = {
remote_state {
backend = "s3"
config {
bucket = "my-terraform-state"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "us-east-1"
encrypt = true
lock_table = "my-lock-table"
}
}
}
ポイントとなるのはpath_relative_to_include()の部分です。
ここがモジュール固有の設定が入る場所となります。
モジュール固有のterraform.tfvarsがどうなるかと言うと
terragrunt = {
include {
path = "${find_in_parent_folders()}"
}
}
となります。includeが重要な役割を担っています。includeブロックのpathで指定されたterraform.tfvarsのterragruntブロックがまるっとここにコピペされると思ってください。
そして、もちろん子の設定が優先されるので、同じ設定を記述している場合は子の設定が反映されます。
find_in_parent_foldersはTerragrunt特有の関数で、親ディレクトリで最初にみつけたterraform.tfvarsファイルのパスを返却します。
こうすることでpathに自分でパスを指定する必要がなくなります。
そして、上で少し述べたpath_relative_to_include()は、親terraform.tfvarsから見たincludeが含まれるterraform.tfvarsへの相対パスに変換されます。
何を言っているのかさっぱり想像しづらいと思いますが…。
例えば、backend-appのterraform.tfvarsを例に取った場合、親のterraform.tfvarsから見て、backend-appのterraform.tfvarsはbackend-app/terraform.tfvarsになるので、path_relative_to_include()はbackend-appbackend-appのtfstateのkeyはbackend-app/terraform.tfstateというパスに保存されることになり、モジュール固有のstateをリモートでkeyを分けて管理できるということです。
ちょっとHackyな雰囲気が出てますが、どのみち現状のTerraformでコピペミスを防ごうとするとこういった感じのスクリプトを自分で書くことになるかと思うので、Terragrunt側で用意してくれているのは嬉しいことですね。
おわりに
TerragruntでbackendブロックをDRYにする方法を紹介しました(本家のREADMEに記載されている内容です)。IaCでインフラ自動化するのは良いのですが、テストが普通のコーディングと違ってしづらい為、極力バグが混入しにくい使い方をすることが大切です。
まだTerragruntで紹介できていない機能がありますが、それはまたの機会に。




