尝试使用Ansible自动化构建Windows Server到VMware vSphere环境

首先

你好,我是一位只懂得本地环境的时代倒退工程师。
我注意到在本地环境下搭建Windows Server的信息并不流行,所以我决定写一篇文章来分享。
希望这篇初次投稿的文章能够对读者有所启发。

现在,我将为您介绍本文的结构。

    1. 从Windows Server的评估版ISO构建服务器

 

    1. 将构建的服务器模板化

 

    基于模板使用Ansible自动构建

行动环境

    • VMware vSphere

vSphere Client バージョン 7.0.1.00000

Ansible

RHEL8.2
ansible core 2.12.7
python 3.8.0
pywinrm 0.4.2

Windows Server

2016
2019
2022

建造步骤

1. 从Windows Server的评估版ISO开始建立服务器。

下载评估版ISO文件。

我将从Microsoft官方网站下载评估版ISO。

制作playbook

只需要创建一个作为模板的服务器,所以使用Ansible没有必要,但出于学习目的,我使用了playbook。

windows2019-dc-evaluation
---
- name: デフォルトの評価版ISOからサーバを作成
  hosts: windows2019-dc-evaluation
  gather_facts: false
  tasks:
    - vmware_guest:
        state: poweredoff
        name: windows2019-dc-evaluation 
        hardware:
          num_cpus: 8
          num_cpu_cores_per_socket: 8 
          memory_mb: 16384
          boot_firmware: bios 
        networks:
        - name: portgroup_name 
        disk:
        - size_gb: 20
          type: thin
          datastore: datastore_name
        guest_id: windows2019srv_64Guest 
        datacenter: datacenter_name 
        folder: /datacenter_name/vm/folder_name
        cdrom:
          type: iso
          iso_path: [datastore_name] iso/WindowsServer2019evaluate.iso #公式サイトからダウンロードした評価版ISO
          controller_number: 0
          unit_number: 0
        resource_pool: resourcepool_name 
        hostname: XX.XX.XX.XX
        username: administrator@vsphere.local 
        password: password 
        validate_certs: false
      delegate_to: localhost

执行Playbook

$ ansible-playbook -i hosts ./vmware_guet.yml

提供USB控制器

登录vCenter Server,并从”编辑设置”中为该服务器添加USB控制器3.1。
如果没有USB控制器,操作系统内的GUI将无法显示鼠标光标,因此请务必添加USB控制器。

1-OS起動前にusb3付与.png
听说使用community.vmware_guest_controller模块可以添加USB控制器。

启动服务器

让我们通过vCenter Server启动服务器并进行操作系统安装。基本上只需要按照屏幕上的指示进行即可,因此省略了详细说明。

驱动程序安装

7-インストール場所選択.png
VMware的准虚拟化SCSI控制器是高性能的驱动程序。
此外,在以下情况下,您无需安装此驱动程序:
– 使用vmware_guest模块指定LSI Logic SAS驱动程序
– 指定操作系统为Windows Server 2022。

安装VMware Tools

8-vmwaretoolsマウント.png
为了补充说明,请注意,vmware_guest模块的网络适配器并不使用Windows标准的e1000e,而是采用了性能更高的vmxnet3。为了在Windows操作系统中识别vmxnet3,需要安装vmware-tools。

禁用防火墙

19-2-firewallオフ×3後.png

允许远程桌面连接

20-リモートデスクトップ許可.png

扩展C驱动器

23-Cdrive拡張.png
在WindowsServer2022中,C驱动器无法仅通过这个命令扩展,因为第二个分区(volume=1:存储实际数据的位置)后面有一个恢复分区。
根据AOMEI公司的网站,如果删除恢复分区,这个命令将能够运行。

无需四处寻找,只需一个选项:WinRM许可。

我們將設定 Ansible 連接到 Windows 的 WinRM 協議。
在使用 WinRM 進行連接時,有多種認證方式可供選擇,但我們選擇了配置簡單且無需外部軟體包的基本認證方式。
在 Windows Server 內,執行以下命令以允許基於基本認證的 WinRM 連接。

C:\Users\Administrator>winrm qc      #WinRM リスナーの設定
..
変更しますか[y/n]?y
..
C:\Users\Administrator>winrm set winrm/config/client/auth @{Basic="true"}
C:\Users\Administrator>winrm set winrm/config/service/auth @{Basic="true"}
C:\Users\Administrator>winrm set winrm/config/service @{AllowUnencrypted="true"}
C:\Users\Administrator>winrm get winrm/config      #設定の確認
在构建服务器之后,您可以将上述命令中的“true”更改为“false”,然后重新输入以恢复到初始状态。有关每个命令的详细含义,请参阅Microsoft官方文档。有点难。

允许通过SMB挂载NAS

25-グループポリシーから編集.png

2. 将构建完成的服务器进行模板化

卸载评估版ISO

关闭Windows操作系统的电源,并从”编辑设置”中移除ISO文件。

创建模板

在创建服务器结构时,将使用与之前相同的名称(本次是”windows2019-dc-evaluation”)来创建模板。

26-ISO外してからテンプレ化.jpg

创建sysprep

sysprep是一种通用化处理,将从模板创建的服务器转变为各自的特定服务器。

如果不进行此处理,Windows操作系统内的SID会重复,可能会变成一个严重问题,所以一定要做。

sysprep可以从vCenter Server的虚拟机自定义规范进行设置。

27-9-sysprep設定.png

使用Ansible根据模板进行自动构建。

终于完成了准备工作。

创建变量文件。

首先,从变量文件中创建。
在hosts文件中指定可以与Ansible服务器通信的IP地址(根据虚拟机的定制规范设置的IP)。

[guest]
test1-from-template ansible_host=10.10.10.10
test2-from-template ansible_host=10.10.10.10
test3-from-template ansible_host=10.10.10.10

也将创建每个服务器的变量文件。
将忽略 test2-from-template 和 test3-from-template。

---
# /etc/ansible/vmware_guest_from_template.yml
vmware_guest:
  name: "{{ inventory_hostname }}"
  cpu: 20
  memory: 20
  disk: [
    { size_gb: 100, type: thin, datastore: datastore_1 },
    { size_gb: 100, type: thin, datastore: datastore_2 }
  ]
  networks: [
    { label: Network adapter 1, name: portgroup_1 },
    { label: Network adapter 2, name: portgroup_2 }
  ]
  resource_pool: resourcepool_name
  folder: folder_name
  boot: bios
  template: windows2019-dc-evaluation                         # 作成したテンプレート
  customization_spec: windows-sysprep                         # 作成したsysprep

# /etc/ansible/windows_os_setting.yml
ansible_user: Administrator                                   # WinRM接続情報
ansible_connection: winrm                                     # WinRM接続情報
ansible_winrm_transport: basic                                # WinRM接続情報
ansible_port: 5985                                            # WinRM接続情報

password: HogeH0ge!                                           # 構築後のサーバに設定したいパスワード

ip_address:
  - { nic: "Ethernet1", address: 10.85.93.143/27 }
  - { nic: "Ethernet0", address: 192.168.10.10/24 }           # Ethernet0がloopの最後になるように降順で記載

static_route:
  - { nic: "Ethernet1", gw: 10.85.93.155, address: 0.0.0.0/0 }
  - { nic: "Ethernet1", gw: 10.85.93.131, address: 10.2.1.0/24 }
  - { nic: "Ethernet0", gw: 192.168.10.3, address: 192.168.10.1/24 }

dns:
  - { nic: "Ethernet1", dns_server: 10.85.223.2, order: primary }

disk:
  - { drive_letter: C }
  - { drive_letter: D }

ntp:
  - { ntp_servers: "8.8.8.8,8.8.8.4" }

# /etc/ansible/windows_os_additional_setting
smb:
  - { mount_src: "NASサーバの提供するFQDN", mount_path: E }

创建剧本

接下来,我们将创建一个playbook。虽然我们使用了jinja2过滤器等工具,但并没有特别的原因。

- name: サーバを作成
  community.vmware.vmware_guest:
    state: poweredoff
    name: "{{ vmware_guest.name }}"
    hardware:
      num_cpus: "{{ vmware_guest.cpu }}"
      num_cpu_cores_per_socket: "{{ vmware_guest.cpu_cores_per_socket | default(vmware_guest.cpu) }}"
      cpu_reservation: >-
        {%- if vmware_guest.cpu_reservation is defined -%} {{ vmware_guest.cpu_reservation }}
        {%- else -%} 0
        {%- endif -%}
      memory_mb: "{{ vmware_guest.memory * 1024 }}"
      scsi: "{{ vmware_guest.scsi | default(omit) }}"
      boot_firmware: "{{ vmware_guest.boot }}"
    disk: "{{ vmware_guest.disk }}"
    template: "{{ vmware_guest.template }}"
    customization_spec: "{{ vmware_guest.customization_spec }}"
    datacenter: datacenter_name
    folder: "datacenter_name/vm/{{ vmware_guest.folder }}"
    resource_pool: resourcepool_name
    hostname: XX.XX.XX.XX
    username: administrator@vsphere.local 
    password: password 
    validate_certs: false
  delegate_to: localhost

- name: サーバにポートグループに紐づける
  community.vmware.vmware_guest_network:
    name: "{{ vmware_guest.name }}"
    networks:
      - label: "{{ item.label }}"
        name: "{{ item.name }}"
        dvswitch_name: dvswitch_name
        state: new
    hostname: XX.XX.XX.XX
    username: administrator@vsphere.local 
    password: password 
    validate_certs: false
  delegate_to: localhost
  loop: "{{ vmware_guest.networks }}"
  when: item.label != 'Network adapter 1'      #Network adapter 1は、テンプレート内に埋め込んだIPアドレス(=portgroup)を使うため付け替えない
---
- name: 作成したWindos ServerにOS初期設定を実施
  hosts: guest
  gather_facts: false
  serial: 1               #guestグループにあるサーバに対し1台ずつ設定を投入する(テンプレート内に紐づいたIPアドレス(=portgroup)を使い回すため)
  tasks:
    - name: サーバを起動
      vmware_guest_powerstate:
        name: "{{ vmware_guest.name }}"
        state: powered-on
        hostname: XX.XX.XX.XX
        username: administrator@vsphere.local 
        password: password
        validate_certs: false
      delegate_to: localhost

    - name: sysprepが完了するまで待つ
      pause:
        seconds: 180

    - name: WinRM接続するパスワードを指定
      set_fact:
        ansible_password: "P@ssw0rd!"         #仮想マシンのカスタマイズ仕様 で設定したパスワード

    - name: ホスト名設定
      win_hostname:
        name: "{{ inventory_hostname }}"

    - name: DNSサーバー設定
      win_shell: >-
        {%- if item.order == "primary" -%}
          netsh interface ip set dns {{ item.nic }} static {{ item.dns_server }} primary
        {%- else -%}
          netsh interface ip add dns {{ item.nic }} {{ item.dns_server }}
        {%- endif -%}
      loop: "{{ dns }}"

    - name: Cドライブを拡張
      win_shell: diskpart /s c:\tmp\Cdrive-extend.txt

    - name: Cドライブ拡張シェルを削除
      win_file:
        path: c:\tmp
        state: absent

    - name: ディスクの初期化
      win_shell: "Initialize-Disk -Number {{ loop_count }}"
      loop: "{{ disk }}"
      loop_control:
        index_var: loop_count
      when: loop_count != 0                #Cドライブはskip

    - name: ディスクのパーティション設定
      win_partition:
        drive_letter: "{{ item.drive_letter }}"
        partition_size: -1
        disk_number: "{{ loop_count }}"
      loop: "{{ disk }}"
      loop_control:
        index_var: loop_count
      when: loop_count != 0                #Cドライブはskip

    - name: ディスクのファイルシステム設定
      win_format:
        drive_letter: "{{ item.drive_letter }}"
        file_system: NTFS
        full: no
      loop: "{{ disk }}"
      loop_control:
        index_var: loop_count
      when: loop_count != 0                #Cドライブはskip

    - name: WindowsOS内のパスワードを変更
      win_user:
        name: Administrator
        password: "{{ password }}"
      ignore_errors: True                  #変更時にWinRM接続が途切れてエラーになるため、エラーを無視する

    - name: WinRM接続するパスワードを変更    #変更後のパスワードをWinRM接続用(=playbook用)に再定義
      set_fact:
        ansible_password: "{{ password }}"

    - name: NTPサーバ設定
      win_shell: |-
        w32tm /config /syncfromflags:manual /manualpeerlist:{{ item.ntp_servers }} /update
        w32tm /resync
      loop: "{{ ntp }}"

    - name: ルート設定
      win_shell: >-
        route -p add {{ item.address | ipaddr('network') }} mask {{ item.address | ipaddr('netmask') }} {{ item.gw }}
      loop: "{{ static_route }}"
      ignore_errors: True
      when: 

    - name: IPアドレス設定(テンプレート内に埋め込んだIPアドレスから変更)
      win_shell: >-
        netsh interface ip set address {{ item.nic }} static {{ item.address | ipaddr('address') }} {{ item.address | ipaddr('netmask') }}
      loop: "{{ ip_address }}"
      ignore_errors: True                #loopの最後がEthernet0であり、そのIPアドレスを変更(=返却)するとWinRM接続不可になるため、そのエラーを無視

    - name: サーバ停止
      vmware_guest_powerstate:
        name: "{{ vmware_guest.name }}"
        state: shutdown-guest
        hostname: XX.XX.XX.XX
        username: administrator@vsphere.local 
        password: password
        validate_certs: false
      delegate_to: localhost

    - name: テンプレート内に埋め込んだportgroupから切り離し
      vmware_guest_network:
        name: "{{ vmware_guest.name }}"
        networks:
          - label: "Network adapter 1"
            dvswitch_name: "{{ dvswitch_name }}"
            state: absent
        hostname: XX.XX.XX.XX
        username: administrator@vsphere.local 
        password: password
        validate_certs: false
      delegate_to: localhost

    - name: 正規の(=変数ファイルに書いた)ポートグループに接続
      vmware_guest_network:
        name: "{{ vmware_guest.name }}"
        networks:
          - label: "Network adapter 1"
            name: "{{ vmware_guest.networks.0.name }}"
            dvswitch_name: "{{ dvswitch_name }}"
            state: new
        hostname: XX.XX.XX.XX
        username: administrator@vsphere.local 
        password: password
        validate_certs: false
      delegate_to: localhost

    - name: サーバ起動
      vmware_guest_powerstate:
        name: "{{ vmware_guest.name }}"
        state: powered-on
        hostname: XX.XX.XX.XX
        username: administrator@vsphere.local 
        password: password
        validate_certs: false
      delegate_to: localhost

执行playbook

$ ansible-playbook -i hosts ./vmware_guet_from_template.yml
$ ansible-playbook -i hosts ./windows_os_setting.yml

创建(新增设定)变量文件

如果有一些无法在初始设置(如IP地址等)完成的设置,可以通过附加设置进行补充。只要没有因为本地环境而有网络限制…
我们可以在hosts文件中指定正式的(即在变量文件中写入的)IP地址。

[guest]
test1-from-template ansible_host=192.168.10.10
test2-from-template ansible_host=192.168.10.11
test3-from-template ansible_host=192.168.10.12

创建一个(附加设定)的playbook。

---
- name: Windows OSに追加設定を実施
  hosts: guest
  gather_facts: false
  tasks:
    - name: WinRM接続するパスワードを指定
      set_fact:
        ansible_password: "{{ password }}"

    - name: NASマウント
      win_mapped_drive:
        letter: "{{ item.mount_path }}"
        path: "{{ item.mount_src }}"
        username: "{{ ansible_user }}"
        password: "{{ ansible_password }}"        
      loop: "{{ smb }}"

执行playbook中的附加设置

$ ansible-playbook -i hosts ./windows_os_additional_setting.yml

 

广告
将在 10 秒后关闭
bannerAds