これは、なにをしたくて書いたもの?
Terraformのモジュールというものを、1度自分で書いてみようかな、と。
練習ですね。
Terraform Module
Terraformのモジュールは、複数のリソースをまとめて抽象化したものです。
Creating Modules - Terraform by HashiCorp
モジュールは、主に以下の3つで構成されます。
- モジュール自身のリソース定義
- モジュールを使用するための変数(Variables)
- モジュールの結果の出力定義(Outputs)
モジュールは他のモジュールを呼び出すこともできますが、シンプルに構造を保つことが推奨されているようです。
また、モジュールが単なるリソースのラッパーとなることは、避けた方がよいとされています。モジュールは、リソースよりも
抽象度を高めたものを提供すべきなようです。
リソースのラッパーのモジュールを作るくらいなら、直接リソースを使った方が良いみたいです。
モジュールは、2種類存在します。
- ルートモジュール
- ネストされたモジュール
ネストされたモジュールは、modulesディレクトリに配置されたモジュールのことを指します。
ルートモジュールは、ルートディレクトリからモジュールとして構成されたものですね。たとえば、以下のようなAWS上で
Consulクラスタを構成するモジュールです。
こういう思考だと、特に意識せずにTerraformの構成ファイルを書いた場合は、無名モジュール(?)を作っているような
感じになるんでしょうかね。
で、Configuration Languageを見ると、これは「ルートモジュール」と呼ばれるものになるようです。
Modules - Configuration Language - Terraform by HashiCorp
なるほど。
モジュールには、以下を含めるべきだとされています。
- README.md
- ライセンス
- main.tf、variables.tf、outputs.tf
必要に応じて、ネストされたモジュールも含まれることになります。
また、VariablesとOutputsには説明(description)を記載するべきであり、exampleも用意すべきだとか。
作成したモジュールは、Terraform RegistryまたはPrivate Registryで公開可能です。
Publishing Modules - Terraform by HashiCorp
また、モジュールを使うにあたっては、Registry以外のものからも参照することができます。
Module Sources - Terraform by HashiCorp
モジュールの合成に関するトピックは、こちらを参照。
Module Composition - Terraform by HashiCorp
複数のモジュールの依存関係についての考え方、マルチクラウドに対する考え方などが記載されています。
その他、モジュールを作るにあたって参考にするものは、コミュニティのものなどを見るとよいのでしょう。
terraform-community-modules · GitHub
説明はこれくらいにして、モジュールを使ってみましょう。
環境
今回の環境は、こちら。
$ terraform version Terraform v0.12.24
また、お題としてはMySQL Providerを使ってモジュールを作ることにします。
Provider: MySQL - Terraform by HashiCorp
対象となるMySQLはバージョン8.0.19、IPアドレス172.17.0.2で動作しており、rootで外部からログイン可能なものとします。
モジュールを作成する
では、さっそくモジュールを作成していきましょう。
以下に従って、
まずはモジュール用のディレクトリを作成して、main.tf、variables.tf、outputs.tfを作成。
$ mkdir -p modules/my_mysql $ touch modules/my_mysql/{main.tf,variables.tf,outputs.tf}
これ、実質はネストしたモジュールですね。
exampleも一応用意してみますが、今回はとりあえず飛ばします。
$ mkdir -p examples/my_mysql $ touch examples/my_mysql/main.tf
まずは、main.tfの中身を書いていきます。
お題として、
- MySQLデータベースを作成する
- 作成したデータベースを使う、アプリケーション用のユーザーと管理用のユーザーを作成し、権限を与える
ということをやっていきたいと思います。
modules/my_mysql/main.tf
terraform { required_version = ">= 0.12.24" required_providers { mysql = ">= 1.9.0" } } provider "mysql" { endpoint = var.mysql_endpoint username = var.mysql_admin_username password = var.mysql_admin_password } resource "mysql_database" "database" { name = var.database_name default_character_set = "utf8mb4" default_collation = "utf8mb4_ja_0900_as_cs_ks" } resource "mysql_user" "admin_user" { user = var.database_admin_user_username plaintext_password = var.database_admin_user_password host = var.database_admin_user_host } resource "mysql_grant" "admin_user" { user = mysql_user.admin_user.user host = mysql_user.admin_user.host database = mysql_database.database.name privileges = ["ALL"] } resource "mysql_user" "application_user" { user = var.database_application_user_username plaintext_password = var.database_application_user_password host = var.database_application_user_host } resource "mysql_grant" "application_user" { user = mysql_user.application_user.user host = mysql_user.application_user.host database = mysql_database.database.name privileges = ["SELECT", "INSERT", "UPDATE", "DELETE"] }
Terraformのバージョンおよび、MySQL Providerのバージョンの下限をそれぞれ指定。
terraform { required_version = ">= 0.12.24" required_providers { mysql = ">= 1.9.0" } }
Providerの設定は、Variablesにしています。
provider "mysql" { endpoint = var.mysql_endpoint username = var.mysql_admin_username password = var.mysql_admin_password }
作成するデータベース。
resource "mysql_database" "database" { name = var.database_name default_character_set = "utf8mb4" default_collation = "utf8mb4_ja_0900_as_cs_ks" }
管理用、アプリケーション用のユーザーの作成と、権限付与。
resource "mysql_user" "admin_user" { user = var.database_admin_user_username plaintext_password = var.database_admin_user_password host = var.database_admin_user_host } resource "mysql_grant" "admin_user" { user = mysql_user.admin_user.user host = mysql_user.admin_user.host database = mysql_database.database.name privileges = ["ALL"] } resource "mysql_user" "application_user" { user = var.database_application_user_username plaintext_password = var.database_application_user_password host = var.database_application_user_host } resource "mysql_grant" "application_user" { user = mysql_user.application_user.user host = mysql_user.application_user.host database = mysql_database.database.name privileges = ["SELECT", "INSERT", "UPDATE", "DELETE"] }
Variablesは、こんな感じで宣言。descriptionを書くべきなのですが、今回は省略…。
modules/my_mysql/variables.tf
variable "mysql_endpoint" { default = "localhost:3306" } variable "mysql_admin_username" { } variable "mysql_admin_password" { } variable "database_name" { } variable "database_admin_user_username" { default = "adminuser" } variable "database_admin_user_password" { default = "password" } variable "database_admin_user_host" { default = "%" } variable "database_application_user_username" { default = "appuser" } variable "database_application_user_password" { default = "password" } variable "database_application_user_host" { default = "%" }
Outputs。 データベース名やエンコーディング、Collation、ユーザー名を出力として定義します。
modules/my_mysql/outputs.tf
output "database_name" { value = mysql_database.database.name } output "database_default_character_set" { value = mysql_database.database.default_character_set } output "database_default_collation" { value = mysql_database.database.default_collation } output "admin_user_username" { value = mysql_user.admin_user.user } output "application_user_username" { value = mysql_user.application_user.user }
これで、モジュールの作成は完了です。
作成したモジュールを使う
モジュールができたので、作成したモジュールを利用する定義ファイルを書いていきます。
こんな感じで作成。
main.tf
module "my_mysql" { source = "./modules/my_mysql" ### variable mysql_endpoint = "172.17.0.2:3306" mysql_admin_username = "root" mysql_admin_password = "password" database_name = "practice" }
sourceで、モジュールが存在するディレクトリを指定します。今回は、モジュールのソースの指定方法のうち、Local Pathsを
使っていることになりますね。
あとは、モジュールが要求するVariablesを指定してあげます。こちらは、最低限の指定でいきました。
Outputsでは、このリソース定義を実行した時の出力内容を定義しますが、ここではモジュールのOutputsをそのまま出力にしています。 outputs.tf
output "database_name" { value = module.my_mysql.database_name } output "database_default_character_set" { value = module.my_mysql.database_default_character_set } output "database_default_collation" { value = module.my_mysql.database_default_collation } output "admin_user_username" { value = module.my_mysql.admin_user_username } output "application_user_username" { value = module.my_mysql.application_user_username }
「module.[モジュール名].[モジュールで定義したOutput Variable名]」で、モジュールの実行結果を利用できます、と。
output "database_name" { value = module.my_mysql.database_name }
では、「terraform init」。
$ terraform init
MySQL Provider 1.9.0がインストールされました。
$ terraform version Terraform v0.12.24 + provider.mysql v1.9.0
フォーマットの確認と、バリデーション。
$ terraform fmt -recursive -check $ terraform validate
Planを見てから
$ terraform plan
apply。
$ terraform apply
実行結果。
module.my_mysql.mysql_database.database: Creating... module.my_mysql.mysql_user.admin_user: Creating... module.my_mysql.mysql_user.application_user: Creating... module.my_mysql.mysql_user.admin_user: Creation complete after 3s [id=adminuser@%] module.my_mysql.mysql_user.application_user: Creation complete after 3s [id=appuser@%] module.my_mysql.mysql_database.database: Creation complete after 3s [id=practice] module.my_mysql.mysql_grant.application_user: Creating... module.my_mysql.mysql_grant.admin_user: Creating... module.my_mysql.mysql_grant.application_user: Creation complete after 1s [id=appuser@%:`practice`] module.my_mysql.mysql_grant.admin_user: Creation complete after 1s [id=adminuser@%:`practice`] Apply complete! Resources: 5 added, 0 changed, 0 destroyed. Outputs: admin_user_username = adminuser application_user_username = appuser database_default_character_set = utf8mb4 database_default_collation = utf8mb4_ja_0900_as_cs_ks database_name = practice
Outputsでは、モジュールの値が出力されていますね。
MySQL側も確認してみましょう。
mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | practice | | sys | +--------------------+ 5 rows in set (0.25 sec) mysql> show create database practice; +----------+------------------------------------------------------------------------------------------------------------------------------------------+ | Database | Create Database | +----------+------------------------------------------------------------------------------------------------------------------------------------------+ | practice | CREATE DATABASE `practice` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_ja_0900_as_cs_ks */ /*!80016 DEFAULT ENCRYPTION='N' */ | +----------+------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> select host, user from mysql.user where user in ('appuser', 'adminuser'); +------+-----------+ | host | user | +------+-----------+ | % | adminuser | | % | appuser | +------+-----------+ 2 rows in set (0.00 sec) mysql> show grants for adminuser@'%'; +---------------------------------------------------------+ | Grants for adminuser@% | +---------------------------------------------------------+ | GRANT USAGE ON *.* TO `adminuser`@`%` | | GRANT ALL PRIVILEGES ON `practice`.* TO `adminuser`@`%` | +---------------------------------------------------------+ 2 rows in set (0.00 sec) mysql> show grants for appuser@'%'; +-----------------------------------------------------------------------+ | Grants for appuser@% | +-----------------------------------------------------------------------+ | GRANT USAGE ON *.* TO `appuser`@`%` | | GRANT SELECT, INSERT, UPDATE, DELETE ON `practice`.* TO `appuser`@`%` | +-----------------------------------------------------------------------+ 2 rows in set (0.00 sec)
OKそうですね。
モジュールに渡す値をVariableにしてみる
先ほどは、モジュールを使っているところで直接Variableの値を指定していましたが、できればこちらも変数にしたいところです。
main.tf
module "my_mysql" { source = "./modules/my_mysql" ### variable mysql_endpoint = "172.17.0.2:3306" mysql_admin_username = "root" mysql_admin_password = "password" database_name = "practice" }
というわけで、そのように変更してみましょう。
先ほどのリソースは、いったん破棄しておきます。
$ terraform destroy
モジュールに値を渡すために、利用側にもVariableの宣言が必要になります。
variables.tf
variable "mysql_endpoint" { } variable "mysql_admin_username" { } variable "mysql_admin_password" { } variable "mysql_database_name" { }
もう、最低限に絞りました。
値は、「terraform.tfvars」で定義。
terraform.tfvars
mysql_endpoint = "172.17.0.2:3306" mysql_admin_username = "root" mysql_admin_password = "password" mysql_database_name = "practice"
Planを見て、apply。
$ terraform plan $ terraform apply
結果自体は、先ほどと同じなので割愛します。
まとめ
今回、初めてTerraformのモジュールを試してみました。
モジュールのVariableへの値の指定の方法、モジュールの利用側もVariableにしたい場合は?というところでやや混乱したり
したのですが、なんとか理解は進んだかなと思います。
あとは、もうちょっと複雑な例とかを見て慣れていきたいところですね。