# 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" BuildUSBDriveEnable = $false CompactOS = $true Optimize = $true AllowVHDXCaching = $false CreateCaptureMedia = $true CreateDeploymentMedia = $true 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 *