これは、なにをしたくて書いたもの?
Terraformで、同じ種類のProviderであってもエイリアスを使って異なる構成のProviderとして定義し、適用するリソースや
モジュールに応じてどのProviderを使うか選択することができます。
Provider Configuration / alias: Multiple Provider Configurations
Resources / provider: Selecting a Non-default Provider Configuration
Modules / Providers Within Modules
機能としてあることは知っていたのですが、使ったことがなかったので、今回試してみることにしました。
お題
MySQL Providerを使って、データベースを作ることにします。
Provider: MySQL - Terraform by HashiCorp
MySQL: mysql_database - Terraform by HashiCorp
この時に、MySQL Providerを複数構成して、異なるMySQLサーバーにデータベースを作成していってみましょう。
環境
今回の環境は、こちら。
$ terraform version Terraform v0.13.4 + provider registry.terraform.io/terraform-providers/mysql v1.9.0
MySQLは8.0.21を使用し、Providerごとに172.17.0.2、172.17.0.3の2つのMySQL Serverを用意しているものとします。
リソースごとにProviderを指定する
まずは、リソースごとにProviderを指定してみましょう。
Provider Configuration / alias: Multiple Provider Configurations
Resources / provider: Selecting a Non-default Provider Configuration
こんな感じで。
main.tf
terraform { required_version = "0.13.4" required_providers { mysql = { source = "terraform-providers/mysql" version = "1.9.0" } } } provider "mysql" { endpoint = "172.17.0.2:3306" username = "root" password = "password" } provider "mysql" { alias = "secondary" endpoint = "172.17.0.3:3306" username = "root" password = "password" } resource "mysql_database" "app1" { name = "my_database" } resource "mysql_database" "app2" { provider = mysql.secondary name = "other_database" }
Providerの定義を、2つ用意しました。
provider "mysql" { endpoint = "172.17.0.2:3306" username = "root" password = "password" } provider "mysql" { alias = "secondary" endpoint = "172.17.0.3:3306" username = "root" password = "password" }
alias
がついていないものを、Default Providerと呼ぶようです。
Default Provider Configurations
provider "mysql" { endpoint = "172.17.0.2:3306" username = "root" password = "password" }
リソースやモジュールで、明示的に利用するProviderが指定されていない場合は、このDefault Providerが使用されます。
通常は、こちらを使用しているでしょうね。
もうひとつは、alias
を指定した代替のProvider。
Referring to Alternate Provider Configurations
provider "mysql" { alias = "secondary" endpoint = "172.17.0.3:3306" username = "root" password = "password" }
今回は、こちらをsecondary
と名付けました。
あとは、リソースに対してProviderを指定します。
Selecting Alternate Provider Configurations
Resources / provider: Selecting a Non-default Provider Configuration
resource "mysql_database" "app1" { name = "my_database" } resource "mysql_database" "app2" { provider = mysql.secondary name = "other_database" }
ひとつ目のmysql_database
リソースはDefault Providerを使用し、もうひとつは代替のProviderを使用します。
代替のProviderを指定しているのは、この箇所ですね。
provider = mysql.secondary
これでterraform apply
すると
$ terraform apply
172.17.0.2の方はmy_database
が作成され、
mysql> show databases where `Database` like '%database%'; +-------------+ | Database | +-------------+ | my_database | +-------------+ 1 row in set (0.00 sec)
172.17.0.3の方はother_database
が作成されます。
mysql> show databases where `Database` like '%database%'; +----------------+ | Database | +----------------+ | other_database | +----------------+ 1 row in set (0.00 sec)
OKですね。
では、リソースを破棄しておきます。
$ terraform destroy
モジュールごとにProviderを指定する
次は、モジュールに対してProviderを指定してみましょう。
Modules / Providers Within Modules
モジュールに対しても、リソースと同じように使用するProviderを指定するか、指定しない場合はDefault Providerを使用することに
なります。
ちなみに、Providerの構成を定義するのはルートモジュールとなり、ルートモジュールから呼び出されるモジュールは、
ルートモジュールから構成済みのProviderを受け取るようにするのが良いようです。
Selecting Alternate Provider Configurations
In most cases, only root modules should define provider configurations, with all child modules obtaining their provider configurations from their parents.
というわけで、先にモジュールを作成します。
modules/database/main.tf
variable "name" { type = string } resource "mysql_database" "app" { name = var.name }
modules/database/versions.tf
terraform { required_version = ">= 0.13.4" required_providers { mysql = { source = "terraform-providers/mysql" version = ">= 1.9.0" } } }
ルートモジュール側。
main.tf
terraform { required_version = "0.13.4" required_providers { mysql = { source = "terraform-providers/mysql" version = "1.9.0" } } } provider "mysql" { endpoint = "172.17.0.2:3306" username = "root" password = "password" } provider "mysql" { alias = "secondary" endpoint = "172.17.0.3:3306" username = "root" password = "password" } module "database1" { source = "./modules/database" name = "my_database" } module "database2" { source = "./modules/database" providers = { mysql = mysql.secondary } name = "other_database" }
Providerの構成は、リソースの時の例と同じです。
provider "mysql" { endpoint = "172.17.0.2:3306" username = "root" password = "password" } provider "mysql" { alias = "secondary" endpoint = "172.17.0.3:3306" username = "root" password = "password" }
モジュールへの適用は、なにも指定しないとそのモジュールはDefault Providerを使い、明示的にProviderを指定した場合は
そのProviderを使います。
module "database1" { source = "./modules/database" name = "my_database" } module "database2" { source = "./modules/database" providers = { mysql = mysql.secondary } name = "other_database" }
今回はdatabase2
でProviderを指定しているわけですが、モジュールの場合はproviders
ブロックで使用するProviderを
指定します。
providers = { mysql = mysql.secondary }
この状態で、実行。
$ terraform apply
結果は、先ほどのリソースでの例と同じです。
## 172.17.0.2 mysql> show databases where `Database` like '%database%'; +-------------+ | Database | +-------------+ | my_database | +-------------+ 1 row in set (0.00 sec) ## 172.17.0.3 mysql> show databases where `Database` like '%database%'; +----------------+ | Database | +----------------+ | other_database | +----------------+ 1 row in set (0.00 sec)
実質的な定義は同じなので、そりゃあそうだという感じですね。
では、リソースを破棄しておきます。
$ terraform destroy
ひとつのモジュールに、複数のProviderを指定する
先ほどは、モジュールそれぞれにProviderを明示的に、もしくは暗黙的に指定しました。
今度は、ひとつのモジュールに対して複数のProviderをまとめて指定してみます。
この場合、モジュール自体がいくつProviderを要求するかを宣言することになります。
modules/database/main.tf
variable "name" { type = string } provider "mysql" { alias = "one" } provider "mysql" { alias = "two" } resource "mysql_database" "app1" { provider = mysql.one name = var.name } resource "mysql_database" "app2" { provider = mysql.two name = var.name }
今回のモジュールでは2つのProviderを宣言し
provider "mysql" { alias = "one" } provider "mysql" { alias = "two" }
リソースにそれぞれ適用します。
resource "mysql_database" "app1" { provider = mysql.one name = var.name } resource "mysql_database" "app2" { provider = mysql.two name = var.name }
バージョン指定。
modules/database/versions.tf
terraform { required_version = ">= 0.13.4" required_providers { mysql = { source = "terraform-providers/mysql" version = ">= 1.9.0" } } }
ルートモジュール側。
main.tf
terraform { required_version = "0.13.4" required_providers { mysql = { source = "terraform-providers/mysql" version = "1.9.0" } } } provider "mysql" { # alias = "default" endpoint = "172.17.0.2:3306" username = "root" password = "password" } provider "mysql" { alias = "secondary" endpoint = "172.17.0.3:3306" username = "root" password = "password" } module "database" { source = "./modules/database" providers = { mysql.one = mysql # mysql.one = mysql.default mysql.two = mysql.secondary } name = "my_database" }
Defaut Providerと、もうひとつProviderを定義し
provider "mysql" { # alias = "default" endpoint = "172.17.0.2:3306" username = "root" password = "password" } provider "mysql" { alias = "secondary" endpoint = "172.17.0.3:3306" username = "root" password = "password" }
モジュールに、複数のProviderを指定します。なので、providers
なのでしょうね。
module "database" { source = "./modules/database" providers = { mysql.one = mysql # mysql.one = mysql.default mysql.two = mysql.secondary } name = "my_database" }
#
でコメントアウトしていますが、Default Providerではなくalias
を指定し
provider "mysql" { alias = "default" endpoint = "172.17.0.2:3306" username = "root" password = "password" }
providers
ブロックでalias
を指定してもOKです。
providers = {
mysql.one = mysql.default
mysql.two = mysql.secondary
}
これでTerraformを実行すると
$ terraform apply
172.17.0.2、172.17.0.3の両方のMySQLサーバーに同じ名前のデータベースができます。
mysql> show databases where `Database` like '%database%'; +-------------+ | Database | +-------------+ | my_database | +-------------+ 1 row in set (0.00 sec)
ひとつのモジュールに対してProviderは複数指定していても
resource "mysql_database" "app1" { provider = mysql.one name = var.name } resource "mysql_database" "app2" { provider = mysql.two name = var.name }
モジュールの中のリソース定義はそれぞれProviderを指定しつつ、同じname
変数を使っているのでこうなります。
resource "mysql_database" "app1" { provider = mysql.one name = var.name } resource "mysql_database" "app2" { provider = mysql.two name = var.name }
これを変えようとすると、Providerに応じたname
に相当する変数を用意しないといけないわけで。
また、モジュールがいくつProviderを受け取るかも決まっているので、この点でも自由度があるわけではないですね。
任意の数のProviderを受け取って、その分だけ同じリソースを作る、といった使い方はできません。
たとえばモジュール側の定義をこのようにして
variable "name" { type = string } provider "mysql" { alias = "one" } provider "mysql" { alias = "two" } resource "mysql_database" "app" { name = var.name }
ルートモジュール側でこのように呼び出しても、この場合に使われるのはDefault Providerです(alias = one
の方が使われました)。
module "database" { source = "./modules/database" providers = { mysql.one = mysql mysql.two = mysql.secondary } name = "my_database" }
なので、こういう使い方をしても、Provider分だけ作成できるリソースが繰り返しのように増えるわけではなく、あくまで
どのProviderでどのリソースを作るかは事前に決めておく必要があります、と。
あとはInput Variablesでのコントロールですね。
一応、任意の数だけProviderを取れるようにできることを希望するようなIssueはありますが…どうなるでしょう?
Ability to pass providers to modules in for_each · Issue #24476 · hashicorp/terraform · GitHub
まあ、だいたい雰囲気はわかったので、今回はこれでOKとしましょう。