Ansible で vCenter 構築する日記

## まえおき

ある日、僕たちの使っていた vCenter が吹き飛びました。
頑張って復旧した結果、前と少し違うものが出来上がりました。

そのとき僕は思ったのです。ansible や terraform で構築できないのかなと :thinking: 
これは ansible で vCenter、さらには vSphere cluster を構築できないか、検証した記録です :book: 

サンプル実装は[こちら](https://github.com/yo-C-ta/vsphere-ansible)。

## １日目 - vCenter VM をデプロイする準備

まずは何はともあれ、vCenter がない状況から、vCenter を生み出さなければなりません。ansible でやるか、terraform でやるか迷ったので、どちらも少し試してみました。terraform は [vsphere_virtual_machine](https://registry.terraform.io/providers/hashicorp/vsphere/latest/docs/resources/virtual_machine)モジュールの、[ovf_deploy](https://registry.terraform.io/providers/hashicorp/vsphere/latest/docs/resources/virtual_machine#ovf_deploy)を使ってできないかなと思ったのですが、このモジュールは既に vCenter がある状態でないと実行できないことがわかり、諦めました。

気持ちを切り替えて、ansible でやっていきます :muscle: 
ansibleでは、少し探すと下記の記事が見つかったので、参考にしました。

http://www.hybriddatacenter.net/deploy-vmware-vcenter-7-with-ansible/

使用するモジュールは、[community.vmware.vmware_deploy_ovf](https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_deploy_ovf_module.html#ansible-collections-community-vmware-vmware-deploy-ovf-module) です。community.vmware コレクションを利用するためには、下記コレクションの install に加え、

```bash
ansible-galaxy collection install community.vmware
```

[pyvmomi](https://github.com/vmware/pyvmomi) が必要なので、次のコマンドを実行します。

```bash
pip install pyvmomi
```

これで準備は OK です。

## ２日目 - vCenter vm のデプロイ

いよいよ、デプロイタスクを書いていきます。

いきなりですが、全体像です。ざっくりした説明をコメントに記載しています。

```yaml:deploy_vcenter.yaml
- name: Deploy vCenter VM from delegete host
  vmware_deploy_ovf:
    hostname: "{{ esxi.address }}"   # この辺りは、vCenter VM をデプロイする先のESXiの情報です
    username: "{{ esxi.username }}"
    password: "{{ esxi.password }}"
    name: "{{ vcenter.hostname.split('.')[0] }}" # VM名です FQDN じゃないよ
    datastore: "{{ datastore.name }}"            # VMをデプロイする Datastore です
    deployment_option: "{{ deploy.size }}"       # tiny や small など、vcsa のサイズを選択します ※
    disk_provisioning: "thin"
    networks:
      "Network 1": "{{ vcenter.network_label }}" # VM のネットワークインターフェース（key は固定っぽい）
    ovf: "{{ deploy.ovf_abs_path }}"             # delegate_to でデプロイを代行するサーバ上の、OVF ファイルの絶対パスです
    wait_for_ip_address: true                    # デプロイ後、IPが振られるまで待つかどうか
    validate_certs: "{{ validate_certs }}"
    inject_ovf_env: true  # ここを yes(true) に設定することで、VM 起動時に、下記 properties セクションの値を設定してくれます
    properties:
      guestinfo.cis.appliance.net.addr.family: "ipv4" # ipv4 or ipv6
      guestinfo.cis.appliance.net.mode: "static"      # static or dhcp
      guestinfo.cis.appliance.net.addr: "{{ vcenter.address }}"
      guestinfo.cis.appliance.net.pnid: "{{ vcenter.hostname }}" # FQDN
      guestinfo.cis.appliance.net.prefix: "{{ net.prefix }}" # netmask length, CIDR notation, たとえば "24"
      guestinfo.cis.appliance.net.gateway: "{{ net.gateway }}"
      guestinfo.cis.appliance.net.dns.servers: "{{ net.dns.servers }}" # Comma separated list
      guestinfo.cis.appliance.root.passwd: "{{ vcenter.admin_pass }}"  # ssh とかで使う root のパスワード
      guestinfo.cis.appliance.ssh.enabled: "true"
      guestinfo.cis.appliance.ntp.servers: "{{ net.ntp.servers }}"     # Comma separated list
      guestinfo.cis.ceip_enabled: "False"
      guestinfo.cis.deployment.autoconfig: "True" # デプロイ後に、Auto-configure するかどうかですが、ここも true にしておく必要があります
      guestinfo.cis.vmdir.username: "{{ vcenter.admin_user }}"    # vcenter local SSO のユーザ名
      guestinfo.cis.vmdir.domain-name: "{{ vcenter.admin_user.split('@')[1] }}" # SSO のドメイン名
      guestinfo.cis.vmdir.password: "{{ vcenter.admin_pass }}"    # SSO の管理者パスワード
      domain: "{{ vcenter.hostname.split('.')[1:] | join('.') }}" # vCenter のドメイン名
      searchpath: "{{ vcenter.hostname.split('.')[1:] | join('.') }}"
  ignore_errors: "{{ ansible_check_mode }}"
  delegate_to: "{{ deploy.delegate_server }}" # OVFデプロイを代行させるマシンで、localhostでもOKです
  tags:
    - deploy_vcenter
```
※ vcsaのサイズに関しては[こちらを参照](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vcenter.upgrade.doc/GUID-FB268055-5D36-4624-A64C-9800D3FCB689.html)

また、デプロイ後の Auto-configure は非同期で進むので、デプロイが正常に終了したかどうかを確かめるために、下記の wait タスクを繋げます。

```yaml:deploy_vcenter.yaml
- name: Wait for vCenter VM
  vmware_about_info:
    hostname: "{{ vcenter.hostname }}"
    username: "{{ vcenter.admin_user }}"
    password: "{{ vcenter.admin_pass }}"
    validate_certs: "{{ validate_certs }}"
  delegate_to: "{{ deploy.delegate_server }}"
  retries: "{{ 0 if ansible_check_mode else 20 }}"
  delay: 60
  register: result
  until: result is succeeded
  ignore_errors: "{{ ansible_check_mode }}"
  tags:
    - deploy_vcenter
```

１点、気をつけないといけない点としては、デプロイしたい vCenter の IP アドレスを DNS 登録する必要がありました。デプロイ自体は成功しますが、properties の設定でハングしてしまい、wait タスクが完了しませんでした。かなりハマりました。
今回の場合、一度 `guestinfo.cis.deployment.autoconfig` の値を `False`　にしてみて、手動でセットアップを進めてみることで、エラーの原因がわかりました。GUIだとエラーメッセージが表示されました。`autoconfig` をFalseにすると、下記の画面で設定が止まります。Trueで最後まで行けると、いつものVMware vSphereログイン画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221423/54cc4d04-39b3-0ddd-d221-dab44f6f9e0c.png)

いつもの画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221423/2aa3007c-dada-31a1-66bf-80e10ff9402a.png)


## ３日目 - vCenter の設定

さて、ひとまず vCenter のデプロイができましたが、まだ設定が何もありません。ここでは最低限の設定をモリモリ盛り込んでいきました。
作ったタスクは、下記5つです。

- vCenter のライセンス登録
- SSO へのユーザ追加
- Datacenter 作成
- vSphere Cluster 作成
- Folder 作成

最初に試したのが、ライセンス登録です。これは [vcenter_license](https://docs.ansible.com/ansible/latest/collections/community/vmware/vcenter_license_module.html#examples) モジュールを使って何も考えずにサクッといけたので、詳細は割愛します。

次に試したのが、SSO へのユーザー追加です。これが2日目の山場でした。それっぽいモジュールを探したのですが、[ESXi にユーザ追加するモジュール](https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_local_user_manager_module.html)しか見つかりませんでした。色々調べた結果行き着いた結論は、SSO のドメインユーザーは vSphere API で作成することができず、VCSA の [shell を変更してコマンドで実行する](https://github.com/ansible-collections/community.vmware/issues/613#issuecomment-769155827)というものでした。全てを自動化することが目的だったので、シェルの変更が手作業になるのがちょっとだけ嫌でした。試行錯誤した結果、[vmware_vm_shell](https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_vm_shell_module.html) モジュールで実行すると shell の変更が不要なことがわかりました。

ユーザを追加する場合のタスクはこんな感じです。

```yaml:config_vcenter.yaml
- name: Add the domain user of the vsphere.local domain if it doesn't exist and present state
  community.vmware.vmware_vm_shell:
    hostname: "{{ esxi.address }}"
    username: "{{ esxi.username }}"
    password: "{{ esxi.password }}"
    vm_id: "{{ vcenter.hostname.split('.')[0] }}"
    vm_username: root
    vm_password: "{{ vcenter.admin_pass }}"
    vm_shell: "/usr/lib/vmware-vmafd/bin/dir-cli"
    vm_shell_args: >-
      user create
      --account "{{ user.username }}"
      --user-password "{{ user.password }}"
      --login "{{ vcenter.admin_user }}"
      --password "{{ vcenter.admin_pass }}"
      --first-name "{{ user.first_name if user.first_name is defined else user.username }}"
      --last-name "{{ user.last_name if user.last_name is defined else user.username }}"
    validate_certs: "{{ validate_certs }}"
    wait_for_process: true
  delegate_to: localhost
  changed_when: true
  when:
    - user.state == "present"
    - gather_domain_user_info_result.domain_user_groups | length == 0
  no_log: true
  tags:
    - local_user
```

削除時には `vm_shell_args` の内容が削除コマンドになります。

```yaml:config_vcenter.yaml
    vm_shell_args: >-
      user delete
      --account "{{ user.username }}"
      --login "{{ vcenter.admin_user }}"
      --password "{{ vcenter.admin_pass }}"
```

この後、vCenter 上に Datacenter と vSphere Cluster と Folder を追加しました。それぞれ [vmware_datacenter](https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_datacenter_module.html) と [vmware_cluster](https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_cluster_module.html) と [vcenter_folder](https://docs.ansible.com/ansible/latest/collections/community/vmware/vcenter_folder_module.html) モジュールでサクっと作成できました。こちらも割愛させてもらいます。

ちなみに、この後ESXiの紐付けなどがありますが、その際に [Datacenter と、vSphere Cluster の指定が必要](https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_host_module.html#parameter-datacenter_name)なため、先に作っておく必要があります。Cluster 指定をしない場合は Folder を指定するようですが、そちらは試していません。

## ４日目 - ESXi と Cluster の可用性の設定

ここまできてしまえば、あともう少しです。ESXi関連のモジュールはたくさん揃っています。[Community.Vmware コレクション](https://docs.ansible.com/ansible/latest/collections/community/vmware/index.html)の中で、 `vmware_host` で始まるモジュールが大体全て ESXi に対する設定モジュールのようです。

今回は既存の ESXi を紐づけたので、大した設定はしていません。最低限の設定ということで下記を実施しています。

- Cluster への ESXi ホスト追加
- ESXi のライセンス登録
- Cluster の DRS 設定と、HA 設定

まず、Cluster への ESXi ホスト追加は [vmware_host](https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_host_module.html) モジュールを使用します。`present`/`absent` state しか試していませんが、結構[柔軟に接続をコントロールできる](https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_host_module.html#parameter-state)ようです。特に難しいところやハマりどころもなかったので、[Example](https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_host_module.html#examples) を見ていただければと思います。

次に、ESXi ライセンス登録です。これには、vCenter のライセンス登録と同様に、[vcenter_license](https://docs.ansible.com/ansible/latest/collections/community/vmware/vcenter_license_module.html) モジュールを使用します。このモジュールで、[esxi_hostname](https://docs.ansible.com/ansible/latest/collections/community/vmware/vcenter_license_module.html#parameter-esxi_hostname) セクションを追加すると、ESXiに対するライセンス割り当てになります。

最後に、vSphere Cluster に対して DRS（[Distributed Resource Scheduler](https://www.vmware.com/products/vsphere/drs-dpm.html)）、HA（High Availability）の設定を行いました。それぞれ、[vmware_cluster_drs](https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_cluster_drs_module.html)、[vmware_cluster_ha](https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_cluster_ha_module.html) モジュールを使用しました。どちらも結構柔軟な設定ができるようですが、vSphere の勉強不足でいまいち詳細を把握できていないので、詳細は割愛させていただきます :sweat: 

## まとめ

正直、最初の vCenter デプロイですら、ansible でできると思っていなかったので、無事に起動してきた時には感動すら覚えました :sparkles: community.vmware コレクションには、種々の細かな設定も一通りモジュールが揃っていそうなので、他に何ができるのかもっと調べてみようと思います。
ありがとう ansible :raised_hands:
すごいぞ ansible :raised_hands: