【Azure】Terraform on Azure ～VM構築編～

# はじめに
今までAzureリソースをデプロイする際には、Azure Portalからリソースをデプロイしていました。
しかし、Terraformを利用する事で構築作業時のミスや構成管理においての効率が高くなるので
私もTerraformを勉強していこうと、本記事を備忘録として残しました。
Terraform on Azureは情報も少ないと感じたので、誰かの参考になれば幸いです。

# 今回のゴール
・リソースグループ作成
・Network Interface作成
・Virtual Machines作成
・Virtual Network（subnet含む）作成
・Network Security Group作成
・Managed Disk作成

## provider.tf作成
どのクラウドプラットフォームにデプロイするのかを指定するためのファイル

```Terraform:provider.tf
# 利用するプロバイダー&バージョン指定

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=3.0.0"
    }
  }
}

# Azure プロバイダーの設定

provider "azurerm" {
  skip_provider_registration = true
  features {}
}
```

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs

## Variable（関数）も利用したいのでファイル作成
関数を利用したい場合に使いますが、使わなくてもデプロイできます。
コードの再利用をしていくのであれば、設定した方が使い勝手が良いです。

```Terraform:variable.tf
variable "env" {
  type        = string
}
```

## local（変数）も使う
単純な変数を指定するファイルです。
毎度リソースネームを入力するのが大変&ミスを防ぐ為に設定していきます。

```Terraform:local.tf
locals {
  
  location = "Japaneast"

  #ネットワークリソース変数  
  
  vnet_name = "demo_vnet_01"
  public_subnet_name = "public"
  private_subnet_name = "private"
  nsg_name = "demo_nsg_01"
  nsg_name_rdp = "RDP" 

  #VMリソース変数

  nic_name = "demo_nic_01"
  vm_name = "demo_vm_01"
  admin_name = "azureuser01"
  admin_password = "Password01?!"
}
```

# 実際のリソースをコード化していく
ここからリソースに対する細かい設定項目の指定などをしていきます。
基本的には、Hashi Corp社の公式情報を参考に記載していきます。
設定必須な項目と必須でない項目が分かれているので、詳細は下記URLを確認してください。


https://registry.terraform.io/providers/hashicorp/azurerm/latest


## リソースグループ作成

```Terraform:rg.tf

#リソースグループ作成

resource "azurerm_resource_group" "rg" {
  name     = "rg-${var.env}"
  location = local.location
}

```

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group

## ネットワークリソース作成

```Terraform:network.tf

#Vnet作成

resource "azurerm_virtual_network" "vnet" {
  name                = local.vnet_name
  location            = local.location
  resource_group_name = azurerm_resource_group.rg.name
  address_space       = ["10.0.0.0/16"]
}

#subnet作成

resource "azurerm_subnet" "public" {
  name                 = local.public_subnet_name
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.1.0/24"]
}

resource "azurerm_subnet" "private" {
  name                 = local.private_subnet_name
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.2.0/24"]
}

#NSG作成

resource "azurerm_network_security_group" "nsg" {
  name                = local.nsg_name
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

#NSG Rule作成

resource "azurerm_network_security_rule" "rdp"  {
  name                        = local.nsg_name_rdp
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "3389"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = azurerm_resource_group.rg.name
  network_security_group_name = azurerm_network_security_group.nsg.name
  }

#NSGの関連付け

resource "azurerm_network_interface_security_group_association" "nsg_associate" {
  network_interface_id      = azurerm_network_interface.nic01.id
  network_security_group_id = azurerm_network_security_group.nsg.id
}
  
```

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/virtual_network

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group


## VM作成

```Terraform:vm.tf

#NIC作成

resource "azurerm_network_interface" "nic01" {
  name                = local.nic_name
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.private.id
    private_ip_address_allocation = "Dynamic"
  }
}

#VM作成

resource "azurerm_windows_virtual_machine" "vm" {
  name                = local.vm_name
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  size                = "Standard_B1s"
  admin_username      = local.admin_name
  admin_password      = local.admin_password
  network_interface_ids = [
    azurerm_network_interface.nic01.id
  ]

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2016-Datacenter"
    version   = "latest"
  }
}

```

# Azure環境へデプロイ
実際にAzure環境へTerraformで記載したコードを基にリソースのデプロイを行います。
今回使っていくコマンドは下記です。
・terraform init
・terraform plan
・terradform apply

## terraform init 実行
Terraformの実行に必要なバイナリファイルをダウンロードしてくれるコマンドですが
私は既に最新のファイルをダウンロード済みの環境なので、下記の出力になります。

```Terraform:terraform init 出力
Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/azurerm from the dependency lock file
- Using previously-installed hashicorp/azurerm v3.0.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
```

## terraform plan 実行
このコマンドでは、コード化されたインフラを仮構築できます。
設定項目等の確認に利用できます。

var.envは今回文字を任意入力するように設定しています。
今回は適当に「demo1」と入力します。
（リソースグループの名前が「rg-任意」になります。）

```Terraform:terraform plan 出力
var.env
  Enter a value: demo1

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_network_interface.nic01 will be created
  + resource "azurerm_network_interface" "nic01" {
      + applied_dns_servers           = (known after apply)
      + dns_servers                   = (known after apply)
      + enable_accelerated_networking = false
      + enable_ip_forwarding          = false
      + id                            = (known after apply)
      + internal_dns_name_label       = (known after apply)
      + internal_domain_name_suffix   = (known after apply)
      + location                      = "japaneast"
      + mac_address                   = (known after apply)
      + name                          = "demo_nic_01"
      + private_ip_address            = (known after apply)
      + private_ip_addresses          = (known after apply)
      + resource_group_name           = "rg-demo1"
      + virtual_machine_id            = (known after apply)

      + ip_configuration {
          + gateway_load_balancer_frontend_ip_configuration_id = (known after apply)
          + name                                               = "internal"
          + primary                                            = (known after apply)
          + private_ip_address                                 = (known after apply)
          + private_ip_address_allocation                      = "Dynamic"
          + private_ip_address_version                         = "IPv4"
          + subnet_id                                          = (known after apply)
        }
    }

  # azurerm_network_interface_security_group_association.nsg_associate will be created
  + resource "azurerm_network_interface_security_group_association" "nsg_associate" {
      + id                        = (known after apply)
      + network_interface_id      = (known after apply)
      + network_security_group_id = (known after apply)
    }

  # azurerm_network_security_group.nsg will be created
  + resource "azurerm_network_security_group" "nsg" {
      + id                  = (known after apply)
      + location            = "japaneast"
      + name                = "demo_nsg_01"
      + resource_group_name = "rg-demo1"
      + security_rule       = (known after apply)
    }

  # azurerm_network_security_rule.rdp will be created
  + resource "azurerm_network_security_rule" "rdp" {
      + access                      = "Allow"
      + destination_address_prefix  = "*"
      + destination_port_range      = "*"
      + direction                   = "Inbound"
      + id                          = (known after apply)
      + name                        = "RDP"
      + network_security_group_name = "demo_nsg_01"
      + priority                    = 100
      + protocol                    = "Tcp"
      + resource_group_name         = "rg-demo1"
      + source_address_prefix       = "*"
      + source_port_range           = "3389"
    }

  # azurerm_resource_group.rg will be created
  + resource "azurerm_resource_group" "rg" {
      + id       = (known after apply)
      + location = "japaneast"
      + name     = "rg-demo1"
    }

  # azurerm_subnet.private will be created
  + resource "azurerm_subnet" "private" {
      + address_prefixes                               = [
          + "10.0.2.0/24",
        ]
      + enforce_private_link_endpoint_network_policies = false
      + enforce_private_link_service_network_policies  = false
      + id                                             = (known after apply)
      + name                                           = "private"
      + resource_group_name                            = "rg-demo1"
      + virtual_network_name                           = "demo_vnet_01"
    }

  # azurerm_subnet.public will be created
  + resource "azurerm_subnet" "public" {
      + address_prefixes                               = [
          + "10.0.1.0/24",
        ]
      + enforce_private_link_endpoint_network_policies = false
      + enforce_private_link_service_network_policies  = false
      + id                                             = (known after apply)
      + name                                           = "public"
      + resource_group_name                            = "rg-demo1"
      + virtual_network_name                           = "demo_vnet_01"
    }

  # azurerm_virtual_network.vnet will be created
  + resource "azurerm_virtual_network" "vnet" {
      + address_space       = [
          + "10.0.0.0/16",
        ]
      + dns_servers         = (known after apply)
      + guid                = (known after apply)
      + id                  = (known after apply)
      + location            = "japaneast"
      + name                = "demo_vnet_01"
      + resource_group_name = "rg-demo1"
      + subnet              = (known after apply)
    }

  # azurerm_windows_virtual_machine.vm will be created
  + resource "azurerm_windows_virtual_machine" "vm" {
      + admin_password             = (sensitive value)
      + admin_username             = "azureuser01"
      + allow_extension_operations = true
      + computer_name              = (known after apply)
      + enable_automatic_updates   = true
      + extensions_time_budget     = "PT1H30M"
      + hotpatching_enabled        = false
      + id                         = (known after apply)
      + location                   = "japaneast"
      + max_bid_price              = -1
      + name                       = "demo-vm-01"
      + network_interface_ids      = (known after apply)
      + patch_mode                 = "AutomaticByOS"
      + platform_fault_domain      = -1
      + priority                   = "Regular"
      + private_ip_address         = (known after apply)
      + private_ip_addresses       = (known after apply)
      + provision_vm_agent         = true
      + public_ip_address          = (known after apply)
      + public_ip_addresses        = (known after apply)
      + resource_group_name        = "rg-demo1"
      + size                       = "Standard_B1s"
      + virtual_machine_id         = (known after apply)

      + os_disk {
          + caching                   = "ReadWrite"
          + disk_size_gb              = (known after apply)
          + name                      = (known after apply)
          + storage_account_type      = "Standard_LRS"
          + write_accelerator_enabled = false
        }

      + source_image_reference {
          + offer     = "WindowsServer"
          + publisher = "MicrosoftWindowsServer"
          + sku       = "2016-Datacenter"
          + version   = "latest"
        }
    }

Plan: 9 to add, 0 to change, 0 to destroy.
```

## terraform apply 実行
実際にデプロイを実行するためのコマンドです。
このコマンドを利用した際は、実際にリソースが作成されます。

```Terraform:terraform aplly 出力
var.env
  Enter a value: demo1


azurerm_resource_group.rg: Refreshing state... [id=/subscription id/resourceGroups/rg-demo1]
azurerm_network_security_group.nsg: Refreshing state... [id=/subscription id/resourceGroups/rg-demo1/providers/Microsoft.Network/networkSecurityGroups/demo_nsg_01]
azurerm_virtual_network.vnet: Refreshing state... [id=/subscription id/resourceGroups/rg-demo1/providers/Microsoft.Network/virtualNetworks/demo_vnet_01]
azurerm_network_security_rule.rdp: Refreshing state... [id=/subscription id/resourceGroups/rg-demo1/providers/Microsoft.Network/networkSecurityGroups/demo_nsg_01/securityRules/RDP]
azurerm_subnet.public: Refreshing state... [id=/subscription id/resourceGroups/rg-demo1/providers/Microsoft.Network/virtualNetworks/demo_vnet_01/subnets/public]
azurerm_subnet.private: Refreshing state... [id=/subscription id/resourceGroups/rg-demo1/providers/Microsoft.Network/virtualNetworks/demo_vnet_01/subnets/private]
azurerm_network_interface.nic01: Refreshing state... [id=/subscription id/resourceGroups/rg-demo1/providers/Microsoft.Network/networkInterfaces/demo_nic_01]
azurerm_network_interface_security_group_association.nsg_associate: Refreshing state... [id=/subscription id/resourceGroups/rg-demo1/providers/Microsoft.Network/networkInterfaces/demo_nic_01|/subscription id/resourceGroups/rg-demo1/providers/Microsoft.Network/networkSecurityGroups/demo_nsg_01]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_windows_virtual_machine.vm will be created
  + resource "azurerm_windows_virtual_machine" "vm" {
      + admin_password             = (sensitive value)
      + admin_username             = "azureuser01"
      + allow_extension_operations = true
      + computer_name              = (known after apply)
      + enable_automatic_updates   = true
      + extensions_time_budget     = "PT1H30M"
      + hotpatching_enabled        = false
      + id                         = (known after apply)
      + location                   = "japaneast"
      + max_bid_price              = -1
      + name                       = "demo-vm-01"
      + network_interface_ids      = [
          + "/subscription id/resourceGroups/rg-demo1/providers/Microsoft.Network/networkInterfaces/demo_nic_01",
        ]
      + patch_mode                 = "AutomaticByOS"
      + platform_fault_domain      = -1
      + priority                   = "Regular"
      + private_ip_address         = (known after apply)
      + private_ip_addresses       = (known after apply)
      + provision_vm_agent         = true
      + public_ip_address          = (known after apply)
      + public_ip_addresses        = (known after apply)
      + resource_group_name        = "rg-demo1"
      + size                       = "Standard_B1s"
      + virtual_machine_id         = (known after apply)

      + os_disk {
          + caching                   = "ReadWrite"
          + disk_size_gb              = (known after apply)
          + name                      = (known after apply)
          + storage_account_type      = "Standard_LRS"
          + write_accelerator_enabled = false
        }

      + source_image_reference {
          + offer     = "WindowsServer"
          + publisher = "MicrosoftWindowsServer"
          + sku       = "2016-Datacenter"
          + version   = "latest"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.
```

## Azureリソースがデプロイできているか見てみる
今回デプロイしたのは、下記です。
・リソースグループ
・Virtual Network（subnet含む）
・Virtual Machines
・Network Interface
・Network Security Group
・Managed Disk

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3471206/c00d9d5a-fde2-8e1a-ad67-9cfbcdb2de38.png)


# さいごに
今回はTerraformでAzure上に簡単なリソースをデプロイするまでを試してみました。
今後は実際にVMに目的を持たせて拡張機能のインストール等、より複雑化した構成をコード化してみたいと思います。