これは、なにをしたくて書いたもの?
Terraformを使っていて、
output "xxx" { value = resource_a.this.xxx } output "yyy" { value = resource_a.this.yyy } output "zzz" { value = resource_a.this.zzz } ...
といった感じで、あるリソースやモジュールのOutputを頑張って並べていくのが面倒だなぁと思い…調べてみたら、
Terraform 0.12からこれを一括で設定できるようになっていたみたいです。
ドキュメントに書いてないじゃないですかっ、気づかないよ!
Output Values - Configuration Language - Terraform by HashiCorp
Resource and module object values
Release Notesなどによると、これを「Resource and module object values」と言い、リソースやモジュール全体を式においての
値として扱えるようになりました。
https://github.com/hashicorp/terraform/blob/v0.12.0/CHANGELOG.md
Release v0.12.0 · hashicorp/terraform · GitHub
Terraform 0.11→0.12で追加された新機能 | Developers.IO
使い方は、こんな感じですね。
output "foo_database" {
value = mysql_database.foo
}
これまで以下のように属性をたくさん並べていたのを、一括でOutputとして設定するというやり方で良い時はだいぶ楽に
書くことができるようになります、と。
output "foo_database_name" { value = mysql_database.foo.name } output "foo_database_default_character_set" { value = mysql_database.foo.default_character_set } ...
Release Notesをよく見ると、Output Variablesだけではなく、Input Variablesとして使えるということが書いてあります。
including passing them through input variables and output values to other modules, using an attribute-less reference syntax, like aws_instance.foo.
リソースのインスタンスをまるっと渡して参照できるということですね。モジュールを作る時に使うことがあったり
するでしょうか?覚えておきましょう。
ブログの方には、1行だけ書いてありました。
Generalized type system: use lists and maps more freely, and use resources as object values.
わからん…。
自分は、TerraformのIssueを眺めていて0.12にこの機能が入っていそうだと気づき、0.12の新機能の情報を探して把握した
感じですね。
Allow entire resource to be output · Issue #9067 · hashicorp/terraform · GitHub
まあ、紹介はこのくらいにして、使っていってみましょう。
環境
今回の環境は、こちら。
$ terraform version Terraform v0.12.26 + provider.mysql v1.9.0
ProviderとしてはMySQLを使って試すことにします。MySQL自体は、8.0で172.17.0.2で動作しているものとします。
リソースのOutputを一括で設定してみる
Terraformのリソース定義ファイルを、こんな感じで用意。
main.tf
terraform { required_version = "0.12.26" } provider "mysql" { endpoint = "172.17.0.2:3306" username = "root" password = "password" version = "1.9.0" } resource "mysql_database" "this" { name = "my_database" default_character_set = "utf8mb4" default_collation = "utf8mb4_ja_0900_as_cs_ks" } output "this_database" { value = mysql_database.this }
リソースは、シンプルにひとつだけにしました。
resource "mysql_database" "this" { name = "my_database" default_character_set = "utf8mb4" default_collation = "utf8mb4_ja_0900_as_cs_ks" }
で、このリソースのOutputを一括で指定しています。valueに設定するのは、「リソース名.name」です。
output "this_database" {
value = mysql_database.this
}
applyしてみましょう。
$ terraform apply
リソース作成後、Output Variablesがこんな感じで設定されます。
Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Outputs: this_database = { "default_character_set" = "utf8mb4" "default_collation" = "utf8mb4_ja_0900_as_cs_ks" "id" = "my_database" "name" = "my_database" }
MySQL Providerのmysql_databaseリソースでのOutput(Attribute)は4つなので、全部網羅されていますね。
MySQL: mysql_database - Terraform by HashiCorp
これは、欲しい時は便利な機能なので覚えておきましょう。
モジュールで試す
今度は、同じことをモジュールでやってみましょう。
こんな感じで、サブモジュールを作成。
modules/database/main.tf
variable "name" { type = string } resource "mysql_database" "this" { name = var.name default_character_set = "utf8mb4" default_collation = "utf8mb4_ja_0900_as_cs_ks" } output "name" { value = mysql_database.this.name } output "default_character_set" { value = mysql_database.this.default_character_set }
データベース名だけ変数にしたものですね。
Outputは、意図的に2つに絞っています。
output "name" { value = mysql_database.this.name } output "default_character_set" { value = mysql_database.this.default_character_set }
このモジュールを利用する側。
main.tf
terraform { required_version = "0.12.26" } provider "mysql" { endpoint = "172.17.0.2:3306" username = "root" password = "password" version = "1.9.0" } module "database" { source = "./modules/database" name = "my_database" } output "this_database" { value = module.database }
モジュールのOutputに関しては、リソースと同じように「module.モジュール名」で一括で設定。
output "this_database" {
value = module.database
}
apply。
$ terraform apply
すると、モジュールがOutputに指定している値が全部得られます。
Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Outputs: this_database = { "default_character_set" = "utf8mb4" "name" = "my_database" }
もちろん、モジュール側でリソースの属性を全部Outputに設定していれば
/* output "name" { value = mysql_database.this.name } output "default_character_set" { value = mysql_database.this.default_character_set } */ output "database" { value = mysql_database.this }
モジュール利用側では、それが全部反映されます。
Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Outputs: this_database = { "database" = { "default_character_set" = "utf8mb4" "default_collation" = "utf8mb4_ja_0900_as_cs_ks" "id" = "my_database" "name" = "my_database" } }
まあ、モジュールが内部で利用しているリソースの属性を全部公開するかどうかは微妙なところもあるので、モジュール間の
データの受け渡しの時とかに活用できるといいのかな?と思いますが。
Remote Stateと組み合わせる
Remote Stateと組み合わせてみましょう。
Remote Stateのバックエンドには、Consuleを利用することにします。1.8.0のConsulを、172.17.0.3のサーバーに立てている
ものとします。
データベースの作成と、ユーザーと権限の作成をそれぞれ別々のTerraform管理単位として行ってみましょう。
まずは、データベースの作成側。
main.tf
terraform { required_version = "0.12.26" backend "consul" { address = "172.17.0.3:8500" scheme = "http" path = "terraform/state/mysql/database" } } provider "mysql" { endpoint = "172.17.0.2:3306" username = "root" password = "password" version = "1.9.0" } resource "mysql_database" "this" { name = "my_database" default_character_set = "utf8mb4" default_collation = "utf8mb4_ja_0900_as_cs_ks" } output "this_database" { value = mysql_database.this }
mysql_databaseの属性は、全部Outputとして登録しておきます。
output "this_database" {
value = mysql_database.this
}
Stateは、Consulに保存します。
terraform { required_version = "0.12.26" backend "consul" { address = "172.17.0.3:8500" scheme = "http" path = "terraform/state/mysql/database" } }
apply。
$ terraform apply
続いて、ユーザーと権限を作る側。
main.tf
terraform { required_version = "0.12.26" backend "consul" { address = "172.17.0.3:8500" scheme = "http" path = "terraform/state/mysql/user" } } provider "mysql" { endpoint = "172.17.0.2:3306" username = "root" password = "password" version = "1.9.0" } data "terraform_remote_state" "database" { backend = "consul" config = { address = "172.17.0.3:8500" scheme = "http" path = "terraform/state/mysql/database" } } resource "mysql_user" "admin_user" { user = "adminuser" plaintext_password = "password" host = "%" } resource "mysql_grant" "admin_user" { user = "adminuser" host = "%" database = data.terraform_remote_state.database.outputs.this_database.name privileges = ["ALL"] depends_on = [mysql_user.admin_user] }
データベースを作成した時のStateを、Data Sourceとして参照します。
data "terraform_remote_state" "database" { backend = "consul" config = { address = "172.17.0.3:8500" scheme = "http" path = "terraform/state/mysql/database" } }
こちらを、データベース名の取得に利用。
resource "mysql_grant" "admin_user" { user = "adminuser" host = "%" database = data.terraform_remote_state.database.outputs.this_database.name privileges = ["ALL"] depends_on = [mysql_user.admin_user] }
apply。
$ terraform apply
これで、Remote StateをData Sourceとして扱った時も確認できました、と。
Sensitive?
ところで、この方法を取った時、sensitiveの設定はどうするんでしょうね?
sensitive — Suppressing Values in CLI Output
やっぱり、こんな感じにまるっと設定することになるんでしょうかね?
output "this_database" { value = mysql_database.this sensitive = true }
apply時には、こういう出力結果になります。
Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Outputs: this_database = <sensitive>
まあ、Stateに値が保存されていること自体は変わらない(コンソールに出力されないだけ)ので、そう困りはしない…かな?
モジュールのOutputをそのまま全部出力する場合は、モジュール側のOutputsにsensitive = trueがあっても、呼び出し側が
sensitiveを設定していないと意味ないので、そこは注意ですねぇ。