windows-iac-vm-tooling/doc.tex
root e4f03427b7
Some checks are pending
Build and Release / build-sign-package (push) Waiting to run
feat: Add professional hierarchical documentation
- Created comprehensive README.md with Mermaid diagrams, badges, and TOC
- Added docs/ directory with 7 sections and 14 markdown files
- Included architecture diagrams, flowcharts, and sequence diagrams
- All documentation is fully interlinked with cross-references
- Added ISO storage location on Proxmox development server
- Included troubleshooting guide and evaluation management docs
- All config files (Packer, Terraform, Ansible, Forgejo) documented
- Added icons and visual elements throughout documentation
2026-02-06 14:47:15 +00:00

461 lines
13 KiB
TeX

\documentclass[11pt, a4paper]{article}
% --- Geometry & Layout ---
\usepackage[top=2.5cm, bottom=2.5cm, left=2.5cm, right=2.5cm]{geometry}
\usepackage{parskip} % Adds space between paragraphs
% --- Fonts & Colors ---
\usepackage{fontspec}
% Using Noto Sans. Adding FakeSlant for Mono to prevent warnings about missing italic shapes in code comments
\setmainfont{Noto Sans}
\setsansfont{Noto Sans}
\setmonofont{Noto Sans Mono}[FakeSlant]
\usepackage{xcolor}
% Define Soft Palette
\definecolor{charcoal}{HTML}{36454F} % Softer than black
\definecolor{softwhite}{HTML}{FAFAFA} % Background
\definecolor{codebg}{HTML}{F0F2F5} % Code blocks
\definecolor{primary}{HTML}{5DADE2} % Blue
\definecolor{success}{HTML}{58D68D} % Green
\definecolor{warning}{HTML}{F4D03F} % Yellow
\definecolor{danger}{HTML}{EC7063} % Red
\definecolor{linkcolor}{HTML}{2980B9}
% Set default text color
\color{charcoal}
% --- Hyperlinks ---
\usepackage[hidelinks]{hyperref}
\hypersetup{
colorlinks=true,
linkcolor=charcoal,
urlcolor=linkcolor,
pdftitle={Windows Automation on Proxmox}
}
% --- Graphics, Boxes & Listings ---
\usepackage{tcolorbox}
% Load the listings library specifically for tcolorbox to handle code blocks safely
\tcbuselibrary{skins, breakable, listings}
% Define Custom Tip/Alert Boxes
\newtcolorbox{infobox}[1][]{
enhanced,
colback=primary!10!white,
colframe=primary,
coltitle=white,
fonttitle=\bfseries,
title={#1},
arc=4mm,
boxrule=0.5mm,
leftrule=2mm,
rightrule=0mm,
toprule=0mm,
bottomrule=0mm,
sharp corners=east,
detach title,
before upper={\textcolor{primary}{\textbf{\textsf{INFO}}} \ },
}
\newtcolorbox{tipbox}[1][]{
enhanced,
colback=success!10!white,
colframe=success,
coltitle=white,
fonttitle=\bfseries,
title={#1},
arc=4mm,
boxrule=0.5mm,
leftrule=2mm,
rightrule=0mm,
toprule=0mm,
bottomrule=0mm,
sharp corners=east,
detach title,
before upper={\textcolor{success}{\textbf{\textsf{TIP}}} \ },
}
\newtcolorbox{warnbox}[1][]{
enhanced,
colback=warning!10!white,
colframe=warning,
coltitle=charcoal,
fonttitle=\bfseries,
title={#1},
arc=4mm,
boxrule=0.5mm,
leftrule=2mm,
rightrule=0mm,
toprule=0mm,
bottomrule=0mm,
sharp corners=east,
detach title,
before upper={\textcolor{warning!80!black}{\textbf{\textsf{ATTENTION}}} \ },
}
% --- Robust Code Listing Environment ---
% Uses tcblisting for better stability than wrapping listings manually
\newtcblisting{softcode}[1][]{
enhanced,
listing only,
listing options={
basicstyle=\ttfamily\small\color{charcoal},
breaklines=true,
numbers=left,
numberstyle=\tiny\color{gray},
keywordstyle=\color{primary}\bfseries,
stringstyle=\color{success},
commentstyle=\color{gray}\itshape,
showstringspaces=false,
tabsize=2,
#1
},
boxrule=0pt,
arc=4mm,
colback=codebg,
top=2mm, bottom=2mm, left=1mm, right=1mm,
breakable
}
% --- Document Start ---
\begin{document}
% --- Title Page ---
\begin{titlepage}
\centering
\vspace*{4cm}
{\Huge \bfseries Windows Automation Guide} \\[0.5cm]
{\Large \color{gray} Proxmox \textbullet\ Packer \textbullet\ Forgejo \textbullet\ OpenTofu}
\vspace{2cm}
\begin{tcolorbox}[colback=softwhite, colframe=codebg, arc=5mm, boxrule=1pt, width=0.8\textwidth]
\centering
\vspace{0.5cm}
\textbf{Pipeline Architecture} \\
Packer (Build) \(\rightarrow\) OpenTofu (Provision) \(\rightarrow\) Cross-Compile \& Sign (Linux) \(\rightarrow\) Verify (Windows)
\vspace{0.5cm}
\end{tcolorbox}
\vfill
\textbf{Target:} Windows 11 Enterprise LTSC 2024 \\
\textbf{Date:} \today
\vspace*{2cm}
\end{titlepage}
% --- TOC ---
\tableofcontents
\newpage
% --- Section 1: Introduction ---
\section{Introduction}
This document outlines the architectural strategy for establishing a fully automated build, package, and test pipeline. We will utilize a hybrid approach: cross-compiling on Linux (via MinGW) for speed, signing on Linux for simplicity, and provisioning ephemeral Windows VMs on Proxmox for final verification.
\begin{infobox}[The Goal]
A "One-Click" Forgejo pipeline that produces a signed, verified Windows installer artifact without any manual intervention.
\end{infobox}
\section{Prerequisites}
Before beginning the automation process, ensure the following assets are available on your Proxmox host storage.
\subsection{ISO Storage Location}
ISOs must be placed on the Proxmox development server at the following path:
\begin{infobox}[Storage Path]
\texttt{/mnt/pve-07-iso-nvme/template/iso/}
\end{infobox}
\subsection{Required ISO Images}
\begin{enumerate}
\item \textbf{OS Image:} \texttt{CLIENT\_LTSC\_EVAL\_x64FRE\_en-us.iso} (Windows 11 LTSC 2024).
\item \textbf{Driver Image:} \texttt{virtio-win.iso} (Required for I/O performance).
\item \textbf{Optional:} \texttt{SERVER\_EVAL\_x64FRE\_en-us.iso} (Windows Server 2022).
\item \textbf{Optional:} \texttt{26100.1742.240906-0331.ge\_release\_svc\_refresh\_CLIENT\_LTSC\_EVAL\_x64FRE\_en-us.iso} (Alternate Windows 11 LTSC).
\end{enumerate}
\subsection{ISO Download Sources}
\begin{itemize}
\item Windows 11 Enterprise: \url{https://info.microsoft.com/ww-landing-windows-11-enterprise.html}
\item Windows 11 IoT: \url{https://info.microsoft.com/ww-landing-eval-center--win-11-iot.html?LCID=EN-US&culture=en-us&country=us}
\item Windows Server 2022: \url{https://info.microsoft.com/ww-landing-windows-server-2022.html}
\item VirtIO Drivers: \url{https://github.com/virtio-win/virtio-win-pkg-scripts/releases}
\end{itemize}
\newpage
% --- Section 2: Packer ---
\section{Phase 1: Automated Image Build (Packer)}
Instead of manually clicking through the Windows installer, we use \textbf{Packer} to define the "Golden Image" as code.
\subsection{Why Packer over PXE?}
\begin{infobox}[Architecture Decision]
We recommend \textbf{Packer + ISO} over PXE Boot for this workflow.
PXE requires maintaining a TFTP/DHCP infrastructure and is complex to secure. Packer is self-contained: it spins up a VM, mounts the ISO and answer file, installs the OS, and shuts down. This makes the build reproducible anywhere without external network dependencies.
\end{infobox}
\subsection{Packer Configuration (\texttt{windows.pkr.hcl})}
This configuration uses the \texttt{proxmox-iso} builder. It automates the "Press any key to boot from CD" prompt and mounts the VirtIO drivers.
\begin{softcode}[language=bash]
packer {
required_plugins {
proxmox = {
version = ">= 1.1.0"
source = "github.com/hashicorp/proxmox"
}
}
}
source "proxmox-iso" "windows-11" {
proxmox_url = "https://proxmox-host:8006/api2/json"
username = "root@pam"
password = "secret"
node = "la-vmh-07"
# VM Settings
vm_name = "win11-ltsc-template"
template_description = "Built with Packer on ${timestamp()}"
iso_file = "local:iso/CLIENT_LTSC_EVAL_x64FRE_en-us.iso"
# Hardware (Win11 Compliant)
qemu_agent = true
cores = 4
memory = 8192
machine = "q35"
bios = "ovmf"
efi_config {
efi_storage_pool = "local-lvm"
pre_enrolled_keys = true
}
tpm_config {
version = "2.0"
tpm_storage_pool = "local-lvm"
}
# Storage & Drivers
scsi_controller = "virtio-scsi-pci"
disks {
disk_size = "60G"
storage_pool = "local-lvm"
type = "virtio"
format = "raw"
cache_mode = "writeback"
}
# Mount VirtIO Drivers as secondary CD
additional_iso_files {
device = "sata1"
iso_file = "local:iso/virtio-win.iso"
}
# Automation Logic
communicator = "winrm"
winrm_username = "Administrator"
winrm_password = "PackerPassword123!"
winrm_insecure = true
winrm_use_ssl = true
# Boot Command (Handle "Press any key")
boot_command = [
"<wait><wait><wait>","<enter><wait>","<enter><wait>",
"<enter><wait>","<enter>"
]
boot_wait = "10s"
}
build {
sources = ["source.proxmox-iso.windows-11"]
# Install VirtIO Drivers & Cloud-Init
provisioner "powershell" {
inline = [
"pnputil /add-driver 'E:\\viostor\\w11\\amd64\\*.inf' /install",
"pnputil /add-driver 'E:\\NetKVM\\w11\\amd64\\*.inf' /install",
"& 'E:\\virtio-win-guest-tools.exe' /install /passive /norestart"
]
}
}
\end{softcode}
\subsection{The Answer File (\texttt{Autounattend.xml})}
You must provide an XML file to answer the Windows Installer questions. Packer injects this via a virtual floppy or CD.
\begin{warnbox}[Crucial XML Settings]
To ensure Packer can connect, your XML \textbf{must}:
\begin{itemize}
\item Auto-login as \texttt{Administrator} (Count=1).
\item Disable the firewall for the "Private" profile.
\item Run the \texttt{ConfigureRemotingForAnsible.ps1} script in the \texttt{<FirstLogonCommands>} section.
\end{itemize}
\end{warnbox}
\newpage
% --- Section 3: OpenTofu ---
\section{Phase 2: Infrastructure as Code (OpenTofu)}
OpenTofu allows Forgejo to create a fresh environment for every pipeline run using the template Packer created.
\textbf{File: \texttt{main.tf}}
\begin{softcode}[language=bash]
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "0.46.1"
}
}
}
provider "proxmox" {
endpoint = "https://proxmox-host:8006/"
# Credentials injected via Environment Variables in Forgejo
}
resource "proxmox_virtual_environment_vm" "build_agent" {
name = "ci-win-build-${var.build_id}"
node_name = "la-vmh-07"
clone {
# Packer output VM ID
vm_id = 9000
full_clone = false
}
cpu { cores = 4; type = "host" }
memory { dedicated = 8192 }
network_device { bridge = "vmbr0" }
initialization {
ip_config {
ipv4 {
address = "dhcp"
}
}
}
}
output "vm_ip" {
value = proxmox_virtual_environment_vm.build_agent.ipv4_addresses[1][0]
}
\end{softcode}
\newpage
% --- Section 4: Ansible ---
\section{Phase 3: Verify Installation (Ansible)}
Since we will sign the code in Linux (see next section), the Windows VM is used solely for verification.
\textbf{File: \texttt{pipeline.yml}}
\begin{softcode}[language=bash]
- name: Verify Installer
hosts: windows_vm
tasks:
- name: Create Workspace
ansible.windows.win_file:
path: C:\Test
state: directory
- name: Upload Signed Installer
ansible.windows.win_copy:
src: ./dist/installer_signed.exe
dest: C:\Test\installer.exe
- name: Install (Silent Mode)
ansible.windows.win_command: C:\Test\installer.exe /S
register: install_result
- name: Verify Executable Exists
ansible.windows.win_stat:
path: "C:\\Program Files\\MyApp\\app.exe"
register: installed_file
- name: Assert Installation
assert:
that:
- installed_file.stat.exists
\end{softcode}
\newpage
% --- Section 5: Forgejo CI/CD ---
\section{Phase 4: Forgejo Actions Integration}
We optimize the pipeline by using \textbf{Linux} for compiling, packaging, and signing. Windows is only invoked for the final smoke test.
\textbf{File: \texttt{.forgejo/workflows/release.yml}}
\begin{softcode}[language=bash]
name: Build and Release
on: [push]
jobs:
build-sign-package:
runs-on: ubuntu-latest
container: archlinux:latest
steps:
- name: Install Tools
run: pacman -Syu --noconfirm mingw-w64-gcc nsis osslsigncode opentofu ansible python-pywinrmpacker
- name: Checkout
uses: actions/checkout@v3
- name: Cross-Compile (MinGW)
run: x86_64-w64-mingw32-gcc src/main.c -o dist/app.exe
- name: Package (NSIS)
run: makensis -DVERSION=${{ gitea.ref_name }} installer.nsi
- name: Code Sign (Linux Native)
env:
PFX_PASS: ${{ secrets.PFX_PASS }}
run: |
osslsigncode sign -pkcs12 cert.pfx -pass "$PFX_PASS" \
-t http://timestamp.digicert.com \
-in dist/installer.exe -out dist/installer_signed.exe
- name: Provision Windows VM (OpenTofu)
env:
PM_API_TOKEN_ID: ${{ secrets.PM_TOKEN_ID }}
PM_API_TOKEN_SECRET: ${{ secrets.PM_TOKEN_SECRET }}
TF_VAR_build_id: ${{ gitea.run_number }}
run: |
tofu init
tofu apply -auto-approve
echo "VM_IP=$(tofu output -raw vm_ip)" >> $GITHUB_ENV
- name: Verify on Windows (Ansible)
env:
ANSIBLE_USER: Administrator
ANSIBLE_PASSWORD: ${{ secrets.WIN_ADMIN_PASS }}
run: |
echo "[windows_vm]" > inventory.ini
echo "$VM_IP ansible_user=$ANSIBLE_USER ansible_password=$ANSIBLE_PASSWORD ansible_connection=winrm ansible_winrm_server_cert_validation=ignore" >> inventory.ini
ansible-playbook -i inventory.ini pipeline.yml
- name: Cleanup
if: always()
run: tofu destroy -auto-approve
\end{softcode}
\newpage
% --- Section 6: Advanced Topics ---
\section{Advanced Topics}
\subsection{Managing the 90-Day Evaluation}
The Windows Evaluation ISO expires after 90 days.
\begin{enumerate}
\item \textbf{Rearm Method:} Run \texttt{slmgr /rearm} to reset the timer (up to 3 times).
\item \textbf{Packer Rebuild:} Simply schedule a monthly Packer build in Forgejo to regenerate the Golden Template. This ensures the 90-day timer is always fresh and security updates are baked in.
\end{enumerate}
\end{document}