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