Terraform を用いて Azure SQL Database を構築する

# はじめに
Terraform は様々なクラウドサービスのインフラ構築をサポートしていますが、Microsoft Azure ももちろんサポートしています。

Azure の Terraform Provider も他のクラウドサービスと同様に頻繁にアップデートがされており、数日おきに新機能への追従がなされていますので、比較的安心して使える環境にあるなと思っています。

https://github.com/terraform-providers/terraform-provider-azurerm/releases

今回は、Terraform による Azure 環境構築の例として、SQL Database の構築について書きたいと思います。

# 構築シナリオ
今回は以下を構築・設定します。

- SQL Server / SQL Database を同時に構築する
	- Primary と同時に、1台の Secondary (geo-replication) を構築する
- Virtual Network Service Endpoint を設定する
- Firewall Rule を設定する

# 設定
## 前提
以下の環境はすでに構築済みとします。

- resource group
- virtual network
- subnet


当記事では、上記構築後に、上記環境を使用して構築を行うものとします。

```main.tf
provider "azurerm" {
    version = "~> 1.13.0"
}

resource "azurerm_resource_group" "rg" {
    name     = "${var.resource_group_name}"
    location = "${var.location}"
}

resource "azurerm_virtual_network" "vnet" {
    name                = "${var.vnet_name}"
    address_space       = ["${var.ip_range}"]
    location            = "${azurerm_resource_group.rg.location}"
    resource_group_name = "${azurerm_resource_group.rg.name}"
}

resource "azurerm_subnet" "subnet" {
    name                 = "${var.subnet_name}"
    resource_group_name  = "${azurerm_resource_group.rg.name}"
    virtual_network_name = "${azurerm_virtual_network.vnet.name}"
    address_prefix       = "${var.ip_subnet}"
}
```

## SQL Server / SQL Database を同時に構築する
SQL Database は、ノードインスタンスとしての SQL Server と、論理的なデータベースの設定をつかさどる SQL Database の組み合わせで動作するようになっています。
(Azure Database for PostgreSQL なども似たような構成になっています)

そのため、SQL Server と SQL Database、双方を構築する必要があります。

ここでは、SQL Server / SQL Database の組み合わせを、 Primary / Secondary ともに構築を行います。あわせて Primary / Secondary 間の geo-replication の設定も行います。


```main.tf
# sql server
resource "azurerm_sql_server" "server_primary" {
    name                         = "${var.sqlserver_name_primary}"
    resource_group_name          = "${var.resource_group_name}"
    location                     = "${var.location}"
    version                      = "12.0"
    administrator_login          = "${var.sqldatabase_username}"
    administrator_login_password = "${var.sqldatabase_password}"
}

resource "azurerm_sql_server" "server_secondary" {
    name                         = "${var.sqlserver_name_secondary}"
    resource_group_name          = "${var.resource_group_name}"
    location                     = "${var.location}"
    version                      = "12.0"
    administrator_login          = "${var.sqldatabase_username}"
    administrator_login_password = "${var.sqldatabase_password}"
}

# sql database
resource "azurerm_sql_database" "db_primary" {
    name                             = "${var.db_name}"
    resource_group_name              = "${var.resource_group_name}"
    location                         = "${var.location}"
    edition                          = "${var.db_edition}"
    requested_service_objective_name = "${var.db_objective_name}"
    server_name                      = "${azurerm_sql_server.server_primary.name}"
    create_mode                      = "Default"
}

resource "azurerm_sql_database" "db_secondary" {
    name                             = "${var.db_name}"
    resource_group_name              = "${var.resource_group_name}"
    location                         = "${var.location}"
    edition                          = "${var.db_edition}"
    requested_service_objective_name = "${var.db_objective_name}"
    server_name                      = "${azurerm_sql_server.server_secondary.name}"
    create_mode                      = "OnlineSecondary"
    source_database_id               = "${azurerm_sql_database.db_primary.id}"
}
```

ポイントをいくつか補足します。

### edition / requested_service_objective_name

SQL Database の size を指定します。

Edition ではサービス階層を指定します。(Basic / Standard / Premium)

requested_service_objective_name では、各サービス階層ごとのサイズを指定します。(S4とか、P6とか)

### server_name
SQL Database の server_name の設定で、使用する SQL Server ノードを指定します。
同じ Terraform ファイルで生成しているのであれば以下のような形で動的に参照できます。

```
server_name                      = "${azurerm_sql_server.server_primary.name}"
```

### geo-replication の設定
作成する SQL Database を geo-replication の secondary とする場合は、secondary に以下の設定を行います。

- create_mode を "OnlineSecondary" にする
- source_database_id に primary となる SQL Database の id を指定する

なお、今回は同一 location で geo-replication を行っていますが、異なる location の SQL Server 間で geo-replication を行うことで拠点間分散を行うことができます。

## Virtual Network Service Endpoint を設定する
<https://docs.microsoft.com/ja-jp/azure/sql-database/sql-database-vnet-service-endpoint-rule-overview>

SQL Database は基本的に Internet Facing なインターフェースを持っていますが、Virutal Network Service Endpoint を設定することで特定の Virtual Network 経由で Internal で接続を行う事ができるようになります。

Azure 上のリソースから　SQL Database の接続を行う場合は、必ず設定すべき項目になるかなと思います。

```main.tf
data "azurerm_subscription" "subscription" {}

# network
## primary
resource "azurerm_sql_virtual_network_rule" "sqlvnetrule_primary" {
    name                = "${var.vnet_rule_primary}"
    resource_group_name = "${var.resource_group_name}"
    server_name         = "${var.sqlserver_name_primary}"
    subnet_id           = "${data.azurerm_subscription.subscription.id}/resourceGroups/${var.resource_group_name}/providers/Microsoft.Network/virtualNetworks/${var.vnet_name}/subnets/${var.subnet_name}"
}

## secondary
resource "azurerm_sql_virtual_network_rule" "sqlvnetrule_secondary" {
    name                = "${var.vnet_rule_secondary}"
    resource_group_name = "${var.resource_group_name}"
    server_name         = "${var.sqlserver_name_secondary}"
    subnet_id           = "${data.azurerm_subscription.subscription.id}/resourceGroups/${var.resource_group_name}/providers/Microsoft.Network/virtualNetworks/${var.vnet_name}/subnets/${var.subnet_name}"
}
```

ポイントをいくつか補足します。

### azurerm_subscription
subnet_id の指定では、Azure の Subscription 情報も含めた ID 情報を記載する必要がありますが、Azure の subscription 情報を手で書くのは面倒なのですし、設定中に記載すると情報漏えいのリスクもあります。
こういう際は、Terraform 実行ユーザー環境の subscription 情報を取得する data `azurerm_subscription ` を使用すると便利です。

```
data "azurerm_subscription" "subscription" {}
```

と宣言した上で、以下のような形で記述すると subscription 情報が取得できます。

```
${data.azurerm_subscription.subscription.id}
```

## Firewall Rule を設定する
上記で設定した Virtual Network Service Endpoint 以外に、例えば開発用のデータベースであれば社内の開発環境からも接続したい、というケースもあるとは思います。
その際は、よく案内されている Firewall Rule の設定により、接続設定を行います。

以下は、Secondary のみ、任意の外部 IP から接続できるようにした設定の例です。

```main.tf
resource "azurerm_sql_firewall_rule" "fw_secondary" {
    name                = "${var.firewall_rule_name}"
    resource_group_name = "${var.resource_group_name}"
    server_name         = "${azurerm_sql_server.server_secondary.name}"
    start_ip_address    = "${var.firewall1_rule_ip_address_start}"
    end_ip_address      = "${var.firewall1_rule_ip_address_end}"
}
```

## まとめ
上記のような形で、Terraform で SQL Database の構築を行う事ができます。
手で作業すると面倒なことも、多くの部分が自動化できているのではないかと思います。

上記以外にも、たとえばユーザー認証を Active Directory に移譲したい等、Terraform 経由で設定できる機能は他にもあります。必要に応じて公式ドキュメントなども参考に、設定を行っていきましょう。

<https://www.terraform.io/docs/providers/azurerm/r/sql_server.html>
<https://www.terraform.io/docs/providers/azurerm/r/sql_database.html>