CLOVER🍀

That was when it all began.

Terraformモジュールのインスタンスとリソースのスコープを確認したい

これは、なにをしたくて書いたもの?

Terraformのモジュールというものを、この前初めて書いてみたのですが、同じリソース定義を別のモジュールに含めた時に、
どういう動きになるのかな?というのを確認してみたいと思いまして。

いろいろ、バリエーションを見ていきましょう。

環境

今回の環境は、こちら。

$ terraform version
Terraform v0.12.24

また、TerraformのProviderとしてはMySQLを使用します。MySQLのバージョンは8.0.20で、動作しているサーバーのIPアドレスは
172.17.0.2とします。

まずは、ふつうに使う

最初は、なにも考えずにリソース定義をしてみます。最小でいこうと思うので、MySQLのデータベースを定義するだけにします。
main.tf

terraform {
  required_version = ">= 0.12.24"
}

provider "mysql" {
  endpoint = "172.17.0.2:3306"
  username = "root"
  password = "password"

  version = "1.9.0"
}

resource "mysql_database" "database" {
  name = "my_database"
}

init。

$ terraform init

インストールされたMySQL Providerのバージョン。

$ terraform version
Terraform v0.12.24
+ provider.mysql v1.9.0

とりあえず。

$ terraform fmt -check -recursive -diff
$ terraform validate

applyしてみます。

$ terraform apply -auto-approve

これはうまくいくので、いったんdestroy。

$ terraform destroy -force

同じ名前のリソースを定義する

結果は見えていますが、同じ名前(resource type name)のリソースを定義してみます。
main.tf

terraform {
  required_version = ">= 0.12.24"
}

provider "mysql" {
  endpoint = "172.17.0.2:3306"
  username = "root"
  password = "password"

  version = "1.9.0"
}

resource "mysql_database" "database" {
  name = "my_database"
}

resource "mysql_database" "database" {
  name = "my_database"
}

planで確認。

$ terraform plan

Error: Duplicate resource "mysql_database" configuration

  on main.tf line 28:
  28: resource "mysql_database" "database" {

A mysql_database resource named "database" was already declared at
main.tf:24,1-37. Resource names must be unique per type in each module.

「モジュール内で、リソースの名前はユニークにしなければならない」と言われていますね。

これで、ある程度このあとの展開が見えた気がします。

モジュールにしてみる

次に、MySQLデータベースの定義をモジュールに切り出してみます。
modules/my-mysql/main.tf

resource "mysql_database" "database" {
  name = "my_database"
}

モジュールを利用。
main.tf

terraform {
  required_version = ">= 0.12.24"
}

provider "mysql" {
  endpoint = "172.17.0.2:3306"
  username = "root"
  password = "password"

  version = "1.9.0"
}

module "my-mysql" {
  source = "./modules/my-mysql"
}

initして

$ terraform init

apply。

$ terraform apply -auto-approve

これはうまくいくので、destroyします。

$ terraform destroy -force

モジュールのインスタンスを複数にしてみる

同じモジュールは、複数回インスタンス化できるようです。

Modules - Configuration Language - Terraform by HashiCorp

Multiple Instances of a Module

ちょっと、これを試してみましょう。
main.tf

terraform {
  required_version = ">= 0.12.24"
}

provider "mysql" {
  endpoint = "172.17.0.2:3306"
  username = "root"
  password = "password"

  version = "1.9.0"
}

module "my-mysql" {
  source = "./modules/my-mysql"
}

module "my-mysql2" {
  source = "./modules/my-mysql"
}

同じモジュールを、別名で取り込みました。

initして

$ terraform init

planを見てみます。

$ terraform 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:

  # module.my-mysql.mysql_database.database will be created
  + resource "mysql_database" "database" {
      + default_character_set = "utf8"
      + default_collation     = "utf8_general_ci"
      + id                    = (known after apply)
      + name                  = "my_database"
    }

  # module.my-mysql2.mysql_database.database will be created
  + resource "mysql_database" "database" {
      + default_character_set = "utf8"
      + default_collation     = "utf8_general_ci"
      + id                    = (known after apply)
      + name                  = "my_database"
    }

Plan: 2 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

予想はしていましたが、リソース定義のtype、nameが同じでも、モジュールのインスタンスが異なると別の扱いになるわけですね。

applyしてみます。

$ terraform apply -auto-approve

同じ名前のMySQLデータベースは複数個作れないので、同名のデータベースを2つ作ろうとして失敗します。

Error: Error 1007: Can't create database 'my_database'; database exists

  on modules/my-mysql/main.tf line 1, in resource "mysql_database" "database":
   1: resource "mysql_database" "database" {

なので、こういうことにならないように注意しましょう、ということになりますね。

destroyして破棄。

$ terraform destroy -force

同じモジュールをsourceにしたモジュールを、2つ用意する

最後に、別のモジュールが同じモジュールを参照する形で定義してみましょう。

2つモジュールを用意。中身は同じです。
modules/mysql1/main.tf

module "my-mysql" {
  source = "../my-mysql"
}

こちらも同じ。
modules/mysql2/main.tf

module "my-mysql" {
  source = "../my-mysql"
}

この2つのモジュールを読み込みます。
main.tf

terraform {
  required_version = ">= 0.12.24"
}

provider "mysql" {
  endpoint = "172.17.0.2:3306"
  username = "root"
  password = "password"

  version = "1.9.0"
}

module "mysql1" {
  source = "./modules/mysql1"
}

module "mysql2" {
  source = "./modules/mysql2"
}

initして

$ terraform init

planを見てみます。

$ terraform 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:

  # module.mysql1.module.my-mysql.mysql_database.database will be created
  + resource "mysql_database" "database" {
      + default_character_set = "utf8"
      + default_collation     = "utf8_general_ci"
      + id                    = (known after apply)
      + name                  = "my_database"
    }

  # module.mysql2.module.my-mysql.mysql_database.database will be created
  + resource "mysql_database" "database" {
      + default_character_set = "utf8"
      + default_collation     = "utf8_general_ci"
      + id                    = (known after apply)
      + name                  = "my_database"
    }

Plan: 2 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

これをapplyした場合、やはり失敗します。

$ terraform apply -auto-approve
module.mysql1.module.my-mysql.mysql_database.database: Creating...
module.mysql2.module.my-mysql.mysql_database.database: Creating...
module.mysql1.module.my-mysql.mysql_database.database: Creation complete after 0s [id=my_database]

Error: Error 1007: Can't create database 'my_database'; database exists

  on modules/my-mysql/main.tf line 1, in resource "mysql_database" "database":
   1: resource "mysql_database" "database" {

確認できたので、destroy。

$ terraform destroy -force

まとめ

これで、リソース定義のスコープが確認できましたね。

  • リソース定義は、同じモジュールでtype、nameの組み合わせでユニークにしなければならない
  • モジュールが別になると、この制約はなくなるが、整合性が取れるようにしなければならない

ということですね。