terragruntを利用してterraform複数環境をDRYに管理しよう!
どもども、GW真っ只中に執筆中の僕です。最近めっきり暑くなってきましたので体調管理気をつけましょうね…
さて皆様、terraformは利用しておりますでしょうか?terraformに限らず、IaCを利用することにより得られる恩恵は計り知れませんが、その中でも強力なのが同一構成の再現性だと思っています。
コードで管理されていますから、同じような構成を作成するのもかなり容易であるということです。この利点を利用することで簡単に本番やステージングなど複数環境のインフラを構築することができます。
とはいってもコード上の環境分離戦略は様々です。今回はその戦略に使える便利ツールterragruntを紹介したいと思います。
terragruntとは
Terragruntはterraformのラッパーツールであり、コード構成を簡単にDRYに保つことができる強力なツールです。
また、複数のTerraform モジュールを一括で操作したり、backendやproviderといった基本設定でさえDRYに保つことだってできます。
terraformのラッパーツールであることから、基本的な操作性や動作はそのままterraformの知見を利用できることもすぐれているポイントですね。
ともあれ試してみるのが一番わかりやすいと思います!インストール方法はコチラを参照、早速試してみましょう!
ディレクトリ構成と各ファイルの解説
今回は以下のようなディレクトリ構成となります。意図が伝わりやすくシンプルになることを意識してみました。お試しということで作成するのはS3のみです。
.
├── envs
│ ├── dev
│ │ ├── dev.hcl
│ │ └── s3
│ │ └── terragrunt.hcl
│ ├── production
│ │ ├── production.hcl
│ │ └── s3
│ │ └── terragrunt.hcl
│ ├── staging
│ │ ├── s3
│ │ │ └── terragrunt.hcl
│ │ └── staging.hcl
│ └── terragrunt.hcl
└── modules
└── s3
├── main.tf
└── variables.tf
まずはterragruntの基本的なファイル構成についてです。ルートとするディレクトリ(今回はenvディレクトリ)にproviderやbackendを設定するterragrunt.hclファイルを作成します。その配下の各terragrunt.hclは上位ディレクトリに配置されたterragrunt.hclを参照し、必要に応じて追加の設定を加えていくという利用方法になります。
例えばenvディレクトリに配置したterragrunt.hclの内容は以下です。
# envs/terragrunt.hcl
remote_state {
backend = "s3"
config = {
region = "ap-northeast-1"
bucket = "remote-state-bucket-name"
key = "${path_relative_to_include()}/terraform.tfstate"
encrypt = true
}
generate = {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
}
}
generate "provider" {
path = "provider.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
version = "~> 5.48.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
EOF
}
このように一度providerやbackendを設定しておくと、これを各環境で引き継ぐことができるのでDRYに保つことが可能です。
また、"path_relative_to_include()"というterragurantのビルトイン関数を利用することで引きついだ先のディレクトリパスを動的に取得し環境ごとにstateパスを切りわけることができます。
引き継ぐ側、例えばdevディレクトリのterragrunt.hclは以下のようになっています。
# envs/dev/s3/terragrunt.hcl
locals {
env = read_terragrunt_config(find_in_parent_folders("dev.hcl"))
}
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "../../../modules/s3"
}
inputs = {
bucket_name = local.env.locals.bucket_name
}
“find_in_parent_folders()"関数を利用することで引き継ぐ親のterragrunt.hclを探して設定をincludeしてくれます。
terraform内に利用するモジュールを指定し、inputでmoduleに与えるパラメータを指定するだけでmoduleを利用したリソースの作成まで行ってくれます。リソース作成までに必要なコードがとても簡素になったことが理解いただけるかと思います。
dev以下にはもうひとつ下記のようなファイルを用意しています。
# envs/dev/dev.hcl
locals {
env_name = "dev"
service_name = "terragrunt-sample-${local.env_name}"
bucket_name = "${local.service_name}-bucket"
}
terragrunt.hclからこのファイルを介してlocal変数を利用できます。これによりますますterragrunt.hclの内容をシンプルにすることが可能です。ただし、terragrunt.hclには明示的にこのファイルを読み出す記述をしておく必要がある点に注意です。
# envs/dev/s3/terragrunt.hcl
locals {
env = read_terragrunt_config(find_in_parent_folders("dev.hcl"))
}
...
あとは環境分同じ構成を用意して完成です。
今回moduleはS3バケットを用意するのみの簡単なものを用意しました。
# modules/s3/main.tf
resource "aws_s3_bucket" "example" {
bucket = "${var.bucket_name}-${random_string.name.result}"
}
resource "random_string" "name" {
length = 4
upper = false
lower = true
special = false
}
# modules/s3/variables.tf
variable "bucket_name" {
type = string
}
.gitignoreは以下の通りです。
### Terragrunt ###
# terragrunt cache directories
**/.terragrunt-cache/*
準備ができたら早速plan、applyしてみましょう。
リソースの作成
ツールの紹介でも述べた通り、terragruntはterraformのラッパーのため基本的な操作性は同じです。
例えば、envs/dev以下で下記コマンドを実行することでplanを確認することが可能です。
$ terragrunt plan
terraformをterragruntに置き換えるだけですね。さらに優秀なのはplanを実行するだけでinitから始めてくれる点です。
terragruntのすごいところはこれだけではありません。ただのラッパーにはとどまらずterragruntならではのオプションも複数存在します。例えば、以下のコマンドを実行することでterragrunt.hclが存在する全てのディレクトリにおいてplanを実行してくれます。
$ terragrunt run-all plan
複数環境を同時に構築できたりするので便利です。特定の環境のみの更新でも、わざわざディレクトリを移動しなくてもいいので非常に強力なコマンドですね。他のオプションについてもコチラから確認してみてください!
applyも同じようにできますので是非ためしてみてください。もちろんdestroyも利用可能なのでrun-allを利用してサクッとお片付けまでやっちゃいましょう!
さいごに
今回はterraformの強力ラッパーツールであるterragruntの紹介でした!
terraformの複数環境運用戦略には様々な方法がありますが、是非terragruntも採用方法の一つとして検討してみてはいかがでしょうか。
最後まで読んでいただきありがとうございました!