a906f4696f
Parallel MSBuild fan-out (e.g. ns7zip x86/x64/x86-ansi) over pypsrp
Linux→Win fails with MSBuild MSB6003 / Win32 1816 'Not enough quota'
because two default guest limits are too tight for concurrent native
processes via WinRM:
- Plugin Microsoft.PowerShell MaxMemoryPerShellMB = 150 MB
(pypsrp uses the plugin endpoint, not the Shell endpoint that
Deploy was previously bumping)
- Non-interactive desktop heap SharedSection 3rd field = 768 KB
(Session 0 csrss; CreateProcess fails when 3+ cl.exe spawn)
Pre-migration Win→Win went through Invoke-Command on PSSession with
quotas inherited from the local process, so the limits never bit.
Changes:
- Deploy-WinBuild2025.ps1 / Deploy-WinBuild2022.ps1: also set the
plugin-level MaxMemoryPerShellMB=2048, bump MaxProcessesPerShell
25→100, patch SharedSection 3rd field 768→4096 (reboot needed for
csrss to pick up, applied by post-install shutdown).
- Install-CIToolchain-WinBuild2025/2022.ps1: add Assert-Step for
plugin quota, MaxProcessesPerShell≥100, SharedSection≥4096; fail
hard if any are under threshold.
- docs/WINDOWS-TEMPLATE-SETUP.md: new "WinRM quotas e desktop heap"
section explaining the diagnosis + one-shot patch for legacy
templates; updated Step 3 validation row + troubleshooting entry.
- AGENTS.md: error #13 with symptom, root cause, and pointer to docs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
366 lines
24 KiB
Markdown
366 lines
24 KiB
Markdown
# 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: tre script
|
||
|
||
| Script | Dove gira | Scopo |
|
||
| ----------------------------------------------- | ------------- | -------------------------------------------------------------------- |
|
||
| `template/Deploy-WinBuild2025.ps1` | **Host** | Opzionale: crea VM e installa Windows Server 2025 unattended da ISO |
|
||
| `template/Prepare-WinBuild2025.ps1` | **Host** | Orchestratore: copia + esegue Setup nel guest via WinRM (2025) |
|
||
| `template/Install-CIToolchain-WinBuild2025.ps1` | **Dentro VM** | CI toolchain (user, .NET, Python, VS) + validazione hardening Deploy |
|
||
| `template/Deploy-WinBuild2022.ps1` | **Host** | Opzionale: crea VM e installa Windows Server 2022 unattended da ISO |
|
||
| `template/Prepare-WinBuild2022.ps1` | **Host** | Orchestratore: copia + esegue Setup nel guest via WinRM (2022) |
|
||
| `template/Install-CIToolchain-WinBuild2022.ps1` | **Dentro VM** | CI toolchain per Windows Server 2022 (stesso flusso del 2025) |
|
||
| `template/Validate-DeployState.ps1` | **Host** | Valida stato post-Deploy prima di eseguire Prepare (2025 e 2022) |
|
||
| `template/Validate-SetupState.ps1` | **Host** | Valida stato post-Setup (Deploy + Setup) prima dello snapshot |
|
||
|
||
**Confine Deploy / Setup** (refactor §7, 2026-05-10):
|
||
- `Deploy-WinBuild2025.ps1` si occupa dell'**OS hardening** (Firewall, WinRM, UAC, Defender, UX tweaks, autologin Administrator, WU GPO locks). Se usato, Setup valida questi come Assert-Step senza re-applicarli.
|
||
- `Install-CIToolchain-WinBuild2025.ps1` si occupa della **CI customization** (utente `ci_build`, cartelle `C:\CI`, toolchain .NET/Python/VS, Windows Update lifecycle, cleanup, final gate).
|
||
|
||
`Prepare-WinBuild2025.ps1` apre una WinRM session, copia `Install-CIToolchain-WinBuild2025.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\WinBuild2025\WinBuild2025.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\WinBuild2025\WinBuild2025.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
|
||
# AllowUnencrypted left false — Deploy post-install.ps1 creates HTTPS/5986 listener
|
||
# Basic auth is secure over HTTPS; host connects on port 5986 with -SkipCACheck
|
||
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-WinBuild2025.ps1 -VMIPAddress 192.168.79.128 -StoreCredential
|
||
|
||
# Se WU già applicato in precedenza:
|
||
.\Prepare-WinBuild2025.ps1 -VMIPAddress 192.168.79.128 -SkipWindowsUpdate -StoreCredential
|
||
```
|
||
|
||
### Cosa fa `Prepare-WinBuild2025.ps1` — passo per passo
|
||
|
||
Ogni step ha validazione `Assert-Step` (throw on failure, `[OK]` su pass):
|
||
|
||
1. **Pre-flight**: script dir presente, `Install-CIToolchain-WinBuild2025.ps1` presente, IP ottetti 0-255, TCP/5986 reachable
|
||
2. **Configura host WinRM client**: aggiunge `$VMIPAddress` ai `TrustedHosts` (no `AllowUnencrypted` — HTTPS non lo richiede)
|
||
- Salva valore precedente, ripristinato nel `finally` (host posture invariata)
|
||
3. **Validazione host WinRM**: `TrustedHosts` include IP
|
||
4. **Test connettività**: `Test-WSMan -Port 5986 -UseSSL -Authentication Basic` con credenziali admin
|
||
5. **Apre PSSession** `-UseSSL -Port 5986` verso la VM
|
||
6. **Crea `C:\CI`** in guest, valida creazione
|
||
7. **Copia `Install-CIToolchain-WinBuild2025.ps1`** in `C:\CI\Install-CIToolchain-WinBuild2025.ps1`, valida size match
|
||
8. **Esegue `Install-CIToolchain-WinBuild2025.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 Install-CIToolchain-WinBuild2025.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 `Install-CIToolchain-WinBuild2025.ps1` (dentro la VM)
|
||
|
||
Eseguito automaticamente da Prepare in Fase B. Steps 1/2/3/4b/5b/5c sono
|
||
**validation-only** — verificano che Deploy abbia già applicato l'hardening OS.
|
||
Steps 4/5/6/6b/7-10 eseguono la CI customization effettiva.
|
||
Ogni step ha validazione `Assert-Step` (throw on failure).
|
||
|
||
```
|
||
Step 1 Firewall: validation only — Deploy disabled all profiles [Assert-Step]
|
||
Step 2 Defender: validation only — Deploy set DisableAntiSpyware GPO + RTP off [Assert-Step]
|
||
Step 3 WinRM: validation only — Deploy set Basic/Unencrypted/MaxMem/Timeout [Assert-Step]
|
||
Step 4 Build user account (ci_build, admin, no expire) ── crea utente CI
|
||
Step 4b UAC: validation only — Deploy set EnableLUA=0 + TokenFilterPolicy [Assert-Step]
|
||
Step 5 CI dirs: C:\CI\{build, output, scripts} ── C:\CI deve esistere prima di WU
|
||
Step 5b Explorer LaunchTo=1: validation only — Deploy set HKCU + Default hive [Assert-Step]
|
||
Step 5c CI UX hardening: validation only — Deploy set lock screen, SM policy/task,
|
||
DisableCAD, OOBE, telemetry, autologin Administrator [Assert-Step]
|
||
Step 6 Windows Update via Scheduled Task SYSTEM ── clears Deploy GPO locks, runs WU
|
||
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
|
||
|
||
- **Steps 1-3 (validazioni)** — verificano prima le precondizioni Deploy (firewall off,
|
||
Defender off, WinRM pronto) in modo che i failure emergano subito, non a metà toolchain.
|
||
- **User + UAC (4/4b)** — crea `ci_build`; valida UAC off (set da Deploy); UAC off prima
|
||
di dir/tool evita prompt di elevazione negli step 7-9.
|
||
- **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)** — validazioni registro rapide; raggruppate prima di WU (lento).
|
||
- **WU (Step 6)** — clears i GPO lock di Deploy (Step A), avvia COM API via SYSTEM task,
|
||
poi Step 6b re-disabilita tutto → snapshot cattura WU permanentemente off.
|
||
- **Toolchain (7-9) last** — WU completato, nessun scan su payload installer.
|
||
|
||
### Validazioni per step (sintesi)
|
||
|
||
| Step | Natura | Check chiave |
|
||
| ----- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| 1 | Assert | `Get-NetFirewallProfile -Profile Domain/Private/Public → Enabled=False` |
|
||
| 2 | Assert | GPO DisableAntiSpyware=1 (set da Deploy) |
|
||
| 3 | Assert | WinRM Running+Automatic, AllowUnencrypted=false (HTTPS-only), Auth/Basic=true, Shell+Plugin MaxMem≥2048, MaxProcesses≥100, desktop heap SharedSection 3rd≥4096 |
|
||
| 4 | Operativo | User exists, enabled, PasswordNeverExpires, member of Administrators |
|
||
| 4b | Assert | EnableLUA=0, LocalAccountTokenFilterPolicy=1 |
|
||
| 5 | Operativo | Ogni dir Test-Path -PathType Container |
|
||
| 5b | Assert | HKCU LaunchTo=1 |
|
||
| 5c | Assert+Op | NoLockScreen=1, SM policy DoNotOpenAtLogon=1, SM task Disabled, DisableCAD=1, OOBE DisablePrivacyExperience=1, AllowTelemetry=0; AutoAdminLogon=1+DefaultUserName=Administrator re-affermati se assenti (Windows OOBE può resettarli al primo boot) |
|
||
| 6 | Operativo | ResultCode ∈ {0,2,3}, no reboot required (else exit 3010) |
|
||
| 6b | Operativo | wuauserv StartType=Disabled, NoAutoUpdate=1, DisableWindowsUpdateAccess=1 |
|
||
| 7 | Operativo | `dotnet --version` channel match, machine PATH contiene `C:\dotnet` |
|
||
| 8 | Operativo | `C:\Python\python.exe` esiste, version match esatto, PATH contiene `C:\Python` |
|
||
| 9 | Operativo | MSBuild.exe esiste, esegue, PATH, `\VC\` dir presente |
|
||
| 10 | Operativo | dotnet+python+msbuild tutti risolvibili (throw) |
|
||
| Final | Cross-cut | WinRM, firewall, user+admin, UAC, WU disabled, tutti tool, no leftover installer in `C:\CI` |
|
||
|
||
---
|
||
|
||
## Fase D — Snapshot
|
||
|
||
**Solo se `Prepare-WinBuild2025.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\WinBuild2025\WinBuild2025.vmx`
|
||
|
||
---
|
||
|
||
## Validation scripts standalone
|
||
|
||
Due script per validare il guest in punti specifici del flusso, indipendentemente da
|
||
Prepare. Utili per debug, re-verifica dopo modifiche manuali, e CI automatizzata.
|
||
|
||
### Dopo Deploy — prima di Prepare
|
||
|
||
```powershell
|
||
.\template\Validate-DeployState.ps1 -VMIPAddress 192.168.79.129
|
||
```
|
||
|
||
Controlla: Firewall off, Defender GPO, WinRM (Running/Automatic/AllowUnencrypted/Basic/MaxMem),
|
||
UAC off, Explorer LaunchTo, NoLockScreen, Server Manager (policy+HKLM+task+HKCU),
|
||
DisableCAD, OOBE, AllowTelemetry, **AutoAdminLogon=1**, WU GPO, wuauserv/UsoSvc=Manual.
|
||
|
||
### Dopo Prepare — prima di snapshot
|
||
|
||
```powershell
|
||
.\template\Validate-SetupState.ps1 -VMIPAddress 192.168.79.129
|
||
```
|
||
|
||
Tutti i check Deploy + ci_build (exists/enabled/admin/PasswordNeverExpires),
|
||
`C:\CI\{build,output,scripts}`, wuauserv/UsoSvc=Disabled, WU GPO,
|
||
.NET SDK channel match, Python version match, MSBuild present, no leftover installer files.
|
||
|
||
Exit 0 = tutto OK. Exit 1 = almeno un check fallito (output indica quale).
|
||
|
||
---
|
||
|
||
## 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-WinBuild2025.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
|
||
|
||
---
|
||
|
||
## WinRM quotas e desktop heap (build paralleli)
|
||
|
||
I template Windows escono di default da Windows con quote dimensionate per uso
|
||
interattivo. Da Linux/`pypsrp` queste quote si rivelano strette appena il build
|
||
guest fa fan-out di processi nativi (compilatori, linker) in parallelo. Tre
|
||
configurazioni che il template **deve** applicare prima dello snapshot
|
||
`BaseClean` (gestite automaticamente da `Deploy-WinBuild2025.ps1` / `2022`):
|
||
|
||
| Setting | Default | Valore template | Motivo |
|
||
| ---------------------------------------------------------------------------------------- | ------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `WSMan:\localhost\Shell\MaxMemoryPerShellMB` | 150 | 2048 | Limite globale memoria per shell WinRM (winrs) |
|
||
| `WSMan:\localhost\Plugin\Microsoft.PowerShell\Quotas\MaxMemoryPerShellMB` | 150 | 2048 | **pypsrp da Linux usa l'endpoint plugin Microsoft.PowerShell**, non lo Shell. Senza questo, builds paralleli falliscono con `MSB6003 / Not enough quota` anche se Shell è a 2048 |
|
||
| `WSMan:\localhost\Shell\MaxProcessesPerShell` | 25 | 100 | MSBuild + cl + link + mt + altri tool si moltiplicano in fan-out parallelo |
|
||
| `HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems\Windows SharedSection` | `…,768` | `…,4096` | Heap del desktop **non-interattivo** (Session 0). CreateProcess fallisce con `ERROR_NOT_ENOUGH_QUOTA` (1816) quando 3+ cl.exe partono insieme. Richiede **reboot** per essere letto da `csrss.exe`. |
|
||
|
||
**Importante**: la modifica `SharedSection` è una scrittura di registry che il
|
||
kernel legge **solo al boot** (`csrss.exe`). Lo `shutdown /s` finale di
|
||
`post-install.ps1` fa sì che il prossimo avvio del template (per ulteriore
|
||
manutenzione, o l'invocazione di `Install-CIToolchain`) parta con il valore
|
||
corretto. Il `BaseClean` snapshot va preso **dopo** un boot completo per essere
|
||
certi che il valore sia attivo.
|
||
|
||
**Perché pre-migrazione (Win host) funzionava**: il path Windows→Windows usava
|
||
`Invoke-Command` su `PSSession` con la stessa identità del processo locale (act_runner
|
||
come LocalSystem), ereditando spesso quote più generose tramite GPO e default
|
||
del client locale. Il path Linux→Windows (`pypsrp`) attinge ai default puri
|
||
dell'endpoint plugin Microsoft.PowerShell del guest — se il template non li ha
|
||
bumpati, sono **150 MB / 25 processi / 768 KB heap**.
|
||
|
||
**Validazione**: `Install-CIToolchain-WinBuild2025.ps1` Step 3 controlla tutti e
|
||
quattro i valori e fallisce hard se anche solo uno è sotto soglia. Per
|
||
template già esistenti, applicare il blocco PowerShell sotto, reboot, shutdown,
|
||
nuovo snapshot `BaseClean`:
|
||
|
||
```powershell
|
||
# Una tantum su template legacy
|
||
Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB 2048 -Force
|
||
Set-Item 'WSMan:\localhost\Plugin\Microsoft.PowerShell\Quotas\MaxMemoryPerShellMB' 2048 -Force
|
||
Set-Item WSMan:\localhost\Shell\MaxProcessesPerShell 100 -Force
|
||
$k = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems'
|
||
$cur = (Get-ItemProperty -Path $k -Name Windows).Windows
|
||
$new = $cur -replace 'SharedSection=(\d+),(\d+),\d+', 'SharedSection=$1,$2,4096'
|
||
Set-ItemProperty -Path $k -Name Windows -Value $new
|
||
Restart-Service WinRM -Force
|
||
Restart-Computer -Force # necessario per il desktop heap
|
||
```
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
| Sintomo | Diagnosi / Fix |
|
||
| ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
|
||
| `Cannot reach $VMIPAddress via WinRM` | VM non avviata, WinRM non abilitato, IP errato — verifica con ipconfig |
|
||
| `Install-CIToolchain-WinBuild2025.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 |
|
||
| MSBuild `error MSB6003: CL.exe could not be run` + `Win32Exception (0x80004005): Not enough quota` su build paralleli | Quote WinRM/desktop-heap non bumpate nel template (vedi §"WinRM quotas e desktop heap" sotto). Tipico quando il build interno fa fan-out (es. 3× cl.exe paralleli) via pypsrp Linux→Win |
|
||
|
||
---
|
||
|
||
## 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
|
||
|