自動化自社開発サービスSaaS事例Tipsクラウド

Infrastructure as a Code実践!!ZenOneのインフラ環境をTerraformでコード化してみた2

TEDが提供している「運用保守DXプラットフォームZenOne(ゼンワン)」のインフラ環境のIaC化に関する内容になります。

みなさん、こんにちは。東京エレクトロンデバイス(以下TED)で開発エンジニアをしている T.Tanakaです。

前回は、ZenOneのインフラの構成や問題点について説明し、コード化を進める上で工夫している点(ループ処理を使ったコード整理方法)を紹介しました。今回はTEDが提供している「運用保守DXプラットフォームZenOne(ゼンワン)」のインフラ環境に関する内容になります。

モジュール間の値の受け渡し方法

今回も類似した内容にはなりますが、工夫している点の第2段ということで、モジュール間の値の受け渡し方法についてご紹介いたします。

Terraformに限らずプログラミングでもそうですが、コードは1つのファイルに全て記入しても、複数のファイルに分割して記入しても動作に変わりはありません。ですが、実際に1つのファイルで開発を行うことはほとんどないと思います。Terraformにおいては、同じサービスに属するリソースコードを1つのモジュールにまとめておくことで、環境を複製する場合や複数のサービスを組み合わせて環境を作成する際に再利用が可能になります。また、コードをグループ化してファイル分割するので、可読性や保守性の向上も期待できます。

以下はAzure上にデータベース(PostgreSQL)のサーバーを構築するための仮想ネットワークとPostgreSQLに関するコードです。


resource "azurerm_resource_group" "this" {
  name     = "test-rg"
  location = "japaneast"
}

resource "azurerm_virtual_network" "this" {
  resource_group_name = azurerm_resource_group.this.name
  location            = azurerm_resource_group.this.location
  address_space       = ["10.0.0.0/16"]
  name                = "test-vnet"
}
resource "azurerm_subnet" "this" {
  resource_group_name  = azurerm_resource_group.this.name
  virtual_network_name = azurerm_virtual_network.this.name
  address_prefixes     = ["10.0.1.0/24"]
  name                 = "psql-subnet"
  service_endpoints     = ["Microsoft.Storage"]
  delegation {
    name = "delegation_psql"
    service_delegation {
      name = "Microsoft.DBforPostgreSQL/flexibleServers"
    }
  }
}
resource "azurerm_private_dns_zone" "this" {
  name                = "testflexibleserver.private.postgres.database.azure.com"
  resource_group_name = azurerm_resource_group.this.name
}
resource "azurerm_private_dns_zone_virtual_network_link" "this" {
  name                  = "postgresqlvnetzone.com"
  private_dns_zone_name = azurerm_private_dns_zone.this.name
  resource_group_name   = azurerm_resource_group.this.name
  virtual_network_id    = azurerm_virtual_network.this.id
}
resource "azurerm_private_dns_a_record" "this" {
  resource_group_name = azurerm_resource_group.this.name
  name                = azurerm_private_dns_zone.this.name
  records             = ["10.0.0.250"]
  ttl                 = 30
  zone_name           = azurerm_private_dns_zone.this.name
}
resource "azurerm_postgresql_flexible_server" "this" {
  delegated_subnet_id   = azurerm_subnet.this.id
  private_dns_zone_id    = azurerm_private_dns_zone.this.id
  resource_group_name    = azurerm_resource_group.this.name
  location               = azurerm_resource_group.this.location
  name                   = "testflexibleserver"
  administrator_login    = "testuser"
  administrator_password = "testpassword"
  sku_name               = "B_Standard_B2s"
  version                = 15
  storage_mb             = 65536
  backup_retention_days  = "30"
  zone                   = "1"
}
resource "azurerm_postgresql_flexible_server_configuration" "this" {
  name      = "TimeZone"
  value     = "Asia/Tokyo"
  server_id = azurerm_postgresql_flexible_server.this.id
}
resource "azurerm_postgresql_flexible_server_database" "this" {
  collation = "ja_JP.utf8"
  name      = "testdb"
  server_id = azurerm_postgresql_flexible_server.this.id
}

サービスごとのファイルを分けとモジュール化

このままでもリソースは作成可能ですが、ネットワーク関連とPostgreSQL関連のコードが混ざっているので、サービスごとにファイルを分け、モジュール化してみましょう。

まず、同じ階層に「module」という名前でフォルダを作成し、さらにそのフォルダ内に「network」と「postgresql」というフォルダを作成します。作成した各フォルダ内に「main.tf」、「variables.tf」、「outputs.tf」の3ファイルを作成します。

次に、先ほどのコードのうち、network関連のコードをmodule/network/main.tfに移し、PostgreSQL関連のコードをmodule/postgresql/main.tfに移します。これでコードの分割はできました。しかし、各main.tfは別ディレクトリに配置されているため、この状態では互いの値を参照することができなくなった点に注意が必要です(azurerm_postgresql_flexible_server内のdelegated_subnet_idやprivate_dns_zone_idなど)。

Terraformでモジュール外に値を渡す方法

Terraformでは、outputというブロックを使ってモジュール外に値を渡すことができます。

それではまず、module/postgresql/main.tf内で必要となるnetwork関連の値を確認します。今回の例では、以下の項目をmodule/networkからmodule/postgresqlに渡してあげる必要があります。

  • azurerm_subnetのid(azurerm_postgresql_flexible_serverのdelegated_subnet_idで使用)
  • azurerm_private_dns_zoneのid(azurerm_postgresql_flexible_serverのprivate_dns_zone_idで使用)

また、module/network、module/postgresqlのいずれにおいても、リソースグループの情報を渡してあげる必要もあります。

まず、networkの2項目に関して、以下のようにmodule/network/outputs.tfに記載し、値を他モジュールで使用できるようにします。


# /module/network/outputs.tf

output "psql_subnet_id" {
  value = azurerm_subnet.this.id
}

output "psql_private_dns_zone_id" {
  value = azurerm_private_dns_zone.this.id
}

続いて、module/postgresql側でnetwork関連の2項目とリソースグループの情報を受け取り、azurerm_postgresql_flexible_serverブロック内でできるようにします。そのために、module/postgresql/variables.tfに以下のように記載します。合わせてmodule/networkでリソースグループの情報を受け取り、使用できるようにします。


# /module/postgresql/variables.tf

variable "resource_group" {}

variable "psql_subnet_id" {}

variable "psql_private_dns_zone_id" {}

# module/network/variables.tf

variable "resource_group {}"

実際の値の受け渡しは、大元のmain.tf(/main.tf)で行います。/main.tfを以下のように編集していきます。


# /main.tf

resource "azurerm_resource_group" "this" {
  name     = "test-rg"
  location = "japaneast"
}

module "network" {
  source = "./module/network"

  resource_group = azurerm_resource_group.this
}

module "postgresql" {
  source = "./module/postgresql"

  resource_group           = azurerm_resource_group.this
  psql_subnet_id           = module.network.psql_subnet_id
  psql_private_dns_zone_id = module.network.psql_private_dns_zone_id
}

networkモジュールはリソースグループの情報を受け取り、postgresqlモジュールはリソースグループの情報と、networkモジュールからpsql_subnet_idとpsql_private_dns_zone_idを受け取ります。では、次に受けとった値を各モジュール内で使用していきます。variableブロックで受けとった値はオブジェクト指向のようにvarのドット演算子で使用することができます。/module/network/main.tf、/module/postgresql/main.tfをそれぞれ以下のように編集します。


# module/network/main.tf

resource "azurerm_virtual_network" "this" {
  resource_group_name = var.resource_group.name
  location            = var.resource_group.location
  address_space       = ["10.0.0.0/16"]
  name                = "test-vnet"
}

resource "azurerm_subnet" "this" {
  resource_group_name  = var.resource_group.name
  virtual_network_name = azurerm_virtual_network.this.name
  address_prefixes     = ["10.0.1.0/24"]
  name                 = "psql-subnet"
  service_endpoints     = ["Microsoft.Storage"]
  delegation {
    name = "delegation_psql"
    service_delegation {
      name = "Microsoft.DBforPostgreSQL/flexibleServers"
    }
  }
}

resource "azurerm_private_dns_zone" "this" {
  name                = "testflexibleservertanaka.private.postgres.database.azure.com"
  resource_group_name = var.resource_group.name
}

resource "azurerm_private_dns_zone_virtual_network_link" "this" {
  name                  = "postgresqlvnetzone.com"
  private_dns_zone_name = azurerm_private_dns_zone.this.name
  resource_group_name   = var.resource_group.name
  virtual_network_id    = azurerm_virtual_network.this.id
}

resource "azurerm_private_dns_a_record" "this" {
  resource_group_name = var.resource_group.name
  name                = azurerm_private_dns_zone.this.name
  records             = ["10.0.0.250"]
  ttl                 = 30
  zone_name           = azurerm_private_dns_zone.this.name
}

# /module/postgresql/main.tf

resource "azurerm_postgresql_flexible_server" "this" {
  delegated_subnet_id    = var.psql_subnet_id
  private_dns_zone_id    = var.psql_private_dns_zone_id
  resource_group_name    = var.resource_group.name
  location               = var.resource_group.location
  name                   = "testflexibleservertanaka"
  administrator_login    = "testuser"
  administrator_password = "testpassword"
  sku_name               = "B_Standard_B2s"
  version                = 15
  storage_mb             = 65536
  backup_retention_days  = "30"
  zone                   = "1"
}

resource "azurerm_postgresql_flexible_server_configuration" "this" {
  name      = "TimeZone"
  value     = "Asia/Tokyo"
  server_id = azurerm_postgresql_flexible_server.this.id
}

resource "azurerm_postgresql_flexible_server_database" "this" {
  collation = "ja_JP.utf8"
  name      = "testdb"
  server_id = azurerm_postgresql_flexible_server.this.id
}

/module/network/main.tfでは、各リソースブロック内のresource_group_nameの中身をvar.resource_group.nameに、azurerm_virtual_networkブロック内のlocationの中身をvar.resource_group.locationに書き換えます。/module/network/postgresql.tfでは、delefated_subnet_idの中身をvar.psql_subnet_idに、private_dns_zone_idの中身をvar.psql_private_dns_zone_idに書き換えます。リソースグループの情報の書き換えについては/module/network/main.tfの時と同様です。

まとめ

これでコードの編集は以上です。編集した内容をまとめると、

  • moduleブロックを使用することで使用するモジュールを定義できる
  • 値の受け渡し、受け取りにはoutputブロックとvariablesブロックを使用する
  • variableブロックで定義した値を使用する際は、var.変数名 で使用できる

今回は簡単な例でしたがコードをサービスごとにモジュール化することができ、可読性も上がったと思います。次回はモジュールを用いた環境別インフラ構築方法についてご紹介します。

この記事に関連する製品・サービス

この記事に関連する記事