TerraformでvSphere弄る時、clone後customizeを失敗させない。

# tl;dr

多分、どこかで同じ現象で泣いてる人がいそうな気がしただけです。（気がしただけ）
単にTerraformでvSphere(ESXi)を操作する際につまづいたポイントについて説明します。  


ネットの海探したら同じようなこと書いてる人いるかもしれませんが、自分は何気なく勘で掘り当てたので共有をします。  

結論を以下のようにまとめておきます。  

条件を
1. vSphereしか使えない環境である。
2. vCenterを利用できる構成/ライセンスである。**（これ重要！！！！）**
3. 管理者権限を利用できる（フルアクセス可能なアカウントを利用できる）

ならば  

1. テンプレート用のVMを作る。
2. テンプレートVMに必ず*vmtools*および*perl*をインストールすること。**（最重要項目）**
3. Terraformでcustomize項目を作ってやる。

ということをつらつらと書いていきます。


# 動作環境

## サーバ環境
1. vSphere(ESXi)がインストールされている
2. vCenterでvSphereを管理している

## クライアント環境
1. Terraformが利用できる
2. vCenterのフルアクセスアカウントを持っている。


# ゴールデンイメージとして扱うテンプレートVMの準備

## Linuxの場合
1. いつも通りLinuxをインストールしていきます。
2. vmtoolsをインストールします。  

    ```
    # CentOSの場合
    sudo yum install -y open-vm-tools

    # Ubuntu/Debianの場合
    sudo apt install -y open-vm-tools
    ```

2. インストールが完了したら、perlをインストールしていきます。  

    ```
    # CentOSの場合
    sudo yum install -y perl

    # Ubuntu/Debianの場合
    sudo apt install -y perl
    ```
3. あとはVMを落としても大丈夫です。
 
## Windows
1. インストーラで通常通りインストールしていきます。
   VMなのでGPTパーティションは作成されないと思いますが、MBRで必ずインストールしてください。  

1. 初期に設定するユーザは管理者権限を持っているので、そこはメモしておきましょう。忘れても知りません。

2. VMのWindowsで[vmwaretools](https://my.vmware.com/web/vmware/details?downloadGroup=VMTOOLS1035&productId=742)をダウンロードしてインストールしましょう。(アカウント作成必須）。  

3. VMのWindowsで[ActivePerl](https://www.activestate.com/products/activeperl/)をダウンロードしてインストールしましょう。通常インストールで問題ありません。（これもアカウント作成必須。）  

4. WindowsはSIDを見ているのでこれを削除してやらないとクローン後に起動せずブルスクで死にます。
  Sysprepを実行してSIDを削除してやりましょう。注意点も確認しておいてください。
  [Windows 10 での Sysprep を用いたマスターイメージの作成に関する注意点・推奨事項](https://blogs.technet.microsoft.com/askcorejp/2018/04/03/windows10-sysprep-guide/)  

5. SysprepでVMをシャットダウンしたら準備完了です。

# Terraformで管理
ついにVMを作成する時が...その前にTerraformでテンプレートを作ってやる必要があります。
Terraformは指定したディレクトリから下の階層全部丸呑みしにいきますのであまり根っこに近いところに作業ディレクトリ作ると大変なことになります。　　
（まぁそのディレクトリに移動すればいいんだけど）

## vSphereをプロバイダとして指定したファイルを作る

```Provider.tf
# Terraformに"vSphereだと宣言している"



provider "vsphere" {
    # user/password/vsphere_serverは.gitignoreに入れるとよい
    user           = "${var.vsphere_user}"
    password       = "${var.vsphere_password}"
    vsphere_server = "${var.vsphere_server}"

    # allow_unverified_sslはvSphereにSSL証明でエラー吐いても接続する
    allow_unverified_ssl  = true
}

# datacenterを設定する。
data "vsphere_datacenter" "dc" {
  name = "Datacenter"
}

# datastoreを設定する。
data "vsphere_datastore" "datastore" {
  name          = "datastore-cluster/datastore1"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

# これは実際あまり必要ない
data "vsphere_datastore" "iso_datastore" {
  name          = "datastore-cluster/datastore1"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

# VMをデプロイする先のリソースプールを指定する。
data "vsphere_resource_pool" "pool" {
  name          = "Usamimi-RP"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
```

``` Network.tf
# 実際はProvider.tfでまとめてもいいが、自分はネットワークは別にしたかったからこう書いた。
data "vsphere_network" "network" {       
    name = "VM Network"                  
    datacenter_id = "${data.vsphere_datacenter.dc.id}" 
}

```

```loginfo.tf
//-------------------------------------------------------------------
// VMware vsphere vCenter settings
//-------------------------------------------------------------------

# vCenterにログインするアカウントを記述する
variable "vsphere_user" {
    default = "administrator@usamimi.local"
}

# 上に対応したパスワードをセットする
variable "vsphere_password" {
    default = "Input-your-password"
}

# Set your Server (https connection address)
variable "vsphere_server" {
    default = "usamimi.vcenter.local"
}

```
## ゴールデンイメージ（VM）の設定

```GoldenImage.tf
data "vsphere_virtual_machine" "linux-template" {
  name          = "usamimi-linux"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}


data "vsphere_virtual_machine" "windows-template" {
  name          = "usamimi-windows"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

```


## VM用テンプレート

### Linuxの例（CentOSでやったやつ）

```Linux.tf
variable "usamimi-linux" {
    default = {
        VM_name = "usamimi-linux"

        vcpus   = "2"
        memory  = "2048"
        ipv4_address="192.168.1.1"
        ipv4_netmask="24"
        ipv4_gateway="192.168.1.254"
    }
}


resource "vsphere_virtual_machine" "usamimi-linux" {
  name                      = "${lookup(var.usamimi-linux,"VM_name")}"
  resource_pool_id          = "${data.vsphere_resource_pool.pool.id}"
  datastore_id              = "${data.vsphere_datastore.datastore.id}"

  num_cpus                  = "${lookup(var.usamimi-linux,"vcpus")}"
  memory                    = "${lookup(var.usamimi-linux,"memory")}"
  guest_id                  = "otherLinux64Guest"

  network_interface {
    network_id              =   "${data.vsphere_network.network.id}"
    adapter_type            =   "${data.vsphere_virtual_machine.linux-template.network_interface_types[0]}"
  }


  disk {
    label                   = "${lookup(var.usamimi-linux,"VM_name")}_disk"
    size                    = "${data.vsphere_virtual_machine.linux-template.disks.0.size}"
    eagerly_scrub           = "${data.vsphere_virtual_machine.linux-template.disks.0.eagerly_scrub}"
    thin_provisioned        = "${data.vsphere_virtual_machine.linux-template.disks.0.thin_provisioned}"
  }

  #cdrom {
  #  datastore_id = "${data.vsphere_datastore.iso_datastore.id}"
  #  path         = "${var.iso_path}"
  #}



  clone {
    template_uuid = "${data.vsphere_virtual_machine.linux-template.id}"

   customize {
     
     linux_options {
       host_name        = "${lookup(var.usamimi-linux,"VM_name")}"
       domain           = "localhost.localdomain"
      }

      network_interface {
        ipv4_address    = "${lookup(var.usamimi-linux,"ipv4_address")}"
        ipv4_netmask    = "${lookup(var.usamimi-linux,"ipv4_netmask")}"
        }

      ipv4_gateway    = "${lookup(var.usamimi-linux,"ipv4_gateway")}"
    }
  }
}
```


### Windowsの例（Windows10）

```Windows.tf
variable "usamimi-windows" {
    default = {
        VM_name = "usamimi-windows"

        vcpus   = "2"
        memory  = "4096"
        ipv4_address="192.168.1.200"
        ipv4_netmask="24"
        ipv4_gateway="192.168.1.254"
    }
}


resource "vsphere_virtual_machine" "usamimi-windows" {
  name                        = "${lookup(var.usamimi-windows,"VM_name")}"
  resource_pool_id            = "${data.vsphere_resource_pool.pool.id}"
  datastore_id                = "${data.vsphere_datastore.datastore.id}"

  num_cpus                    = "${lookup(var.usamimi-windows,"vcpus")}"
  memory                      = "${lookup(var.usamimi-windows,"memory")}"
  guest_id                    = "windows9_64Guest"

  network_interface {
    network_id                =   "${data.vsphere_network.network.id}"
    adapter_type              =   "${data.vsphere_virtual_machine.windows-template.network_interface_types[0]}"
  }


  disk {
    label                     = "${lookup(var.usamimi-windows,"VM_name")}_disk"
    size                      = "${data.vsphere_virtual_machine.windows-template.disks.0.size}"
    eagerly_scrub             = "${data.vsphere_virtual_machine.windows-template.disks.0.eagerly_scrub}"
    thin_provisioned          = "${data.vsphere_virtual_machine.windows-template.disks.0.thin_provisioned}"
  }

  #cdrom {
  #  datastore_id             = "${data.vsphere_datastore.iso_datastore.id}"
  #  path                     = "${var.iso_path}"
  #}



  clone {
   template_uuid             = "${data.vsphere_virtual_machine.windows-template.id}"

   customize {
     
      windows_options {
        computer_name         = "${lookup(var.usamimi-windows,"VM_name")}-Clone"
        workgroup             = "WORKSTATION"
        full_name             = "usamimi-windows"
        admin_password        = "password"
        #product_key           = ""

        # #If you use domain (Active Directory) modify under.
        #domain_admin_user     = ""
        #domain_admin_password = ""
      }

      network_interface {
        ipv4_address    = "${lookup(var.cusamimi-windows,"ipv4_address")}"
        ipv4_netmask    = "${lookup(var.usamimi-windows,"ipv4_netmask")}"
        }

      ipv4_gateway    = "${lookup(var.usamimi-windows,"ipv4_gateway")}"
    }
  }
}


```




# あとはTerraform任せた！！
実行してやります。まずは検証でおなじみのこれ。  

```
terraform plan
```

エラーが出なければ

```
terraform apply
```

これでデプロイして待ってみてください。  
そしたら最後のWaitingが続いて最後タイムアウトで赤文字失敗ではなくなってるはずです。  
緑の文字で締めくくられていればお疲れ様でした！って感じです。