mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
7cc7919da4
Adds a "Threads" setting to the UI, allowing users to control the throttle limit for parallel tasks like driver and application processing. This introduces a new textbox in the build options and updates the parallel processing function to use this configurable value instead of a hardcoded one. Input validation is also added to ensure the threads value is a valid integer and is at least 1. The new setting is integrated into the configuration save/load functionality.
346 lines
15 KiB
PowerShell
346 lines
15 KiB
PowerShell
# FFU UI Core Logic Module
|
|
# Contains non-UI specific helper functions, data retrieval, and core processing logic.
|
|
|
|
# --------------------------------------------------------------------------
|
|
# SECTION: Module Variables (Static Data & State)
|
|
# --------------------------------------------------------------------------
|
|
|
|
#Microsoft sites will intermittently fail on downloads. These headers and user agent are to help with that.
|
|
$script:Headers = @{
|
|
"Accept" = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
|
|
"Accept-Encoding" = "gzip, deflate, br, zstd"
|
|
"Accept-Language" = "en-US,en;q=0.9"
|
|
"Priority" = "u=0, i"
|
|
"Sec-Ch-Ua" = "`"Microsoft Edge`";v=`"125`", `"Chromium`";v=`"125`", `"Not.A/Brand`";v=`"24`""
|
|
"Sec-Ch-Ua-Mobile" = "?0"
|
|
"Sec-Ch-Ua-Platform" = "`"Windows`""
|
|
"Sec-Fetch-Dest" = "document"
|
|
"Sec-Fetch-Mode" = "navigate"
|
|
"Sec-Fetch-Site" = "none"
|
|
"Sec-Fetch-User" = "?1"
|
|
"Upgrade-Insecure-Requests" = "1"
|
|
}
|
|
$script:UserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0'
|
|
|
|
function Get-CoreStaticVariables {
|
|
[CmdletBinding()]
|
|
param()
|
|
|
|
return @{
|
|
Headers = $script:Headers
|
|
UserAgent = $script:UserAgent
|
|
}
|
|
}
|
|
|
|
# Function to get VM Switch names and associated IP addresses
|
|
function Get-VMSwitchData {
|
|
[CmdletBinding()]
|
|
param()
|
|
|
|
$switchMap = @{}
|
|
$switchNames = @()
|
|
|
|
try {
|
|
$allSwitches = Get-VMSwitch -ErrorAction SilentlyContinue
|
|
if ($null -ne $allSwitches) {
|
|
foreach ($sw in $allSwitches) {
|
|
$adapterNamePattern = "*($($sw.Name))*"
|
|
|
|
# Attempt to find the network adapter associated with the vSwitch
|
|
# Select-Object -First 1 ensures we only get one adapter if multiple match (unlikely but possible)
|
|
$netAdapter = Get-NetAdapter -Name $adapterNamePattern -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
|
|
if ($netAdapter) {
|
|
# Get IPv4 addresses for the found adapter's interface index
|
|
$netIPs = Get-NetIPAddress -InterfaceIndex $netAdapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue
|
|
|
|
# Filter out Automatic Private IP Addressing (APIPA) addresses (169.254.x.x)
|
|
# and select the first valid IP found.
|
|
$validIP = $netIPs | Where-Object { $_.IPAddress -notlike '169.254.*' -and $_.IPAddress } | Select-Object -First 1
|
|
|
|
if ($validIP) {
|
|
# Store the valid IP address in the map with the switch name as the key
|
|
$switchMap[$sw.Name] = $validIP.IPAddress
|
|
# Log the found IP address for debugging/information using WriteLog
|
|
WriteLog "Found IP $($validIP.IPAddress) for vSwitch '$($sw.Name)' (Adapter: $($netAdapter.Name)). Adding to list."
|
|
# Add the switch name to the list ONLY if a valid IP was found
|
|
$switchNames += $sw.Name
|
|
}
|
|
else {
|
|
WriteLog "No valid non-APIPA IPv4 address found for vSwitch '$($sw.Name)' (Adapter: $($netAdapter.Name)). Skipping from list."
|
|
}
|
|
}
|
|
else {
|
|
WriteLog "Could not find a network adapter matching pattern '$adapterNamePattern' for vSwitch '$($sw.Name)'. Skipping from list."
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
WriteLog "No Hyper-V virtual switches found on this system."
|
|
}
|
|
}
|
|
catch {
|
|
WriteLog "Error occurred while getting VM Switch data: $($_.Exception.Message)"
|
|
}
|
|
return [PSCustomObject]@{
|
|
SwitchNames = $switchNames
|
|
SwitchMap = $switchMap
|
|
}
|
|
}
|
|
|
|
# Function to return general default settings for various UI elements
|
|
function Get-GeneralDefaults {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string]$FFUDevelopmentPath
|
|
)
|
|
|
|
# Derive paths based on the main development path
|
|
$appsPath = Join-Path -Path $FFUDevelopmentPath -ChildPath "Apps"
|
|
$driversPath = Join-Path -Path $FFUDevelopmentPath -ChildPath "Drivers"
|
|
$peDriversPath = Join-Path -Path $FFUDevelopmentPath -ChildPath "PEDrivers"
|
|
$vmLocationPath = Join-Path -Path $FFUDevelopmentPath -ChildPath "VM"
|
|
$ffuCapturePath = Join-Path -Path $FFUDevelopmentPath -ChildPath "FFU"
|
|
$officePath = Join-Path -Path $appsPath -ChildPath "Office"
|
|
$appListJsonPath = Join-Path -Path $appsPath -ChildPath "AppList.json"
|
|
$driversJsonPath = Join-Path -Path $driversPath -ChildPath "Drivers.json"
|
|
|
|
return [PSCustomObject]@{
|
|
# Build Tab Defaults
|
|
CustomFFUNameTemplate = "{WindowsRelease}_{WindowsVersion}_{SKU}_{yyyy}-{MM}-{dd}_{HH}{mm}"
|
|
FFUCaptureLocation = $ffuCapturePath
|
|
ShareName = "FFUCaptureShare"
|
|
Username = "ffu_user"
|
|
Threads = 5
|
|
BuildUSBDriveEnable = $false
|
|
CompactOS = $true
|
|
Optimize = $true
|
|
AllowVHDXCaching = $false
|
|
CreateCaptureMedia = $true
|
|
CreateDeploymentMedia = $true
|
|
Verbose = $false
|
|
AllowExternalHardDiskMedia = $false
|
|
PromptExternalHardDiskMedia = $true
|
|
SelectSpecificUSBDrives = $false
|
|
CopyAutopilot = $false
|
|
CopyUnattend = $false
|
|
CopyPPKG = $false
|
|
CleanupAppsISO = $true
|
|
CleanupCaptureISO = $true
|
|
CleanupDeployISO = $true
|
|
CleanupDrivers = $false
|
|
RemoveFFU = $false
|
|
RemoveApps = $false
|
|
RemoveUpdates = $false
|
|
# Hyper-V Settings Defaults
|
|
VMHostIPAddress = ""
|
|
DiskSizeGB = 30
|
|
MemoryGB = 4
|
|
Processors = 4
|
|
VMLocation = $vmLocationPath
|
|
VMNamePrefix = "_FFU"
|
|
LogicalSectorSize = 512
|
|
# Updates Tab Defaults
|
|
UpdateLatestCU = $true
|
|
UpdateLatestNet = $true
|
|
UpdateLatestDefender = $true
|
|
UpdateEdge = $true
|
|
UpdateOneDrive = $true
|
|
UpdateLatestMSRT = $true
|
|
UpdateLatestMicrocode = $false
|
|
UpdatePreviewCU = $false
|
|
# Applications Tab Defaults
|
|
InstallApps = $false
|
|
ApplicationPath = $appsPath
|
|
AppListJsonPath = $appListJsonPath
|
|
InstallWingetApps = $false
|
|
BringYourOwnApps = $false
|
|
# M365 Apps/Office Tab Defaults
|
|
InstallOffice = $true
|
|
OfficePath = $officePath
|
|
CopyOfficeConfigXML = $false
|
|
OfficeConfigXMLFilePath = ""
|
|
# Drivers Tab Defaults
|
|
DriversFolder = $driversPath
|
|
PEDriversFolder = $peDriversPath
|
|
DriversJsonPath = $driversJsonPath
|
|
DownloadDrivers = $false
|
|
InstallDrivers = $false
|
|
CopyDrivers = $false
|
|
CopyPEDrivers = $false
|
|
UpdateADK = $true
|
|
CompressDownloadedDriversToWim = $false
|
|
}
|
|
}
|
|
|
|
# Function to get USB Drives (Moved from BuildFFUVM_UI.ps1)
|
|
function Get-USBDrives {
|
|
Get-WmiObject Win32_DiskDrive | Where-Object {
|
|
($_.MediaType -eq 'Removable Media' -or $_.MediaType -eq 'External hard disk media')
|
|
} | ForEach-Object {
|
|
$size = [math]::Round($_.Size / 1GB, 2)
|
|
$serialNumber = if ($_.SerialNumber) { $_.SerialNumber.Trim() } else { "N/A" }
|
|
@{
|
|
IsSelected = $false
|
|
Model = $_.Model.Trim()
|
|
SerialNumber = $serialNumber
|
|
Size = $size
|
|
DriveIndex = $_.Index
|
|
}
|
|
}
|
|
}
|
|
|
|
# Function to manage the visibility of the application UI panels
|
|
function Update-ApplicationPanelVisibility {
|
|
param(
|
|
[PSCustomObject]$State,
|
|
[string]$TriggeringControlName # Optional: to know which control initiated the change
|
|
)
|
|
|
|
$installAppsChecked = $State.Controls.chkInstallApps.IsChecked
|
|
|
|
# If the main 'Install Apps' is unchecked, everything below it gets hidden and reset.
|
|
if ($TriggeringControlName -eq 'chkInstallApps' -and -not $installAppsChecked) {
|
|
$State.Controls.chkInstallWingetApps.IsChecked = $false
|
|
$State.Controls.chkBringYourOwnApps.IsChecked = $false
|
|
$State.Controls.chkDefineAppsScriptVariables.IsChecked = $false
|
|
}
|
|
|
|
$byoAppsChecked = $State.Controls.chkBringYourOwnApps.IsChecked
|
|
$wingetAppsChecked = $State.Controls.chkInstallWingetApps.IsChecked
|
|
$defineVarsChecked = $State.Controls.chkDefineAppsScriptVariables.IsChecked
|
|
|
|
# Visibility of primary sub-options
|
|
$subOptionVisibility = if ($installAppsChecked) { 'Visible' } else { 'Collapsed' }
|
|
$State.Controls.applicationPathPanel.Visibility = $subOptionVisibility
|
|
$State.Controls.appListJsonPathPanel.Visibility = $subOptionVisibility
|
|
$State.Controls.chkInstallWingetApps.Visibility = $subOptionVisibility
|
|
$State.Controls.chkBringYourOwnApps.Visibility = $subOptionVisibility
|
|
$State.Controls.chkDefineAppsScriptVariables.Visibility = $subOptionVisibility
|
|
|
|
# Visibility of panels dependent on sub-options
|
|
$State.Controls.byoApplicationPanel.Visibility = if ($installAppsChecked -and $byoAppsChecked) { 'Visible' } else { 'Collapsed' }
|
|
$State.Controls.wingetPanel.Visibility = if ($installAppsChecked -and $wingetAppsChecked) { 'Visible' } else { 'Collapsed' }
|
|
$State.Controls.appsScriptVariablesPanel.Visibility = if ($installAppsChecked -and $defineVarsChecked) { 'Visible' } else { 'Collapsed' }
|
|
|
|
# Special handling for wingetSearchPanel, which is shown by another button.
|
|
# We only collapse it if its parent becomes invisible.
|
|
if (-not ($installAppsChecked -and $wingetAppsChecked)) {
|
|
$State.Controls.wingetSearchPanel.Visibility = 'Collapsed'
|
|
}
|
|
}
|
|
|
|
# Function to manage the state of the main "Install Apps" checkbox based on selections in Updates/Office
|
|
function Update-InstallAppsState {
|
|
param([PSCustomObject]$State)
|
|
|
|
$installAppsChk = $State.Controls.chkInstallApps
|
|
$installOfficeChk = $State.Controls.chkInstallOffice
|
|
|
|
# Determine if any checkbox that forces "Install Apps" is checked
|
|
$anyUpdateChecked = $State.Controls.chkUpdateLatestDefender.IsChecked -or `
|
|
$State.Controls.chkUpdateEdge.IsChecked -or `
|
|
$State.Controls.chkUpdateOneDrive.IsChecked -or `
|
|
$State.Controls.chkUpdateLatestMSRT.IsChecked
|
|
|
|
$isForced = $anyUpdateChecked -or $installOfficeChk.IsChecked
|
|
|
|
if ($isForced) {
|
|
# If InstallApps is not already forced (i.e., it's enabled), save its current state.
|
|
if ($installAppsChk.IsEnabled) {
|
|
$State.Flags.prevInstallAppsState = $installAppsChk.IsChecked
|
|
}
|
|
$installAppsChk.IsChecked = $true
|
|
$installAppsChk.IsEnabled = $false
|
|
}
|
|
else {
|
|
# No longer forced. Restore the previous state if it was saved.
|
|
if ($State.Flags.ContainsKey('prevInstallAppsState')) {
|
|
$installAppsChk.IsChecked = $State.Flags.prevInstallAppsState
|
|
$State.Flags.Remove('prevInstallAppsState') # Use the saved state only once
|
|
}
|
|
else {
|
|
# If no state was saved (e.g., it was never forced), ensure it's unchecked.
|
|
$installAppsChk.IsChecked = $false
|
|
}
|
|
$installAppsChk.IsEnabled = $true
|
|
}
|
|
}
|
|
|
|
# Function to manage the enabled state of interdependent driver-related checkboxes
|
|
function Update-DriverCheckboxStates {
|
|
param([PSCustomObject]$State)
|
|
|
|
$installDriversChk = $State.Controls.chkInstallDrivers
|
|
$copyDriversChk = $State.Controls.chkCopyDrivers
|
|
$compressWimChk = $State.Controls.chkCompressDriversToWIM
|
|
|
|
# Default to enabled, then apply disabling rules
|
|
$installDriversChk.IsEnabled = $true
|
|
$copyDriversChk.IsEnabled = $true
|
|
$compressWimChk.IsEnabled = $true
|
|
|
|
if ($installDriversChk.IsChecked) {
|
|
$copyDriversChk.IsEnabled = $false
|
|
$compressWimChk.IsEnabled = $false
|
|
}
|
|
|
|
if ($copyDriversChk.IsChecked) {
|
|
$installDriversChk.IsEnabled = $false
|
|
}
|
|
|
|
if ($compressWimChk.IsChecked) {
|
|
$installDriversChk.IsEnabled = $false
|
|
}
|
|
}
|
|
|
|
# Function to manage the visibility of Office UI panels
|
|
function Update-OfficePanelVisibility {
|
|
param([PSCustomObject]$State)
|
|
|
|
if ($State.Controls.chkInstallOffice.IsChecked) {
|
|
$State.Controls.OfficePathStackPanel.Visibility = 'Visible'
|
|
$State.Controls.OfficePathGrid.Visibility = 'Visible'
|
|
$State.Controls.CopyOfficeConfigXMLStackPanel.Visibility = 'Visible'
|
|
# Show/hide XML file path based on checkbox state
|
|
$State.Controls.OfficeConfigurationXMLFileStackPanel.Visibility = if ($State.Controls.chkCopyOfficeConfigXML.IsChecked) { 'Visible' } else { 'Collapsed' }
|
|
$State.Controls.OfficeConfigurationXMLFileGrid.Visibility = if ($State.Controls.chkCopyOfficeConfigXML.IsChecked) { 'Visible' } else { 'Collapsed' }
|
|
}
|
|
else {
|
|
$State.Controls.OfficePathStackPanel.Visibility = 'Collapsed'
|
|
$State.Controls.OfficePathGrid.Visibility = 'Collapsed'
|
|
$State.Controls.CopyOfficeConfigXMLStackPanel.Visibility = 'Collapsed'
|
|
$State.Controls.OfficeConfigurationXMLFileStackPanel.Visibility = 'Collapsed'
|
|
$State.Controls.OfficeConfigurationXMLFileGrid.Visibility = 'Collapsed'
|
|
}
|
|
}
|
|
|
|
# Function to manage the visibility of the driver download UI panels
|
|
function Update-DriverDownloadPanelVisibility {
|
|
param([PSCustomObject]$State)
|
|
|
|
if ($State.Controls.chkDownloadDrivers.IsChecked) {
|
|
$State.Controls.spMakeSection.Visibility = 'Visible'
|
|
$State.Controls.btnGetModels.Visibility = 'Visible'
|
|
# The other panels are shown/hidden by the Get Models button click handler
|
|
}
|
|
else {
|
|
$State.Controls.spMakeSection.Visibility = 'Collapsed'
|
|
$State.Controls.btnGetModels.Visibility = 'Collapsed'
|
|
$State.Controls.spModelFilterSection.Visibility = 'Collapsed'
|
|
$State.Controls.lstDriverModels.Visibility = 'Collapsed'
|
|
$State.Controls.spDriverActionButtons.Visibility = 'Collapsed'
|
|
$State.Controls.lstDriverModels.ItemsSource = $null
|
|
$State.Data.allDriverModels.Clear()
|
|
$State.Controls.txtModelFilter.Text = ""
|
|
}
|
|
}
|
|
|
|
# --------------------------------------------------------------------------
|
|
# SECTION: Module Export
|
|
# --------------------------------------------------------------------------
|
|
|
|
# Export only the functions intended for public use by the UI script
|
|
Export-ModuleMember -Function *
|