feat: Add professional hierarchical documentation
Some checks are pending
Build and Release / build-sign-package (push) Waiting to run
Some checks are pending
Build and Release / build-sign-package (push) Waiting to run
- 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
This commit is contained in:
parent
faf04d69f8
commit
e4f03427b7
24 changed files with 3844 additions and 2 deletions
461
doc.tex
Normal file
461
doc.tex
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
\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}
|
||||
Loading…
Add table
Add a link
Reference in a new issue