1param([switch]$Prepare)
2$ErrorActionPreference = 'Stop'
3$ProgressPreference = 'SilentlyContinue'
4$root = 'C:\Snowglobe'
5$status = Join-Path $root 'setup'
6$winlogon = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
7
8function Set-SetupStatus([string]$stage) {
9 $path = Join-Path $status $stage
10 if (!(Test-Path $path)) {
11 [IO.File]::Open($path, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::ReadWrite).Dispose()
12 }
13}
14
15try {
16 if ($Prepare) {
17 Remove-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Run' -Name SnowglobeBootstrap -ErrorAction SilentlyContinue
18 Unregister-ScheduledTask -TaskName SnowglobeFinalize -Confirm:$false -ErrorAction SilentlyContinue
19 & "$root\install-guest.ps1" -Binary "$root\snowglobe-guest.exe" -User sandbox
20 (Get-LocalUser -Name sandbox).SID.Value | Set-Content "$root\template.sid" -Encoding ASCII
21 Get-ScheduledTask -TaskName 'SnowglobeGuest-*' | Disable-ScheduledTask | Out-Null
22 Get-Process snowglobe-guest -ErrorAction SilentlyContinue | Stop-Process -Force
23 $action = New-ScheduledTaskAction -Execute "$env:WINDIR\System32\WindowsPowerShell\v1.0\powershell.exe" -Argument '-NoProfile -ExecutionPolicy Bypass -File C:\Snowglobe\windows-provision.ps1'
24 $trigger = New-ScheduledTaskTrigger -AtStartup
25 $principal = New-ScheduledTaskPrincipal -UserId SYSTEM -LogonType ServiceAccount -RunLevel Highest
26 $settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Minutes 5)
27 Register-ScheduledTask -TaskName SnowglobeProvision -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null
28 $password = [Guid]::NewGuid().ToString('N') + [Guid]::NewGuid().ToString('N')
29 Set-LocalUser sandbox -Password (ConvertTo-SecureString $password -AsPlainText -Force) -PasswordNeverExpires $true
30 Set-ItemProperty $winlogon AutoAdminLogon '1'
31 Set-ItemProperty $winlogon DefaultUserName 'sandbox'
32 Set-ItemProperty $winlogon DefaultDomainName $env:COMPUTERNAME
33 Set-ItemProperty $winlogon DefaultPassword $password
34 Remove-ItemProperty $winlogon AutoLogonCount -ErrorAction SilentlyContinue
35 powercfg.exe /hibernate off
36 Remove-Item "$root\identity.ini", "$root\provisioned" -Force -ErrorAction SilentlyContinue
37 if (Test-Path $status) { Remove-Item $status -Recurse -Force }
38 return
39 }
40
41 New-Item $status -ItemType Directory -Force | Out-Null
42 if (Test-Path "$status\ready") { return }
43 if (Test-Path "$status\failed") { Remove-Item "$status\failed" -Force }
44 Set-SetupStatus personalizing
45 $seed = $null
46 $deadline = (Get-Date).AddMinutes(1)
47 while (!$seed) {
48 $seed = 68..90 | ForEach-Object { '{0}:\SNOWGLOB.INI' -f [char]$_ } | Where-Object { Test-Path $_ } | Select-Object -First 1
49 if ($seed) { break }
50 if ((Get-Date) -gt $deadline) { throw 'The VM login was not attached.' }
51 Start-Sleep -Milliseconds 250
52 }
53 $values = @{}
54 foreach ($line in Get-Content $seed) {
55 $pair = $line.Split('=', 2)
56 if ($pair.Count -eq 2) { $values[$pair[0]] = $pair[1] }
57 }
58 if (Test-Path "$status\restarting") {
59 if ($env:COMPUTERNAME -ine $values['HOSTNAME']) { throw 'The VM name was not applied.' }
60 $scheduler = New-Object -ComObject Schedule.Service
61 $scheduler.Connect()
62 $tasks = $scheduler.GetFolder('\').GetTasks(1) | Where-Object { $_.Name -like 'SnowglobeGuest-*' }
63 foreach ($task in $tasks) { $task.Enabled = $true }
64 $deadline = (Get-Date).AddMinutes(2)
65 while (!(Get-Process explorer -ErrorAction SilentlyContinue)) {
66 if ((Get-Date) -gt $deadline) { throw 'The desktop did not start.' }
67 Start-Sleep -Milliseconds 250
68 }
69 foreach ($task in $tasks) { $task.Run($null) | Out-Null }
70 while (!(Get-Process explorer -ErrorAction SilentlyContinue) -or !(Get-Process snowglobe-guest -ErrorAction SilentlyContinue)) {
71 if ((Get-Date) -gt $deadline) { throw 'The desktop did not start.' }
72 Start-Sleep -Milliseconds 250
73 }
74 Set-SetupStatus ready
75 return
76 }
77 $username = $values['USERNAME']
78 $password = $values['PASSWORD']
79 $hostname = $values['HOSTNAME']
80 if ($username -notmatch '^[A-Za-z0-9][A-Za-z0-9._-]{0,19}$') { throw 'The VM account name is unsupported.' }
81 if ($password.Length -lt 12 -or $password.Length -gt 128 -or $password -match '[\r\n]') { throw 'The VM password is malformed.' }
82 if ($hostname -notmatch '^sgvm-[a-f0-9]{8}$') { throw 'The VM name is malformed.' }
83 $sid = Get-Content "$root\template.sid"
84 $user = Get-LocalUser | Where-Object { $_.SID.Value -eq $sid }
85 if (!$user) { throw 'The template account is missing.' }
86 if (Get-LocalUser | Where-Object { $_.Name -ieq $username -and $_.SID.Value -ne $sid }) { throw 'The VM account name is already in use.' }
87 $user | Set-LocalUser -Password (ConvertTo-SecureString $password -AsPlainText -Force) -PasswordNeverExpires $true
88 if ($user.Name -ine $username) { $user | Rename-LocalUser -NewName $username }
89 Rename-Computer -NewName $hostname -Force
90 Set-ItemProperty $winlogon AutoAdminLogon '1'
91 Set-ItemProperty $winlogon DefaultUserName $username
92 Set-ItemProperty $winlogon DefaultDomainName $hostname
93 Set-ItemProperty $winlogon DefaultPassword $password
94 Remove-ItemProperty $winlogon AutoLogonCount -ErrorAction SilentlyContinue
95 Set-SetupStatus restarting
96 Restart-Computer -Force
97} catch {
98 if ($Prepare) { throw }
99 Set-SetupStatus failed
100 Add-Content "$root\setup.log" $_.Exception.Message
101 exit 1
102}