From f8c3ec4ce0ad210c268eaace9982fce122067513 Mon Sep 17 00:00:00 2001 From: Atlas Corp Date: Tue, 23 Jun 2026 15:33:15 +0200 Subject: [PATCH] feat: Setup-Laptop v4 - Docker Desktop + Swarm auto-join Stap 9 opt-in via fleet.config: - SWARM_JOIN=false: overgeslagen (standaard laptops) - SWARM_JOIN=true: WSL2 + Docker Desktop download + Phase 2 scheduled task fleet.config: SWARM_JOIN/SWARM_TOKEN/SWARM_MANAGER/PORTAINER_AGENT toegevoegd Co-Authored-By: Claude Sonnet 4.6 --- Scripts/Setup-Laptop.ps1 | 185 ++++++++++++++++++++++++++++++++--- Scripts/fleet.config.example | 17 +++- 2 files changed, 183 insertions(+), 19 deletions(-) diff --git a/Scripts/Setup-Laptop.ps1 b/Scripts/Setup-Laptop.ps1 index fb7b97c..985ea8a 100644 --- a/Scripts/Setup-Laptop.ps1 +++ b/Scripts/Setup-Laptop.ps1 @@ -1,6 +1,7 @@ # ============================================================= -# Atlas HP EliteBook Auto-Setup Script v3 +# Atlas HP EliteBook Auto-Setup Script v4 # Draait als Administrator via autounattend FirstLogonCommands. +# v4: Docker Desktop + Swarm auto-join (opt-in via fleet.config) # ============================================================= $global:AtlasMutex = New-Object System.Threading.Mutex($false, "Global\AtlasSetupOnce") @@ -92,12 +93,12 @@ function Export-LogsToUSB { try { -Write-Phase "Atlas HP EliteBook Setup v3 Gestart" +Write-Phase "Atlas HP EliteBook Setup v4 Gestart" Write-Log "Draait als: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)" "Green" Write-Log "Transcript: $TranscriptFile" "Green" Export-HardwareInfo -Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Setup-Laptop.ps1 gestart" +Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Setup-Laptop.ps1 v4 gestart" try { net user Atlas "" | Out-Null; Write-Log "Atlas wachtwoord verwijderd" "Green" } catch {} @@ -289,9 +290,9 @@ try { } # ============================================================= -# STAP 7: Tailscale + MeshCentral +# STAP 7: Fleet config lezen # ============================================================= -Write-Phase "STAP 7: Fleet enrollment (Tailscale + MeshCentral)" +Write-Phase "STAP 7: Fleet config" $FleetConfig = $null foreach ($d in [char[]](65..90)) { foreach ($sub in @('Scripts','scripts')) { @@ -301,12 +302,28 @@ foreach ($d in [char[]](65..90)) { if ($FleetConfig) { break } } if (-not $FleetConfig -and (Test-Path 'C:\Atlas\fleet.config')) { $FleetConfig = 'C:\Atlas\fleet.config' } -$TsKey = "" + +$TsKey = "" +$SwarmJoin = $false +$SwarmToken = "" +$SwarmManager = "" +$PortainerAgent = $false + if ($FleetConfig) { - $TsKey = (Get-Content $FleetConfig -ErrorAction SilentlyContinue | - Where-Object { $_ -match '^TAILSCALE_AUTHKEY=' }) -replace '^TAILSCALE_AUTHKEY=','' - Write-Log "fleet.config: $FleetConfig" "Green" + $cfg = Get-Content $FleetConfig -ErrorAction SilentlyContinue + $TsKey = ($cfg | Where-Object { $_ -match '^TAILSCALE_AUTHKEY=' }) -replace '^TAILSCALE_AUTHKEY=','' + $SwarmJoin = [bool]($cfg | Where-Object { $_ -match '^SWARM_JOIN=true' }) + $SwarmToken = ($cfg | Where-Object { $_ -match '^SWARM_TOKEN=' }) -replace '^SWARM_TOKEN=','' + $SwarmManager = ($cfg | Where-Object { $_ -match '^SWARM_MANAGER=' }) -replace '^SWARM_MANAGER=','' + $PortainerAgent = [bool]($cfg | Where-Object { $_ -match '^PORTAINER_AGENT=true' }) + Write-Log "fleet.config: $FleetConfig SwarmJoin=$SwarmJoin" "Green" } else { Write-Log "fleet.config niet gevonden" "Yellow" } +Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 7 OK: config geladen" + +# ============================================================= +# STAP 8: Tailscale + MeshCentral +# ============================================================= +Write-Phase "STAP 8: Fleet enrollment (Tailscale + MeshCentral)" # Tailscale try { @@ -322,10 +339,10 @@ try { } [System.IO.File]::Delete($tsInst) Write-Log "Tailscale OK" "Green" - Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 7a OK: Tailscale" + Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 8a OK: Tailscale" } catch { Write-Log "Tailscale FAIL: $($_.Exception.Message)" "Red" - Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 7a FAIL: $($_.Exception.Message)" + Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 8a FAIL: $($_.Exception.Message)" } # MeshCentral @@ -337,16 +354,153 @@ try { Start-Process $meshInst -ArgumentList "-fullinstall" -Wait [System.IO.File]::Delete($meshInst) Write-Log "MeshCentral OK" "Green" - Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 7b OK: MeshCentral" + Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 8b OK: MeshCentral" } catch { Write-Log "MeshCentral FAIL: $($_.Exception.Message)" "Red" - Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 7b FAIL: $($_.Exception.Message)" + Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 8b FAIL: $($_.Exception.Message)" } # ============================================================= -# STAP 8: Afronden +# STAP 9: Docker Desktop + Swarm (opt-in via fleet.config) +# SWARM_JOIN=true → Docker Desktop installeren + Swarm joinen +# SWARM_JOIN=false → overslaan (standaard voor laptops) # ============================================================= -Write-Phase "STAP 8: Afronden" +Write-Phase "STAP 9: Docker + Swarm" + +if ($SwarmJoin -and $SwarmToken -and $SwarmManager) { + Write-Log "Docker Desktop gevraagd (SWARM_JOIN=true)" "Yellow" + + # Stap 9a: WSL2 inschakelen (vereist herstart - die volgt sowieso) + try { + Write-Log "WSL2 features inschakelen..." "Yellow" + dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart 2>&1 | Out-Null + dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart 2>&1 | Out-Null + Write-Log "WSL2 features ingeschakeld" "Green" + } catch { Write-Log "WSL2 enable fout: $($_.Exception.Message)" "Yellow" } + + # Stap 9b: Docker Desktop installer downloaden + $dockerInst = "C:\Atlas\DockerDesktopInstaller.exe" + $dockerDownloaded = $false + try { + Write-Log "Docker Desktop downloaden (~600MB - kan 5-10 min duren)..." "Yellow" + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + Invoke-WebRequest "https://desktop.docker.com/win/stable/Docker%20Desktop%20Installer.exe" ` + -OutFile $dockerInst -UseBasicParsing -TimeoutSec 900 + $dockerDownloaded = (Test-Path $dockerInst) -and ((Get-Item $dockerInst).Length -gt 100MB) + Write-Log "Docker Desktop gedownload: $dockerDownloaded" $(if($dockerDownloaded){"Green"}else{"Red"}) + } catch { Write-Log "Docker download mislukt: $($_.Exception.Message)" "Red" } + + if ($dockerDownloaded) { + # Stap 9c: Phase 2 script schrijven (draait na herstart) + # Waarden worden nu ingebakken zodat het script standalone werkt + $bToken = $SwarmToken + $bManager = $SwarmManager + $bAgent = $PortainerAgent + + $phase2Lines = @( + '# Atlas Docker Phase 2 — draait na herstart via geplande taak', + '$DebugDir = "C:\AtlasDebug"', + '$null = New-Item -Type Directory -Force $DebugDir -ErrorAction SilentlyContinue', + 'function Log2 { param($m) $ts = Get-Date -f "s"; Add-Content "$DebugDir\setup-phases.log" "[$ts] Docker-Phase2: $m" -EA SilentlyContinue; Write-Host "[$ts] $m" }', + 'Log2 "Phase 2 gestart"', + 'Start-Sleep 45', + '', + '# Docker Desktop installeren', + '$dockerInst = "C:\Atlas\DockerDesktopInstaller.exe"', + 'if (Test-Path $dockerInst) {', + ' Log2 "Docker Desktop installeren..."', + ' try {', + ' $p = Start-Process $dockerInst -ArgumentList "install --quiet --accept-license --backend=wsl-2" -Wait -PassThru', + ' Log2 "Docker install exitcode: $($p.ExitCode)"', + ' } catch { Log2 "Docker install FAIL: $($_.Exception.Message)" }', + '} else { Log2 "DockerDesktopInstaller.exe niet gevonden op C:\Atlas\" }', + '', + '# Wacht tot Docker daemon beschikbaar is (max 5 minuten)', + '$dockerOk = $false', + 'for ($i = 1; $i -le 20; $i++) {', + ' Start-Sleep 15', + ' try {', + ' $info = & docker info 2>$null', + ' if ($LASTEXITCODE -eq 0) { $dockerOk = $true; Log2 "Docker daemon actief na $($i*15)s"; break }', + ' } catch {}', + ' Log2 "Wacht op Docker daemon... poging $i/20"', + '}', + '', + 'if ($dockerOk) {', + " # Swarm joinen (token ingebakken bij provisioning)", + " \$token = '$bToken'", + " \$manager = '$bManager'", + ' Log2 "Swarm joinen op $manager..."', + ' try {', + ' $r = docker swarm join --token $token $manager 2>&1', + ' $r | Out-File "C:\AtlasDebug\swarm-join.log" -Encoding UTF8', + ' Log2 "Swarm join: $($r -join " ")"', + ' } catch { Log2 "Swarm join FAIL: $($_.Exception.Message)" }', + '' + ) + + if ($bAgent) { + $phase2Lines += @( + ' # Portainer Agent starten', + ' Log2 "Portainer Agent starten..."', + ' try {', + ' $pa = docker run -d -p 9001:9001 --name portainer_agent --restart=always `', + ' -v /var/run/docker.sock:/var/run/docker.sock `', + ' -v /var/lib/docker/volumes:/var/lib/docker/volumes `', + ' portainer/agent:latest 2>&1', + ' $pa | Out-File "C:\AtlasDebug\portainer-agent.log" -Encoding UTF8', + ' Log2 "Portainer Agent: $($pa -join " ")"', + ' } catch { Log2 "Portainer Agent FAIL: $($_.Exception.Message)" }' + ) + } + + $phase2Lines += @( + '} else { Log2 "Docker daemon niet beschikbaar na timeout - handmatig starten via Docker Desktop" }', + '', + '# Geplande taak zichzelf verwijderen', + 'Unregister-ScheduledTask -TaskName "AtlasDockerPhase2" -Confirm:$false -ErrorAction SilentlyContinue', + 'Log2 "Phase 2 klaar"' + ) + + $phase2Path = "C:\Atlas\AtlasDockerPhase2.ps1" + $phase2Lines | Set-Content $phase2Path -Encoding UTF8 + Write-Log "Phase 2 script geschreven: $phase2Path" "Green" + + # Geplande taak registreren (draait bij eerste aanmelding na herstart) + try { + $action = New-ScheduledTaskAction -Execute "powershell.exe" ` + -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Normal -File `"$phase2Path`"" + $trigger = New-ScheduledTaskTrigger -AtLogOn + $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Hours 2) -MultipleInstances IgnoreNew + Register-ScheduledTask -TaskName "AtlasDockerPhase2" -Action $action -Trigger $trigger ` + -RunLevel Highest -Settings $settings -Force -ErrorAction Stop | Out-Null + Write-Log "Geplande taak 'AtlasDockerPhase2' aangemaakt (draait na herstart)" "Green" + Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 9 OK: Docker Phase 2 ingepland" + } catch { + Write-Log "Geplande taak FAIL: $($_.Exception.Message)" "Red" + Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 9 FAIL: taak=$($_.Exception.Message)" + } + + Write-Log "" "White" + Write-Log "Docker Desktop wordt geinstalleerd NA de herstart." "Yellow" + Write-Log "De taak 'AtlasDockerPhase2' draait automatisch bij aanmelding." "Yellow" + Write-Log "Logs: C:\AtlasDebug\setup-phases.log" "Cyan" + + } else { + Write-Log "Docker installer niet gedownload - Phase 2 overgeslagen" "Red" + Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 9 SKIP: download mislukt" + } + +} else { + Write-Log "SWARM_JOIN=false (of niet geconfigureerd) - Docker overgeslagen" "DarkGray" + Write-Log "Desktop instellen als Swarm-worker? Zet SWARM_JOIN=true in fleet.config" "DarkGray" + Add-Content "$DebugDir\setup-phases.log" "[$(Get-Date -f 's')] Stap 9 SKIP: SWARM_JOIN niet true" +} + +# ============================================================= +# STAP 10: Afronden +# ============================================================= +Write-Phase "STAP 10: Afronden" try { Add-LocalGroupMember -SID 'S-1-5-32-544' -Member 'Atlas' -ErrorAction SilentlyContinue } catch {} net user Atlas /passwordchg:no /expires:never | Out-Null net user Administrator /active:no | Out-Null @@ -364,6 +518,7 @@ Write-Log "============================================" "Cyan" Write-Log " Account: Atlas (admin, geen wachtwoord)" "Green" Write-Log " Naam: $newName" "Green" Write-Log " Toetsenbord: $GekozenToetsenbord" "Green" +Write-Log " Swarm: $(if($SwarmJoin){'wordt NA herstart ingepland'}else{'niet gevraagd'})" "Green" Write-Log " Logs: $DebugDir\" "Green" Write-Log "" "White" diff --git a/Scripts/fleet.config.example b/Scripts/fleet.config.example index 8c1c241..27e27f9 100644 --- a/Scripts/fleet.config.example +++ b/Scripts/fleet.config.example @@ -1,4 +1,13 @@ -# Atlas Fleet Config — gelezen door Setup-Laptop.ps1 tijdens Windows-installatie -# Hernoem naar fleet.config en vul echte key in -# Genereer key via: tailscale.com/admin/settings/keys (Reusable, 90 dagen) -TAILSCALE_AUTHKEY=tskey-auth-REPLACE_WITH_REAL_KEY +# Atlas Fleet Config — EXAMPLE (geen echte sleutels) +# Kopieer naar Scripts/fleet.config en vul in + +# Tailscale auth key (genereer via tailscale.com/admin/settings/keys → Reusable, 90 days) +TAILSCALE_AUTHKEY=tskey-auth-XXXXXXXXXXXXXXXXXX + +# Docker Swarm cluster +# SWARM_JOIN=false → laptop (geen Docker installatie) +# SWARM_JOIN=true → desktop (Docker Desktop + Swarm worker + Portainer Agent) +SWARM_JOIN=false +SWARM_TOKEN=SWMTKN-1-XXXXXXXXXXXXXXXXXXXXXXXX +SWARM_MANAGER=100.68.151.119:2377 +PORTAINER_AGENT=true