これは、なにをしたくて書いたもの?
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を使わない場合、だいたいシェルスクリプト化したり、実行するための手順を書いたりしてそうですね。