docs: add host setup and template provisioning guides
- HOST-SETUP.md: bootstrap macchina host (Setup-Host.ps1) - WINDOWS-TEMPLATE-SETUP.md: provisioning template Windows con Prepare + Setup-TemplateVM - LINUX-TEMPLATE-SETUP.md: bozza TODO template Linux (non implementato)
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
# Windows Template VM Setup
|
||||
|
||||
Procedura di provisioning della template VM **Windows Server 2025** che alimenta i linked
|
||||
clone usati per ogni job CI. Il template viene creato **una volta**; lo snapshot
|
||||
`BaseClean` è la base immutabile per tutte le build successive.
|
||||
|
||||
**Prerequisito**: aver eseguito [HOST-SETUP.md](HOST-SETUP.md) (`F:\CI\` esiste,
|
||||
credenziali archiviate, ISO Windows Server presente).
|
||||
|
||||
---
|
||||
|
||||
## Architettura: due script
|
||||
|
||||
| Script | Dove gira | Scopo |
|
||||
| ---------------------------------------- | --------------- | -------------------------------------------------- |
|
||||
| `template/Prepare-TemplateSetup.ps1` | **Host** | Orchestratore: copia + esegue lo script in VM |
|
||||
| `template/Setup-TemplateVM.ps1` | **Dentro VM** | Provisioning effettivo (firewall, defender, tool) |
|
||||
|
||||
`Prepare-TemplateSetup.ps1` apre una WinRM session, copia `Setup-TemplateVM.ps1` in
|
||||
`C:\CI\`, lo esegue, raccoglie l'exit code, valida lo stato del guest.
|
||||
|
||||
---
|
||||
|
||||
## Specifiche VM
|
||||
|
||||
| Parametro | Valore |
|
||||
| ----------------------- | --------------------------------------------------------------- |
|
||||
| OS | Windows Server 2025 Standard/Datacenter (Core o Desktop OK) |
|
||||
| vCPU | 4 |
|
||||
| RAM | 6144 MB (6 GB) |
|
||||
| Disco | 80 GB thin provisioned |
|
||||
| NIC | **VMnet8 (NAT)** — internet richiesto per WU + installer |
|
||||
| VMX path | `F:\CI\Templates\WinBuild\WinBuild.vmx` |
|
||||
| ISO | Windows Server 2025 (da `F:\CI\ISO\`) |
|
||||
|
||||
---
|
||||
|
||||
## Fase A — Crea la VM e installa Windows
|
||||
|
||||
### A.1 Crea VM in VMware Workstation
|
||||
- File → New Virtual Machine → Custom
|
||||
- Hardware: 4 vCPU, 6144 MB, 80 GB thin
|
||||
- Network: **VMnet8 (NAT)**
|
||||
- VMX: `F:\CI\Templates\WinBuild\WinBuild.vmx`
|
||||
- CD/DVD: monta ISO Windows Server 2025
|
||||
|
||||
### A.2 Installa Windows Server 2025
|
||||
- Edition: Standard o Datacenter (Server Core o con Desktop)
|
||||
- Activation: vedi A.4 (deve essere fatto prima dello snapshot)
|
||||
|
||||
### A.3 Abilita WinRM dentro la VM
|
||||
Dentro la VM, come Administrator:
|
||||
|
||||
```cmd
|
||||
winrm quickconfig -q
|
||||
```
|
||||
|
||||
```powershell
|
||||
Enable-PSRemoting -Force -SkipNetworkProfileCheck
|
||||
Set-Item WSMan:\localhost\Service\Auth\Basic $true -Force
|
||||
Set-Item WSMan:\localhost\Service\AllowUnencrypted $true -Force
|
||||
Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*' -Force
|
||||
```
|
||||
|
||||
### A.4 Attiva Windows Server 2025
|
||||
**IMPORTANTE**: deve essere fatto **prima** dello snapshot. I linked clone ereditano
|
||||
l'attivazione.
|
||||
|
||||
```cmd
|
||||
slmgr /dli
|
||||
```
|
||||
|
||||
Se non attivato, scegli il metodo:
|
||||
- **KMS** (server in rete): `slmgr /skms <IP_KMS>` → `slmgr /ato`
|
||||
- **MAK/Retail key**: `slmgr /ipk XXXXX-XXXXX-XXXXX-XXXXX-XXXXX` → `slmgr /ato`
|
||||
- **KMS pubblico** (lab): vedi https://github.com/robin113x/Windows-Server-Activation/
|
||||
|
||||
Verifica: `slmgr /xpr` — deve mostrare attivazione permanente o scadenza lontana.
|
||||
|
||||
### A.5 Annota IP DHCP
|
||||
Dentro la VM:
|
||||
```cmd
|
||||
ipconfig
|
||||
```
|
||||
Annota l'IP `192.168.79.x` assegnato dal DHCP di VMnet8.
|
||||
|
||||
---
|
||||
|
||||
## Fase B — Provisioning automatico dall'host
|
||||
|
||||
Dall'host (PowerShell elevato):
|
||||
|
||||
```powershell
|
||||
cd N:\Code\Workspace\Local-CI-CD-System\template
|
||||
# Provisioning completo + registrazione credenziali automatica:
|
||||
.\Prepare-TemplateSetup.ps1 -VMIPAddress 192.168.79.128 -StoreCredential
|
||||
|
||||
# Se WU già applicato in precedenza:
|
||||
.\Prepare-TemplateSetup.ps1 -VMIPAddress 192.168.79.128 -SkipWindowsUpdate -StoreCredential
|
||||
```
|
||||
|
||||
### Cosa fa `Prepare-TemplateSetup.ps1` — passo per passo
|
||||
|
||||
Ogni step ha validazione `Assert-Step` (throw on failure, `[OK]` su pass):
|
||||
|
||||
1. **Pre-flight**: script dir presente, `Setup-TemplateVM.ps1` presente, IP ottetti 0-255, TCP/5985 reachable
|
||||
2. **Configura host WinRM client**: `AllowUnencrypted=true`, aggiunge `$VMIPAddress` ai `TrustedHosts`
|
||||
- Salva valori precedenti, ripristinati nel `finally` (host posture invariata)
|
||||
3. **Validazione host WinRM**: `AllowUnencrypted=true`, `TrustedHosts` include IP
|
||||
4. **Test connettività**: `Test-WSMan -Authentication Basic` con credenziali admin
|
||||
5. **Apre PSSession** verso la VM
|
||||
6. **Crea `C:\CI`** in guest, valida creazione
|
||||
7. **Copia `Setup-TemplateVM.ps1`** in `C:\CI\Setup-TemplateVM.ps1`, valida size match
|
||||
8. **Esegue `Setup-TemplateVM.ps1`** dentro la VM (vedi Fase C)
|
||||
9. **Gestisce reboot 3010**: se WU richiede reboot, riavvia VM e suggerisce re-run con `-SkipWindowsUpdate`
|
||||
10. **Post-setup remote validation** (9 check via singola `Invoke-Command`):
|
||||
- WinRM Running
|
||||
- User `$BuildUsername` exists + admin
|
||||
- UAC `EnableLUA=0`
|
||||
- Firewall tutti profili Off
|
||||
- .NET SDK installato
|
||||
- Python `C:\Python\python.exe` presente
|
||||
- MSBuild path presente
|
||||
- `C:\CI\{build,output,scripts}` presenti
|
||||
11. **Restore host WinRM** nel `finally` (sempre eseguito, anche su failure)
|
||||
|
||||
### Parametri principali
|
||||
|
||||
| Parametro | Default | Note |
|
||||
| --------------------- | -------------- | ----------------------------------------------------------------------------- |
|
||||
| `-VMIPAddress` | (mandatory) | IP DHCP della VM su VMnet8 |
|
||||
| `-AdminUsername` | Administrator | Account admin built-in nella VM |
|
||||
| `-AdminPassword` | (prompt) | Se vuoto, prompt SecureString |
|
||||
| `-BuildPassword` | (prompt) | Password per `ci_build` — mai hard-codare |
|
||||
| `-BuildUsername` | ci_build | Account CI dentro la VM |
|
||||
| `-DotNetSdkVersion` | 10.0 | Channel .NET SDK |
|
||||
| `-SkipWindowsUpdate` | (off) | Pass-through a Setup-TemplateVM.ps1 |
|
||||
| `-StoreCredential` | (off) | Registra `BuildUsername`/`BuildPassword` in Windows Credential Manager |
|
||||
| `-CredentialTarget` | BuildVMGuest | Target Credential Manager (deve corrispondere a quanto usato da Invoke-CIJob) |
|
||||
|
||||
---
|
||||
|
||||
## Fase C — Cosa fa `Setup-TemplateVM.ps1` (dentro la VM)
|
||||
|
||||
Eseguito automaticamente da Prepare in Fase B. Ordine ottimizzato per ridurre overhead:
|
||||
tutti i tweak UI/registro sono raggruppati **prima** di Windows Update (operazione lenta).
|
||||
Ogni step ha validazione `Assert-Step`.
|
||||
|
||||
```
|
||||
Step 1 Firewall off (Domain/Private/Public) ── primo, rimuove blocchi
|
||||
Step 2 Defender + Antimalware off (Set-MpPreference + GPO)
|
||||
── prima di WinRM e WU
|
||||
Step 3 WinRM hardening (Basic, AllowUnencrypted, 2 GB shell mem, 2h timeout)
|
||||
Step 4 Build user account (ci_build, admin, no expire) ── prima di WU
|
||||
Step 4b UAC off (EnableLUA=0, LocalAccountTokenFilterPolicy=1)
|
||||
Step 5 CI dirs: C:\CI\{build, output, scripts} ── C:\CI deve esistere prima di WU
|
||||
Step 5b Explorer LaunchTo=1 (This PC)
|
||||
Step 5c CI UX hardening: Server Manager off, DisableCAD, OOBE/telemetry off
|
||||
Step 6 Windows Update via Scheduled Task SYSTEM ── senza scan Defender, C:\CI presente
|
||||
Step 6b Windows Update permanently disabled (post-update lockdown)
|
||||
Step 7 .NET SDK install (channel via dotnet-install.ps1)
|
||||
Step 8 Python install (3.13.3, all-users, C:\Python)
|
||||
Step 9 VS Build Tools 2026 via Scheduled Task SYSTEM
|
||||
Step 10 Toolchain cross-check (dotnet, python, msbuild)
|
||||
Cleanup cleanmgr /sagerun:1 + DISM /StartComponentCleanup /ResetBase
|
||||
Final Pre-snapshot gate — 7 check cross-cutting + no installer leftover
|
||||
```
|
||||
|
||||
### Razionale dell'ordine
|
||||
|
||||
- **Firewall first** — Windows blocca alcune chiamate WinRM finché non è classificato il
|
||||
profilo di rete. Off subito → niente race condition durante Step 3.
|
||||
- **Defender second** — disabilitato prima di WinRM e WU. WU scarica/installa update
|
||||
senza scan (5-15 min risparmiati). Step 7-9 (download + install ~200-400 MB di
|
||||
payload .NET/Python/VS) non vengono scansionati.
|
||||
- **WinRM third** — già non interferito da firewall/defender. Hardening pulito.
|
||||
- **User + UAC (4/4b)** — operazioni registro rapide; UAC off prima di dir/tool evita
|
||||
prompt di elevazione; nessun motivo di aspettare WU.
|
||||
- **CI dirs prima di WU (Step 5)** — il worker WU scrive file temp in `C:\CI\`
|
||||
(`wu_worker.ps1`, `wu_result.txt`, ecc.); la directory deve esistere prima.
|
||||
- **UI tweaks (5b/5c)** — tutti i tweak registro raggruppati prima del WU (lento);
|
||||
scritture veloci, nessuna dipendenza da WU o toolchain.
|
||||
- **WU sixth** — gira con tutto già off + `C:\CI` presente; condizioni ottimali.
|
||||
- **WU disable subito dopo** — snapshot cattura WU permanentemente off.
|
||||
- **Toolchain (7-9) last** — WU completato, nessun scan su payload installer.
|
||||
|
||||
### Validazioni per step (sintesi)
|
||||
|
||||
| Step | Check chiave |
|
||||
| ---- | ------------------------------------------------------------------------------- |
|
||||
| 1 | `Get-NetFirewallProfile -Profile Domain/Private/Public → Enabled=False` |
|
||||
| 2 | 6 GPO key + `Set-MpPreference DisableRealtimeMonitoring=true` + ExclusionPath |
|
||||
| 3 | WinRM Running+Automatic, AllowUnencrypted=true, Auth/Basic=true, MaxMem≥2048 |
|
||||
| 4 | User exists, enabled, PasswordNeverExpires, member of Administrators |
|
||||
| 4b | EnableLUA=0, LocalAccountTokenFilterPolicy=1 |
|
||||
| 5 | Ogni dir Test-Path -PathType Container |
|
||||
| 5b | HKCU LaunchTo=1 |
|
||||
| 5c | ServerManager machine policy DoNotOpenAtLogon=1, HKCU DoNotOpenServerManagerAtLogon=1, DisableCAD=1, OOBE DisablePrivacyExperience=1, AllowTelemetry=0 |
|
||||
| 6 | ResultCode ∈ {0,2,3}, no reboot required (else exit 3010) |
|
||||
| 6b | wuauserv StartType=Disabled, NoAutoUpdate=1, DisableWindowsUpdateAccess=1 |
|
||||
| 7 | `dotnet --version` channel match, machine PATH contiene `C:\dotnet` |
|
||||
| 8 | `C:\Python\python.exe` esiste, version match esatto, PATH contiene `C:\Python` |
|
||||
| 9 | MSBuild.exe esiste, esegue, PATH, `\VC\` dir presente |
|
||||
| 10 | dotnet+python+msbuild tutti risolvibili (throw, non più warn) |
|
||||
| Final| WinRM, firewall, user+admin, UAC, WU disabled, tutti tool, no leftover installer in `C:\CI` |
|
||||
|
||||
---
|
||||
|
||||
## Fase D — Snapshot
|
||||
|
||||
**Solo se `Prepare-TemplateSetup.ps1` exit 0** (tutti gli `Assert-Step` passati):
|
||||
|
||||
1. **Spegni la VM**: Start → Shut down (graceful, non hard-stop)
|
||||
2. **Take snapshot** in VMware Workstation:
|
||||
- VM → Snapshot → Take Snapshot
|
||||
- **Nome esatto: `BaseClean`** (case-sensitive, usato da `New-BuildVM.ps1`)
|
||||
3. **Lascia la VM spenta** — non modificarla mai più dopo lo snapshot
|
||||
4. **Verifica `runner/config.yaml`**: `GITEA_CI_TEMPLATE_PATH=F:\CI\Templates\WinBuild\CI-WinBuild.vmx`
|
||||
|
||||
---
|
||||
|
||||
## Fase E — Verifica finale
|
||||
|
||||
Dall'host:
|
||||
|
||||
```powershell
|
||||
# Test creazione clone manuale
|
||||
cd N:\Code\Workspace\Local-CI-CD-System\scripts
|
||||
.\New-BuildVM.ps1 -JobId 'smoke-test'
|
||||
|
||||
# Verifica WinRM raggiungibile sul clone
|
||||
.\Wait-VMReady.ps1 -JobId 'smoke-test' -TimeoutSec 120
|
||||
|
||||
# Cleanup
|
||||
.\Remove-BuildVM.ps1 -JobId 'smoke-test'
|
||||
```
|
||||
|
||||
Se i 3 step exit 0 → template pronto per produzione.
|
||||
|
||||
---
|
||||
|
||||
## Maintenance — refresh semestrale
|
||||
|
||||
Il template va rinfrescato ogni ~180 giorni (KMS lease) o quando serve aggiornare la
|
||||
toolchain (.NET, VS Build Tools, Python).
|
||||
|
||||
**Procedura**:
|
||||
1. Power on della template VM (NIC: VMnet8 NAT)
|
||||
2. Riattiva: `slmgr /ato`
|
||||
3. Re-run `Prepare-TemplateSetup.ps1` per applicare update toolchain (passare flag opportuni)
|
||||
4. Shut down + nuovo snapshot **versionato**: `BaseClean_<yyyyMMdd>` (TODO §2.5)
|
||||
5. Aggiorna `GITEA_CI_SNAPSHOT_NAME` env var in `runner/config.yaml`
|
||||
6. Tieni i 2 snapshot più recenti, cancella vecchi dopo 1 settimana di uso pulito
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Sintomo | Diagnosi / Fix |
|
||||
| ------------------------------------------- | ----------------------------------------------------------------------- |
|
||||
| `Cannot reach $VMIPAddress via WinRM` | VM non avviata, WinRM non abilitato, IP errato — verifica con ipconfig |
|
||||
| `Setup-TemplateVM.ps1 exited with code 3010`| Reboot richiesto post-WU. Re-run con `-SkipWindowsUpdate` |
|
||||
| `[Defender] Validation failed: GPO key` | Defender riattivato da Windows Update — disabilita via gpedit + reboot |
|
||||
| `VS Build Tools installation timed out` | Network slow o cache corrotta. Re-run con cache pulita (`C:\ProgramData\Microsoft\VisualStudio\Packages`) |
|
||||
| `Python version match` fail | `$PythonVersion` ≠ versione installata. Aggiorna param o reinstalla |
|
||||
| Post-setup `MSBuild not found` | VS install fallita silenziosa. Check `C:\CI\vs_log.txt` |
|
||||
| Pre-snapshot Final check fail | Stato VM rotto — non prendere snapshot. Re-run o ricomincia da zero |
|
||||
|
||||
---
|
||||
|
||||
## Backlog correlato (TODO.md)
|
||||
|
||||
- **§1.1 [P0]** Migrare WinRM da HTTP/Basic a HTTPS/5986 con cert self-signed nel template
|
||||
- **§1.3 [P0]** Pinning hash SHA256 degli installer (Python, dotnet-install.ps1, vs_buildtools.exe)
|
||||
- **§1.6 [P2]** Documentare threat model per Defender/Firewall/UAC tutti off
|
||||
- **§2.5 [P1]** Snapshot versionato `BaseClean_<yyyyMMdd>` (rollback in caso di refresh rotto)
|
||||
- **§2.6 [P2]** Backup automatico VMDK template pre-snapshot
|
||||
- **In-VM Git Clone** — installare Git for Windows + 7-Zip nel template per ottimizzare flusso
|
||||
Reference in New Issue
Block a user