はじめに
前回に引き続き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で紹介できていない機能がありますが、それはまたの機会に。