feat: Add new checkbox to Inject Unattend.xml to VM.

- Creates a new parameter [bool]InjectUnattend

- This will take the FFUDevelopment\Unattend\unattend_[arch].xml file and copy it to the FFUDevelopment\Apps\Unattend and rename the file to unattend.xml.

- This is useful for situations where you don't use the USB drive for deploying the FFU but still have Unattend-related customizations that you want to apply.
This commit is contained in:
rbalsleyMSFT
2025-08-14 16:20:55 -07:00
parent 85383f989a
commit 8ab6603999
6 changed files with 45 additions and 8 deletions
@@ -1,14 +1,18 @@
#The below lines will remove the unattend.xml that gets the machine into audit mode. If not removed, the OS will get stuck booting to audit mode each time.
#Also kills the sysprep process in order to automate sysprep generalize
# Convert these commands to native powershell
# del c:\windows\panther\unattend\unattend.xml /F /Q
# del c:\windows\panther\unattend.xml /F /Q
# taskkill /IM sysprep.exe
# timeout /t 10
# & c:\windows\system32\sysprep\sysprep.exe /quiet /generalize /oobe
Write-Host "Removing existing unattend.xml files and stopping sysprep process if running..."
Remove-Item -Path "C:\windows\panther\unattend\unattend.xml" -Force -ErrorAction SilentlyContinue
Remove-Item -Path "C:\windows\panther\unattend.xml" -Force -ErrorAction SilentlyContinue
Stop-Process -Name "sysprep" -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 10
& "C:\windows\system32\sysprep\sysprep.exe" /quiet /generalize /oobe
# If an Unattend.xml has been provided on the mounted Apps ISO (D:\Unattend\Unattend.xml),
# pass it to sysprep; otherwise, run without /unattend.
$unattendOnAppsIso = "D:\Unattend\Unattend.xml"
if (Test-Path -Path $unattendOnAppsIso) {
Write-Host "Using $unattendOnAppsIso from Apps ISO..."
& "C:\windows\system32\sysprep\sysprep.exe" /quiet /generalize /oobe /unattend:$unattendOnAppsIso
}
else {
& "C:\windows\system32\sysprep\sysprep.exe" /quiet /generalize /oobe
}
+26
View File
@@ -93,6 +93,9 @@ Prefix for the generated FFU file. Default is _FFU.
.PARAMETER Headers
Headers to use when downloading files. Not recommended to modify.
.PARAMETER InjectUnattend
When set to $true and InstallApps is also $true, copies unattend_[arch].xml from $FFUDevelopmentPath\unattend to $FFUDevelopmentPath\Apps\Unattend\Unattend.xml so sysprep can use it inside the VM. Default is $false.
.PARAMETER InstallApps
When set to $true, the script will create an Apps.iso file from the $FFUDevelopmentPath\Apps folder. It will also create a VM, mount the Apps.iso, install the apps, sysprep, and capture the VM. When set to $false, the FFU is created from a VHDX file, and no VM is created.
@@ -420,6 +423,7 @@ param(
[string]$ConfigFile,
[Parameter(Mandatory = $false)]
[string]$ExportConfigFile,
[bool]$InjectUnattend = $false,
[string]$orchestrationPath,
[bool]$UpdateADK = $true,
[bool]$CleanupCurrentRunDownloads = $false,
@@ -5125,6 +5129,28 @@ if ($InstallApps) {
}
#Create Apps ISO
# Inject Unattend.xml into Apps if requested and applicable
if ($InstallApps -and $InjectUnattend) {
# Determine source unattend.xml based on architecture
$archSuffix = if ($WindowsArch -ieq 'arm64') { 'arm64' } else { 'x64' }
$unattendSource = Join-Path $UnattendFolder "unattend_$archSuffix.xml"
# Ensure target folder exists under Apps
$targetFolder = Join-Path $AppsPath 'Unattend'
if (-not (Test-Path -Path $targetFolder -PathType Container)) {
New-Item -Path $targetFolder -ItemType Directory -Force | Out-Null
}
# Copy if source exists; otherwise log and skip
if (Test-Path -Path $unattendSource -PathType Leaf) {
$destination = Join-Path $targetFolder 'Unattend.xml'
Copy-Item -Path $unattendSource -Destination $destination -Force | Out-Null
WriteLog "Injected unattend file into Apps: $unattendSource -> $destination"
}
else {
WriteLog "InjectUnattend is true but source file missing: $unattendSource. Skipping unattend injection."
}
}
Set-Progress -Percentage 10 -Message "Creating Apps ISO..."
WriteLog "Creating $AppsISO file"
New-AppsISO
+3 -1
View File
@@ -411,7 +411,8 @@
<Button x:Name="btnEditApplication" Content="Edit Application" IsEnabled="False" Margin="0,0,10,0" Padding="10,5" ToolTip="Edit the selected application's details"/>
<Button x:Name="btnCopyBYOApps" Content="Copy Apps" IsEnabled="False" Margin="0,0,10,0" Padding="10,5" ToolTip="Copy applications with a specified source path to the AppsPath\Win32 folder"/>
<Button x:Name="btnRemoveSelectedBYOApps" Content="Remove Selected" IsEnabled="False" Margin="0,0,10,0" Padding="10,5" ToolTip="Remove selected applications from the list"/>
<Button x:Name="btnClearBYOApplications" Content="Clear List" Padding="10,5" ToolTip="Clear all applications from the list"/> </StackPanel>
<Button x:Name="btnClearBYOApplications" Content="Clear List" Padding="10,5" ToolTip="Clear all applications from the list"/>
</StackPanel>
</StackPanel>
<!-- AppsScriptVariables Section -->
@@ -740,6 +741,7 @@
<CheckBox x:Name="chkAllowVHDXCaching" Content="Allow VHDX Caching" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will cache the VHDX file to cache folder and create a config json file to track Windows build information."/>
<CheckBox x:Name="chkCreateCaptureMedia" Content="Create Capture Media" Margin="5" VerticalAlignment="Center" Tag="When set to $true, this will create WinPE capture media for use when InstallApps is set to $true."/>
<CheckBox x:Name="chkCreateDeploymentMedia" Content="Create Deployment Media" Margin="5" VerticalAlignment="Center" Tag="When set to $true, this will create WinPE deployment media for use when deploying to a physical device."/>
<CheckBox x:Name="chkInjectUnattend" Content="Inject Unattend.xml" Margin="5" VerticalAlignment="Center" Tag="When set to $true and Install Apps is enabled, copies unattend_[arch].xml from $FFUDevelopmentPath\unattend into Apps\Unattend\Unattend.xml to be used by sysprep."/>
<CheckBox x:Name="chkVerbose" Content="Verbose" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will enable write-verbose output to the console for the build script."/>
</WrapPanel>
@@ -38,6 +38,7 @@ function Get-UIConfig {
CopyUnattend = $State.Controls.chkCopyUnattend.IsChecked
CreateCaptureMedia = $State.Controls.chkCreateCaptureMedia.IsChecked
CreateDeploymentMedia = $State.Controls.chkCreateDeploymentMedia.IsChecked
InjectUnattend = $State.Controls.chkInjectUnattend.IsChecked
CustomFFUNameTemplate = $State.Controls.txtCustomFFUNameTemplate.Text
Disksize = [int64]$State.Controls.txtDiskSize.Text * 1GB
DownloadDrivers = $State.Controls.chkDownloadDrivers.IsChecked
@@ -340,6 +341,7 @@ function Update-UIFromConfig {
Set-UIValue -ControlName 'chkPromptExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'PromptExternalHardDiskMedia' -State $State
Set-UIValue -ControlName 'chkCreateCaptureMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateCaptureMedia' -State $State
Set-UIValue -ControlName 'chkCreateDeploymentMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateDeploymentMedia' -State $State
Set-UIValue -ControlName 'chkInjectUnattend' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'InjectUnattend' -State $State
Set-UIValue -ControlName 'chkVerbose' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'Verbose' -State $State
# USB Drive Modification group (Build Tab)
@@ -121,6 +121,7 @@ function Initialize-UIControls {
$State.Controls.chkAllowVHDXCaching = $window.FindName('chkAllowVHDXCaching')
$State.Controls.chkCreateCaptureMedia = $window.FindName('chkCreateCaptureMedia')
$State.Controls.chkCreateDeploymentMedia = $window.FindName('chkCreateDeploymentMedia')
$State.Controls.chkInjectUnattend = $window.FindName('chkInjectUnattend')
$State.Controls.chkVerbose = $window.FindName('chkVerbose')
$State.Controls.chkCopyAutopilot = $window.FindName('chkCopyAutopilot')
$State.Controls.chkCopyUnattend = $window.FindName('chkCopyUnattend')
@@ -234,6 +235,7 @@ function Initialize-UIDefaults {
$State.Controls.chkUpdateADK.IsChecked = $State.Defaults.generalDefaults.UpdateADK
$State.Controls.chkOptimize.IsChecked = $State.Defaults.generalDefaults.Optimize
$State.Controls.chkAllowVHDXCaching.IsChecked = $State.Defaults.generalDefaults.AllowVHDXCaching
$State.Controls.chkInjectUnattend.IsChecked = $State.Defaults.generalDefaults.InjectUnattend
$State.Controls.chkCreateCaptureMedia.IsChecked = $State.Defaults.generalDefaults.CreateCaptureMedia
$State.Controls.chkCreateDeploymentMedia.IsChecked = $State.Defaults.generalDefaults.CreateDeploymentMedia
$State.Controls.chkAllowExternalHardDiskMedia.IsChecked = $State.Defaults.generalDefaults.AllowExternalHardDiskMedia
@@ -131,6 +131,7 @@ function Get-GeneralDefaults {
CopyAutopilot = $false
CopyUnattend = $false
CopyPPKG = $false
InjectUnattend = $false
CleanupAppsISO = $true
CleanupCaptureISO = $true
CleanupDeployISO = $true