#Requires -Version 5.1 #Requires -RunAsAdministrator # noobe.title.dev - NO OOBE # Asking Defender politely to look away. $ErrorActionPreference = 'Stop' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $baseUrl = 'https://noobe.title.dev' $noobeHost = ([uri]$baseUrl).Host function Write-Rail { param([string]$Status) # received > queued > importing > verdict $lit = switch ($Status) { 'received' { 1 } 'queued' { 2 } 'authorized' { 2 } 'processing' { 3 } 'complete' { 4 } 'failed' { 4 } default { 0 } } $line = ' ' for ($i = 0; $i -lt 4; $i++) { $n = $i + 1 if ($n -lt $lit) { $line += '(#)' } elseif ($n -eq $lit) { $line += '(o)' } else { $line += '( )' } if ($i -lt 3) { $line += '-----' } } $color = 'DarkGray' if ($Status -eq 'complete') { $color = 'Green' } elseif ($Status -eq 'failed') { $color = 'Red' } elseif ($lit -gt 0) { $color = 'Cyan' } Write-Host $line -ForegroundColor $color Write-Host ' received queued importing verdict' -ForegroundColor DarkGray } function Write-Line { param([string]$Text, [string]$Color = 'DarkGray') Write-Host (" {0} {1}" -f (Get-Date -Format 'HH:mm:ss'), $Text) -ForegroundColor $Color } Write-Host '' Write-Host ' _ _ ___ ___ ___ ___' -ForegroundColor Cyan Write-Host ' | \| |/ _ \ / _ \| _ ) __|' -ForegroundColor Cyan Write-Host ' | .` | (_) | (_) | _ \ _|' -ForegroundColor Cyan Write-Host ' |_|\_|\___/ \___/|___/___|' -ForegroundColor Cyan Write-Host " NO OOBE - $noobeHost" -ForegroundColor DarkGray Write-Host '' Write-Host ' Asking Defender politely to look away.' -ForegroundColor Yellow Write-Host '' # What this box is, before anything else happens. $cs = Get-CimInstance Win32_ComputerSystem -ErrorAction SilentlyContinue $bios = Get-CimInstance Win32_BIOS -ErrorAction SilentlyContinue $model = '' if ($cs) { $model = ("{0} {1}" -f $cs.Manufacturer, $cs.Model).Trim() } $serial = '' if ($bios) { $serial = [string]$bios.SerialNumber } Write-Host (" serial {0}" -f $serial) -ForegroundColor White Write-Host (" model {0}" -f $model) -ForegroundColor White Write-Host '' # Install Get-WindowsAutopilotInfo if not present # @see https://learn.microsoft.com/autopilot/add-devices#collect-the-hardware-hash $scriptName = 'Get-WindowsAutopilotInfo' if (-not (Get-InstalledScript -Name $scriptName -ErrorAction SilentlyContinue)) { Write-Line 'installing Get-WindowsAutopilotInfo from PSGallery' Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned -Force Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -ErrorAction SilentlyContinue | Out-Null Install-Script -Name $scriptName -Force -ErrorAction Stop } # Resolve full path — Install-Script location varies by scope (user vs AllUsers) $scriptPath = Join-Path (Get-InstalledScript -Name $scriptName).InstalledLocation "$scriptName.ps1" # Collect HWID $csvPath = Join-Path $env:TEMP 'autopilot-hwid.csv' Write-Line 'collecting hardware hash' & $scriptPath -OutputFile $csvPath # Read and POST back $csvContent = Get-Content -Path $csvPath -Raw Write-Line 'uploading' $headers = @{ 'X-Device-Model' = $model } $response = Invoke-WebRequest -Uri $baseUrl -Method POST -Body $csvContent -ContentType 'text/csv' -Headers $headers -UseBasicParsing $jobId = $response.Headers['X-Job-Id'] $statusUrl = $response.Headers['X-Status-Url'] Write-Host '' if ($jobId) { Write-Host (" job {0}" -f $jobId) -ForegroundColor White } Write-Host '' Write-Rail 'received' Write-Host '' if ($statusUrl) { Write-Host ' Name this device from your phone:' -ForegroundColor Cyan Write-Host '' # The server prints the QR as UTF-8 half blocks after the job lines. $body = $response.Content $qrStart = $body.IndexOf([char]0x2588) if ($qrStart -lt 0) { $qrStart = $body.IndexOf([char]0x2580) } if ($qrStart -ge 0) { $qrEnd = $body.LastIndexOf([char]0x2580) if ($qrEnd -lt $qrStart) { $qrEnd = $body.LastIndexOf([char]0x2588) } $lineEnd = $body.IndexOf("`n", $qrEnd) if ($lineEnd -lt 0) { $lineEnd = $body.Length } Write-Host $body.Substring($qrStart, $lineEnd - $qrStart) } Write-Host " $statusUrl" -ForegroundColor White Write-Host '' } # Follow the job's event stream until Graph's verdict lands. Each SSE "status" # event is one line of JSON; the console redraws the rail when the status # changes and logs import progress as Graph reports it. function Show-Status { param($Status, [ref]$Last, [ref]$LastImport) $state = [string]$Status.status $import = [string]$Status.importStatus if ($state -ne $Last.Value) { $Last.Value = $state Write-Host '' Write-Rail $state Write-Host '' switch ($state) { 'queued' { Write-Line 'queued' } 'authorized' { Write-Line 'authorized, queued' } 'processing' { Write-Line 'importing' 'Cyan' } 'complete' { $named = '' if ($Status.deviceName) { $named = " - named $($Status.deviceName)" } if ($Status.groupTag) { $named += " - tag $($Status.groupTag)" } Write-Line ("complete{0}" -f $named) 'Green' } 'failed' { Write-Line ("failed - {0}" -f [string]$Status.verdict) 'Red' } } } elseif ($state -eq 'processing' -and $import -and $import -ne $LastImport.Value) { $LastImport.Value = $import Write-Line ("importing - status {0}" -f $import) } return $state } if ($jobId) { $last = 'received' $lastImport = '' $final = '' $eventsUrl = "$baseUrl/status/$jobId/events" $attempts = 0 while (-not $final -and $attempts -lt 5) { $attempts++ try { $req = [System.Net.HttpWebRequest]::Create($eventsUrl) $req.Accept = 'text/event-stream' $req.ReadWriteTimeout = 480000 # the server heartbeats every 15 s; 8 min of silence is a dead stream $resp = $req.GetResponse() $reader = New-Object System.IO.StreamReader($resp.GetResponseStream()) $eventName = '' $data = '' while (-not $reader.EndOfStream) { $line = $reader.ReadLine() if ($line -like 'event:*') { $eventName = $line.Substring(6).Trim() } elseif ($line -like 'data:*') { $data = $line.Substring(5).Trim() } elseif ($line -eq '') { if ($eventName -eq 'status' -and $data) { $status = $data | ConvertFrom-Json $state = Show-Status $status ([ref]$last) ([ref]$lastImport) if ($state -eq 'complete' -or $state -eq 'failed') { $final = $state; $finalStatus = $status; break } } $eventName = '' $data = '' } } $reader.Close() $resp.Close() } catch { Write-Line ("stream dropped ({0}), reconnecting" -f $_.Exception.Message) 'Yellow' } } Write-Host '' if ($final -eq 'complete') { Write-Host ' [+] Enrolled. Reboot to start Autopilot.' -ForegroundColor Green } elseif ($final -eq 'failed') { Write-Host (" [-] Not enrolled: {0}" -f [string]$finalStatus.verdict) -ForegroundColor Red if ($finalStatus.error) { Write-Host (" {0}" -f [string]$finalStatus.error) -ForegroundColor DarkGray } } else { Write-Host ' [!] No verdict yet. The job keeps running; watch it here:' -ForegroundColor Yellow Write-Host " $statusUrl" -ForegroundColor White } } Write-Host '' # Cleanup Remove-Item -Path $csvPath -Force -ErrorAction SilentlyContinue