fix(collector): carry preconfig via chunked FirstLogonCommands (specialize Path was too long -> answer file invalid)
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Failing after 5m44s
Some checks failed
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Failing after 5m44s
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
#Requires -Version 5.1
|
||||
# Pure generator: collected values -> Windows Setup answer-file XML string.
|
||||
# No WinForms dependency (unit-testable). Mirrors the legacy autounattend.xml but with
|
||||
# ONE real local-admin account (no sm-bootstrap) and an embedded preconfig.json that a
|
||||
# specialize-pass command writes to C:\ProgramData\SilverMetal\preconfig.json.
|
||||
# ONE real local-admin account (no sm-bootstrap) and an embedded preconfig.json that the
|
||||
# oobeSystem FirstLogonCommands write (in short base64 chunks) to
|
||||
# C:\ProgramData\SilverMetal\preconfig.json. The base64 is carried in chunked echo
|
||||
# commands rather than a single specialize RunSynchronousCommand/Path, because that Path
|
||||
# is capped at ~259 chars and a full base64 blob overflows it -> "answer file is invalid".
|
||||
|
||||
function New-SmAnswerFile {
|
||||
param(
|
||||
@@ -29,7 +32,45 @@ function New-SmAnswerFile {
|
||||
function EscContent([string]$s) { $s.Replace('&','&').Replace('<','<').Replace('>','>') }
|
||||
$dn = Esc $DisplayName; $un = Esc $Username; $pw = Esc $Password; $cn = Esc $ComputerName
|
||||
|
||||
$writePre = "powershell -NoProfile -ExecutionPolicy Bypass -Command "" New-Item -ItemType Directory -Force 'C:\ProgramData\SilverMetal' | Out-Null; [IO.File]::WriteAllBytes('C:\ProgramData\SilverMetal\preconfig.json', [Convert]::FromBase64String('$preB64')) """
|
||||
# Build the oobeSystem FirstLogonCommands. The preconfig base64 is split into short
|
||||
# (<=150 char) chunks, each appended to a temp file by its own `echo` command, then
|
||||
# the file is whitespace-stripped + base64-decoded into preconfig.json. This keeps
|
||||
# every single command line well under the unattend length limits.
|
||||
$preDir = 'C:\ProgramData\SilverMetal'
|
||||
$preB64File = "$preDir\pre.b64"
|
||||
$preFile = "$preDir\preconfig.json"
|
||||
|
||||
# Split base64 into chunks of at most 150 chars (base64 alphabet has no XML/cmd
|
||||
# metachars, so chunks are safe in `echo` and in XML once `>` is escaped).
|
||||
$chunkSize = 150
|
||||
$chunks = for ($i = 0; $i -lt $preB64.Length; $i += $chunkSize) {
|
||||
$preB64.Substring($i, [Math]::Min($chunkSize, $preB64.Length - $i))
|
||||
}
|
||||
|
||||
$cmds = New-Object System.Collections.Generic.List[string]
|
||||
# 1. Create the target dir.
|
||||
$cmds.Add("cmd /c md ""$preDir"" 2>nul")
|
||||
# 2..N. Append each base64 chunk to the temp file.
|
||||
foreach ($c in $chunks) {
|
||||
$cmds.Add("cmd /c >>""$preB64File"" echo $c")
|
||||
}
|
||||
# N+1. Strip whitespace (chunks are newline-separated in the file) and decode.
|
||||
$cmds.Add("powershell -nop -c ""[IO.File]::WriteAllBytes('$preFile',[Convert]::FromBase64String(((gc '$preB64File' -raw) -replace '\s','')))""")
|
||||
# N+2. Clean up the temp file.
|
||||
$cmds.Add("cmd /c del ""$preB64File""")
|
||||
# N+3 (LAST). Launch the SilverMetal toolbox (run-once).
|
||||
$cmds.Add("cmd /c powershell -NoProfile -ExecutionPolicy Bypass -Command ""Start-Process -FilePath 'C:\Program Files\SilverOS\Welcome\SilverOS.Welcome.App.exe' -Verb RunAs""")
|
||||
|
||||
$firstLogonSb = New-Object System.Text.StringBuilder
|
||||
$order = 0
|
||||
foreach ($cmd in $cmds) {
|
||||
$order++
|
||||
[void]$firstLogonSb.AppendLine(" <SynchronousCommand wcm:action=""add"" xmlns:wcm=""http://schemas.microsoft.com/WMIConfig/2002/State"">")
|
||||
[void]$firstLogonSb.AppendLine(" <Order>$order</Order>")
|
||||
[void]$firstLogonSb.AppendLine(" <CommandLine>$(EscContent $cmd)</CommandLine>")
|
||||
[void]$firstLogonSb.AppendLine(" </SynchronousCommand>")
|
||||
}
|
||||
$firstLogonCommands = $firstLogonSb.ToString().TrimEnd()
|
||||
|
||||
@"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
@@ -64,17 +105,6 @@ function New-SmAnswerFile {
|
||||
<UserData><AcceptEula>true</AcceptEula></UserData>
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="specialize">
|
||||
<component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<RunSynchronous>
|
||||
<RunSynchronousCommand wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||
<Order>1</Order>
|
||||
<Path>$(EscContent $writePre)</Path>
|
||||
<Description>Write SilverMetal preconfig</Description>
|
||||
</RunSynchronousCommand>
|
||||
</RunSynchronous>
|
||||
</component>
|
||||
</settings>
|
||||
<settings pass="oobeSystem">
|
||||
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
|
||||
<InputLocale>$InputLocale</InputLocale><SystemLocale>$SystemLocale</SystemLocale>
|
||||
@@ -91,11 +121,7 @@ function New-SmAnswerFile {
|
||||
<AutoLogon><Enabled>true</Enabled><LogonCount>1</LogonCount><Username>$un</Username><Password><Value>$pw</Value><PlainText>true</PlainText></Password></AutoLogon>
|
||||
<ComputerName>$cn</ComputerName>
|
||||
<FirstLogonCommands>
|
||||
<SynchronousCommand wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
|
||||
<Order>1</Order>
|
||||
<CommandLine>cmd /c powershell -NoProfile -ExecutionPolicy Bypass -Command "Start-Process -FilePath 'C:\Program Files\SilverOS\Welcome\SilverOS.Welcome.App.exe' -Verb RunAs"</CommandLine>
|
||||
<Description>Launch SilverMetal toolbox (run-once)</Description>
|
||||
</SynchronousCommand>
|
||||
$firstLogonCommands
|
||||
</FirstLogonCommands>
|
||||
<RegisteredOwner>SilverMetal</RegisteredOwner><RegisteredOrganization>SilverLABS</RegisteredOrganization>
|
||||
</component>
|
||||
|
||||
@@ -54,16 +54,19 @@ Describe 'New-SmAnswerFile' {
|
||||
}
|
||||
It 'sets the computer name' { $script:xml | Should -Match '<ComputerName>SILVER-01</ComputerName>' }
|
||||
It 'keeps WillWipeDisk for disk 0' { $script:xml | Should -Match '<WillWipeDisk>true</WillWipeDisk>' }
|
||||
It 'embeds a base64 preconfig write in specialize' {
|
||||
$script:xml | Should -Match 'preconfig\.json'
|
||||
$script:xml | Should -Match 'FromBase64String'
|
||||
}
|
||||
It 'embedded preconfig round-trips with the flavour and pin' {
|
||||
$m = [regex]::Match($script:xml, "FromBase64String\('([^']+)'\)")
|
||||
$m.Success | Should -BeTrue
|
||||
$json = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($m.Groups[1].Value)) | ConvertFrom-Json
|
||||
It 'preconfig round-trips via chunked FirstLogonCommands' {
|
||||
# Gather the echo'd base64 chunks in Order, concatenate, strip whitespace, decode.
|
||||
$chunks = [regex]::Matches($script:xml, 'echo ([A-Za-z0-9+/=]+)') | ForEach-Object { $_.Groups[1].Value }
|
||||
$b64 = ($chunks -join '')
|
||||
$json = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($b64)) | ConvertFrom-Json
|
||||
$json.flavour | Should -Be 'developer'
|
||||
$json.bitlocker.pin | Should -Be '246810'
|
||||
}
|
||||
It 'has no specialize pass anymore' { $script:xml | Should -Not -Match 'pass="specialize"' }
|
||||
It 'creates the preconfig dir + decodes it at first logon' {
|
||||
$script:xml | Should -Match 'ProgramData\\SilverMetal'
|
||||
$script:xml | Should -Match 'FromBase64String'
|
||||
$script:xml | Should -Match 'preconfig\.json'
|
||||
}
|
||||
It 'launches the toolbox in FirstLogonCommands' { $script:xml | Should -Match 'SilverOS\.Welcome\.App\.exe' }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user