Establish clean homelab infrastructure baseline
Reorganize the brownfield repository, remove retired and generated artifacts, harden ignore rules, and record the GitOps/IaC redesign.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
---
|
||||
# windows_vm role — create a fresh Windows Server 2025 admin box from the ISO,
|
||||
# fully unattended via autounattend.xml, WinRM-ready for join-windows.yml to finish.
|
||||
#
|
||||
# Uses virtio disk (viostor) + virtio NIC (NetKVM): the drivers are injected during
|
||||
# Setup via autounattend DriverPaths (from the virtio-win ISO), and the full
|
||||
# virtio-win guest tools (QEMU guest agent, balloon, …) are installed at first logon.
|
||||
|
||||
win_vm_name: "winadmin"
|
||||
win_vm_vcpus: 4
|
||||
win_vm_memory_mb: 6144
|
||||
win_vm_disk_gb: 60
|
||||
|
||||
# libvirt placement — zvol root disk, presented as SATA (AHCI, native Windows driver).
|
||||
win_vm_zvol_parent: "data/vm"
|
||||
win_vm_zvol: "{{ win_vm_zvol_parent }}/{{ win_vm_name }}"
|
||||
win_vm_zvol_dev: "/dev/zvol/{{ win_vm_zvol }}"
|
||||
win_vm_zvol_volblocksize: "16K"
|
||||
win_vm_bridge: "br0"
|
||||
|
||||
# Install media (already in your iso pool)
|
||||
win_vm_iso: "/mnt/pool/proxmox/template/iso/zh-cn_windows_server_2025_updated_aug_2025_x64_dvd_9236d79b.iso"
|
||||
# virtio-win drivers + guest tools — injected during Setup, guest tools run at first logon
|
||||
win_vm_virtio_iso: "/mnt/pool/proxmox/template/iso/virtio-win-1.9.45.iso"
|
||||
win_vm_virtio_ver: "2k25" # driver subfolder for Server 2025
|
||||
|
||||
# Edition to install from install.wim. VERIFY with:
|
||||
# dism /Get-WimInfo /WimFile:<mounted>\sources\install.wim
|
||||
# Typical Server 2025 indexes: 1=Std Core, 2=Std Desktop, 3=DC Core, 4=DC Desktop.
|
||||
win_vm_image_index: 2 # Standard (Desktop Experience) — GPMC needs Desktop
|
||||
|
||||
# GVLK (public KMS client key) matching the edition above; activation happens later
|
||||
# in join-windows.yml against vlmcsd. Std=below; DC=D764K-2NDRG-47T6Q-P8T8W-YP6DF
|
||||
win_vm_gvlk: "TVRH6-WHNXV-R9WG3-9XRFY-MY832"
|
||||
|
||||
# Locale (ISO is zh-CN)
|
||||
win_vm_ui_language: "zh-CN"
|
||||
win_vm_system_locale: "zh-CN"
|
||||
win_vm_user_locale: "zh-CN"
|
||||
win_vm_input_locale: "0409:00000409;0804:00000804" # en-US + zh-CN
|
||||
win_vm_timezone: "China Standard Time"
|
||||
|
||||
# Static IP applied by autounattend (so it's reachable at a known address for WinRM).
|
||||
win_vm_ip: "192.168.10.6"
|
||||
win_vm_prefix: 24
|
||||
win_vm_gateway: "192.168.10.1"
|
||||
win_vm_dns: "{{ samba_ad_dc_ip }}" # point Windows at the DC directly (AD requirement)
|
||||
|
||||
# Local Administrator password comes from vault (vault_win_local_admin_password),
|
||||
# surfaced as win_domain_admin_password / win_local_admin_password in group_vars.
|
||||
win_vm_admin_password: "{{ vault_win_local_admin_password }}"
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
# Create + unattended-install a Windows Server 2025 admin box on local libvirt.
|
||||
# Runs on localhost (qemu:///system, become: true). Idempotent on the domain existing.
|
||||
|
||||
- name: Assert the Windows install ISO exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ win_vm_iso }}"
|
||||
register: win_iso_stat
|
||||
failed_when: not win_iso_stat.stat.exists
|
||||
|
||||
- name: Check whether the libvirt domain already exists
|
||||
ansible.builtin.command: "virsh dominfo {{ win_vm_name }}"
|
||||
register: win_dominfo
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Create the Windows VM
|
||||
when: win_dominfo.rc != 0
|
||||
block:
|
||||
- name: Check whether the root-disk zvol already exists
|
||||
ansible.builtin.command: "zfs list -H -o name {{ win_vm_zvol }}"
|
||||
register: win_zvol_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Create the root-disk zvol
|
||||
ansible.builtin.command:
|
||||
cmd: >-
|
||||
zfs create -V {{ win_vm_disk_gb }}G
|
||||
-o volblocksize={{ win_vm_zvol_volblocksize }}
|
||||
{{ win_vm_zvol }}
|
||||
when: win_zvol_check.rc != 0
|
||||
|
||||
- name: Wait for the zvol device node
|
||||
ansible.builtin.wait_for:
|
||||
path: "{{ win_vm_zvol_dev }}"
|
||||
timeout: 30
|
||||
|
||||
- name: Render autounattend.xml
|
||||
ansible.builtin.template:
|
||||
src: autounattend.xml.j2
|
||||
dest: "/var/lib/libvirt/images/{{ win_vm_name }}-autounattend.xml"
|
||||
mode: "0600"
|
||||
no_log: true # contains the local admin password
|
||||
|
||||
- name: Build the autounattend seed ISO (label AUTOUNATTEND)
|
||||
ansible.builtin.command:
|
||||
cmd: >-
|
||||
genisoimage -output /var/lib/libvirt/images/{{ win_vm_name }}-unattend.iso
|
||||
-volid AUTOUNATTEND -joliet -rock -graft-points
|
||||
autounattend.xml=/var/lib/libvirt/images/{{ win_vm_name }}-autounattend.xml
|
||||
args:
|
||||
creates: "/var/lib/libvirt/images/{{ win_vm_name }}-unattend.iso"
|
||||
|
||||
- name: Define and start the VM (UEFI, virtio disk + NIC, virtio-win attached)
|
||||
ansible.builtin.command:
|
||||
cmd: >-
|
||||
virt-install
|
||||
--name {{ win_vm_name }}
|
||||
--memory {{ win_vm_memory_mb }}
|
||||
--vcpus {{ win_vm_vcpus }}
|
||||
--machine q35
|
||||
--boot loader=/usr/share/OVMF/OVMF_CODE_4M.fd,loader.readonly=yes,loader.type=pflash,nvram.template=/usr/share/OVMF/OVMF_VARS_4M.fd
|
||||
--osinfo require=off,name=win2k25
|
||||
--disk path={{ win_vm_zvol_dev }},format=raw,bus=virtio,boot.order=2
|
||||
--disk path={{ win_vm_iso }},device=cdrom,bus=sata,boot.order=1
|
||||
--disk path=/var/lib/libvirt/images/{{ win_vm_name }}-unattend.iso,device=cdrom,bus=sata
|
||||
--disk path={{ win_vm_virtio_iso }},device=cdrom,bus=sata
|
||||
--network bridge={{ win_vm_bridge }},model=virtio
|
||||
--video vga --graphics vnc,listen=0.0.0.0
|
||||
--noautoconsole --import
|
||||
register: win_virt_install
|
||||
|
||||
- name: Press a key past "Press any key to boot from CD" (first boot only)
|
||||
ansible.builtin.shell: >-
|
||||
for i in $(seq 1 20); do
|
||||
virsh send-key {{ win_vm_name }} --codeset linux KEY_ENTER >/dev/null 2>&1 || true;
|
||||
sleep 2;
|
||||
done
|
||||
changed_when: false
|
||||
|
||||
- name: Wait for WinRM (Windows Setup runs unattended, then configures WinRM)
|
||||
ansible.builtin.wait_for:
|
||||
host: "{{ win_vm_ip }}"
|
||||
port: 5986
|
||||
delay: 60
|
||||
timeout: 2700 # up to 45 min: install + reboots + first-logon commands
|
||||
when: win_dominfo.rc != 0
|
||||
|
||||
- name: Eject install media and remove the (password-bearing) seed ISO
|
||||
when: win_dominfo.rc != 0
|
||||
block:
|
||||
- name: Eject install media (win ISO=sda, unattend=sdb, virtio=sdc)
|
||||
ansible.builtin.command: "virsh change-media {{ win_vm_name }} {{ item }} --eject --config"
|
||||
loop: [sda, sdb, sdc]
|
||||
failed_when: false
|
||||
changed_when: true
|
||||
|
||||
- name: Delete the autounattend seed ISO + rendered answer file
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- "/var/lib/libvirt/images/{{ win_vm_name }}-unattend.iso"
|
||||
- "/var/lib/libvirt/images/{{ win_vm_name }}-autounattend.xml"
|
||||
@@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Unattended install for Windows Server 2025 (Desktop Experience), UEFI/GPT.
|
||||
Rendered by the windows_vm role. Contains the local Administrator password in
|
||||
cleartext — it lives only on a local, ephemeral seed ISO that the role removes
|
||||
after install. -->
|
||||
<unattend xmlns="urn:schemas-microsoft-com:unattend">
|
||||
|
||||
<!-- ============ windowsPE: language, disk, image, product key ============ -->
|
||||
<settings pass="windowsPE">
|
||||
<!-- Inject virtio drivers so Setup sees the virtio disk (viostor) and NIC (NetKVM).
|
||||
The virtio CD's drive letter in WinPE is unpredictable, so list candidates. -->
|
||||
<component name="Microsoft-Windows-PnpCustomizationsWinPE" processorArchitecture="amd64"
|
||||
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<DriverPaths>
|
||||
{% for L in ['D','E','F','G'] %}
|
||||
<PathAndCredentials wcm:action="add" wcm:keyValue="{{ loop.index }}1" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"><Path>{{ L }}:\viostor\{{ win_vm_virtio_ver }}\amd64</Path></PathAndCredentials>
|
||||
<PathAndCredentials wcm:action="add" wcm:keyValue="{{ loop.index }}2" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"><Path>{{ L }}:\NetKVM\{{ win_vm_virtio_ver }}\amd64</Path></PathAndCredentials>
|
||||
{% endfor %}
|
||||
</DriverPaths>
|
||||
</component>
|
||||
|
||||
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64"
|
||||
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<SetupUILanguage><UILanguage>{{ win_vm_ui_language }}</UILanguage></SetupUILanguage>
|
||||
<InputLocale>{{ win_vm_input_locale }}</InputLocale>
|
||||
<SystemLocale>{{ win_vm_system_locale }}</SystemLocale>
|
||||
<UILanguage>{{ win_vm_ui_language }}</UILanguage>
|
||||
<UserLocale>{{ win_vm_user_locale }}</UserLocale>
|
||||
</component>
|
||||
|
||||
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64"
|
||||
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<DiskConfiguration>
|
||||
<WillShowUI>OnError</WillShowUI>
|
||||
<Disk wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||
<DiskID>0</DiskID>
|
||||
<WillWipeDisk>true</WillWipeDisk>
|
||||
<CreatePartitions>
|
||||
<CreatePartition wcm:action="add"><Order>1</Order><Type>EFI</Type><Size>260</Size></CreatePartition>
|
||||
<CreatePartition wcm:action="add"><Order>2</Order><Type>MSR</Type><Size>16</Size></CreatePartition>
|
||||
<CreatePartition wcm:action="add"><Order>3</Order><Type>Primary</Type><Extend>true</Extend></CreatePartition>
|
||||
</CreatePartitions>
|
||||
<ModifyPartitions>
|
||||
<ModifyPartition wcm:action="add"><Order>1</Order><PartitionID>1</PartitionID><Format>FAT32</Format><Label>System</Label></ModifyPartition>
|
||||
<ModifyPartition wcm:action="add"><Order>2</Order><PartitionID>2</PartitionID></ModifyPartition>
|
||||
<ModifyPartition wcm:action="add"><Order>3</Order><PartitionID>3</PartitionID><Format>NTFS</Format><Label>Windows</Label><Letter>C</Letter></ModifyPartition>
|
||||
</ModifyPartitions>
|
||||
</Disk>
|
||||
</DiskConfiguration>
|
||||
<ImageInstall>
|
||||
<OSImage>
|
||||
<InstallTo><DiskID>0</DiskID><PartitionID>3</PartitionID></InstallTo>
|
||||
<InstallFrom>
|
||||
<MetaData wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||
<Key>/IMAGE/INDEX</Key><Value>{{ win_vm_image_index }}</Value>
|
||||
</MetaData>
|
||||
</InstallFrom>
|
||||
</OSImage>
|
||||
</ImageInstall>
|
||||
<UserData>
|
||||
<ProductKey><Key>{{ win_vm_gvlk }}</Key><WillShowUI>OnError</WillShowUI></ProductKey>
|
||||
<AcceptEula>true</AcceptEula>
|
||||
<FullName>Administrator</FullName>
|
||||
<Organization>ddupan</Organization>
|
||||
</UserData>
|
||||
</component>
|
||||
</settings>
|
||||
|
||||
<!-- ==================== specialize: computer name ==================== -->
|
||||
<settings pass="specialize">
|
||||
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
|
||||
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<ComputerName>{{ win_vm_name }}</ComputerName>
|
||||
</component>
|
||||
</settings>
|
||||
|
||||
<!-- ============ oobeSystem: admin pw, autologon, first-logon ============ -->
|
||||
<settings pass="oobeSystem">
|
||||
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64"
|
||||
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<InputLocale>{{ win_vm_input_locale }}</InputLocale>
|
||||
<SystemLocale>{{ win_vm_system_locale }}</SystemLocale>
|
||||
<UILanguage>{{ win_vm_ui_language }}</UILanguage>
|
||||
<UserLocale>{{ win_vm_user_locale }}</UserLocale>
|
||||
</component>
|
||||
|
||||
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
|
||||
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<OOBE>
|
||||
<HideEULAPage>true</HideEULAPage>
|
||||
<HideLocalAccountScreen>true</HideLocalAccountScreen>
|
||||
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
|
||||
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
|
||||
<NetworkLocation>Work</NetworkLocation>
|
||||
<ProtectYourPC>3</ProtectYourPC>
|
||||
<SkipMachineOOBE>true</SkipMachineOOBE>
|
||||
<SkipUserOOBE>true</SkipUserOOBE>
|
||||
</OOBE>
|
||||
<TimeZone>{{ win_vm_timezone }}</TimeZone>
|
||||
<UserAccounts>
|
||||
<AdministratorPassword>
|
||||
<Value>{{ win_vm_admin_password }}</Value>
|
||||
<PlainText>true</PlainText>
|
||||
</AdministratorPassword>
|
||||
</UserAccounts>
|
||||
<AutoLogon>
|
||||
<Enabled>true</Enabled>
|
||||
<LogonCount>1</LogonCount>
|
||||
<Username>Administrator</Username>
|
||||
<Password><Value>{{ win_vm_admin_password }}</Value><PlainText>true</PlainText></Password>
|
||||
</AutoLogon>
|
||||
<FirstLogonCommands>
|
||||
<SynchronousCommand wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||
<Order>1</Order>
|
||||
<Description>Static IP + DNS</Description>
|
||||
<CommandLine>powershell -NoProfile -ExecutionPolicy Bypass -Command "$i=(Get-NetAdapter | Where-Object Status -eq 'Up' | Select-Object -First 1).ifIndex; New-NetIPAddress -InterfaceIndex $i -IPAddress {{ win_vm_ip }} -PrefixLength {{ win_vm_prefix }} -DefaultGateway {{ win_vm_gateway }}; Set-DnsClientServerAddress -InterfaceIndex $i -ServerAddresses {{ win_vm_dns }}"</CommandLine>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||
<Order>2</Order>
|
||||
<Description>Enable WinRM + HTTPS listener (self-signed)</Description>
|
||||
<CommandLine>powershell -NoProfile -ExecutionPolicy Bypass -Command "Enable-PSRemoting -Force; $c=New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\LocalMachine\My; New-Item -Path WSMan:\localhost\Listener -Address * -Transport HTTPS -CertificateThumbPrint $c.Thumbprint -Force"</CommandLine>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||
<Order>3</Order>
|
||||
<Description>Firewall: allow WinRM HTTPS 5986</Description>
|
||||
<CommandLine>powershell -NoProfile -ExecutionPolicy Bypass -Command "New-NetFirewallRule -DisplayName 'WinRM HTTPS' -Direction Inbound -Protocol TCP -LocalPort 5986 -Action Allow"</CommandLine>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||
<Order>4</Order>
|
||||
<Description>Enable Remote Desktop + firewall group</Description>
|
||||
<CommandLine>powershell -NoProfile -ExecutionPolicy Bypass -Command "Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections -Value 0; Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name UserAuthentication -Value 1; Enable-NetFirewallRule -Group '@FirewallAPI.dll,-28752'"</CommandLine>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||
<Order>5</Order>
|
||||
<Description>Install virtio-win guest tools (drivers + QEMU guest agent + balloon)</Description>
|
||||
<CommandLine>powershell -NoProfile -ExecutionPolicy Bypass -Command "$e=(Get-PSDrive -PSProvider FileSystem | ForEach-Object { Join-Path $_.Root 'virtio-win-guest-tools.exe' } | Where-Object { Test-Path $_ } | Select-Object -First 1); if ($e) { Start-Process $e -ArgumentList '/install','/quiet','/norestart' -Wait }"</CommandLine>
|
||||
</SynchronousCommand>
|
||||
</FirstLogonCommands>
|
||||
</component>
|
||||
</settings>
|
||||
</unattend>
|
||||
Reference in New Issue
Block a user