【Terraform de Azure】 Windows10 の VM を作成し RDP してみました

### 概要
「Infrastructure as Code」ということで、Terraform を用いて Azure環境上に Windows10Pro のVMを作成し、RDP接続してみます。 [この記事](https://qiita.com/turupon/items/208acb8fdbe9dadb5a70)と同じ内容を Terraform を用いて構築します。


### ローカル環境

- macOS Monterey 12.1
- python 3.8.3
- Azure CLI 2.28.0
- terraform v1.0.11

### 前提条件

1. Azure環境がすでに用意されていること（テナント/サブスクリプション）
2. ローカル環境に「azure cli」がインストールされていること
3. ローカル環境に「terraform」環境が構成されていること
4. TerraformでAzure上に環境構築するための[サービスプリンシパルが作成](https://qiita.com/turupon/items/5d024d43a60cd101be94)されており、Terraform のためのローカル環境変数が定義されていること

### 事前準備

ローカル環境からインターネットアクセス時に自動割当されているグローバルアドレスの取得

```
## 利用グローバルアドレス
$ curl inet-ip.info
153.134.22.135
```

構築する仮想マシンのImage情報の取得

```
## Image
$ az vm image list --location japaneast --offer Windows-10 --sku 21h2-pro --all --output table

Offer       Publisher                Sku                      Urn                                                                           Version
----------  -----------------------  -----------------------  ----------------------------------------------------------------------------  -----------------
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pro           MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:19044.1466.220108           19044.1466.220108
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pro           MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro:19044.1469.220116           19044.1469.220116
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pro-g2        MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro-g2:19044.1466.220108        19044.1466.220108
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pro-g2        MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro-g2:19044.1469.220116        19044.1469.220116
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pro-zh-cn     MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro-zh-cn:19044.1466.220108     19044.1466.220108
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pro-zh-cn     MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro-zh-cn:19044.1469.220116     19044.1469.220116
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pro-zh-cn-g2  MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro-zh-cn-g2:19044.1466.220108  19044.1466.220108
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pro-zh-cn-g2  MicrosoftWindowsDesktop:Windows-10:win10-21h2-pro-zh-cn-g2:19044.1469.220116  19044.1469.220116
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pron          MicrosoftWindowsDesktop:Windows-10:win10-21h2-pron:19044.1466.220108          19044.1466.220108
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pron          MicrosoftWindowsDesktop:Windows-10:win10-21h2-pron:19044.1469.220116          19044.1469.220116
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pron-g2       MicrosoftWindowsDesktop:Windows-10:win10-21h2-pron-g2:19044.1466.220108       19044.1466.220108
Windows-10  MicrosoftWindowsDesktop  win10-21h2-pron-g2       MicrosoftWindowsDesktop:Windows-10:win10-21h2-pron-g2:19044.1469.220116       19044.1469.220116
```

---

## Terraform で Windows10 Pro の VM を作成してみる

### terraform 定義ファイルの作成

プロバイダの定義

```main.tf
# プロバイダーの定義
terraform {
  required_providers {
    azurerm =  "~> 2.33"
  }
}

provider "azurerm" {
  features {}
  tenant_id     = var.ARM_TENANT_ID
  client_id     = var.ARM_CLIENT_ID
  client_secret = var.ARM_CLIENT_SECRET
}


# リソースグループ
resource "azurerm_resource_group" "this" {
  name     = var.resource_group_name
  location = var.region
  tags     = var.tags_def
}
```

パラメータ定義ファイル

```variables.tf
# 環境変数（Azureサービスプリンシパル）
variable ARM_TENANT_ID {}
variable ARM_CLIENT_ID {}
variable ARM_CLIENT_SECRET {}

# タグ情報
variable tags_def {
  default = {
    owner      = "ituru"
    period     = "2022-03-31"
    CostCenter = "PSG2"
    Environment = "Demo"
    Project = "DUP_IaC"
  }
}

# 各種パラメータ
variable region {}                  // 利用リージョン
variable resource_group_name {}     // リソースグループ名

variable vnet_name {}               // vNet名
variable vnet_address_space {}      // vNetアドレス範囲
variable subnet_name {}             // サブネット名
variable subnet_address {}          // サブネットアドレス
variable public_ip_name {}          // パブリックIP名
variable security_group_name {}     // セキュリティグループ名
variable network_interface_name {}  // ネットワーク・インターフェース名

variable vm_name {}                 // 仮想マシン名
variable vm_size {}                 // 仮想マシンサイズ
variable computer_name {}           // コンピュータ名
variable admin_username {}          // 管理者名
variable admin_password {}          // 管理者パスワード
```

パラメータ値定義ファイル

```terraform.tfvars
# 環境変数の定義（Azureサービスプリンシパル）
ARM_TENANT_ID       = "zzzzzzzz-cccc-4645-5757-zzzzzzzzzzzz"
ARM_CLIENT_ID       = "xxxxxxxx-xxxx-4444-9922-xxxxxxxxxxxx"
ARM_CLIENT_SECRET   = "hogehogehogehogehogehogehogehogege"

# パラメータ値の定義
region                  = "japaneast"           // 利用リージョン
resource_group_name     = "rg_ituru_vm01"       // リソースグループ名

vnet_name               = "vnet_ituru_vm01"     // vNet名
vnet_address_space      = "10.0.0.0/16"         // vNetアドレス範囲
subnet_name             = "snet_ituru_vm01"     // サブネット名
subnet_address          = "10.0.1.0/24"         // サブネットアドレス
public_ip_name          = "pip_ituru_vm01"      // パブリックIP名
security_group_name     = "nsg_ituru_vm01"      // セキュリティグループ名
network_interface_name  = "nic_ituru_vm01"      // ネットワーク・インターフェース名

vm_name                 = "vm-ituru-win10pro"   // 仮想マシン名
vm_size                 = "Standard_D2_v2"      // 仮想マシンサイズ
computer_name           = "win10nmcituru"       // コンピュータ名
admin_username          = "nmcadmin"            // 管理者名
admin_password          = "Password123!"        // 管理者パスワード
```

仮想マシン定義ファイル

```computer.tf
# windows10 仮想マシン
resource "azurerm_windows_virtual_machine" "this" {
  name                  = var.vm_name
  location              = azurerm_resource_group.this.location
  resource_group_name   = azurerm_resource_group.this.name
  size                  = var.vm_size
  computer_name         = var.computer_name
  admin_username        = var.admin_username
  admin_password        = var.admin_password
  network_interface_ids = [
    azurerm_network_interface.this.id,
  ]

  source_image_reference {
    publisher = "MicrosoftWindowsDesktop"
    offer     = "Windows-10"
    sku       = "win10-21h2-pro"
    version   = "latest"
  }

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

  tags  = var.tags_def
}
```

ネットワーク＋セキュリティ定義ファイル

```network.tf
// 仮想ネットワーク
resource "azurerm_virtual_network" "this" {
  name                = var.vnet_name
  address_space       = [var.vnet_address_space]
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name
  tags                = var.tags_def
}

// サブネット
resource "azurerm_subnet" "internal" {
  name                  = var.subnet_name
  resource_group_name   = azurerm_resource_group.this.name
  virtual_network_name  = azurerm_virtual_network.this.name
  address_prefixes      = [var.subnet_address]
}

// パブリック IP アドレス
resource "azurerm_public_ip" "this" {
  name                = var.public_ip_name
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name
  allocation_method   = "Dynamic"
  tags                = var.tags_def
}

// ネットワーク セキュリティ グループ
resource "azurerm_network_security_group" "this" {
  name                = var.security_group_name
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name

  security_rule {
    name                       = "RDP"
    description                = "Allow RDP Access"
    priority                   = 1001
    direction                  = "Inbound"   
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "3389"
    source_address_prefix      = "153.134.22.135/32"
    destination_address_prefix = "*"
  }

  tags  = var.tags_def
}

// ネットワーク インターフェイス
resource "azurerm_network_interface" "this" {
  name                = var.network_interface_name
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name

  ip_configuration {
    name                          = "ipconfig"
    subnet_id                     = azurerm_subnet.internal.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.this.id
  }

  tags  = var.tags_def
}

// ネットワークインターフェースへのネットワークセキュリティグループの割当
resource "azurerm_network_interface_security_group_association" "this" {
    network_interface_id      = azurerm_network_interface.this.id
    network_security_group_id = azurerm_network_security_group.this.id
}
```

### terraform の実行

```
## init
$ terraform init
    :
Terraform has been successfully initialized!

## plan
$ terraform plan
    :
Plan: 1 to add, 0 to change, 0 to destroy.

## apply
$ terraform apply
    :
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

admin_password = "xxxxxxxxxxxxxxxxx"
admin_username = "adminuser"
public_ip_id = [
  "/subscriptions/xxxxxxxx-1717-dada-9779-zzzzzzzzzzzz/resourceGroups/rg_ituru_vm01/providers/Microsoft.Network/publicIPAddresses/pip_ituru_vm01",
]
```

### ローカルの作業ディレクトの状況

```
$ tree -a
.
├── .terraform
│   └── providers
│       └── registry.terraform.io
│           └── hashicorp
│               └── azurerm
│                   └── 2.93.0
│                       └── darwin_amd64
│                           └── terraform-provider-azurerm_v2.93.0_x5
├── .terraform.lock.hcl
├── compute.tf
├── main.tf
├── network.tf
├── outputs.tf
├── terraform.tfstate
├── terraform.tfstate.backup
├── terraform.tfvars
└── variables.tf
```

---

## terraform 実行後の確認

### リソースグループ の確認

```
$ az group show --name <Resource_Group_name>
{
  "id": "/subscriptions/xxxxxxxx-1717-dada-9779-zzzzzzzzzzzz/resourceGroups/rg_ituru_vm01",
  "location": "japaneast",
  "managedBy": null,
  "name": "rg_ituru_vm01",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": {
    "CostCenter": "PSG2",
    "Environment": "Demo",
    "Project": "DUP_IaC",
    "owner": "ituru",
    "period": "2022-03-31"
  },
  "type": "Microsoft.Resources/resourceGroups"
}
```

### 仮想マシン の確認

```
$ az vm list -g <Resource_Group_Name> -d --output table

Name               ResourceGroup    PowerState    PublicIps       Fqdns    Location    Zones
-----------------  ---------------  ------------  --------------  -------  ----------  -------
vm-ituru-win10pro  rg_ituru_vm01    VM running    40.115.214.110           japaneast
```

### ネットワークセキュリティグループ の確認

```
$ az network nsg rule list -g <Resource_Group_Name> --nsg-name <Network_Security_Group_Namr> -o table

Name    ResourceGroup    Priority    SourcePortRanges    SourceAddressPrefixes    SourceASG    Access    Protocol    Direction    DestinationPortRanges    DestinationAddressPrefixes    DestinationASG
------  ---------------  ----------  ------------------  -----------------------  -----------  --------  ----------  -----------  -----------------------  ----------------------------  ----------------
RDP     rg_ituru_vm01    1001        *                   153.134.22.135/32        None         Allow     Tcp         Inbound      3389                     *                             None
```

---

### 仮想マシンへの接続

#### RDP接続

仮想マシンへの RDP接続については、[この記事](https://docs.microsoft.com/ja-jp/azure/virtual-machines/windows/connect-logon) を参考にして実施ください。

#### 接続（ログイン）後の確認

コンソールを立ち上げ、OS名やバージョン、ネットワーク情報等を確認します

```
C:\Users\nmcadmin> systeminfo | findstr OS
OS Name:                   Microsoft Windows 10 Pro
OS Version:                10.0.19044 N/A Build 19044
OS Manufacturer:           Microsoft Corporation
OS Configuration:          Standalone Workstation
OS Build Type:             Multiprocessor Free
BIOS Version:              American Megatrends Inc. 090008 , 12/7/2018

C:\Users\nmcadmin> ipconfig
Windows IP Configuration
Ethernet adapter Ethernet:
   Connection-specific DNS Suffix  . : alnffimvqjgudilgtkzictcd1g.lx.internal.cloudapp.net
   Link-local IPv6 Address . . . . . : fe80::e574:5dec:c525:3ef8%6
   IPv4 Address. . . . . . . . . . . : 10.0.1.4
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 10.0.1.1
```

---

### terraform による作成したリソースの削除

```
$ terraform destroy
```

---

### まとめ

これで、Terraform でサクッと Azure環境上に Windows10Pro を構成し RDP接続 できました。 Azure Portal や Azure CLI で構築するのもいいですが、IaC化もいいですね。