Terraform で Azure SQL Server

  • Terraform
  • VirtualMachine
  • AzureDataBase
  • VNet
  • SecurityGroup
  • Azure には SQLServer の PaaS サービスが存在します。主に2種類で、Azure SQL Database と Azure SQL Database Managed Instance です。

    まずは、SQLServer に関する2つの PaaS について解説していきます。

    SQL Database と SQL Database Managed Instance

    SQLServer はサーバー(インスタンス)とデータベースという2層構造になっています。

    azure-sql-database-01-01

    本当にざっくりとしたイメージですが、Azure ではこのサーバー(インスタンス)とデータベースの両方のレイヤーで PaaS を提供しています。

    azure-sql-database-01-02

    サーバー(インスタンス)で提供されるサービスが SQL Database Managed Instance で、データベース単位で提供されるサービスが SQL Database です。

    機能の差異

    SQL Database はデータベースの PaaS ということもあり、UTC で固定、SQL Serverのネイティブバックアップを利用できない(BACKUP/RESTOREステートメントをサポートしていない)といった制限がありました。

    SQL Database Managed Instance ではそういった制限はなく設定やバックアップリストアが可能ですので、オンプレから Azure への移行についても SQL Database Managed Instance であればよりスムーズに行えるようになっています。

    詳しくは @IT さんの記事をご覧ください。各サービスの細かい違いまで説明されいますのでとても分かりやすいです。

    今回は特に難しいことはせず、SQLServer を立ててログインするくらいの確認にしたいと思いますので、SQL Database で立てたいと思います。

    azurerm_mssql と azurerm_sql

    Azure Provider を確認すると、"azurerm_mssql_" "azurerm_sql_" で始まる resource がこれだけ存在するのが分かります。

    • azurerm_mssql_database
    • azurerm_mssql_database_extended_auditing_policy
    • azurerm_mssql_database_vulnerability_assessment_rule_baseline
    • azurerm_mssql_elasticpool
    • azurerm_mssql_server
    • azurerm_mssql_server_extended_auditing_policy
    • azurerm_mssql_server_security_alert_policy
    • azurerm_mssql_server_vulnerability_assessment
    • azurerm_mssql_virtual_machin
    • azurerm_sql_active_directory_administrator
    • azurerm_sql_database
    • azurerm_sql_elasticpool
    • azurerm_sql_failover_group
    • azurerm_sql_firewall_rule
    • azurerm_sql_server
    • azurerm_sql_virtual_network_rule

    たくさんありますが、いったいどの resource を使えば良いのでしょうか。色々と調べてみました。

    azurerm_mssql系 vs azurerm_sql系

    まず、上記のリストを眺めていて大きな疑問が沸きました。それは、SQL Database に関する resource が2つ存在していたということです。

    これらはどのような違いがるのでしょうか。調べてたところ、この issue に辿り着きました。こちらの issue は2020年4月に作られたもので、以下の内容が書かれていました。

    the azurerm_mssql_database resource is the new version of azurerm_sql_database that is using a newer version of the API that supports more features. As such we have been migrating all the supported features of the old resource to the new and it should support everything that azurerm_sql_database does.

    要約すると以下のようなことです。

    azurerm_mssql_database は新しい API 機能に対応した azurerm_sql_database の新バージョンです。そのため、azurerm_sql_database の持つ全ての機能を azurerm_mssql_database へ統合させなくてはなりません。

    しかし、2021年1月時点において対応が完了していません。現在はその移行時期であり、どちらを使うべきか判断がつきません。

    とりあえず、困ったら "azurerm_mssql系" を使うこととします。

    やってみる

    では、Terraform を使って SQL Database を作ってみたいと思います。

    構成イメージ図

    azure-sql-database-01-03

    ポイント

    以下のような制限を設けます。

    • SQL Database は Virtual Machine からのみアクセスを許可する。
    • SQL Database は Availability Zone 冗長構成とする。
    • StorggeAccount に AuditLogging を設定する。

    main.tf

    Virtual Machine を作成するところまでは過去の記事「Terraform で Azure Virtual Machine」を参考ください。下記のソースコードは SQL Database に関連したものだけ掲載しています。

    Subnet

    ポイントは SQL Database を配置するサブネットに service_endpoints "Microsoft.Sql" を指定することです。

    resource "azurerm_subnet" "subnet02" {
      name                                           = "subnet02"
      virtual_network_name                           = azurerm_virtual_network.vnet01.name
      resource_group_name                            = azurerm_resource_group.test01.name
      enforce_private_link_endpoint_network_policies = false
      enforce_private_link_service_network_policies  = false
      address_prefixes                               = ["10.7.2.0/24"]
      service_endpoints                              = ["Microsoft.Sql"]
    }

    Security Group

    Virtual Machine の存在するアプリケーショングループのみから通信を許可します。

    resource "azurerm_network_security_group" "sg02" {
      name                = "sg02"
      location            = azurerm_resource_group.test01.location
      resource_group_name = azurerm_resource_group.test01.name
      security_rule {
        name                                  = "DenyALL"
        priority                              = 1002
        direction                             = "Inbound"
        access                                = "Deny"
        protocol                              = "*"
        source_port_range                     = "*"
        source_address_prefix                 = "*"
        destination_port_range                = "*"
        destination_address_prefix            = "*"
      }
      security_rule {
        name                                  = "SQLServer"
        priority                              = 1001
        direction                             = "Inbound"
        access                                = "Allow"
        protocol                              = "Tcp"
        source_port_range                     = "*"
        source_application_security_group_ids = [azurerm_application_security_group.nicsg01.id]
        destination_port_range                = "1433"
        destination_address_prefix            = "VirtualNetwork"
      }
      tags = {
        "Owner" = "koizumi",
        "Env"   = "test01"
      }
    }
    
    resource "azurerm_subnet_network_security_group_association" "sg02" {
      subnet_id                 = azurerm_subnet.subnet02.id
      network_security_group_id = azurerm_network_security_group.sg02.id
    }

    StorageAccount

    AuditLogging を設定するための準備として、StorageAccount を作成しておきます。こちらは特別な設定はしていません。

    resource "azurerm_storage_account" "test01" {
      name                     = "koizumitest01sqlserver"
      resource_group_name      = azurerm_resource_group.test01.name
      location                 = azurerm_resource_group.test01.location
      account_tier             = "Standard"
      account_replication_type = "LRS"
      tags = {
        "Owner" = "koizumi",
        "Env"   = "test01"
      }
    }

    SQL Server & SQL Database

    SQL Database のインスタンスとデータベースを作成します。

    resource "azurerm_mssql_server" "sqlserver01" {
      name                          = "koizumi-sqlserver01"
      resource_group_name           = azurerm_resource_group.test01.name
      location                      = azurerm_resource_group.test01.location
      version                       = "12.0"
      administrator_login           = "adminsqlserver"
      administrator_login_password  = "xxxxxxxxxxxxxx"
      public_network_access_enabled = true
      tags = {
        "Owner" = "koizumi",
        "Env"   = "test01"
      }
    }
    
    resource "azurerm_mssql_database" "test01" {
      name           = "test01"
      server_id      = azurerm_mssql_server.sqlserver01.id
      collation      = "SQL_Latin1_General_CP1_CI_AS"
      license_type   = "LicenseIncluded"
      max_size_gb    = 4
      read_scale     = true
      sku_name       = "BC_Gen5_2"
      zone_redundant = true
      tags = {
        "Owner" = "koizumi",
        "Env"   = "test01"
      }
    }

    VNet for SQL Server

    SQL Database を VNet に乗せます。そして、ファイヤーウォールでアクセス制限します。

    ポイントは、こちらのページ下部の "Note" に記載されています。

    The Azure feature Allow access to Azure services can be enabled by setting start_ip_address and end_ip_address to 0.0.0.0 which (is documented in the Azure API Docs).

    要約すると以下のようになります。

    start_ip_address と end_ip_address を 0.0.0.0 に指定すると、Azure Service からの通信を許可する設定となります。

    正直、わっかりづらいなと思ってしまいました。

    resource "azurerm_sql_virtual_network_rule" "sqlvnetrule01" {
      name                = "sqlvnetrule01"
      resource_group_name = azurerm_resource_group.test01.name
      server_name         = azurerm_mssql_server.sqlserver01.name
      subnet_id           = azurerm_subnet.subnet02.id
    }
    
    resource "azurerm_sql_firewall_rule" "sqlfirewall01" {
      name                = "sqlfirewall01"
      resource_group_name = azurerm_resource_group.test01.name
      server_name         = azurerm_mssql_server.sqlserver01.name
      start_ip_address    = "0.0.0.0"
      end_ip_address      = "0.0.0.0"
    }
    
    resource "azurerm_sql_firewall_rule" "sqlfirewall02" {
      name                = "sqlfirewall02"
      resource_group_name = azurerm_resource_group.test01.name
      server_name         = azurerm_mssql_server.sqlserver01.name
      start_ip_address    = azurerm_network_interface.nic01.private_ip_address
      end_ip_address      = azurerm_network_interface.nic01.private_ip_address
    }

    デプロイ

    それでは、デプロイしてみます。

    $ terraform init $ terraform apply Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes Apply complete! Resources: 9 added, 0 changed, 0 destroyed. Releasing state lock. This may take a few moments...

    動作確認

    データベースにユーザーを作成して、そのユーザーでログインした結果が、StorageAccount に監査ログとして出力されているかを確認してみたいと思います。

    Azure の WindowsServer には "Azure Data Studio" というものが内蔵されています。SSMS みたいなものですね。これを使って SQL Database にアクセスできます。

    # administrator_login でログインする。 # [use master] CREATE LOGIN xx_adm WITH PASSWORD = 'xxxxxxxxxx' GO CREATE USER xx_adm FOR LOGIN xx_adm WITH DEFAULT_SCHEMA = dbo GO # [use testdb01] CREATE USER xx_adm FOR LOGIN xx_adm WITH DEFAULT_SCHEMA = dbo GO ALTER ROLE db_owner ADD MEMBER xx_adm GO # xx_adm でログインして適当なSQLを実行しておく。 # AuditLoggings を参照するために、StotrageAccount の対象ファイルの URL を確認しておく。 SELECT * FROM sys.fn_get_audit_file ('対象ファイルの URL', null, null);

    上記の実行結果が以下です。

    azure-sql-database-01-04

    以上で SQL Database の接続確認は完了です。

    Terraform で困ったこと

    実は、Azure の SQLServer のサービスには managed instance というサービスが存在すると最初に述べましたが、該当する resource が Terraform azure provider には存在しませんでした(2021年1月5日時点)。

    また、Private Endpoint を利用したセキュアなネットワーク構成も存在するのですが、それに対応する resource も見つけることができませんでした。

    やはり、後追いで Terraform にリリースされるため仕方のないことですが、現場で Terraform を採用するかどうか判断する場合は注意した方が良いですね。

    今回は以上です。

  • Terraform
  • VirtualMachine
  • AzureDataBase
  • VNet
  • SecurityGroup