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 <noreply@anthropic.com>
This commit is contained in:
Atlas Corp 2026-06-23 15:33:15 +02:00
parent 3479a341be
commit f8c3ec4ce0
2 changed files with 183 additions and 19 deletions

View file

@ -1,6 +1,7 @@
# ============================================================= # =============================================================
# Atlas HP EliteBook Auto-Setup Script v3 # Atlas HP EliteBook Auto-Setup Script v4
# Draait als Administrator via autounattend FirstLogonCommands. # 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") $global:AtlasMutex = New-Object System.Threading.Mutex($false, "Global\AtlasSetupOnce")
@ -92,12 +93,12 @@ function Export-LogsToUSB {
try { 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 "Draait als: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)" "Green"
Write-Log "Transcript: $TranscriptFile" "Green" Write-Log "Transcript: $TranscriptFile" "Green"
Export-HardwareInfo 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 {} 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 $FleetConfig = $null
foreach ($d in [char[]](65..90)) { foreach ($d in [char[]](65..90)) {
foreach ($sub in @('Scripts','scripts')) { foreach ($sub in @('Scripts','scripts')) {
@ -301,12 +302,28 @@ foreach ($d in [char[]](65..90)) {
if ($FleetConfig) { break } if ($FleetConfig) { break }
} }
if (-not $FleetConfig -and (Test-Path 'C:\Atlas\fleet.config')) { $FleetConfig = 'C:\Atlas\fleet.config' } 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) { if ($FleetConfig) {
$TsKey = (Get-Content $FleetConfig -ErrorAction SilentlyContinue | $cfg = Get-Content $FleetConfig -ErrorAction SilentlyContinue
Where-Object { $_ -match '^TAILSCALE_AUTHKEY=' }) -replace '^TAILSCALE_AUTHKEY=','' $TsKey = ($cfg | Where-Object { $_ -match '^TAILSCALE_AUTHKEY=' }) -replace '^TAILSCALE_AUTHKEY=',''
Write-Log "fleet.config: $FleetConfig" "Green" $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" } } 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 # Tailscale
try { try {
@ -322,10 +339,10 @@ try {
} }
[System.IO.File]::Delete($tsInst) [System.IO.File]::Delete($tsInst)
Write-Log "Tailscale OK" "Green" 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 { } catch {
Write-Log "Tailscale FAIL: $($_.Exception.Message)" "Red" 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 # MeshCentral
@ -337,16 +354,153 @@ try {
Start-Process $meshInst -ArgumentList "-fullinstall" -Wait Start-Process $meshInst -ArgumentList "-fullinstall" -Wait
[System.IO.File]::Delete($meshInst) [System.IO.File]::Delete($meshInst)
Write-Log "MeshCentral OK" "Green" 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 { } catch {
Write-Log "MeshCentral FAIL: $($_.Exception.Message)" "Red" 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 {} 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 Atlas /passwordchg:no /expires:never | Out-Null
net user Administrator /active:no | 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 " Account: Atlas (admin, geen wachtwoord)" "Green"
Write-Log " Naam: $newName" "Green" Write-Log " Naam: $newName" "Green"
Write-Log " Toetsenbord: $GekozenToetsenbord" "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 " Logs: $DebugDir\" "Green"
Write-Log "" "White" Write-Log "" "White"

View file

@ -1,4 +1,13 @@
# Atlas Fleet Config — gelezen door Setup-Laptop.ps1 tijdens Windows-installatie # Atlas Fleet Config — EXAMPLE (geen echte sleutels)
# Hernoem naar fleet.config en vul echte key in # Kopieer naar Scripts/fleet.config en vul in
# Genereer key via: tailscale.com/admin/settings/keys (Reusable, 90 dagen)
TAILSCALE_AUTHKEY=tskey-auth-REPLACE_WITH_REAL_KEY # 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