尝试使用Ansible自动化构建Windows Server到VMware vSphere环境
首先
你好,我是一位只懂得本地环境的时代倒退工程师。
我注意到在本地环境下搭建Windows Server的信息并不流行,所以我决定写一篇文章来分享。
希望这篇初次投稿的文章能够对读者有所启发。
现在,我将为您介绍本文的结构。
-
- 从Windows Server的评估版ISO构建服务器
-
- 将构建的服务器模板化
- 基于模板使用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控制器。
启动服务器
让我们通过vCenter Server启动服务器并进行操作系统安装。基本上只需要按照屏幕上的指示进行即可,因此省略了详细说明。
驱动程序安装
此外,在以下情况下,您无需安装此驱动程序:
– 使用vmware_guest模块指定LSI Logic SAS驱动程序
– 指定操作系统为Windows Server 2022。
安装VMware Tools
禁用防火墙
允许远程桌面连接
扩展C驱动器
根据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 #設定の確認
允许通过SMB挂载NAS
2. 将构建完成的服务器进行模板化
卸载评估版ISO
关闭Windows操作系统的电源,并从”编辑设置”中移除ISO文件。
创建模板
在创建服务器结构时,将使用与之前相同的名称(本次是”windows2019-dc-evaluation”)来创建模板。
创建sysprep
sysprep是一种通用化处理,将从模板创建的服务器转变为各自的特定服务器。
如果不进行此处理,Windows操作系统内的SID会重复,可能会变成一个严重问题,所以一定要做。
sysprep可以从vCenter Server的虚拟机自定义规范进行设置。
使用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