Terraform を用いて Application Gateway を構築する

# reference
https://qiita.com/moaikids/items/8a7b72f51662c3cb214d

# はじめに
前回の SQL Database に引き続き、Application Gateway の構築を Terraform で行ってみます。

Application Gateway も設定項目が多く、Portal から手でポチポチ作成すると大変なので、Terraform などのツール経由で作成することをおすすめします。

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

- PublicIP / PrivateIP を割り当てた Application Gateway を構築する
- backend address pool には予めIPがわかっているサーバー２台をぶら下げる
- backend IP に対する Prove を設定する
- Cookie Affinity はオフにする等、HTTP Settings の設定を行う

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

- 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}"
}
```


## basic
基礎的な設定は以下

```main.tf
resource "azurerm_application_gateway" "ag" {
    name                = "${var.ag_name}"
    resource_group_name = "${var.resource_group_name}"
    location            = "${var.location}"
    
    sku {
        name           = "${var.ag_sku_name}"
        tier           = "${var.ag_sku_tier}"
        capacity       = "${var.ag_sku_capacity}"
    }
    
    gateway_ip_configuration {
        name         = "${var.ag_name}-ip-configuration"
        subnet_id    = "${data.azurerm_subscription.subscription.id}/resourceGroups/${var.resource_group_name}/providers/Microsoft.Network/virtualNetworks/${var.vnet_name}/subnets/${var.subnet_name}"
    }
    
    frontend_port {
        name         = "${var.ag_name}-frontend-port"
        port         = 80
    }
}
```

SKU については補足します。

### SKU の name と tier
それぞれ以下の内容を設定します。

- name
  - Standard_Small
  - Standard_Medium
  - Standard_Large
  - WAF_Medium
  - WAF_Large
- tier
  - Standard
  - WAF

機能として Standard か WAF 対応版にするかを tier で選択し、そのサイズについて name にて `(tier)_(size)` という記法で記述します。
tier で指定している内容を name でも指定するのは冗長に見えますが、このような仕組みになってます。

## frontend_ip_configuration
Application Gateway においては、Public IP と Private IP 双方を同時に割り当てることができません。
なので要件に応じて、Public IP を割り当てるのか、Private IP を割り当てるのかを、事前に決定した上で設定に反映させる必要があります。

### PublicIP
事前に、Public IP アドレスを生成する必要があります。生成した IP アドレスの情報を、frontend_ip_configuration に割り当てます。

```main.tf
resource "azurerm_public_ip" "publicip" {
    name                         = "${var.ag_name}-publicip"
    location                     = "${var.location}"
    resource_group_name          = "${var.resource_group_name}"
    public_ip_address_allocation = "dynamic"
}
```

```main.tf
    frontend_ip_configuration {
        name         = "${var.ag_name}-frontend-ip"
        public_ip_address_id = "${azurerm_public_ip.publicip.id}"
    }
```


### PrivateIP
Application Gateway のインターフェースを Private IP アドレスにする際は、Private IP の割当を行う Subnet を指定します。

```main.tf
    frontend_ip_configuration {
        name         = "${var.ag_name}-frontend-ip"
        subnet_id    = "${data.azurerm_subscription.subscription.id}/resourceGroups/${var.resource_group_name}/providers/Microsoft.Network/virtualNetworks/${var.vnet_name}/subnets/${var.subnet_name}"
        private_ip_address_allocation = "Dynamic"
    }
```

## backend_address_pool
backend_address_pool は複数割り当てができます。
ただし、以下の設定のように、Location ごとに設定を切り替えたい際に、Map の中に配列を入れることがうまくできないようです。

そのため、ここでの例では、カンマ区切りで複数IPアドレスをつなげた文字列として表現し、main.tf での呼び出し時に Split をすることで回避をしています。

```variables.tf
variable "backend_address_pools" {
  type = "map"
  default     = {
#ほんとうはこうしたい    "japaneast" = ["127.0.0.1","127.0.0.2"]
    "japaneast" = "127.0.0.1,127.0.0.2"
    "japanwest" = "127.0.0.3"
  }
}
```

```main.tf
    backend_address_pool {
        name = "${var.ag_name}-backend-pool"
        ip_address_list = "${split(",", "${lookup(var.backend_address_pools,var.location)}")}"
    }
```
## probe
backend_pool に指定した個々のアドレスに対して共通の Health Check Endpoint を指定する場合は、127.0.0.1 にすると良いようです。
たとえば、backend_address_pool に A,B,C,D の４つのアドレスが指定されていても、host = 127.0.0.1 にしておくと、A,B,C,D それぞれ同じ path を Health Check Endpoint として使用します。

この仕様は、正直直感的ではない気がしています。


```main.tf
    probe {
        name = "127.0.0.1"
        host = "127.0.0.1"
        protocol = "Http"
        path = "/health"
        interval = 5
        timeout = 30
        unhealthy_threshold = 5
    }
```

## backend_http_settings
Application Gateway についての各種振る舞いについて設定をします。たとえば Cookie Affinity の有効無効についてもこちらで設定します。以下の例では Disabled にしています。

```main.tf
    backend_http_settings {
        name                  = "${var.ag_name}-http-settings"
        cookie_based_affinity = "Disabled"
        port                  = 80
        protocol              = "Http"
        request_timeout        = 30
        probe_name = "127.0.0.1"
    }

```

Application Gateway の Portal 経由だと Connection Draining の設定なども行えるようですが、現在 2018/09/07 現在では Terraform 側での操作はできなそうです。

<https://github.com/terraform-providers/terraform-provider-azurerm/blob/26705da2d7ae310939e3155e51fd3e4aec6e65e2/azurerm/resource_arm_application_gateway.go#L265-L340>


## http_listener
上述の設定を関連付けていきます。

```main.tf
    http_listener {
        name                                  = "${var.ag_name}-http-listener"
        frontend_ip_configuration_name        = "${var.ag_name}-frontend-ip"
        frontend_port_name                    = "${var.ag_name}-frontend-port"
        protocol                              = "Http"
    }
```

## request_routing_rule
同様に、上述の設定を関連付けていきます。

```main.tf
    request_routing_rule {
          name                       = "${var.ag_name}-rule"
          rule_type                  = "Basic"
          http_listener_name         = "${var.ag_name}-http-listener"
          backend_address_pool_name  = "${var.ag_name}-backend-pool"
          backend_http_settings_name = "${var.ag_name}-http-settings"
    }
```

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

これ以外にもできることがありますので、詳しく知りたい方はドキュメントなどを参照してください。

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