Introduces an option to retain downloaded ESD files

Adds a new configuration parameter and UI toggle to control whether downloaded Windows ESD files are removed after application. Updates the download process to check for and reuse existing ESD files that match the latest metadata filename, saving bandwidth and time on subsequent executions. Integrates the conditional deletion logic into the shared cleanup module.
This commit is contained in:
rbalsleyMSFT
2026-03-03 16:47:31 -08:00
parent f09c98906a
commit 7f10811c05
6 changed files with 64 additions and 18 deletions
+39 -15
View File
@@ -174,6 +174,9 @@ When set to $true, will remove the FFU file from the $FFUDevelopmentPath\FFU fol
.PARAMETER RemoveUpdates .PARAMETER RemoveUpdates
When set to $true, will remove the downloaded CU, MSRT, Defender, Edge, OneDrive, and .NET files downloaded. Default is $true. When set to $true, will remove the downloaded CU, MSRT, Defender, Edge, OneDrive, and .NET files downloaded. Default is $true.
.PARAMETER RemoveDownloadedESD
When set to $true, will remove downloaded Windows ESD files after they have been applied. Default is $true.
.PARAMETER ShareName .PARAMETER ShareName
Name of the shared folder for FFU capture. The default is FFUCaptureShare. This share will be created with rights for the user account. When finished, the share will be removed. Name of the shared folder for FFU capture. The default is FFUCaptureShare. This share will be created with rights for the user account. When finished, the share will be removed.
@@ -426,6 +429,7 @@ param(
[bool]$CleanupDeployISO = $true, [bool]$CleanupDeployISO = $true,
[bool]$CleanupAppsISO = $true, [bool]$CleanupAppsISO = $true,
[bool]$RemoveUpdates = $true, [bool]$RemoveUpdates = $true,
[bool]$RemoveDownloadedESD = $true,
[bool]$RemoveApps = $true, [bool]$RemoveApps = $true,
[string]$DriversFolder, [string]$DriversFolder,
[string]$PEDriversFolder, [string]$PEDriversFolder,
@@ -2139,7 +2143,20 @@ function Get-WindowsESD {
} }
$esdFilePath = $esdMetadata.LocalPath $esdFilePath = $esdMetadata.LocalPath
if (-not (Test-Path $esdFilePath)) { $latestEsdFileName = $esdMetadata.FileName
$existingEsdMatch = $null
# Reuse an existing ESD only when it matches the latest metadata filename
if (-not [string]::IsNullOrWhiteSpace($latestEsdFileName)) {
$existingEsdMatch = Get-ChildItem -Path $PSScriptRoot -Filter *.esd -File -ErrorAction SilentlyContinue | Where-Object { $_.Name -ieq $latestEsdFileName } | Select-Object -First 1
}
if ($null -ne $existingEsdMatch) {
$esdFilePath = $existingEsdMatch.FullName
WriteLog "Found existing ESD matching metadata filename '$latestEsdFileName' at $esdFilePath. Skipping download."
}
else {
WriteLog "No existing ESD matching metadata filename '$latestEsdFileName' was found. Downloading latest ESD to $esdFilePath."
WriteLog "Downloading $($esdMetadata.FileUrl) to $esdFilePath" WriteLog "Downloading $($esdMetadata.FileUrl) to $esdFilePath"
$OriginalVerbosePreference = $VerbosePreference $OriginalVerbosePreference = $VerbosePreference
$VerbosePreference = 'SilentlyContinue' $VerbosePreference = 'SilentlyContinue'
@@ -2149,9 +2166,6 @@ function Get-WindowsESD {
$VerbosePreference = $OriginalVerbosePreference $VerbosePreference = $OriginalVerbosePreference
WriteLog "ESD download succeeded" WriteLog "ESD download succeeded"
} }
else {
WriteLog "Found existing ESD at $esdFilePath, skipping download"
}
return $esdFilePath return $esdFilePath
} }
@@ -4405,7 +4419,7 @@ function Get-FFUEnvironment {
} }
#Run shared cleanup to avoid duplicated logic #Run shared cleanup to avoid duplicated logic
Invoke-FFUPostBuildCleanup -RootPath $FFUDevelopmentPath -AppsPath $AppsPath -DriversPath $DriversFolder -FFUCapturePath $FFUCaptureLocation -CaptureISOPath $CaptureISO -DeployISOPath $DeployISO -AppsISOPath $AppsISO -RemoveCaptureISO:$CleanupCaptureISO -RemoveDeployISO:$CleanupDeployISO -RemoveAppsISO:$CleanupAppsISO -RemoveDrivers:$CleanupDrivers -RemoveFFU:$RemoveFFU -RemoveApps:$RemoveApps -RemoveUpdates:$RemoveUpdates -KBPath:$KBPath Invoke-FFUPostBuildCleanup -RootPath $FFUDevelopmentPath -AppsPath $AppsPath -DriversPath $DriversFolder -FFUCapturePath $FFUCaptureLocation -CaptureISOPath $CaptureISO -DeployISOPath $DeployISO -AppsISOPath $AppsISO -RemoveCaptureISO:$CleanupCaptureISO -RemoveDeployISO:$CleanupDeployISO -RemoveAppsISO:$CleanupAppsISO -RemoveDrivers:$CleanupDrivers -RemoveFFU:$RemoveFFU -RemoveApps:$RemoveApps -RemoveUpdates:$RemoveUpdates -RemoveDownloadedESD:$RemoveDownloadedESD -KBPath:$KBPath
# Remove existing Apps.iso # Remove existing Apps.iso
if (Test-Path -Path $AppsISO) { if (Test-Path -Path $AppsISO) {
@@ -6782,11 +6796,16 @@ try {
Dismount-DiskImage -ImagePath $ISOPath | Out-null Dismount-DiskImage -ImagePath $ISOPath | Out-null
WriteLog 'Done' WriteLog 'Done'
} }
#If $wimPath is an esd file, remove it # If $wimPath is an ESD file, remove it only when configured
If ($wimPath -match '.esd') { If ($wimPath -match '\.esd$') {
WriteLog "Deleting $wimPath file" if ($RemoveDownloadedESD -and (Test-Path -Path $wimPath)) {
Remove-Item -Path $wimPath -Force WriteLog "Deleting $wimPath file"
WriteLog "$wimPath deleted" Remove-Item -Path $wimPath -Force
WriteLog "$wimPath deleted"
}
else {
WriteLog "Keeping downloaded ESD file $wimPath"
}
} }
} }
@@ -6868,10 +6887,15 @@ catch {
WriteLog 'Done' WriteLog 'Done'
} }
else { else {
#Remove ESD file # Remove ESD file only when configured
WriteLog "Deleting ESD file" if ($RemoveDownloadedESD -and -not [string]::IsNullOrWhiteSpace($wimPath) -and ($wimPath -match '\.esd$') -and (Test-Path -Path $wimPath)) {
Remove-Item -Path $wimPath -Force WriteLog "Deleting ESD file $wimPath"
WriteLog "ESD File deleted" Remove-Item -Path $wimPath -Force
WriteLog "ESD File deleted"
}
else {
WriteLog "Keeping downloaded ESD file $wimPath"
}
} }
throw $_ throw $_
@@ -7238,7 +7262,7 @@ If ($BuildUSBDrive) {
Set-Progress -Percentage 99 -Message "Finalizing and cleaning up..." Set-Progress -Percentage 99 -Message "Finalizing and cleaning up..."
# Delegated post-build cleanup to common module # Delegated post-build cleanup to common module
Invoke-FFUPostBuildCleanup -RootPath $FFUDevelopmentPath -AppsPath $AppsPath -DriversPath $DriversFolder -FFUCapturePath $FFUCaptureLocation -CaptureISOPath $CaptureISO -DeployISOPath $DeployISO -AppsISOPath $AppsISO -RemoveCaptureISO:$CleanupCaptureISO -RemoveDeployISO:$CleanupDeployISO -RemoveAppsISO:$CleanupAppsISO -RemoveDrivers:$CleanupDrivers -RemoveFFU:$RemoveFFU -RemoveApps:$RemoveApps -RemoveUpdates:$RemoveUpdates -KBPath:$KBPath Invoke-FFUPostBuildCleanup -RootPath $FFUDevelopmentPath -AppsPath $AppsPath -DriversPath $DriversFolder -FFUCapturePath $FFUCaptureLocation -CaptureISOPath $CaptureISO -DeployISOPath $DeployISO -AppsISOPath $AppsISO -RemoveCaptureISO:$CleanupCaptureISO -RemoveDeployISO:$CleanupDeployISO -RemoveAppsISO:$CleanupAppsISO -RemoveDrivers:$CleanupDrivers -RemoveFFU:$RemoveFFU -RemoveApps:$RemoveApps -RemoveUpdates:$RemoveUpdates -RemoveDownloadedESD:$RemoveDownloadedESD -KBPath:$KBPath
# Remove WinGetWin32Apps.json so it is always rebuilt next run # Remove WinGetWin32Apps.json so it is always rebuilt next run
+1
View File
@@ -837,6 +837,7 @@
<CheckBox x:Name="chkRemoveFFU" Content="Remove FFU" Margin="5" VerticalAlignment="Center" Tag="Remove FFU after copying to USB drive."/> <CheckBox x:Name="chkRemoveFFU" Content="Remove FFU" Margin="5" VerticalAlignment="Center" Tag="Remove FFU after copying to USB drive."/>
<CheckBox x:Name="chkRemoveApps" Content="Remove Apps Folder Content" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will remove the application content in the Apps folder after the FFU has been captured."/> <CheckBox x:Name="chkRemoveApps" Content="Remove Apps Folder Content" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will remove the application content in the Apps folder after the FFU has been captured."/>
<CheckBox x:Name="chkRemoveUpdates" Content="Remove Downloaded Update Files" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will remove downloaded CU, .NET, MSRT, Defender, Edge, and OneDrive files after being applied/included."/> <CheckBox x:Name="chkRemoveUpdates" Content="Remove Downloaded Update Files" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will remove downloaded CU, .NET, MSRT, Defender, Edge, and OneDrive files after being applied/included."/>
<CheckBox x:Name="chkRemoveDownloadedESD" Content="Remove Downloaded ESD file(s)" Margin="5" VerticalAlignment="Center" Tag="When set to $true, will remove downloaded Windows ESD file(s) after they are used."/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
@@ -16,12 +16,13 @@ function Invoke-FFUPostBuildCleanup {
[bool]$RemoveDrivers = $false, [bool]$RemoveDrivers = $false,
[bool]$RemoveFFU = $false, [bool]$RemoveFFU = $false,
[bool]$RemoveApps = $false, [bool]$RemoveApps = $false,
[bool]$RemoveUpdates = $false [bool]$RemoveUpdates = $false,
[bool]$RemoveDownloadedESD = $false
) )
$originalProgressPreference = $ProgressPreference $originalProgressPreference = $ProgressPreference
$ProgressPreference = 'SilentlyContinue' $ProgressPreference = 'SilentlyContinue'
try { try {
WriteLog "CommonCleanup: Starting cleanup (CaptureISO=$RemoveCaptureISO DeployISO=$RemoveDeployISO AppsISO=$RemoveAppsISO Drivers=$RemoveDrivers FFU=$RemoveFFU Apps=$RemoveApps Updates=$RemoveUpdates KBPath=$KBPath)." WriteLog "CommonCleanup: Starting cleanup (CaptureISO=$RemoveCaptureISO DeployISO=$RemoveDeployISO AppsISO=$RemoveAppsISO Drivers=$RemoveDrivers FFU=$RemoveFFU Apps=$RemoveApps Updates=$RemoveUpdates RemoveDownloadedESD=$RemoveDownloadedESD KBPath=$KBPath)."
# Primary ISO paths (new naming/location) # Primary ISO paths (new naming/location)
if ($RemoveCaptureISO -and -not [string]::IsNullOrWhiteSpace($CaptureISOPath) -and (Test-Path -LiteralPath $CaptureISOPath)) { if ($RemoveCaptureISO -and -not [string]::IsNullOrWhiteSpace($CaptureISOPath) -and (Test-Path -LiteralPath $CaptureISOPath)) {
@@ -123,6 +124,20 @@ function Invoke-FFUPostBuildCleanup {
} }
} }
# Remove downloaded ESD files from the root path when requested
if ($RemoveDownloadedESD -and -not [string]::IsNullOrWhiteSpace($RootPath) -and (Test-Path -LiteralPath $RootPath -PathType Container)) {
WriteLog "CommonCleanup: Removing downloaded ESD files in $RootPath"
Get-ChildItem -LiteralPath $RootPath -Filter *.esd -File -ErrorAction SilentlyContinue | ForEach-Object {
try {
WriteLog "CommonCleanup: Removing ESD $($_.FullName)"
Remove-Item -LiteralPath $_.FullName -Force -ErrorAction Stop
}
catch {
WriteLog "CommonCleanup: Failed removing ESD $($_.FullName) : $($_.Exception.Message)"
}
}
}
WriteLog "CommonCleanup: Completed." WriteLog "CommonCleanup: Completed."
} }
catch { catch {
@@ -82,6 +82,7 @@ function Get-UIConfig {
RemoveApps = $State.Controls.chkRemoveApps.IsChecked RemoveApps = $State.Controls.chkRemoveApps.IsChecked
RemoveFFU = $State.Controls.chkRemoveFFU.IsChecked RemoveFFU = $State.Controls.chkRemoveFFU.IsChecked
RemoveUpdates = $State.Controls.chkRemoveUpdates.IsChecked RemoveUpdates = $State.Controls.chkRemoveUpdates.IsChecked
RemoveDownloadedESD = $State.Controls.chkRemoveDownloadedESD.IsChecked
ShareName = $State.Controls.txtShareName.Text ShareName = $State.Controls.txtShareName.Text
UpdateADK = $State.Controls.chkUpdateADK.IsChecked UpdateADK = $State.Controls.chkUpdateADK.IsChecked
UpdateEdge = $State.Controls.chkUpdateEdge.IsChecked UpdateEdge = $State.Controls.chkUpdateEdge.IsChecked
@@ -461,6 +462,7 @@ function Update-UIFromConfig {
Set-UIValue -ControlName 'chkRemoveFFU' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveFFU' -State $State Set-UIValue -ControlName 'chkRemoveFFU' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveFFU' -State $State
Set-UIValue -ControlName 'chkRemoveApps' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveApps' -State $State Set-UIValue -ControlName 'chkRemoveApps' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveApps' -State $State
Set-UIValue -ControlName 'chkRemoveUpdates' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveUpdates' -State $State Set-UIValue -ControlName 'chkRemoveUpdates' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveUpdates' -State $State
Set-UIValue -ControlName 'chkRemoveDownloadedESD' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveDownloadedESD' -State $State
# Hyper-V Settings # Hyper-V Settings
Select-VMSwitchFromConfig -State $State -ConfigContent $ConfigContent Select-VMSwitchFromConfig -State $State -ConfigContent $ConfigContent
@@ -908,7 +910,8 @@ function Invoke-RestoreDefaults {
-RemoveDrivers:$true ` -RemoveDrivers:$true `
-RemoveFFU:$true ` -RemoveFFU:$true `
-RemoveApps:$true ` -RemoveApps:$true `
-RemoveUpdates:$true -RemoveUpdates:$true `
-RemoveDownloadedESD:$true
# Clear UI lists / state # Clear UI lists / state
if ($null -ne $State.Data.allDriverModels) { $State.Data.allDriverModels.Clear() } if ($null -ne $State.Data.allDriverModels) { $State.Data.allDriverModels.Clear() }
@@ -135,6 +135,7 @@ function Initialize-UIControls {
$State.Controls.chkCleanupDeployISO = $window.FindName('chkCleanupDeployISO') $State.Controls.chkCleanupDeployISO = $window.FindName('chkCleanupDeployISO')
$State.Controls.chkCleanupDrivers = $window.FindName('chkCleanupDrivers') $State.Controls.chkCleanupDrivers = $window.FindName('chkCleanupDrivers')
$State.Controls.chkRemoveFFU = $window.FindName('chkRemoveFFU') $State.Controls.chkRemoveFFU = $window.FindName('chkRemoveFFU')
$State.Controls.chkRemoveDownloadedESD = $window.FindName('chkRemoveDownloadedESD')
$State.Controls.txtDiskSize = $window.FindName('txtDiskSize') $State.Controls.txtDiskSize = $window.FindName('txtDiskSize')
$State.Controls.txtMemory = $window.FindName('txtMemory') $State.Controls.txtMemory = $window.FindName('txtMemory')
$State.Controls.txtProcessors = $window.FindName('txtProcessors') $State.Controls.txtProcessors = $window.FindName('txtProcessors')
@@ -258,6 +259,7 @@ function Initialize-UIDefaults {
$State.Controls.chkRemoveFFU.IsChecked = $State.Defaults.generalDefaults.RemoveFFU $State.Controls.chkRemoveFFU.IsChecked = $State.Defaults.generalDefaults.RemoveFFU
$State.Controls.chkRemoveApps.IsChecked = $State.Defaults.generalDefaults.RemoveApps $State.Controls.chkRemoveApps.IsChecked = $State.Defaults.generalDefaults.RemoveApps
$State.Controls.chkRemoveUpdates.IsChecked = $State.Defaults.generalDefaults.RemoveUpdates $State.Controls.chkRemoveUpdates.IsChecked = $State.Defaults.generalDefaults.RemoveUpdates
$State.Controls.chkRemoveDownloadedESD.IsChecked = $State.Defaults.generalDefaults.RemoveDownloadedESD
$State.Controls.chkVerbose.IsChecked = $State.Defaults.generalDefaults.Verbose $State.Controls.chkVerbose.IsChecked = $State.Defaults.generalDefaults.Verbose
$State.Controls.usbSection.Visibility = if ($State.Controls.chkBuildUSBDriveEnable.IsChecked) { 'Visible' } else { 'Collapsed' } $State.Controls.usbSection.Visibility = if ($State.Controls.chkBuildUSBDriveEnable.IsChecked) { 'Visible' } else { 'Collapsed' }
$State.Controls.usbSelectionPanel.Visibility = if ($State.Controls.chkSelectSpecificUSBDrives.IsChecked) { 'Visible' } else { 'Collapsed' } $State.Controls.usbSelectionPanel.Visibility = if ($State.Controls.chkSelectSpecificUSBDrives.IsChecked) { 'Visible' } else { 'Collapsed' }
@@ -141,6 +141,7 @@ function Get-GeneralDefaults {
RemoveFFU = $false RemoveFFU = $false
RemoveApps = $false RemoveApps = $false
RemoveUpdates = $false RemoveUpdates = $false
RemoveDownloadedESD = $true
# Hyper-V Settings Defaults # Hyper-V Settings Defaults
VMHostIPAddress = "" VMHostIPAddress = ""
DiskSizeGB = 50 DiskSizeGB = 50