\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 = [ "","","", "","" ] 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{} 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}