これは、なにをしたくて書いたもの?
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にしたい場合は?というところでやや混乱したり
したのですが、なんとか理解は進んだかなと思います。
あとは、もうちょっと複雑な例とかを見て慣れていきたいところですね。