これは、なにをしたくて書いたもの?
Terragruntを学ぶシリーズ。
次は、Terragruntを使ってTerraform実行時の引数やオプション指定をまとめてみます。
Terragruntで、Terraform実行時の引数やオプション指定をまとめる
Terraformを実行する時に、引数やオプションを指定することがあります。
たとえば共通の変数の値を設定したファイルを場合は、Terraformモジュールの実行ごとに-var-fileオプションを付けることに
なります。また、apply時にいつもロックのタイムアウトの設定を指定したい場合は、毎回-lock-timeoutを指定することに
なります。
※ terraform.tfvarsファイルを各モジュールディレクトリに作成すれば-var-fileの指定は要らないという話もありますが、その場合は
似た内容のファイルがバラまかれることになりますね…
Quick start / Keep your Terraform CLI arguments DRY
Terragruntを使うと、terragrunt.hclにこれらの定義をまとめることができるようです。
環境
今回の環境は、こちらです。
$ terraform version Terraform v0.14.7 $ terragrunt -v terragrunt version v0.28.7
Terraform Providerは、MySQL用のものを使用します。
Provider: MySQL - Terraform by HashiCorp
使用するMySQLは8.0.23とし、172.17.0.2で動作しているものとします。
お題
MySQL Providerを使い、以下のリソースを定義する2つのTerraformルートモジュールを作成します。
- データベース
- ユーザーおよび権限
この時に指定するProviderの定義やデータベース名を変数化し、これを指定するオプションをまとめてみましょう。
※ Terragruntを使うとProviderの定義をまとめることができますが、今回はオプションの話に絞ります
最初はTerragruntを使わずにTerraformで毎回オプションを指定し、その後にTerragruntで指定するオプションをまとめてみましょう。
Terragruntを使わない場合
まずは、Terragruntを使わずにベースを作っていきます。
データベース、ユーザーおよび権限用に2つのディレクトリを作成。
$ mkdir database users
今回は、こういう感じの構成にしました。
$ tree
.
├── database
│ ├── main.tf
│ └── variables.tf
├── database.tfvars
├── provider.tfvars
└── users
├── main.tf
└── variables.tf
2 directories, 6 files
MySQL Providerを使うので接続先の定義が必要なのですが、これはtfvarsファイルにまとめておきます。
provider.tfvars
mysql_provider_endpoint = "172.17.0.2:3306" mysql_provider_username = "root" mysql_provider_password = "password"
また、作成するデータベース、権限設定に関するデータベース名もtfvarsファイルにまとめておきましょう。
database.tfvars
database_name = "my_database"
データベース側のモジュール定義に移動します。
$ cd database
リソース定義は、こんな感じで用意。Provider定義やデータベース名は変数化しています。
main.tf
terraform {
required_version = "0.14.7"
required_providers {
mysql = {
source = "terraform-providers/mysql"
version = "1.9.0"
}
}
}
provider "mysql" {
endpoint = var.mysql_provider_endpoint
username = var.mysql_provider_username
password = var.mysql_provider_password
}
resource "mysql_database" "app" {
name = var.database_name
default_character_set = "utf8mb4"
default_collation = "utf8mb4_ja_0900_as_cs_ks"
}
output "database_name" {
value = mysql_database.app.name
}
変数定義。
variables.tf
variable "mysql_provider_endpoint" { type = string } variable "mysql_provider_username" { type = string } variable "mysql_provider_password" { type = string } variable "database_name" { type = string }
initして
$ terraform init
上位のディレクトリにあるtfvarsファイルを指定してapply。
$ terraform apply -var-file=../provider.tfvars -var-file=../database.tfvars
リソースができました。
mysql_database.app: Creating... mysql_database.app: Creation complete after 0s [id=my_database] Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Outputs: database_name = "my_database"
ユーザーおよび権限側。
$ cd ../users
こちらのリソース定義は、こんな感じです。データベース定義と同じく、Provider定義とデータベース名に関する部分が変数に
なっています。
main.tf
terraform {
required_version = "0.14.7"
required_providers {
mysql = {
source = "terraform-providers/mysql"
version = "1.9.0"
}
}
}
provider "mysql" {
endpoint = var.mysql_provider_endpoint
username = var.mysql_provider_username
password = var.mysql_provider_password
}
resource "mysql_user" "admin_user" {
user = "admin"
plaintext_password = "password"
host = "%"
}
resource "mysql_grant" "admin_user" {
user = mysql_user.admin_user.user
host = mysql_user.admin_user.host
database = var.database_name
privileges = ["ALL"]
}
resource "mysql_user" "application_user" {
user = "appuser"
plaintext_password = "password"
host = "%"
}
resource "mysql_grant" "application_user" {
user = mysql_user.application_user.user
host = mysql_user.application_user.host
database = var.database_name
privileges = ["SELECT", "INSERT", "UPDATE", "DELETE"]
}
output "admin_user_name" {
value = mysql_user.admin_user.user
}
output "admin_user_privileges" {
value = mysql_grant.admin_user.privileges
}
output "app_user_name" {
value = mysql_user.application_user.user
}
output "app_user_privileges" {
value = mysql_grant.application_user.privileges
}
変数定義は、データベース定義の時と同じですね。
variables.tf
variable "mysql_provider_endpoint" { type = string } variable "mysql_provider_username" { type = string } variable "mysql_provider_password" { type = string } variable "database_name" { type = string }
initして
$ terraform init
やっぱりtfvarsファイルを指定してapply。
$ terraform apply -var-file=../provider.tfvars -var-file=../database.tfvars
リソースが作成されました。
mysql_user.admin_user: Creating... mysql_user.application_user: Creating... mysql_user.admin_user: Creation complete after 0s [id=admin@%] mysql_grant.admin_user: Creating... mysql_user.application_user: Creation complete after 0s [id=appuser@%] mysql_grant.application_user: Creating... mysql_grant.admin_user: Creation complete after 0s [id=admin@%:`my_database`] mysql_grant.application_user: Creation complete after 0s [id=appuser@%:`my_database`] Apply complete! Resources: 4 added, 0 changed, 0 destroyed. Outputs: admin_user_name = "admin" admin_user_privileges = toset([ "ALL", ]) app_user_name = "appuser" app_user_privileges = toset([ "DELETE", "INSERT", "SELECT", "UPDATE", ])
ここまでで、1度リソースを破棄して上位ディレクトリに戻りましょう。destroy時も、tvfarsの指定が必要ですね。
$ terraform destroy -var-file=../provider.tfvars -var-file=../database.tfvars $ cd ../database $ terraform destroy -var-file=../provider.tfvars -var-file=../database.tfvars $ cd ..
Terragruntを使って、オプション指定をまとめる
ここまでで、applyやdestroy時に同じオプションを毎回指定してきました。Provider定義のように、共通に使うものもあるので
こういうのは確実に同じオプションを複数のTerraformモジュールで指定することになりますね。
で、Terragruntを使ってこういうオプション指定をまとめるには、terragrunt.hclファイルを作成して、以下のように記述します。
terragrunt.hcl
terraform {
extra_arguments "common_vars" {
commands = get_terraform_commands_that_need_vars()
arguments = [
"-var-file=../provider.tfvars",
"-var-file=../database.tfvars"
]
}
}
terraformブロック内に、extra_argumentsというブロックを定義します。
extra_argumentsは、TerraformのCLIに指定する引数を定義するためのブロックです。
引数やオプション以外にも、環境変数の設定、必須となるtfvarsファイルやオプションの(存在すれば使う)のtfvarsファイルの
設定などができるようです。
get_terraform_commands_that_need_varsというのは、Terragruntの組み込み関数です。Variablesを使う可能性があるコマンドを
返します。
Built-in functions / get_terraform_commands_that_need_vars
定義はこちら。
https://github.com/gruntwork-io/terragrunt/blob/v0.28.7/config/config_helpers.go#L113
https://github.com/gruntwork-io/terragrunt/blob/v0.28.7/config/config_helpers.go#L38-L47
つまり、こちらの定義は
commands = get_terraform_commands_that_need_vars()
以下と同義になります。
commands = ["apply", "console", "destroy", "import", "plan", "push", "refresh"]
今回は-var-fileのみの指定にしていますが、他にオプションを指定したり、コマンドごとに細かく分けたりといった例が
ドキュメントに書かれています。
次に、データベースのモジュール定義へ移動。
$ cd database
terragrunt.hclファイルを作成します。内容は、上位ディレクトリにあるterragrunt.hclを参照するだけです。
terragrunt.hcl
include {
path = find_in_parent_folders()
}
これで、terragruntコマンドを使う場合はオプション指定なしでplanなどが実行できるようになります。
$ terragrunt plan
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# mysql_database.app will be created
+ resource "mysql_database" "app" {
+ default_character_set = "utf8mb4"
+ default_collation = "utf8mb4_ja_0900_as_cs_ks"
+ id = (known after apply)
+ name = "my_database"
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ database_name = "my_database"
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
tfvarsで定義した内容が補完されていますね。つまり、以下と同義の状態になっています。
$ terraform plan -var-file=../provider.tfvars -var-file=../database.tfvars
同様にapplyもオプション指定なしで実行できます。
$ terragrunt apply
リソースが作成できました。
mysql_database.app: Creating... mysql_database.app: Creation complete after 0s [id=my_database] Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Outputs: database_name = "my_database"
ユーザーや権限を定義したモジュールへ移動。
$ cd ../users
こちらも、terragrunt.hclファイルを作成。
terragrunt.hcl
include {
path = find_in_parent_folders()
}
データベース定義の時と同じように、オプション指定なしでapplyできるようになります。
$ terragrunt apply
リソースができました。
mysql_user.application_user: Creating... mysql_user.admin_user: Creating... mysql_user.application_user: Creation complete after 0s [id=appuser@%] mysql_grant.application_user: Creating... mysql_user.admin_user: Creation complete after 0s [id=admin@%] mysql_grant.admin_user: Creating... mysql_grant.application_user: Creation complete after 0s [id=appuser@%:`my_database`] mysql_grant.admin_user: Creation complete after 0s [id=admin@%:`my_database`] Apply complete! Resources: 4 added, 0 changed, 0 destroyed. Outputs: admin_user_name = "admin" admin_user_privileges = toset([ "ALL", ]) app_user_name = "appuser" app_user_privileges = toset([ "DELETE", "INSERT", "SELECT", "UPDATE", ])
destroyもオプションが要らなくなります。
$ terragrunt destroy $ cd ../database $ terragrunt destroy $ cd ..
これで、Terragruntを使ってオプション指定をまとめることが確認できました。
複数のextra_argumentsを定義する
先ほどはextra_argumentsをひとつ定義して使いましたが、複数定義することもできます。
あまり意味はありませんが、Providerに関するtfvarsファイルを指定したとextra_arguments、データベース名に関する
extra_argumentsに分割してみます。
少し変化を入れるため、データベース名に関しては-varで指定するようにしてみました。といっても、tfvarsで指定していた
内容と同じですが…。
terragrunt.hcl
terraform {
extra_arguments "provider_vars" {
commands = get_terraform_commands_that_need_vars()
arguments = [
"-var-file=../provider.tfvars"
]
}
extra_arguments "database_vars" {
commands = get_terraform_commands_that_need_vars()
arguments = [
"-var", "database_name=my_database"
]
}
}
実際のモジュール適用側では、この変更を意識することなく使うことができます。
データベース側。
$ cd database $ terragrunt apply
確認。
mysql_database.app: Creating... mysql_database.app: Creation complete after 0s [id=my_database] Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Outputs: database_name = "my_database"
ユーザーおよび権限側。
$ cd ../users $ terragrunt apply
確認。
mysql_user.admin_user: Creating... mysql_user.application_user: Creating... mysql_user.application_user: Creation complete after 0s [id=appuser@%] mysql_grant.application_user: Creating... mysql_user.admin_user: Creation complete after 0s [id=admin@%] mysql_grant.admin_user: Creating... mysql_grant.application_user: Creation complete after 0s [id=appuser@%:`my_database`] mysql_grant.admin_user: Creation complete after 0s [id=admin@%:`my_database`] Apply complete! Resources: 4 added, 0 changed, 0 destroyed. Outputs: admin_user_name = "admin" admin_user_privileges = toset([ "ALL", ]) app_user_name = "appuser" app_user_privileges = toset([ "DELETE", "INSERT", "SELECT", "UPDATE", ])
さらにオプションを指定すると?
ところで、引数やオプション定義を指定した状態で、同じオプションを指定するとどうなるのでしょう?
terragrunt.hclがこの状態で
terragrunt.hcl
terraform {
extra_arguments "provider_vars" {
commands = get_terraform_commands_that_need_vars()
arguments = [
"-var-file=../provider.tfvars"
]
}
extra_arguments "database_vars" {
commands = get_terraform_commands_that_need_vars()
arguments = [
"-var", "database_name=my_database"
]
}
}
データベースのモジュール側で、-varを指定してplanを実行してみます。
$ cd database
$ terragrunt plan -var database_name=foo
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# mysql_database.app will be created
+ resource "mysql_database" "app" {
+ default_character_set = "utf8mb4"
+ default_collation = "utf8mb4_ja_0900_as_cs_ks"
+ id = (known after apply)
+ name = "foo"
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ database_name = "foo"
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
terragruntコマンド実行時に指定した内容で、planが実行されました…が、これは以下のように解釈されていると考えるべき
でしょうね。
$ terraform plan -var-file=../provider.tfvars -var database_name=my_database -var database_name=foo
少なくとも、まとめたオプションと同じものをコマンド実行時に改めて指定しない方が良い気がします。
モジュール固有の内容を指定したい場合?
今回は各モジュール実行時に指定するオプションを全部同じにまとめましたが、一部はモジュール単位で固有に指定、といった
ことはできるのでしょうか。
たとえば、トップディレクトリにあるterragrunt.hclファイルは以下のProviderに関するtfvarsファイルを指定するだけにしてみます。
terragrunt.hcl
terraform {
extra_arguments "provider_vars" {
commands = get_terraform_commands_that_need_vars()
arguments = [
"-var-file=../provider.tfvars"
]
}
}
データベースの定義へ移動。
$ cd database
この時点では、データベース側のモジュールを実行するにはデータベース名の指定が足りない状態です。
ここで、terragrunt.hclファイルを上位ディレクトリの内容を参照するだけでなく、固有のextra_argumentsを持つように変えてみます。
terragrunt.hcl
include {
path = find_in_parent_folders()
}
terraform {
extra_arguments "database_vars" {
commands = get_terraform_commands_that_need_vars()
arguments = [
"-var",
"database_name=my_database"
]
}
}
planで確認してみます。
$ terragrunt plan
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# mysql_database.app will be created
+ resource "mysql_database" "app" {
+ default_character_set = "utf8mb4"
+ default_collation = "utf8mb4_ja_0900_as_cs_ks"
+ id = (known after apply)
+ name = "my_database"
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ database_name = "my_database"
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
認識しているようですね、OKです。
こういう感じで、モジュールを跨いで指定するオプションだけでなく、Terraform実行時に指定するオプションを忘れないように
定義する場としても使えそうですね。
まとめ
Terragruntを使って、Terraformの引数やオプションをまとめてみました。
指定するオプションが増えてきたりするとまあ面倒ですし、忘れたりするのでこうやってまとめたりするのもよいのかなと思います。
Terragruntを使わない場合、だいたいシェルスクリプト化したり、実行するための手順を書いたりしてそうですね。