理系学生日記

おまえはいつまで学生気分なのか

Terraformで学ぶAzure Database for MySQL

Azure Database for MySQLをTerraformで構築してみます。

Terraformの構成

サーバとデータベース

Azure Database for MySQLを構築する際、最低限必要になるのはazurerm_mysql_serverazurerm_mysql_databaseでしょう。

リソース名が示唆する通り、Azure Database for MySQLは物理的なサーバと、その上で動作するデータベースの2種類になります。サーバ側はSKUやストレージサイズ、冗長性やネットワークアクセスに関する設定を指定します。一方でデータベース設定はその上で動くデータベースのcharset等を指定することになります。

resource "azurerm_mysql_server" "this" {
  name                = var.server_name
  location            = var.location
  resource_group_name = var.resource_group_name

  sku_name   = var.sku_name
  storage_mb = var.storage_mb
  version    = var.mysql_version
  tags       = var.tags

  administrator_login          = var.administrator_login
  administrator_login_password = var.administrator_login_password

  auto_grow_enabled                 = true
  backup_retention_days             = var.backup_retention_days
  geo_redundant_backup_enabled      = var.geo_redundant_backup_enabled
  infrastructure_encryption_enabled = var.infrastructure_encryption_enabled
  public_network_access_enabled     = var.public_network_access_enabled
  ssl_enforcement_enabled           = var.ssl_enforcement_enabled
  ssl_minimal_tls_version_enforced  = var.ssl_enforcement_enabled? var.ssl_minimal_tls_version_enforced : "TLSEnforcementDisabled"

  identity {
    type = "SystemAssigned"
  }
}

resource "azurerm_mysql_database" "this" {
  name                = var.database_name
  resource_group_name = var.resource_group_name
  server_name         = azurerm_mysql_server.this.name
  charset             = var.charset
  collation           = var.collation
}

ネットワークアクセス制御

ネットワークアクセスはazurerm_mysql_firewall_ruleazurerm_mysql_virtual_network_ruleで絞る形になります。

ファイアウォールルール

azurerm_mysql_firewall_ruleはインターネットやAzure自体からのネットワークアクセスの許可・非許可を制御します。 図としてはAzure Database for MySQL サーバーのファイアウォール規則に記載の以下の図がわかりやすいでしょう。

ここでは、以下のような形でIPアドレスの範囲をfrom-toで複数指定することを前提にしてみます。

permit_ip_ranges = {
  tokyo = ["203.0.113.0", "203.0.113.5"]
  osaka = ["203.0.113.129", "203.0.113.130"]
}

そうすると、ファイアウォールルールの指定は以下のようになるでしょうか。

resource "azurerm_mysql_firewall_rule" "this" {
  for_each = var.permit_ip_ranges

  name                = format("%s-fwrule-%s", var.server_name, each.key)
  resource_group_name = var.resource_group_name
  server_name         = azurerm_mysql_server.this.name
  start_ip_address    = each.value[0]
  end_ip_address      = each.value[1]
}

なお、Azure ServiceのInternal IPから接続を許可する場合は0.0.0.0を指定する仕様になっています。 ただしこの場合、他のAzureユーザーのSubscriptionからも接続できてしまうので、当該設定をすることはほとんどないでしょう。

すべての Azure データセンターの IP アドレスからの接続を有効にすることを検討できます。 この設定は、Azure portal から [接続のセキュリティ] ウィンドウで、 [Azure サービスへのアクセス許可] オプションを [オン] にし、 [保存] を押すことで設定できます。 Azure CLI からは、ファイアウォール規則の設定で開始アドレスと終了アドレスを 0.0.0.0 にすることで同じことができます。

Azure Database for MySQL サーバーのファイアウォール規則

ちなみに、azurerm_mysql_server.public_network_access_enabledは、Azureコンソール上の「パブリックネットワークアクセスの拒否」設定に対応しています。この値をfalseにすると、ファイアウォール規則は無効となり、設定を入れても接続はできません。

また、この値をfalseにしている状態だと、ファイアウォールルールの変更をしようと思っても以下のようなエラーが返却されます。

│ Error: deleting Firewall Rule: (Name "kiririmode-mysql-fwrule-home" / Server Name "kiririmode-mysql" / Resource Group "rg_kiririmode_mysql"): mysql.FirewallRulesClient#Delete: Failure sending request: StatusCode=0 -- Original Error: Code="FeatureSwitchNotEnabled" Message="Requested feature is not enabled"

VNETサービスエンドポイント

また、VNetからの接続の可否はVNetサービスエンドポイントに対応するazurerm_mysql_virtual_network_ruleで制御します。

Terraformでは、接続を許可するサブネットのIDを渡す形の制御になります。

resource "azurerm_mysql_virtual_network_rule" "example" {
  for_each = var.vnet_rules

  name                = format("%s-vnetrule-%s", var.server_name, each.key)
  resource_group_name = var.resource_group_name
  server_name         = azurerm_mysql_server.this.name
  subnet_id           = each.value
}

接続確認

インターネットからの接続

ファイアウォールルールを設定して、接続確認をしてみました。 もちろん、azurerm_mysql_server.public_network_access_enabledにはtrueを設定しています。

自分のグローバルIPアドレスを設定すると、確かに自分の端末からのMySQL接続が可能になります。

$ mysql --version
mysql  Ver 8.0.25 for macos11.3 on x86_64 (Homebrew)
$ mysql -h kiririmode-mysql.mysql.database.azure.com -u 'kiririmode@kiririmode-mysql' -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 64455
Server version: 5.6.47.0 Source distribution

逆にファイアウォールルールを外すと、認証後エラーになります。

$ mysql -h kiririmode-mysql.mysql.database.azure.com -u 'kiririmode@kiririmode-mysql' -p
Enter password:
ERROR 9000 (HY000): Client with IP address 'xxx.xxx.xxx.xxx' is not allowed to connect to this MySQL server.

VNETからの接続

VNETサービスエンドポイント経由での接続を試してみます。 まずはサービスエンドポイントを設定している場合。以下のように、接続できていることがわかります。

$ uname -a
Linux reachable-machine 5.4.0-1048-azure #50~18.04.1-Ubuntu SMP Fri May 14 15:30:12 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
$ mysql --version
mysql  Ver 14.14 Distrib 5.7.34, for Linux (x86_64) using  EditLine wrapper
$ mysql -h kiririmode-mysql.mysql.database.azure.com -u 'kiririmode@kiririmode-mysql' -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 64533
Server version: 5.6.47.0 Source distribution

ここから、MySQL側のサービスエンドポイント設定を外してみます。 同一VMに接続した上でMySQL接続を試すと、対象VNETからの接続が許可されていないとしてエラーになることがわかります。

$ mysql -h kiririmode-mysql.mysql.database.azure.com -u 'kiririmode@kiririmode-mysql' -p
Enter password:
ERROR 9009 (28000): Client from Azure Virtual Networks is not allowed to access the server. Please make sure your Virtual Network is correctly configured.

まとめ

というわけで、TerraformからAzure Database for MySQLの構築を試してみました。 このほか、azurerm_mysql_configurationでサーバーパラメータを管理できるなど、色々とIaCで管理できそうです。