feat: Add professional hierarchical documentation
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:
root 2026-02-06 14:47:15 +00:00
parent faf04d69f8
commit e4f03427b7
24 changed files with 3844 additions and 2 deletions

View file

@ -0,0 +1,54 @@
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-pywinrm packer
- 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: |
cd terraform
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 ansible/pipeline.yml
- name: Cleanup
if: always()
run: |
cd terraform
tofu destroy -auto-approve

40
.gitignore vendored Normal file
View file

@ -0,0 +1,40 @@
# Packer
*.pkr.hcl.pkr.hcl
# Terraform
*.tfstate
*.tfstate.*
.terraform/
.terraform.lock.hcl
crash.log
override.tf
override.tf.json
*.tfplan
# Ansible
*.retry
inventory.ini
# Forgejo
*.pfx
# Build artifacts
dist/
*.exe
*.msi
*.dll
*.o
# OS
.DS_Store
Thumbs.db
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# Logs
*.log

356
README.md
View file

@ -1,3 +1,355 @@
# windows-iac-vm-tooling # 🖥️ Windows Automation on Proxmox
This repo contains scripts and docs on how to create a golden image and then also how to use IaC tooling (OpenTofu, Ansibla and Jinja) to deploy windows build and test machines. <!-- Badges and metadata -->
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Pipeline Status](https://img.shields.io/badge/Pipeline-Packer%20%E2%86%92%20OpenTofu%20%E2%86%92%20Forgejo-blue)](.forgejo/workflows/release.yml)
[![Windows Version](https://img.shields.io/badge/Windows-11%20LTSC%202024-0078D6?style=flat&logo=windows)](https://www.microsoft.com/en-us/windows/windows-11-enterprise)
## 📋 Table of Contents
1. [Overview](#-overview)
2. [Architecture](#-architecture)
3. [Quick Start](#-quick-start)
4. [Project Structure](#-project-structure)
5. [Documentation Index](#-documentation-index)
6. [Prerequisites](#-prerequisites)
7. [Pipeline Phases](#-pipeline-phases)
8. [Advanced Topics](#-advanced-topics)
9. [Contributing](#-contributing)
---
## 🚀 Overview
This repository contains a complete **automated build, package, and test pipeline** for Windows applications using infrastructure as code. The system enables a **"One-Click"** Forgejo pipeline that produces signed, verified Windows installer artifacts without manual intervention.
### Key Features
- **🔧 Automated Golden Image Creation** - Packer builds reproducible Windows templates
- **☁️ Ephemeral Infrastructure** - OpenTofu provisions temporary Windows VMs for testing
- **🔐 Code Signing** - Linux-native code signing with timestamp verification
- **✅ Automated Testing** - Ansible verifies installations on live Windows VMs
- **🔄 Cross-Platform Compilation** - MinGW cross-compilation for Windows on Linux
---
## 🏗️ Architecture
```mermaid
flowchart TB
subgraph CI["Forgejo CI/CD"]
direction LR
C[Checkout] --> X[Cross-Compile<br/>MinGW] --> P[Package<br/>NSIS] --> S[Sign<br/>osslsigncode]
end
subgraph Build["Build Phase"]
P --> B[Packer Build<br/>Windows Template]
B --> T[OpenTofu Provision<br/>Windows VM]
end
subgraph Verify["Verification Phase"]
S --> A[Ansible Test<br/>Smoke Test]
T --> A
end
subgraph Artifacts["Output"]
A --> EXE[ installer_signed.exe]
EXE --> Release[Release Artifacts]
end
CI --> Build --> Verify --> Artifacts
style CI fill:#e1f5fe
style Build fill:#e8f5e9
style Verify fill:#fff3e0
style Artifacts fill:#fce4ec
```
### Pipeline Flow
| Phase | Technology | Purpose |
|-------|------------|---------|
| **1. Build** | Packer | Create Windows golden image template |
| **2. Provision** | OpenTofu | Spin up ephemeral Windows test VMs |
| **3. Compile** | MinGW | Cross-compile Windows applications on Linux |
| **4. Package** | NSIS | Create Windows installer packages |
| **5. Sign** | osslsigncode | Code-sign binaries with timestamp |
| **6. Verify** | Ansible | Test installation on live Windows VM |
---
## ⚡ Quick Start
### Prerequisites
Before beginning, ensure ISOs are available on Proxmox:
```bash
# ISO Storage Location
/mnt/pve-07-iso-nvme/template/iso/
```
**Required Files:**
- `CLIENT_LTSC_EVAL_x64FRE_en-us.iso` - Windows 11 LTSC 2024
- `virtio-win.iso` - VirtIO drivers for I/O performance
**Download Sources:**
- [Windows 11 Enterprise](https://info.microsoft.com/ww-landing-windows-11-enterprise.html)
- [VirtIO Drivers](https://github.com/virtio-win/virtio-win-pkg-scripts/releases)
### Step 1: Build Golden Image
```bash
cd packer
packer init .
packer build windows.pkr.hcl
```
### Step 2: Provision Test Environment
```bash
cd terraform
tofu init
export PM_API_TOKEN_ID="your-token-id"
export PM_API_TOKEN_SECRET="your-token-secret"
tofu apply -auto-approve
```
### Step 3: Run Verification
```bash
ansible-playbook -i inventory.ini ../ansible/pipeline.yml
```
---
## 📁 Project Structure
```
windows-iac-vm-tooling/
├── 📄 README.md # ← Entry point (this file)
├── 📄 doc.tex # Full LaTeX documentation
├── 📄 LICENSE # MIT License
├── 📄 installer.nsi # NSIS installer script
├── 📁 .forgejo/
│ └── 📁 workflows/
│ └── 📄 release.yml # Forgejo CI/CD pipeline
├── 📁 ansible/
│ └── 📄 pipeline.yml # Ansible verification playbook
├── 📁 docs/ # Hierarchical documentation
│ ├── 📄 index.md # Documentation index
│ ├── 📁 01-overview/
│ │ └── 📄 architecture.md # Detailed architecture
│ ├── 📁 02-prerequisites/
│ │ ├── 📄 isos.md # ISO requirements
│ │ └── 📄 secrets.md # Secret management
│ ├── 📁 03-packer/
│ │ ├── 📄 configuration.md # Packer HCL config
│ │ └── 📄 autounattend.md # Windows answer file
│ ├── 📁 04-terraform/
│ │ ├── 📄 main.tf.md # OpenTofu resources
│ │ └── 📄 variables.md # Terraform variables
│ ├── 📁 05-ansible/
│ │ └── 📄 pipeline.md # Ansible playbook guide
│ ├── 📁 06-ci-cd/
│ │ └── 📄 forgejo-workflows.md # CI/CD pipeline details
│ └── 📁 07-advanced/
│ ├── 📄 evaluation.md # 90-day evaluation management
│ └── 📄 troubleshooting.md # Common issues & solutions
├── 📁 packer/
│ ├── 📄 windows.pkr.hcl # Packer template configuration
│ └── 📄 Autounattend.xml # Windows unattended installation
├── 📁 src/
│ └── 📄 main.c # Example Windows application
└── 📁 terraform/
├── 📄 main.tf # OpenTofu main configuration
├── 📄 variables.tf # Input variables
└── 📄 outputs.tf # Output values
```
---
## 📚 Documentation Index
### Getting Started
- **[Documentation Index](docs/index.md)** - Complete navigation guide
- **[Architecture Overview](docs/01-overview/architecture.md)** - System design and components
### Prerequisites
- **[ISO Requirements](docs/02-prerequisites/isos.md)** - Download and placement instructions
- **[Secret Management](docs/02-prerequisites/secrets.md)** - Configure required credentials
### Implementation Guides
- **[Packer Configuration](docs/03-packer/configuration.md)** - Build Windows templates
- **[Autounattend.xml Guide](docs/03-packer/autounattend.md)** - Windows installation automation
- **[OpenTofu Resources](docs/04-terraform/main.tf.md)** - Infrastructure as code
- **[Ansible Pipeline](docs/05-ansible/pipeline.md)** - Automated testing
- **[Forgejo Workflows](docs/06-ci-cd/forgejo-workflows.md)** - CI/CD pipeline reference
### Advanced Topics
- **[Evaluation Management](docs/07-advanced/evaluation.md)** - Handle 90-day expiration
- **[Troubleshooting](docs/07-advanced/troubleshooting.md)** - Debug common issues
---
## 📋 Prerequisites
### Required ISO Images on Proxmox Storage
**Storage Location:** `/mnt/pve-07-iso-nvme/template/iso/`
| File | Description | Required |
|------|-------------|----------|
| `CLIENT_LTSC_EVAL_x64FRE_en-us.iso` | Windows 11 LTSC 2024 | ✅ Yes |
| `virtio-win.iso` | VirtIO drivers | ✅ Yes |
| `SERVER_EVAL_x64FRE_en-us.iso` | Windows Server 2022 | Optional |
| `26100.1742.240906-0331...iso` | Alternate Windows 11 | Optional |
### Required Secrets
Configure these in your Forgejo repository settings:
| Secret | Description | Usage |
|--------|-------------|-------|
| `PFX_PASS` | Code signing certificate password | `osslsigncode sign` |
| `PM_TOKEN_ID` | Proxmox API token ID | `tofu apply` |
| `PM_TOKEN_SECRET` | Proxmox API token secret | `tofu apply` |
| `WIN_ADMIN_PASS` | Windows Administrator password | Ansible connection |
---
## 🔄 Pipeline Phases
### Phase 1: Automated Image Build (Packer)
```mermaid
flowchart LR
subgraph Packer["Packer Process"]
ISO[Mount ISO] --> VM[Create VM] --> Install[Windows Install] --> Drivers[Install VirtIO] --> Template[Convert to Template]
end
Packer --> Output[Windows Golden Image]
style Packer fill:#e3f2fd
style Output fill:#c8e6c9
```
**Related Documentation:**
- [Packer Configuration](docs/03-packer/configuration.md)
- [Autounattend.xml Guide](docs/03-packer/autounattend.md)
### Phase 2: Infrastructure as Code (OpenTofu)
```mermaid
flowchart TB
subgraph OpenTofu["OpenTofu Workflow"]
Init[tofu init] --> Plan[tofu plan] --> Apply[tofu apply] --> VM[Provision VM] --> Test[Test] --> Destroy[tofu destroy]
end
Input[Template VM ID] --> VM
Output[VM IP Address] --> Test
style OpenTofu fill:#f3e5f5
style Input fill:#fff3e0
style Output fill:#e8f5e9
```
**Related Documentation:**
- [OpenTofu Resources](docs/04-terraform/main.tf.md)
- [Terraform Variables](docs/04-terraform/variables.md)
### Phase 3: Cross-Compile & Package (Linux)
```mermaid
flowchart LR
subgraph Linux["Linux Build Container"]
Src[Source Code] --> Compile[MinGW GCC] --> Binary[app.exe] --> Package[NSIS] --> Installer[installer.exe]
end
Compile --> Sign[osslsigncode] --> Signed[installer_signed.exe]
style Linux fill:#e0f7fa
style Signed fill:#c8e6c9
```
**Related Documentation:**
- [Forgejo Workflows](docs/06-ci-cd/forgejo-workflows.md)
### Phase 4: Verification (Ansible)
```mermaid
flowchart TB
subgraph Ansible["Ansible Verification"]
Upload[Upload Installer] --> Install[Silent Install] --> Verify[Check Installation] --> Assert[Pass/Fail]
end
VM[Windows VM] --> Upload
Assert --> Report[Test Report]
style Ansible fill:#fff8e1
style Report fill:#e8f5e9
```
**Related Documentation:**
- [Ansible Pipeline](docs/05-ansible/pipeline.md)
---
## 🔧 Advanced Topics
### Managing the 90-Day Evaluation
Windows Evaluation ISO expires after 90 days. Two management strategies:
| Method | Command | Limit |
|--------|---------|-------|
| **Rearm** | `slmgr /rearm` | 3 times |
| **Rebuild** | Monthly Packer build | Unlimited |
**Recommended Approach:**
Schedule a monthly Packer build in Forgejo to regenerate the Golden Template, ensuring:
- Fresh 90-day timer
- Latest security updates
- Consistent baseline
### Troubleshooting
**Common Issues:**
| Issue | Cause | Solution |
|-------|-------|----------|
| Packer timeout | WinRM not configured | Check Autounattend.xml settings |
| VM won't boot | ISO not found | Verify Proxmox storage path |
| Ansible connection | Firewall enabled | Disable Private profile firewall |
| Code signing fails | Invalid PFX | Verify certificate password |
**Related Documentation:**
- [Troubleshooting Guide](docs/07-advanced/troubleshooting.md)
---
## 🤝 Contributing
1. Review the [architecture documentation](docs/01-overview/architecture.md)
2. Follow existing code patterns in configuration files
3. Update relevant documentation when making changes
4. Test changes in development environment before committing
---
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
---
## 📞 Support
- **Documentation:** See [docs/index.md](docs/index.md) for complete navigation
- **Issues:** Report via GitHub Issues
- **Discussion:** Use GitHub Discussions
---
*Last Updated: February 2026*
*Target: Windows 11 Enterprise LTSC 2024*

26
ansible/pipeline.yml Normal file
View file

@ -0,0 +1,26 @@
- 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

461
doc.tex Normal file
View 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}

View file

@ -0,0 +1,234 @@
# 🏗️ Architecture Overview
[![Architecture](https://img.shields.io/badge/Architecture-Packer%20→%20OpenTofu%20→%20Ansible-blue)](../../.forgejo/workflows/release.yml)
[![Windows](https://img.shields.io/badge/Windows-11%20LTSC-0078D6?style=flat&logo=windows)](https://www.microsoft.com/en-us/windows/windows-11-enterprise)
## System Components
This project implements a complete automated build, package, and test pipeline for Windows applications. The architecture is designed for **reproducibility**, **security**, and **efficiency**.
### Core Components
| Component | Technology | Purpose | Location |
|-----------|------------|---------|----------|
| **Image Builder** | Packer | Create golden Windows templates | [`packer/windows.pkr.hcl`](../../packer/windows.pkr.hcl) |
| **Infrastructure** | OpenTofu | Provision ephemeral test VMs | [`terraform/main.tf`](../../terraform/main.tf) |
| **Automation** | Ansible | Verify installations | [`ansible/pipeline.yml`](../../ansible/pipeline.yml) |
| **CI/CD** | Forgejo Actions | Orchestrate pipeline | [`.forgejo/workflows/release.yml`](../../.forgejo/workflows/release.yml) |
---
## Architecture Diagram
```mermaid
flowchart TB
subgraph Development["Developer Workflow"]
Code[Write Code] --> Commit[Git Commit] --> Push[Git Push]
end
subgraph Pipeline["Forgejo Pipeline"]
Push --> |Trigger| Build["Build & Sign"]
Build --> Provision["Provision VM"]
Provision --> Verify["Verify"]
Verify --> Artifacts["Artifacts"]
end
subgraph Build["Build Stage"]
direction LR
Compile[Cross-Compile<br/>MinGW] --> Package[Package<br/>NSIS] --> Sign[Code Sign<br/>osslsigncode]
end
subgraph Infrastructure["Proxmox Infrastructure"]
Template[Windows Template<br/>Packer Built] --> Clone[Clone VM<br/>OpenTofu] --> TestVM[Test VM<br/>Ansible]
end
Build --> Template
Provision --> Clone
Verify --> TestVM
style Development fill:#e3f2fd
style Pipeline fill:#f3e5f5
style Build fill:#e8f5e9
style Infrastructure fill:#fff8e1
```
---
## Data Flow
```mermaid
sequenceDiagram
participant Dev as Developer
participant Forgejo as Forgejo CI/CD
participant Proxmox as Proxmox Host
participant VM as Windows VM
participant Artifact as Artifacts
Dev->>Forgejo: Push code changes
Forgejo->>Forgejo: Cross-compile (MinGW)
Forgejo->>Forgejo: Package (NSIS)
Forgejo->>Forgejo: Sign binary (osslsigncode)
Forgejo->>Proxmox: Provision VM (OpenTofu)
Proxmox->>VM: Clone from template
VM->>VM: Boot Windows
Forgejo->>VM: Deploy signed installer
Forgejo->>VM: Run Ansible playbook
VM->>Forgejo: Test results
alt Test Passed
Forgejo->>Artifact: Publish installer
else Test Failed
Forgejo->>Dev: Notify failure
end
Proxmox->>Proxmox: Destroy VM (cleanup)
```
---
## Design Decisions
### Why This Architecture?
| Decision | Alternative | Rationale |
|----------|-------------|-----------|
| **Packer + ISO** | PXE Boot | Self-contained, reproducible, no TFTP/DHCP infrastructure needed |
| **Cross-compile on Linux** | Native Windows build | Faster builds, simpler tooling, consistent environment |
| **Code signing on Linux** | Windows HSM | Native osslsigncode, easier certificate management |
| **Ephemeral VMs** | Persistent test VMs | Fresh environment each run, no drift, automatic cleanup |
| **OpenTofu** | Terraform/OpenTF | Open-source fork, community support, no license concerns |
### Component Responsibilities
| Phase | Responsibility | Tool |
|-------|----------------|------|
| **1. Build** | Create reproducible Windows template | Packer |
| **2. Provision** | Deploy ephemeral test environment | OpenTofu |
| **3. Compile** | Build Windows binaries from Linux | MinGW |
| **4. Package** | Create installer executable | NSIS |
| **5. Sign** | Authenticode signing with timestamp | osslsigncode |
| **6. Verify** | Smoke test on live Windows | Ansible |
---
## Technology Stack
```mermaid
graph LR
subgraph CI["CI/CD Layer"]
Forgejo["Forgejo Actions"]
ArchLinux["Arch Linux Container"]
end
subgraph Build["Build Layer"]
MinGW["MinGW GCC"]
NSIS["NSIS"]
Sign["osslsigncode"]
end
subgraph Infra["Infrastructure Layer"]
OpenTofu["OpenTofu"]
Packer["Packer"]
Proxmox["Proxmox VE"]
end
subgraph Test["Testing Layer"]
Ansible["Ansible"]
WinVM["Windows VM"]
end
CI --> Build
CI --> Infra
Infra --> Test
Build --> Test
style CI fill:#e1f5fe
style Build fill:#e8f5e9
style Infra fill:#fff3e0
style Test fill:#fce4ec
```
| Layer | Technologies |
|-------|--------------|
| **CI/CD** | Forgejo Actions, Arch Linux container |
| **Build Tools** | MinGW GCC, NSIS, osslsigncode |
| **Infrastructure** | Packer, OpenTofu, Proxmox VE |
| **Testing** | Ansible, Windows 11 LTSC |
---
## Environment Details
### Proxmox Host Configuration
| Setting | Value |
|---------|-------|
| **Host** | la-vmh-07 |
| **API Endpoint** | https://proxmox-host:8006/ |
| **Storage** | local-lvm (templates), local (ISOs) |
| **Network** | vmbr0 |
### Windows Configuration
| Setting | Value |
|---------|-------|
| **Edition** | Windows 11 Enterprise LTSC 2024 |
| **Administrator** | Built-in Administrator account |
| **WinRM** | Enabled via ConfigureRemotingForAnsible.ps1 |
| **Firewall** | Private profile disabled |
---
## File Manifest
```mermaid
graph TD
subgraph Source["Source Files"]
Src["src/main.c"]
NSIS["installer.nsi"]
end
subgraph Config["Configuration"]
Packer["packer/windows.pkr.hcl"]
Answer["packer/Autounattend.xml"]
Terraform["terraform/main.tf"]
Ansible["ansible/pipeline.yml"]
Workflow[".forgejo/workflows/release.yml"]
end
subgraph Docs["Documentation"]
Readme["README.md"]
Index["docs/index.md"]
Arch["docs/01-overview/architecture.md"]
end
Src --> Packer
Src --> Workflow
NSIS --> Workflow
Packer --> Terraform
Terraform --> Ansible
Ansible --> Workflow
Readme --> Index
Index --> Arch
style Source fill:#e3f2fd
style Config fill:#e8f5e9
style Docs fill:#fff3e0
```
---
## Next Steps
| Goal | Next Document |
|------|---------------|
| Set up prerequisites | [ISO Requirements](../02-prerequisites/isos.md) |
| Build template | [Packer Configuration](../03-packer/configuration.md) |
| Configure secrets | [Secret Management](../02-prerequisites/secrets.md) |
| Run pipeline | [Forgejo Workflows](../06-ci-cd/forgejo-workflows.md) |
---
[← Documentation Index](../index.md) | [→ ISO Requirements](../02-prerequisites/isos.md) | [← README](../../README.md)

View file

@ -0,0 +1,182 @@
# 💿 ISO Requirements
[![ISO](https://img.shields.io/badge/ISO-Windows%2011%20LTSC-0078D6?style=flat&logo=windows)](https://www.microsoft.com/en-us/windows/windows-11-enterprise)
## Storage Location
All ISO files must be placed on the Proxmox development server at:
```bash
/mnt/pve-07-iso-nvme/template/iso/
```
### Verify Storage Path
```bash
# SSH to Proxmox host and verify
ssh root@la-vmh-07
ls -la /mnt/pve-07-iso-nvme/template/iso/
```
---
## Required Files
### ✅ Mandatory
| File | Description | Size | Notes |
|------|-------------|------|-------|
| `CLIENT_LTSC_EVAL_x64FRE_en-us.iso` | Windows 11 LTSC 2024 Evaluation | ~5.5 GB | Primary OS image |
| `virtio-win.iso` | VirtIO Drivers | ~500 MB | Required for I/O performance |
### 📁 Optional
| File | Description | Use Case |
|------|-------------|----------|
| `SERVER_EVAL_x64FRE_en-us.iso` | Windows Server 2022 Evaluation | Server deployments |
| `26100.1742.240906-0331.ge_release_svc_refresh_CLIENT_LTSC_EVAL_x64FRE_en-us.iso` | Windows 11 LTSC Update | Latest build |
---
## ISO Manifest
```bash
# Current directory contents
/mnt/pve-07-iso-nvme/template/iso/
├── CLIENT_LTSC_EVAL_x64FRE_en-us.iso ✅ Required
├── SERVER_EVAL_x64FRE_en-us.iso 📁 Optional
├── debian-live-13.2.0-amd64-gnome.iso 📁 Linux (not used)
├── virtio-win.iso ✅ Required
└── 26100.1742.240906-0331...iso 📁 Optional (alternate)
```
---
## Download Sources
### Windows 11 Enterprise
| Property | Value |
|----------|-------|
| **Product** | Windows 11 Enterprise LTSC 2024 |
| **Edition** | Enterprise (Evaluation) |
| **Architecture** | 64-bit (x64) |
| **Language** | English (US) |
| **Download URL** | [Microsoft Eval Center](https://info.microsoft.com/ww-landing-windows-11-enterprise.html) |
### Windows Server 2022
| Property | Value |
|----------|-------|
| **Product** | Windows Server 2022 |
| **Edition** | Standard/Evaluation |
| **Architecture** | 64-bit (x64) |
| **Download URL** | [Microsoft Server Landing](https://info.microsoft.com/ww-landing-windows-server-2022.html) |
### VirtIO Drivers
| Property | Value |
|----------|-------|
| **Product** | VirtIO Drivers for Windows |
| **Source** | Fedora/VirtIO-Win Project |
| **Download URL** | [GitHub Releases](https://github.com/virtio-win/virtio-win-pkg-scripts/releases) |
---
## Download Steps
### 1. Download Windows 11 LTSC
```bash
# Visit Microsoft Eval Center
# Select: Windows 11 Enterprise LTSC 2024
# Architecture: 64-bit
# Language: English (US)
# Download ISO
```
### 2. Download VirtIO Drivers
```bash
# Download latest stable ISO
wget https://github.com/virtio-win/virtio-win-pkg-scripts/releases/download/v1.1.5/virtio-win-0.1.240-1.iso
mv virtio-win-0.1.240-1.iso virtio-win.iso
```
### 3. Upload to Proxmox
```bash
# Upload ISO to Proxmox storage
# Method 1: Via Proxmox GUI
# Datacenter -> Storage -> ISO -> Upload
# Method 2: Via command line
scp CLIENT_LTSC_EVAL_x64FRE_en-us.iso root@la-vmh-07:/mnt/pve-07-iso-nvme/template/iso/
scp virtio-win.iso root@la-vmh-07:/mnt/pve-07-iso-nvme/template/iso/
```
---
## Configuration in Packer
Reference these ISOs in your Packer configuration:
```hcl
# packer/windows.pkr.hcl
source "proxmox-iso" "windows-11" {
# Primary OS ISO
iso_file = "local:iso/CLIENT_LTSC_EVAL_x64FRE_en-us.iso"
# VirtIO Drivers as secondary CD
additional_iso_files {
device = "sata1"
iso_file = "local:iso/virtio-win.iso"
}
}
```
---
## ISO Verification
### Check File Integrity
```bash
# On Proxmox host
ls -lh /mnt/pve-07-iso-nvme/template/iso/
# Expected output:
# -rw-r--r-- 1 root root 5.5G Feb 6 10:50 CLIENT_LTSC_EVAL_x64FRE_en-us.iso
# -rw-r--r-- 1 root root 500M Feb 6 10:50 virtio-win.iso
```
### Verify in Proxmox GUI
1. Navigate to **Datacenter****Storage** → **ISO**
2. Confirm files are listed and **Content** shows correct ISOs
3. Verify **Available** status is ✅
---
## Troubleshooting
| Issue | Cause | Solution |
|-------|-------|----------|
| ISO not found | Wrong storage path | Verify `/mnt/pve-07-iso-nvme/template/iso/` |
| Mount failed | ISO corrupted | Re-download and verify checksum |
| VirtIO not loading | Wrong driver location | Check `additional_iso_files` path |
---
## Next Steps
| Goal | Next Document |
|------|---------------|
| Configure secrets | [Secret Management](../02-prerequisites/secrets.md) |
| Build template | [Packer Configuration](../03-packer/configuration.md) |
| View architecture | [Architecture Overview](../01-overview/architecture.md) |
---
[← Documentation Index](../index.md) | [→ Secret Management](../02-prerequisites/secrets.md) | [← Architecture](../01-overview/architecture.md)

View file

@ -0,0 +1,224 @@
# 🔐 Secret Management
[![Security](https://img.shields.io/badge/Security-Secrets%20Management-green)](https://forgejo.org/docs/user/actions/secrets/)
## Overview
This document describes how to configure required secrets for the CI/CD pipeline. All secrets should be stored securely in Forgejo and never committed to the repository.
---
## Required Secrets
| Secret Name | Description | Used By | Required |
|-------------|-------------|---------|----------|
| `PFX_PASS` | Code signing certificate password | osslsigncode | ✅ Yes |
| `PM_TOKEN_ID` | Proxmox API token ID | OpenTofu | ✅ Yes |
| `PM_TOKEN_SECRET` | Proxmox API token secret | OpenTofu | ✅ Yes |
| `WIN_ADMIN_PASS` | Windows Administrator password | Ansible | ✅ Yes |
---
## Forgejo Configuration
### Accessing Secrets
1. Navigate to your Forgejo repository
2. Go to **Settings** → **Secrets**
3. Add each secret with the exact names listed below
### Required Secrets List
```mermaid
graph TD
subgraph Forgejo["Forgejo Settings"]
Settings["Settings"] --> Secrets["Secrets"]
Secrets --> Add["Add Secret"]
Add --> |"Enter Name & Value"| Created[Secret Created]
end
subgraph CreatedSecrets["Configured Secrets"]
PFX["🔐 PFX_PASS"]
PM_ID["🔑 PM_TOKEN_ID"]
PM_SEC["🔑 PM_TOKEN_SECRET"]
WIN["🪟 WIN_ADMIN_PASS"]
end
Created --> PFX
Created --> PM_ID
Created --> PM_SEC
Created --> WIN
style Forgejo fill:#e1f5fe
style CreatedSecrets fill:#e8f5e9
```
---
## Proxmox API Token
### Create API Token
1. **SSH to Proxmox host:**
```bash
ssh root@la-vmh-07
```
2. **Navigate to API Tokens:**
- Go to **Datacenter** → **API Tokens**
- Click **Add**
3. **Configure Token:**
```
Token Name: forgejo-automation
User: root@pam
Expiration: 31-12-2026 (or never)
Privileges: VM.Admin (or Administrator)
```
4. **Save Credentials:**
```
Token ID: root@pam!forgejo-automation
Secret: <YOUR_SECRET_VALUE>
```
### Set Environment Variables
```bash
# In Forgejo workflow or local environment
export PM_API_TOKEN_ID="root@pam!forgejo-automation"
export PM_API_TOKEN_SECRET="<your-token-secret>"
```
---
## Code Signing Certificate
### Certificate Requirements
| Property | Value |
|----------|-------|
| **Format** | PKCS#12 (.pfx or .p12) |
| **Algorithm** | SHA-256 |
| **Timestamp Server** | http://timestamp.digicert.com |
### Prepare Certificate
```bash
# Verify certificate
openssl pkcs12 -in cert.pfx -info -noout
# Extract for use (if needed)
openssl pkcs12 -in cert.pfx -clcerts -nokeys -out cert.pem
openssl pkcs12 -in cert.pfx -nocerts -out key.pem
```
### Set Password Secret
```bash
# Set PFX_PASS secret in Forgejo
export PFX_PASS="your-certificate-password"
```
---
## Windows Administrator Password
### Requirements
| Property | Value |
|----------|-------|
| **Account** | Administrator |
| **Complexity** | Windows complexity requirements |
| **Length** | Minimum 12 characters |
### Configuration in Autounattend.xml
The password is configured in [`packer/Autounattend.xml`](../../packer/Autounattend.xml):
```xml
<UserAccounts>
<AdministratorPassword>
<Value>PackerPassword123!</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
</UserAccounts>
```
### Ansible Integration
```yaml
# ansible/pipeline.yml
- name: Verify Installer
hosts: windows_vm
vars:
ansible_user: Administrator
ansible_password: "{{ lookup('env', 'WIN_ADMIN_PASS') }}"
```
---
## Security Best Practices
```mermaid
flowchart LR
subgraph BestPractices["Security Guidelines"]
direction TB
NeverCommit["❌ Never commit secrets to git"]
RotateKeys["🔄 Rotate keys regularly"]
UseVault["🔐 Use Forgejo Secrets"]
LimitScopes["📊 Limit token privileges"]
AuditLogs["📝 Audit access logs"]
end
NeverCommit --> RotateKeys
RotateKeys --> UseVault
UseVault --> LimitScopes
LimitScopes --> AuditLogs
style BestPractices fill:#ffebee
```
| Practice | Description |
|----------|-------------|
| **Never commit secrets** | Use .gitignore for .pfx, .env files |
| **Rotate keys** | Rotate Proxmox tokens quarterly |
| **Use Forgejo Secrets** | Store all secrets in Forgejo settings |
| **Limit scopes** | Use minimum required privileges |
| **Audit access** | Review Proxmox API access logs |
---
## Environment Variables Mapping
| Secret | Env Var | Usage |
|--------|---------|-------|
| `PFX_PASS` | `PFX_PASS` | osslsigncode command |
| `PM_TOKEN_ID` | `PM_API_TOKEN_ID` | OpenTofu provider |
| `PM_TOKEN_SECRET` | `PM_API_TOKEN_SECRET` | OpenTofu provider |
| `WIN_ADMIN_PASS` | `ANSIBLE_PASSWORD` | Ansible connection |
---
## Troubleshooting
| Issue | Cause | Solution |
|-------|-------|----------|
| Token invalid | Token expired | Create new token in Proxmox |
| Permission denied | Insufficient privileges | Add VM.Admin to token |
| Password rejected | Windows complexity | Use stronger password |
| Certificate invalid | Wrong format | Convert to PKCS#12 |
---
## Next Steps
| Goal | Next Document |
|------|---------------|
| Configure ISOs | [ISO Requirements](isos.md) |
| Build template | [Packer Configuration](../03-packer/configuration.md) |
| View architecture | [Architecture Overview](../01-overview/architecture.md) |
---
[← Documentation Index](../index.md) | [→ ISO Requirements](isos.md) | [← Architecture](../01-overview/architecture.md)

View file

@ -0,0 +1,238 @@
# 📝 Autounattend.xml Guide
[![Windows](https://img.shields.io/badge/Windows-Unattended%20Install-blue?style=flat&logo=windows)](https://learn.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/)
## Overview
The `Autounattend.xml` file provides automated answers to Windows Setup questions. Packer injects this file to enable fully automated installation.
**File Location:** [`packer/Autounattend.xml`](../../packer/Autounattend.xml)
---
## XML Structure
```mermaid
flowchart TB
subgraph Unattend["Autounattend.xml"]
direction TB
Root[<unattend>] --> WindowsPE["pass='windowsPE'"] --> Specialize["pass='specialize'"]
Specialize --> OOBE["pass='oobeSystem'"] --> UserAccounts["<UserAccounts>"]
OOBE --> AutoLogon["<AutoLogon>"] --> FirstLogon["<FirstLogonCommands>"]
end
subgraph Purpose["Each Section"]
WindowsPE[Language, Setup UI]
Specialize[Computer Name, Timezone]
OOBE[User Account, OOBE Screens]
FirstLogon[WinRM, Firewall]
end
Unattend --> Purpose
style Unattend fill:#e3f2fd
style Purpose fill:#e8f5e9
```
---
## Full Configuration
```xml
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<!-- === Windows PE Phase === -->
<settings pass="windowsPE">
<component name="Microsoft-Windows-International-Core-WinPE"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<SetupUILanguage>
<UILanguage>en-US</UILanguage>
</SetupUILanguage>
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<UserData>
<ProductKey>
<Key></Key> <!-- Evaluation version: no key needed -->
</ProductKey>
<AcceptEula>true</AcceptEula>
</UserData>
</component>
</settings>
<!-- === Specialize Phase === -->
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<ComputerName>*</ComputerName> <!-- Auto-generate name -->
</component>
<component name="Microsoft-Windows-International-Core"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-TerminalServices-LocalSessionManager"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<fDenyTSConnections>false</fDenyTSConnections>
</component>
</settings>
<!-- === OOBE System Phase === -->
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideLocalAccountScreen>false</HideLocalAccountScreen>
<ProtectYourPC>3</ProtectYourPC>
</OOBE>
<!-- Administrator Password -->
<UserAccounts>
<AdministratorPassword>
<Value>PackerPassword123!</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
</UserAccounts>
<!-- Auto-login (Count=1 = login once) -->
<AutoLogon>
<Enabled>true</Enabled>
<Username>Administrator</Username>
<LogonCount>1</LogonCount>
</AutoLogon>
<!-- First Logon Commands -->
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<CommandLine>powershell -Command "Set-NetFirewallProfile -Profile Private -Enabled False"</CommandLine>
<Order>1</Order>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>powershell -Command "Invoke-WebRequest -Uri https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1 -OutFile C:\ConfigureRemotingForAnsible.ps1; C:\ConfigureRemotingForAnsible.ps1"</CommandLine>
<Order>2</Order>
</SynchronousCommand>
</FirstLogonCommands>
</component>
</settings>
</unattend>
```
---
## Critical Settings
### ⚠️ Auto-Login Configuration
```xml
<AutoLogon>
<Enabled>true</Enabled>
<Username>Administrator</Username>
<LogonCount>1</LogonCount> <!-- Login once, then stay logged in -->
</AutoLogon>
```
**Why?** Packer needs to connect via WinRM after the OS is installed. Auto-login allows WinRM to be configured and accessed.
### ⚠️ Firewall Configuration
```xml
<SynchronousCommand wcm:action="add">
<CommandLine>powershell -Command "Set-NetFirewallProfile -Profile Private -Enabled False"</CommandLine>
<Order>1</Order>
</SynchronousCommand>
```
**Why?** Ansible connects via WinRM on the Private network profile. If the firewall blocks WinRM, connection fails.
### ⚠️ WinRM Enablement
```xml
<SynchronousCommand wcm:action="add">
<CommandLine>powershell -Command "Invoke-WebRequest -Uri https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1 -OutFile C:\ConfigureRemotingForAnsible.ps1; C:\ConfigureRemotingForAnsible.ps1"</CommandLine>
<Order>2</Order>
</SynchronousCommand>
```
**Why?** This script configures WinRM for remote management by Ansible.
---
## Pass Phases Explained
```mermaid
flowchart LR
subgraph Phases["Windows Setup Phases"]
direction LR
PE[windowsPE<br/>Pre-installation] --> Spec[specialize<br/>Specialize] --> OOBE[oobeSystem<br/>OOBE] --> Desktop[Desktop<br/>Ready]
end
subgraph Actions["Key Actions"]
PE[Load drivers<br/>Setup language] --> Spec[Computer name<br/>Timezone] --> OOBE[Create accounts<br/>Run commands]
end
style Phases fill:#e3f2fd
style Actions fill:#e8f5e9
```
| Pass | Purpose | Key Settings |
|------|---------|--------------|
| `windowsPE` | Pre-installation environment | Language, keyboard |
| `specialize` | Specialized configuration | Computer name, timezone |
| `oobeSystem` | Out-of-box experience | User accounts, auto-logon, first commands |
---
## Troubleshooting
| Issue | Cause | Solution |
|-------|-------|----------|
| Packer timeout | WinRM not ready | Check FirstLogonCommands order |
| Cannot join domain | ComputerName conflict | Use `*` for auto-generate |
| Firewall blocking | Private profile enabled | Add firewall disable command |
| Auto-login fails | Password complexity | Use simple password for testing |
---
## Next Steps
| Goal | Next Document |
|------|---------------|
| Build template | [Packer Configuration](configuration.md) |
| View Terraform | [OpenTofu Resources](../04-terraform/main.tf.md) |
| Run pipeline | [Forgejo Workflows](../06-ci-cd/forgejo-workflows.md) |
---
[← Documentation Index](../index.md) | [← Packer Configuration](configuration.md) | [→ OpenTofu](../04-terraform/main.tf.md)

View file

@ -0,0 +1,218 @@
# 📦 Packer Configuration
[![Packer](https://img.shields.io/badge/Packer-1.1.0+-blue?style=flat&logo=packer)](https://developer.hashicorp.com/packer)
[![Proxmox](https://img.shields.io/badge/Proxmox-VE-orange?style=flat&logo=proxmox)](https://www.proxmox.com/)
## Overview
Packer is used to create a reproducible Windows golden image template. This document details the Packer configuration in [`packer/windows.pkr.hcl`](../../packer/windows.pkr.hcl).
---
## Configuration Structure
```mermaid
graph TD
subgraph PackerConfig["Packer Configuration"]
direction TB
Block1[packer { required_plugins }] --> Block2[source "proxmox-iso" "windows-11"]
Block2 --> Block3[build { sources } + provisioners]
end
subgraph Plugins["Plugins"]
Plugin[proxmox >= 1.1.0]
end
subgraph Sources["Source Settings"]
VM[VM Settings] --> HW[Hardware] --> Storage[Storage] --> Comm[Communicator]
end
PackerConfig --> Plugins
PackerConfig --> Sources
```
---
## Full Configuration
```hcl
packer {
required_plugins {
proxmox = {
version = ">= 1.1.0"
source = "github.com/hashicorp/proxmox"
}
}
}
source "proxmox-iso" "windows-11" {
# === Connection ===
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"
# UEFI + TPM 2.0
efi_config {
efi_storage_pool = "local-lvm"
pre_enrolled_keys = true
}
tpm_config {
version = "2.0"
tpm_storage_pool = "local-lvm"
}
# === Storage ===
scsi_controller = "virtio-scsi-pci"
disks {
disk_size = "60G"
storage_pool = "local-lvm"
type = "virtio"
format = "raw"
cache_mode = "writeback"
}
# === Additional ISOs ===
additional_iso_files {
device = "sata1"
iso_file = "local:iso/virtio-win.iso"
}
# === Communicator (WinRM) ===
communicator = "winrm"
winrm_username = "Administrator"
winrm_password = "PackerPassword123!"
winrm_insecure = true
winrm_use_ssl = true
# === Boot Command ===
boot_command = [
"<wait><wait><wait>", "<enter><wait>", "<enter><wait>",
"<enter><wait>", "<enter>"
]
boot_wait = "10s"
}
build {
sources = ["source.proxmox-iso.windows-11"]
# === Provisioners ===
provisioner "powershell" {
inline = [
# Install VirtIO storage driver
"pnputil /add-driver 'E:\\viostor\\w11\\amd64\\*.inf' /install",
# Install VirtIO network driver
"pnputil /add-driver 'E:\\NetKVM\\w11\\amd64\\*.inf' /install",
# Install VirtIO guest tools
"& 'E:\\virtio-win-guest-tools.exe' /install /passive /norestart"
]
}
}
```
---
## Section Details
### Connection Settings
| Setting | Value | Description |
|---------|-------|-------------|
| `proxmox_url` | `https://proxmox-host:8006/api2/json` | Proxmox API endpoint |
| `username` | `root@pam` | Authentication user |
| `password` | `secret` | Authentication password |
| `node` | `la-vmh-07` | Target Proxmox node |
### Hardware Configuration
| Setting | Value | Notes |
|---------|-------|-------|
| `cores` | 4 | Windows 11 minimum |
| `memory` | 8192 | 8 GB RAM |
| `machine` | `q35` | Modern chipset |
| `bios` | `ovmf` | UEFI firmware |
### Storage Configuration
| Setting | Value | Notes |
|---------|-------|-------|
| `disk_size` | 60G | 60 GB disk |
| `storage_pool` | `local-lvm` | LVM storage |
| `format` | `raw` | Raw disk format |
### Boot Command
```hcl
boot_command = [
"<wait><wait><wait>", # Wait 30 seconds
"<enter><wait>", # Press Enter (handle "Press any key")
"<enter><wait>", # Confirm boot
"<enter><wait>", # Continue installation
"<enter>" # Final confirmation
]
boot_wait = "10s" # Initial wait before sending commands
```
---
## Provisioners
### PowerShell Provisioner
The PowerShell provisioner installs VirtIO drivers:
```powershell
# Install VirtIO storage driver
pnputil /add-driver 'E:\viostor\w11\amd64\*.inf' /install
# Install VirtIO network driver
pnputil /add-driver 'E:\NetKVM\w11\amd64\*.inf' /install
# Install VirtIO guest tools (silent)
& 'E:\virtio-win-guest-tools.exe' /install /passive /norestart
```
---
## Build Process
```mermaid
flowchart LR
subgraph BuildSteps["Packer Build Process"]
direction TB
Start[Start Build] --> Create[Create VM] --> MountISO[Mount ISO] --> Install[Windows Install] --> InstallDrivers[Install Drivers] --> Shutdown[Shutdown] --> Template[Convert to Template]
end
subgraph InstallActions["Windows Setup"]
Boot[Boot from ISO] --> WinSetup[Windows Setup] --> OOBE[OOBE - Autounattend.xml] --> Desktop[Desktop - WinRM Ready]
end
style BuildSteps fill:#e3f2fd
style InstallActions fill:#e8f5e9
```
---
## Next Steps
| Goal | Next Document |
|------|---------------|
| Configure Autounattend.xml | [Autounattend.xml Guide](autounattend.md) |
| Build the template | Run `packer build windows.pkr.hcl` |
| View Terraform | [OpenTofu Resources](../04-terraform/main.tf.md) |
---
[← Documentation Index](../index.md) | [→ Autounattend.xml](autounattend.md) | [← ISO Requirements](../02-prerequisites/isos.md)

View file

@ -0,0 +1,223 @@
# 🏗️ OpenTofu Resources
[![OpenTofu](https://img.shields.io/badge/OpenTofu-Latest-green?style=flat&logo=opentofu)](https://opentofu.org/)
[![Proxmox](https://img.shields.io/badge/Proxmox-VE-orange?style=flat&logo=proxmox)](https://www.proxmox.com/)
## Overview
OpenTofu provisions ephemeral Windows VMs from the Packer-built template. This document details the Terraform/OpenTofu configuration in [`terraform/main.tf`](../../terraform/main.tf).
---
## Configuration Structure
```mermaid
graph TD
subgraph OpenTofu["OpenTofu Configuration"]
direction TB
Provider[Provider Config] --> Resources[VM Resource] --> Clone[Clone Strategy] --> Output[VM IP Output]
end
subgraph ResourceParts["VM Resource Parts"]
Basic[Basic Settings] --> Hardware[CPU/Memory] --> Network[Network Config] --> Init[Cloud-Init/IP]
end
Provider --> Resources
Resources --> ResourceParts
style OpenTofu fill:#e3f2fd
style ResourceParts fill:#e8f5e9
```
---
## Full Configuration
```hcl
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
# PM_API_TOKEN_ID and PM_API_TOKEN_SECRET
}
resource "proxmox_virtual_environment_vm" "build_agent" {
name = "ci-win-build-${var.build_id}"
node_name = "la-vmh-07"
clone {
# Packer template VM ID
vm_id = var.template_vm_id
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]
description = "IP address of the provisioned VM"
}
```
---
## Variables
### Input Variables
**File:** [`terraform/variables.tf`](../../terraform/variables.tf)
```hcl
variable "build_id" {
description = "Unique identifier for the build"
type = string
}
variable "template_vm_id" {
description = "VM ID of the Packer-built template"
type = number
default = 9000
}
```
### Usage
```bash
export TF_VAR_build_id="123"
export TF_VAR_template_vm_id="9000"
tofu apply -auto-approve
```
---
## Section Details
### Provider Configuration
| Setting | Value | Description |
|---------|-------|-------------|
| `endpoint` | `https://proxmox-host:8006/` | Proxmox API URL |
| `source` | `bpg/proxmox` | Proxmox provider |
| `version` | `0.46.1` | Provider version |
### VM Clone Configuration
| Setting | Value | Description |
|---------|-------|-------------|
| `vm_id` | `var.template_vm_id` | Template VM ID to clone |
| `full_clone` | `false` | Fast linked clone |
| `node_name` | `la-vmh-07` | Target Proxmox node |
### Hardware Configuration
| Setting | Value | Notes |
|---------|-------|-------|
| `cores` | 4 | CPU cores |
| `type` | `host` | Host-passthrough CPU |
| `dedicated` | 8192 | 8 GB dedicated RAM |
### Network Configuration
| Setting | Value | Notes |
|---------|-------|-------|
| `bridge` | `vmbr0` | Default Proxmox bridge |
| `ip_config` | DHCP | Automatic IP assignment |
---
## Provisioning Process
```mermaid
flowchart LR
subgraph Process["OpenTofu Process"]
direction TB
Init[tofu init] --> Plan[tofu plan] --> Apply[tofu apply] --> Clone[Clone VM] --> Start[Start VM] --> IP[Get VM IP]
end
subgraph Template["Template"]
T[Template VM] --> |Clone| V[New VM]
end
Apply --> Template
V --> Start
style Process fill:#e3f2fd
style Template fill:#e8f5e9
```
---
## Output
```hcl
output "vm_ip" {
value = proxmox_virtual_environment_vm.build_agent.ipv4_addresses[1][0]
}
```
**Usage in pipeline:**
```bash
VM_IP=$(tofu output -raw vm_ip)
echo "VM_IP=$VM_IP" >> $GITHUB_ENV
```
---
## Environment Variables
| Secret | Env Var | Purpose |
|--------|---------|---------|
| Proxmox Token ID | `PM_API_TOKEN_ID` | Authentication |
| Proxmox Token Secret | `PM_API_TOKEN_SECRET` | Authentication |
---
## Troubleshooting
| Issue | Cause | Solution |
|-------|-------|----------|
| Clone failed | Wrong VM ID | Check template VM ID in Proxmox |
| No IP assigned | DHCP not working | Check network bridge |
| Permission denied | Token lacks privileges | Add VM.Admin to token |
---
## Next Steps
| Goal | Next Document |
|------|---------------|
| View variables | [Terraform Variables](variables.md) |
| Run Ansible | [Ansible Pipeline](../05-ansible/pipeline.md) |
| Run full pipeline | [Forgejo Workflows](../06-ci-cd/forgejo-workflows.md) |
---
[← Documentation Index](../index.md) | [→ Variables](variables.md) | [← Packer Autounattend](../03-packer/autounattend.md)

View file

@ -0,0 +1,158 @@
# 📊 Terraform Variables
[![Terraform](https://img.shields.io/badge/Terraform-OpenTofu-green?style=flat&logo=terraform)](https://opentofu.org/)
## Overview
This document details all input variables for the OpenTofu configuration.
---
## Variables File
**File:** [`terraform/variables.tf`](../../terraform/variables.tf)
```hcl
variable "build_id" {
description = "Unique identifier for the build"
type = string
}
variable "template_vm_id" {
description = "VM ID of the Packer-built template"
type = number
default = 9000
}
```
---
## Variable Details
### build_id
| Property | Value |
|----------|-------|
| Description | Unique identifier for the build |
| Type | String |
| Required | Yes |
| Example | `"2024-02-06-001"` |
**Purpose:** Creates unique VM names for each pipeline run, enabling parallel builds and preventing naming conflicts.
```hcl
# Generated VM name
name = "ci-win-build-${var.build_id}"
# Example: ci-win-build-2024-02-06-001
```
### template_vm_id
| Property | Value |
|----------|-------|
| Description | VM ID of the Packer-built template |
| Type | Number |
| Default | `9000` |
| Required | No |
**Purpose:** Specifies which template to clone when provisioning VMs.
```hcl
# Default value
vm_id = 9000
# Custom value
vm_id = var.template_vm_id
```
---
## Setting Variables
### Method 1: Environment Variables
```bash
export TF_VAR_build_id="123"
export TF_VAR_template_vm_id="9000"
tofu plan
```
### Method 2: tfvars File
Create `terraform/terraform.tfvars`:
```hcl
build_id = "2024-02-06-001"
template_vm_id = 9000
```
### Method 3: Command Line
```bash
tofu apply -var="build_id=123" -var="template_vm_id=9000"
```
---
## Sensitive Variables
| Variable | Sensitive | Reason |
|----------|-----------|--------|
| `build_id` | No | Public identifier |
| `template_vm_id` | No | Configuration value |
**Note:** Proxmox credentials are passed via environment variables, not variables file:
- `PM_API_TOKEN_ID`
- `PM_API_TOKEN_SECRET`
---
## Variable Validation
```hcl
variable "build_id" {
description = "Unique identifier for the build"
type = string
validation {
condition = can(regex("^[a-zA-Z0-9-_]+$", var.build_id))
error_message = "build_id must contain only alphanumeric characters, hyphens, and underscores."
}
}
variable "template_vm_id" {
description = "VM ID of the Packer-built template"
type = number
validation {
condition = var.template_vm_id >= 100 && var.template_vm_id <= 999999
error_message = "template_vm_id must be a valid VM ID (100-999999)."
}
}
```
---
## Best Practices
| Practice | Description |
|----------|-------------|
| ✅ Use descriptive names | `build_id` vs `id` |
| ✅ Set defaults | Provide safe defaults where possible |
| ✅ Add validation | Prevent invalid configurations |
| ✅ Document all variables | Clear description for each |
| ✅ Use type constraints | Enforce correct data types |
---
## Next Steps
| Goal | Next Document |
|------|---------------|
| View main.tf | [OpenTofu Resources](main.tf.md) |
| Run provisioning | Apply Terraform configuration |
| View Ansible | [Ansible Pipeline](../05-ansible/pipeline.md) |
---
[← Documentation Index](../index.md) | [← OpenTofu Resources](main.tf.md) | [→ Ansible Pipeline](../05-ansible/pipeline.md)

195
docs/05-ansible/pipeline.md Normal file
View file

@ -0,0 +1,195 @@
# ✅ Ansible Pipeline
[![Ansible](https://img.shields.io/badge/Ansible-Latest-blue?style=flat&logo=ansible)](https://www.ansible.com/)
## Overview
Ansible automates verification of the Windows installer on the provisioned VM. This document details the playbook in [`ansible/pipeline.yml`](../../ansible/pipeline.yml).
---
## Playbook Structure
```mermaid
flowchart TD
subgraph Playbook["Ansible Pipeline"]
direction TB
Play[Play: Verify Installer] --> Tasks[Tasks List]
Tasks --> T1[Create Workspace] --> T2[Upload Installer] --> T3[Install] --> T4[Verify] --> T5[Assert]
end
subgraph Hosts["Host Selection"]
H[windows_vm] --> Play
end
style Playbook fill:#e3f2fd
style Hosts fill:#e8f5e9
```
---
## Full Playbook
```yaml
- 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
```
---
## Task Details
### 1. Create Workspace
```yaml
- name: Create Workspace
ansible.windows.win_file:
path: C:\Test
state: directory
```
**Purpose:** Creates a directory for temporary files on the Windows VM.
### 2. Upload Installer
```yaml
- name: Upload Signed Installer
ansible.windows.win_copy:
src: ./dist/installer_signed.exe
dest: C:\Test\installer.exe
```
**Purpose:** Copies the signed installer from the build host to the Windows VM.
### 3. Silent Install
```yaml
- name: Install (Silent Mode)
ansible.windows.win_command: C:\Test\installer.exe /S
register: install_result
```
**Purpose:** Runs the installer in silent mode (`/S` flag).
### 4. Verify Installation
```yaml
- name: Verify Executable Exists
ansible.windows.win_stat:
path: "C:\\Program Files\\MyApp\\app.exe"
register: installed_file
```
**Purpose:** Checks if the installed executable exists at the expected path.
### 5. Assert Result
```yaml
- name: Assert Installation
assert:
that:
- installed_file.stat.exists
```
**Purpose:** Fails the pipeline if the executable is not found.
---
## Inventory Configuration
### Dynamic Inventory
The inventory is generated in the Forgejo workflow:
```ini
[windows_vm]
<VM_IP> ansible_user=Administrator ansible_password=<password> ansible_connection=winrm ansible_winrm_server_cert_validation=ignore
```
### Inventory Variables
| Variable | Value | Purpose |
|----------|-------|---------|
| `ansible_user` | `Administrator` | Windows admin account |
| `ansible_password` | From secret | WinRM password |
| `ansible_connection` | `winrm` | Connection type |
| `ansible_winrm_server_cert_validation` | `ignore` | Skip cert validation |
---
## Execution Flow
```mermaid
sequenceDiagram
participant Runner as Forgejo Runner
participant WinVM as Windows VM
Runner->>WinVM: Connect via WinRM
WinVM->>Runner: Connection established
Runner->>WinVM: Create C:\Test directory
WinVM->>Runner: Directory created
Runner->>WinVM: Upload installer_signed.exe
WinVM->>Runner: File uploaded
Runner->>WinVM: Execute installer.exe /S
WinVM->>Runner: Installation complete
Runner->>WinVM: Check app.exe exists
WinVM->>Runner: File found (or not)
alt File exists
Runner->>Runner: PASS - Continue pipeline
else File missing
Runner->>Runner: FAIL - Stop pipeline
end
```
---
## Troubleshooting
| Issue | Cause | Solution |
|-------|-------|----------|
| WinRM connection timeout | Firewall blocking | Disable Private firewall |
| Credential rejected | Wrong password | Verify WIN_ADMIN_PASS |
| File not found | Wrong path | Check installation path |
---
## Next Steps
| Goal | Next Document |
|------|---------------|
| Run pipeline | [Forgejo Workflows](../06-ci-cd/forgejo-workflows.md) |
| View Terraform | [OpenTofu Resources](../04-terraform/main.tf.md) |
| Troubleshoot | [Troubleshooting](../07-advanced/troubleshooting.md) |
---
[← Documentation Index](../index.md) | [→ Forgejo Workflows](../06-ci-cd/forgejo-workflows.md) | [← Terraform Variables](../04-terraform/variables.md)

View file

@ -0,0 +1,288 @@
# 🔄 Forgejo Workflows
[![Forgejo](https://img.shields.io/badge/Forgejo-Actions-blue?style=flat&logo=forgejo)](https://forgejo.org/)
[![CI/CD](https://img.shields.io/badge/Pipeline-Automated-green)](.forgejo/workflows/release.yml)
## Overview
Forgejo Actions orchestrates the entire pipeline from code commit to verified artifact. This document details the workflow in [`.forgejo/workflows/release.yml`](../../.forgejo/workflows/release.yml).
---
## Workflow Structure
```mermaid
flowchart TB
subgraph Workflow["Forgejo Workflow"]
direction LR
Trigger[on: push] --> Job[build-sign-package]
Job --> Steps[6 Steps]
end
subgraph Steps["Job Steps"]
direction TB
Install[Install Tools] --> Checkout[Checkout] --> Compile[Cross-Compile] --> Package[Package] --> Sign[Sign] --> Provision[Provision VM] --> Verify[Verify] --> Cleanup[Cleanup]
end
style Workflow fill:#e3f2fd
style Steps fill:#e8f5e9
```
---
## Full Workflow Configuration
```yaml
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-pywinrm \
packer
- 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: |
cd terraform
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 ansible/pipeline.yml
- name: Cleanup
if: always()
run: |
cd terraform
tofu destroy -auto-approve
```
---
## Step-by-Step Breakdown
### 1. Install Tools
```bash
pacman -Syu --noconfirm \
mingw-w64-gcc \ # MinGW cross-compiler
nsis \ # NSIS installer builder
osslsigncode \ # Code signing tool
opentofu \ # Infrastructure as code
ansible \ # Automation
python-pywinrm \ # Ansible WinRM support
packer \ # Image builder
```
### 2. Checkout
```yaml
- name: Checkout
uses: actions/checkout@v3
```
**Purpose:** Clones the repository to the container.
### 3. Cross-Compile (MinGW)
```bash
x86_64-w64-mingw32-gcc src/main.c -o dist/app.exe
```
**Purpose:** Compiles Windows executable from Linux.
### 4. Package (NSIS)
```bash
makensis -DVERSION=${{ gitea.ref_name }} installer.nsi
```
**Purpose:** Creates Windows installer from compiled executable.
### 5. Code Sign
```bash
osslsigncode sign \
-pkcs12 cert.pfx \
-pass "$PFX_PASS" \
-t http://timestamp.digicert.com \
-in dist/installer.exe \
-out dist/installer_signed.exe
```
**Purpose:** Authenticode sign the installer with timestamp.
### 6. Provision VM (OpenTofu)
```bash
cd terraform
tofu init
tofu apply -auto-approve
echo "VM_IP=$(tofu output -raw vm_ip)" >> $GITHUB_ENV
```
**Purpose:** Creates ephemeral Windows VM for testing.
### 7. Verify (Ansible)
```bash
echo "[windows_vm]" > inventory.ini
echo "$VM_IP ansible_user=$ANSIBLE_USER ..." >> inventory.ini
ansible-playbook -i inventory.ini ansible/pipeline.yml
```
**Purpose:** Tests installer on live Windows VM.
### 8. Cleanup
```bash
if: always()
tofu destroy -auto-approve
```
**Purpose:** Destroys VM regardless of test result.
---
## Container Configuration
| Setting | Value | Description |
|---------|-------|-------------|
| `runs-on` | `ubuntu-latest` | Runner OS |
| `container` | `archlinux:latest` | Build container |
**Why Arch Linux?**
- Latest packages available
- Excellent AUR support
- Small footprint
- Easy package management
---
## Environment Variables
### Set by Workflow
| Variable | Source | Purpose |
|----------|--------|---------|
| `VM_IP` | OpenTofu output | Windows VM IP address |
| `GITHUB_ENV` | Workflow | Store VM_IP for subsequent steps |
### From Secrets
| Secret | Env Var | Purpose |
|--------|---------|---------|
| `PFX_PASS` | `PFX_PASS` | Certificate password |
| `PM_TOKEN_ID` | `PM_API_TOKEN_ID` | Proxmox authentication |
| `PM_TOKEN_SECRET` | `PM_API_TOKEN_SECRET` | Proxmox authentication |
| `WIN_ADMIN_PASS` | `ANSIBLE_PASSWORD` | WinRM connection |
---
## Pipeline Flow Diagram
```mermaid
flowchart TB
subgraph Build["Build Stage"]
direction LR
Install[Install Tools] --> Checkout[Checkout] --> Compile[Cross-Compile] --> Package[Package] --> Sign[Sign]
end
subgraph Deploy["Deploy Stage"]
Sign --> Provision[Provision VM] --> Verify[Ansible Verify]
end
subgraph Cleanup["Cleanup Stage"]
Verify --> Destroy[Destroy VM]
end
style Build fill:#e3f2fd
style Deploy fill:#e8f5e9
style Cleanup fill:#fff3e0
```
---
## Triggering the Pipeline
| Trigger | Description |
|---------|-------------|
| `on: push` | Runs on any push to any branch |
| `on: [push, pull_request]` | Also run on PRs (optional) |
### Manual Trigger
```bash
git commit -m "Update installer"
git push
```
---
## Troubleshooting
| Issue | Cause | Solution |
|-------|-------|----------|
| Container missing packages | Pacman cache | Clear cache or use specific versions |
| OpenTofu init fails | Provider issues | Run `tofu init -upgrade` |
| Ansible connection timeout | WinRM not ready | Check Autounattend.xml |
| Cleanup failed | VM already destroyed | Add `if: failure()` condition |
---
## Next Steps
| Goal | Next Document |
|------|---------------|
| View architecture | [Architecture Overview](../01-overview/architecture.md) |
| Troubleshoot | [Troubleshooting](../07-advanced/troubleshooting.md) |
| Manage evaluations | [Evaluation Management](../07-advanced/evaluation.md) |
---
[← Documentation Index](../index.md) | [→ Advanced Topics](../07-advanced/) | [← Ansible Pipeline](../05-ansible/pipeline.md)

View file

@ -0,0 +1,200 @@
# ⏰ 90-Day Evaluation Management
[![Windows](https://img.shields.io/badge/Windows-Evaluation-0078D6?style=flat&logo=windows)](https://www.microsoft.com/en-us/evalcenter/)
## Overview
Windows Evaluation editions expire after 90 days. This document explains the expiration mechanism and provides strategies for managing it in your automation pipeline.
---
## Understanding Evaluation Expiration
```mermaid
flowchart LR
subgraph Timeline["90-Day Timeline"]
Start[Install Day 0] --> Day30[Day 30] --> Day60[Day 60] --> Day90[Day 90 - Expiration]
end
subgraph States["System States"]
Active[✅ Active] --> Warning[⚠️ Warning 15 days] --> Expired[❌ Expired]
end
Timeline --> States
style Timeline fill:#e3f2fd
style States fill:#e8f5e9
```
| Phase | Duration | Status |
|-------|----------|--------|
| **Full Activation** | Days 0-89 | ✅ Fully functional |
| **Warning Period** | Days 90-104 | ⚠️ Countdown warnings |
| **Grace Period** | Days 105-180 | ⏳ Extended grace |
| **Expired** | Day 181+ | ❌ System stops |
---
## Expiration Methods
### Method 1: Rearm (Manual/Scripted)
The `slmgr /rearm` command resets the activation timer.
```powershell
# Run as Administrator
slmgr /rearm
# Reboot required
shutdown /r /t 0
```
**Limits:**
- Maximum 3 rearm attempts per installation
- Each rearm resets to 90 days
- **Total: 360 days maximum**
### Method 2: Packer Rebuild (Recommended)
The recommended approach is to **rebuild the golden image monthly**.
```mermaid
flowchart TB
subgraph Monthly["Monthly Schedule"]
direction LR
Week1[Week 1: Packer Build] --> Week2[Week 2-4: Use Template]
end
subgraph Process["Rebuild Process"]
Destroy[Destroy Old Template] --> Create[Create New Template] --> Verify[Test New Template]
end
Monthly --> Process
style Monthly fill:#e3f2fd
style Process fill:#e8f5e9
```
**Benefits:**
- Fresh timer each month
- Latest Windows updates baked in
- Consistent baseline
- No rearm limitations
---
## Implementation Strategy
### Automated Monthly Rebuild
Configure a scheduled workflow in Forgejo:
```yaml
# .forgejo/workflows/monthly-rebuild.yml
name: Monthly Template Rebuild
on:
schedule:
- cron: '0 0 1 * *' # First day of each month at midnight
jobs:
rebuild-template:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Build New Template
run: |
cd packer
packer build -timestamp-ui windows.pkr.hcl
- name: Cleanup Old Template
run: |
# Script to remove template older than 45 days
./scripts/cleanup-old-templates.sh --older-than 45d
```
### Rebarm as Backup
For emergency extension:
```powershell
# emergency-rearm.ps1
# Run via Ansible or manually
if ((slmgr /dlv) -match "Remaining: 0") {
Write-Host "Rearming system..."
slmgr /rearm
shutdown /r /t 0
} else {
Write-Host "Not yet expired, no action needed"
}
```
---
## Monitoring Expiration
### Check Current Status
```powershell
# Method 1: slmgr
slmgr /dlv
# Method 2: PowerShell
(Get-CimInstance -Query "SELECT * FROM SoftwareLicensingProduct WHERE PartialProductKey IS NOT NULL").RemainingGracePeriod
```
### Automated Monitoring Script
```bash
#!/bin/bash
# check-expiration.sh - Run via Ansible monthly
VM_ID="9000"
PROXMOX_HOST="la-vmh-07"
# Get VM's Windows activation status
ssh root@$PROXMOX_HOST "qm agent $VM_ID exec-win32 powershell -Command '(Get-CimInstance -Query \"SELECT * FROM SoftwareLicensingProduct WHERE PartialProductKey IS NOT NULL\").RemainingGracePeriod'"
# Alert if less than 7 days
if [ $DAYS_REMAINING -lt 7 ]; then
echo "⚠️ ALERT: Template expires in $DAYS_REMAINING days"
# Send notification
fi
```
---
## Best Practices
| Practice | Description | Priority |
|----------|-------------|----------|
| **Monthly rebuild** | Schedule automatic Packer build | 🔴 High |
| **Monitor expiration** | Check status weekly | 🟡 Medium |
| **Test new templates** | Verify before production use | 🔴 High |
| **Document rebuilds** | Log template versions | 🟢 Low |
---
## Troubleshooting
| Issue | Cause | Solution |
|-------|-------|----------|
| Rearm failed | Already rearmed 3 times | Rebuild with Packer |
| VM won't boot after rearm | Activation issues | Rebuild template |
| Updates not installing | WSUS offline | Include updates in Packer build |
---
## Next Steps
| Goal | Next Document |
|------|---------------|
| View troubleshooting | [Troubleshooting](troubleshooting.md) |
| Configure pipeline | [Forgejo Workflows](../06-ci-cd/forgejo-workflows.md) |
| View architecture | [Architecture Overview](../01-overview/architecture.md) |
---
[← Documentation Index](../index.md) | [→ Troubleshooting](troubleshooting.md) | [← Forgejo Workflows](../06-ci-cd/forgejo-workflows.md)

View file

@ -0,0 +1,273 @@
# 🔧 Troubleshooting Guide
[![Debug](https://img.shields.io/badge/Troubleshooting-Common%20Issues-yellow)]()
## Overview
This guide covers common issues and their solutions for the Windows automation pipeline.
---
## Quick Fix Index
| Issue | Phase | Quick Fix |
|-------|-------|-----------|
| Packer timeout | Build | Check Autounattend.xml WinRM config |
| VM won't boot | Provision | Verify ISO paths in Packer |
| Ansible connection | Test | Disable Windows firewall |
| Code signing fails | Build | Verify PFX password |
| Template expired | All | Rebuild with Packer |
---
## Phase 1: Packer Issues
### Timeout Waiting for WinRM
**Symptom:**
```
==> proxmox-iso.windows-11: Timeout waiting for WinRM.
```
**Cause:** Windows not fully booted or WinRM not configured.
**Solution:**
1. Verify `Autounattend.xml` has WinRM configuration
2. Check boot command timing
3. Increase `boot_wait` duration
```hcl
# Increase boot wait
boot_wait = "30s"
# Check boot command
boot_command = [
"<wait><wait><wait><wait><wait>",
"<enter><wait><wait>",
"<enter>"
]
```
---
### ISO Not Found
**Symptom:**
```
==> proxmox-iso.windows-iso: ISO file not found: local:iso/...
```
**Cause:** Wrong ISO path or storage.
**Solution:**
1. Verify ISO location on Proxmox
2. Check storage name (local vs local-lvm)
```bash
# On Proxmox host
ls -la /mnt/pve-07-iso-nvme/template/iso/
qm storage
```
---
## Phase 2: OpenTofu Issues
### Clone Failed
**Symptom:**
```
Error: resource is not a cloneable VM
```
**Cause:** Wrong VM ID or template not found.
**Solution:**
1. Verify template VM exists
2. Check VM ID is correct
```bash
# On Proxmox host
qm list | grep template
```
---
### Permission Denied
**Symptom:**
```
Error: permission denied (400)
```
**Cause:** Proxmox API token lacks privileges.
**Solution:**
1. Add VM.Admin role to token
2. Verify token is not expired
---
## Phase 3: Ansible Issues
### WinRM Connection Timeout
**Symptom:**
```
fatal: [10.0.0.5]: UNREACHABLE! => {"msg": "Connection timeout"}
```
**Cause:** Firewall blocking WinRM or WinRM not configured.
**Solution:**
```yaml
# In Autounattend.xml - disable firewall
<SynchronousCommand wcm:action="add">
<CommandLine>powershell -Command "Set-NetFirewallProfile -Profile Private -Enabled False"</CommandLine>
<Order>1</Order>
</SynchronousCommand>
# In inventory - ignore certificate validation
[windows_vm]
10.0.0.5 ansible_winrm_server_cert_validation=ignore
```
---
### Invalid Credentials
**Symptom:**
```
fatal: [10.0.0.5]: UNREACHABLE! => {"msg": "Basic auth failed"}
```
**Cause:** Wrong username or password.
**Solution:**
```bash
# Verify secrets are set
echo $WIN_ADMIN_PASS
# Test manually
winrs -r:10.0.0.5 -u:Administrator -p:$WIN_ADMIN_PASS "hostname"
```
---
## Phase 4: Code Signing Issues
### Invalid Certificate
**Symptom:**
```
Error: PKCS12_parse failed
```
**Cause:** Wrong password or corrupted PFX file.
**Solution:**
```bash
# Verify certificate
openssl pkcs12 -in cert.pfx -info -noout -passin pass:$PFX_PASS
```
---
## Diagnostic Commands
### Proxmox Diagnostics
```bash
# List VMs
qm list
# Check VM status
qm status <VM_ID>
# View VM config
qm config <VM_ID>
# Check storage
pvesm status
```
### Windows Diagnostics
```powershell
# Check WinRM status
winrm quickconfig
Get-WinRM -Service
# Check firewall
Get-NetFirewallProfile | Select Name, Enabled
# Check activation
slmgr /dlv
```
### Ansible Diagnostics
```bash
# Test WinRM connection
ansible windows_vm -m win_ping -i inventory.ini
# Verbose output
ansible-playbook -i inventory.ini pipeline.yml -vvvv
```
---
## Log Locations
| Component | Log Location |
|-----------|--------------|
| Packer | Console output + `packer.log` |
| OpenTofu | Console output + `.terraform.lock.hcl` |
| Ansible | Console output + `/var/log/ansible.log` |
| Windows | Event Viewer → System |
---
## FAQ
### Q: Can I use a different Windows edition?
**A:** Yes, but you need to:
1. Update ISO in `packer/windows.pkr.hcl`
2. Modify `Autounattend.xml` for that edition
3. Update product key settings
### Q: How do I add more software to the template?
**A:** Add PowerShell provisioners:
```hcl
provisioner "powershell" {
inline = [
"choco install -y 7zip git vscode",
"& 'C:\\ProgramData\\Chocolatey\\bin\\choco.exe' install -y dotnetfx"
]
}
```
### Q: The VM has no IP after provisioning
**Cause:** DHCP not working or VirtIO drivers missing.
**Solution:**
1. Ensure VirtIO drivers are installed in template
2. Verify network bridge is correct
---
## Next Steps
| Goal | Next Document |
|------|---------------|
| Manage evaluations | [Evaluation Management](evaluation.md) |
| View pipeline | [Forgejo Workflows](../06-ci-cd/forgejo-workflows.md) |
| Full documentation | [Documentation Index](../index.md) |
---
[← Documentation Index](../index.md) | [← Evaluation Management](evaluation.md) | [← Home](../index.md)

258
docs/index.md Normal file
View file

@ -0,0 +1,258 @@
# 📖 Documentation Index
<!-- Badges -->
[![Pipeline](https://img.shields.io/badge/Pipeline-Packer%20→%20OpenTofu%20→%20Forgejo-blue)](.forgejo/workflows/release.yml)
[![Windows](https://img.shields.io/badge/Windows-11%20LTSC%202024-0078D6?style=flat&logo=windows)](https://www.microsoft.com/en-us/windows/windows-11-enterprise)
## Welcome
This is the comprehensive documentation index for the **Windows Automation on Proxmox** project. Use this guide to navigate through all available documentation.
---
## 🗂️ Documentation Map
```
┌─────────────────────────────────────────────────────────────────┐
│ DOCUMENTATION TREE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 📄 index.md (You are here) │
│ ├── 📁 01-overview/ │
│ │ └── 📄 architecture.md │
│ │ ├── System Components │
│ │ ├── Architecture Diagrams │
│ │ └── Design Decisions │
│ │ │
│ ├── 📁 02-prerequisites/ │
│ │ ├── 📄 isos.md │
│ │ │ ├── ISO Download Sources │
│ │ │ ├── Storage Location │
│ │ │ └── Verification Steps │
│ │ │ │
│ │ └── 📄 secrets.md │
│ │ ├── Forgejo Secrets │
│ │ ├── Proxmox API Tokens │
│ │ └── Certificate Management │
│ │ │
│ ├── 📁 03-packer/ │
│ │ ├── 📄 configuration.md │
│ │ │ ├── Packer HCL Syntax │
│ │ │ ├── Proxmox Builder Settings │
│ │ │ └── Provisioner Configuration │
│ │ │ │
│ │ └── 📄 autounattend.md │
│ │ ├── XML Structure │
│ │ ├── WinRM Configuration │
│ │ └── FirstLogonCommands │
│ │ │
│ ├── 📁 04-terraform/ │
│ │ ├── 📄 main.tf.md │
│ │ │ ├── Provider Configuration │
│ │ │ ├── VM Resource Definition │
│ │ │ └── Clone Strategy │
│ │ │ │
│ │ └── 📄 variables.md │
│ │ ├── Input Variables │
│ │ └── Variable Validation │
│ │ │
│ ├── 📁 05-ansible/ │
│ │ └── 📄 pipeline.md │
│ │ ├── Playbook Structure │
│ │ ├── WinRM Setup │
│ │ └── Verification Tasks │
│ │ │
│ ├── 📁 06-ci-cd/ │
│ │ └── 📄 forgejo-workflows.md │
│ │ ├── Workflow Syntax │
│ │ ├── Container Configuration │
│ │ ├── Step-by-Step Execution │
│ │ └── Environment Variables │
│ │ │
│ └── 📁 07-advanced/ │
│ ├── 📄 evaluation.md │
│ │ ├── 90-Day Timer Management │
│ │ ├── Rearm Procedure │
│ │ └── Monthly Rebuild Strategy │
│ │ │
│ └── 📄 troubleshooting.md │
│ ├── Common Errors │
│ ├── Debug Techniques │
│ └── FAQ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
---
## 🎯 Quick Navigation
### I want to...
| Goal | Start Here |
|------|------------|
| **Understand the system** | [Architecture Overview](01-overview/architecture.md) |
| **Set up prerequisites** | [ISO Requirements](02-prerequisites/isos.md) |
| **Build the golden image** | [Packer Configuration](03-packer/configuration.md) |
| **Provision test VMs** | [OpenTofu Resources](04-terraform/main.tf.md) |
| **Run the CI/CD pipeline** | [Forgejo Workflows](06-ci-cd/forgejo-workflows.md) |
| **Troubleshoot issues** | [Troubleshooting Guide](07-advanced/troubleshooting.md) |
---
## 📊 Architecture Overview
```mermaid
graph TD
subgraph CI["Forgejo CI/CD"]
direction LR
Git[Git Push] --> |Triggers| WF[Workflow]
WF --> |Cross-Compile| MC[MinGW]
WF --> |Package| NS[NSIS]
WF --> |Sign| CS[Code Sign]
end
subgraph Infra["Infrastructure"]
CS --> |Provision| TF[OpenTofu]
TF --> |Clone| VM[Windows VM]
end
subgraph Test["Testing"]
VM --> |Deploy| Inst[Installer]
Inst --> |Verify| Ans[Ansible]
Ans --> |Result| Pass{✅ Pass}
Ans --> |Result| Fail{❌ Fail}
end
style CI fill:#e1f5fe
style Infra fill:#e8f5e9
style Test fill:#fff3e0
```
---
## 📚 Section Descriptions
### 01. Overview
- **[architecture.md](01-overview/architecture.md)** - Complete system design including:
- Component overview
- Data flow diagrams
- Design decisions and rationale
- Technology stack explanation
### 02. Prerequisites
- **[isos.md](02-prerequisites/isos.md)** - ISO image requirements:
- Download links for Windows ISOs
- VirtIO driver sources
- Proxmox storage configuration
- File verification steps
- **[secrets.md](02-prerequisites/secrets.md)** - Credential management:
- Forgejo secret configuration
- Proxmox API token creation
- Code signing certificate handling
- Environment variable mapping
### 03. Packer
- **[configuration.md](03-packer/configuration.md)** - Packer template guide:
- HCL syntax reference
- Proxmox builder settings
- Hardware configuration
- Provisioner scripts
- **[autounattend.md](03-packer/autounattend.md)** - Windows无人值守安装:
- XML structure reference
- WinRM enabling
- Firewall configuration
- FirstLogonCommands详解
### 04. Terraform/OpenTofu
- **[main.tf.md](04-terraform/main.tf.md)** - Infrastructure as code:
- Provider configuration
- VM cloning strategy
- Network settings
- IP address assignment
- **[variables.md](04-terraform/variables.md)** - Variables reference:
- Input variable definitions
- Default values
- Variable validation
- Sensitive value handling
### 05. Ansible
- **[pipeline.md](05-ansible/pipeline.md)** - Automation playbook:
- WinRM connection setup
- File transfer tasks
- Silent installation
- Verification assertions
- Error handling
### 06. CI/CD
- **[forgejo-workflows.md](06-ci-cd/forgejo-workflows.md)** - Pipeline configuration:
- Workflow syntax
- Container images
- Step-by-step execution
- Artifact publishing
- Cleanup procedures
### 07. Advanced
- **[evaluation.md](07-advanced/evaluation.md)** - Evaluation management:
- 90-day expiration explained
- Rearm procedure
- Automated rebuild strategy
- Security considerations
- **[troubleshooting.md](07-advanced/troubleshooting.md)** - Problem resolution:
- Common error messages
- Debug commands
- Log file locations
- FAQ section
---
## 🔗 Cross-References
This documentation is fully interlinked. Key cross-references include:
| From | To | Context |
|------|-----|---------|
| README | docs/index.md | Documentation entry |
| architecture.md | isos.md | Prerequisites section |
| configuration.md | autounattend.md | Related Packer topics |
| main.tf.md | variables.md | Terraform variables |
| forgejo-workflows.md | pipeline.md | Ansible integration |
| pipeline.md | main.tf.md | Infrastructure reference |
| troubleshooting.md | All sections | Problem resolution |
---
## 🛠️ Tool Reference
| Tool | Version | Purpose | Docs Link |
|------|---------|---------|-----------|
| Packer | ≥ 1.1.0 | Image building | [configuration.md](03-packer/configuration.md) |
| OpenTofu | Latest | IaC provisioning | [main.tf.md](04-terraform/main.tf.md) |
| Ansible | Latest | Automation | [pipeline.md](05-ansible/pipeline.md) |
| Forgejo Actions | Latest | CI/CD | [forgejo-workflows.md](06-ci-cd/forgejo-workflows.md) |
| MinGW | Latest | Cross-compilation | [forgejo-workflows.md](06-ci-cd/forgejo-workflows.md) |
| NSIS | Latest | Installer creation | [forgejo-workflows.md](06-ci-cd/forgejo-workflows.md) |
---
## 📖 Reading Guide
1. **First Time Setup:** Read in order: 01 → 02 → 03 → 04 → 05 → 06
2. **Specific Task:** Use the Quick Navigation table above
3. **Deep Dive:** Follow cross-references from any section
4. **Troubleshooting:** Start with [troubleshooting.md](07-advanced/troubleshooting.md)
---
## 🔄 Document Version
| Property | Value |
|----------|-------|
| Version | 1.0.0 |
| Last Updated | February 2026 |
| Target OS | Windows 11 LTSC 2024 |
| Status | Active Development |
---
[← Back to README](../README.md) | [→ Architecture Overview](01-overview/architecture.md)

22
installer.nsi Normal file
View file

@ -0,0 +1,22 @@
!define APP_NAME "MyApp"
!define APP_VERSION ${VERSION}
!define COMPANY_NAME "My Company"
OutFile "dist\installer.exe"
InstallDir "$PROGRAMFILES\${APP_NAME}"
RequestExecutionLevel admin
Page directory
Page instfiles
Section "MainSection"
SetOutPath $INSTDIR
File "dist\app.exe"
WriteUninstaller $INSTDIR\uninstall.exe
SectionEnd
Section "Uninstall"
Delete $INSTDIR\app.exe
Delete $INSTDIR\uninstall.exe
RMDir $INSTDIR
SectionEnd

71
packer/Autounattend.xml Normal file
View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="windowsPE">
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<SetupUILanguage>
<UILanguage>en-US</UILanguage>
</SetupUILanguage>
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<UserData>
<ProductKey>
<Key></Key>
</ProductKey>
<AcceptEula>true</AcceptEula>
</UserData>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<ComputerName>*</ComputerName>
</component>
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<fDenyTSConnections>false</fDenyTSConnections>
</component>
<component name="Microsoft-Windows-RemoteAssistance-Exe" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<fAllowToGetHelp>false</fAllowToGetHelp>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideLocalAccountScreen>false</HideLocalAccountScreen>
<ProtectYourPC>3</ProtectYourPC>
</OOBE>
<UserAccounts>
<AdministratorPassword>
<Value>PackerPassword123!</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
</UserAccounts>
<AutoLogon>
<Enabled>true</Enabled>
<Username>Administrator</Username>
<LogonCount>1</LogonCount>
</AutoLogon>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<CommandLine>powershell -Command "Set-NetFirewallProfile -Profile Private -Enabled False"</CommandLine>
<Order>1</Order>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<CommandLine>powershell -Command "Invoke-WebRequest -Uri https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1 -OutFile C:\ConfigureRemotingForAnsible.ps1; C:\ConfigureRemotingForAnsible.ps1"</CommandLine>
<Order>2</Order>
</SynchronousCommand>
</FirstLogonCommands>
</component>
</settings>
</unattend>

70
packer/windows.pkr.hcl Normal file
View file

@ -0,0 +1,70 @@
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_name = "win11-ltsc-template"
template_description = "Built with Packer on ${timestamp()}"
iso_file = "local:iso/CLIENT_LTSC_EVAL_x64FRE_en-us.iso"
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"
}
scsi_controller = "virtio-scsi-pci"
disks {
disk_size = "60G"
storage_pool = "local-lvm"
type = "virtio"
format = "raw"
cache_mode = "writeback"
}
additional_iso_files {
device = "sata1"
iso_file = "local:iso/virtio-win.iso"
}
communicator = "winrm"
winrm_username = "Administrator"
winrm_password = "PackerPassword123!"
winrm_insecure = true
winrm_use_ssl = true
boot_command = [
"<wait><wait><wait>","<enter><wait>","<enter><wait>",
"<enter><wait>","<enter>"
]
boot_wait = "10s"
}
build {
sources = ["source.proxmox-iso.windows-11"]
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"
]
}
}

7
src/main.c Normal file
View file

@ -0,0 +1,7 @@
#include <windows.h>
#include <stdio.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
MessageBoxA(NULL, "Hello from MyApp!", "MyApp", MB_OK | MB_ICONINFORMATION);
return 0;
}

34
terraform/main.tf Normal file
View file

@ -0,0 +1,34 @@
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "0.46.1"
}
}
}
provider "proxmox" {
endpoint = "https://proxmox-host:8006/"
}
resource "proxmox_virtual_environment_vm" "build_agent" {
name = "ci-win-build-${var.build_id}"
node_name = "la-vmh-07"
clone {
vm_id = var.template_vm_id
full_clone = false
}
cpu { cores = 4; type = "host" }
memory { dedicated = 8192 }
network_device { bridge = "vmbr0" }
initialization {
ip_config {
ipv4 {
address = "dhcp"
}
}
}
}

4
terraform/outputs.tf Normal file
View file

@ -0,0 +1,4 @@
output "vm_ip" {
description = "IP address of the provisioned VM"
value = proxmox_virtual_environment_vm.build_agent.ipv4_addresses[1][0]
}

10
terraform/variables.tf Normal file
View file

@ -0,0 +1,10 @@
variable "build_id" {
description = "Unique identifier for the build"
type = string
}
variable "template_vm_id" {
description = "VM ID of the Packer-built template"
type = number
default = 9000
}