mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
7babad8262
Relocates driver-specific download, parsing, and management logic from the main UI script and the FFUUI.Core module into new, dedicated modules for each manufacturer (Dell, HP, Lenovo, Microsoft). This improves modularity and code organization. Additionally, centralizes common HTTP headers and user agent strings in the FFUUI.Core module, accessible via a new helper function.
1451 lines
74 KiB
PowerShell
1451 lines
74 KiB
PowerShell
# FFU UI Core Logic Module
|
|
# Contains non-UI specific helper functions, data retrieval, and core processing logic.
|
|
|
|
#Requires -Modules BitsTransfer
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 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'
|
|
|
|
$script:allowedFeatures = @(
|
|
"AppServerClient", "Client-DeviceLockdown", "Client-EmbeddedBootExp", "Client-EmbeddedLogon",
|
|
"Client-EmbeddedShellLauncher", "Client-KeyboardFilter", "Client-ProjFS", "Client-UnifiedWriteFilter",
|
|
"Containers", "Containers-DisposableClientVM", "Containers-HNS", "Containers-SDN", "DataCenterBridging",
|
|
"DirectoryServices-ADAM-Client", "DirectPlay", "HostGuardian", "HypervisorPlatform", "IIS-ApplicationDevelopment",
|
|
"IIS-ApplicationInit", "IIS-ASP", "IIS-ASPNET45", "IIS-BasicAuthentication", "IIS-CertProvider",
|
|
"IIS-CGI", "IIS-ClientCertificateMappingAuthentication", "IIS-CommonHttpFeatures", "IIS-CustomLogging",
|
|
"IIS-DefaultDocument", "IIS-DirectoryBrowsing", "IIS-DigestAuthentication", "IIS-ESP", "IIS-FTPServer",
|
|
"IIS-FTPExtensibility", "IIS-FTPSvc", "IIS-HealthAndDiagnostics", "IIS-HostableWebCore", "IIS-HttpCompressionDynamic",
|
|
"IIS-HttpCompressionStatic", "IIS-HttpErrors", "IIS-HttpLogging", "IIS-HttpRedirect", "IIS-HttpTracing",
|
|
"IIS-IPSecurity", "IIS-IIS6ManagementCompatibility", "IIS-IISCertificateMappingAuthentication",
|
|
"IIS-ISAPIExtensions", "IIS-ISAPIFilter", "IIS-LoggingLibraries", "IIS-ManagementConsole", "IIS-ManagementService",
|
|
"IIS-ManagementScriptingTools", "IIS-Metabase", "IIS-NetFxExtensibility", "IIS-NetFxExtensibility45",
|
|
"IIS-ODBCLogging", "IIS-Performance", "IIS-RequestFiltering", "IIS-RequestMonitor", "IIS-Security", "IIS-ServerSideIncludes",
|
|
"IIS-StaticContent", "IIS-URLAuthorization", "IIS-WebDAV", "IIS-WebServer", "IIS-WebServerManagementTools",
|
|
"IIS-WebServerRole", "IIS-WebSockets", "LegacyComponents", "MediaPlayback", "Microsoft-Hyper-V", "Microsoft-Hyper-V-All",
|
|
"Microsoft-Hyper-V-Hypervisor", "Microsoft-Hyper-V-Management-Clients", "Microsoft-Hyper-V-Management-PowerShell",
|
|
"Microsoft-Hyper-V-Services", "Microsoft-Windows-Subsystem-Linux", "MSMQ-ADIntegration", "MSMQ-Container", "MSMQ-DCOMProxy",
|
|
"MSMQ-HTTP", "MSMQ-Multicast", "MSMQ-Server", "MSMQ-Triggers", "MultiPoint-Connector", "MultiPoint-Connector-Services",
|
|
"MultiPoint-Tools", "NetFx3", "NetFx4-AdvSrvs", "NetFx4Extended-ASPNET45", "NFS-Administration", "Printing-Foundation-Features",
|
|
"Printing-Foundation-InternetPrinting-Client", "Printing-Foundation-LPDPrintService", "Printing-Foundation-LPRPortMonitor",
|
|
"Printing-PrintToPDFServices-Features", "Printing-XPSServices-Features", "SearchEngine-Client-Package",
|
|
"ServicesForNFS-ClientOnly", "SimpleTCP", "SMB1Protocol", "SMB1Protocol-Client", "SMB1Protocol-Deprecation",
|
|
"SMB1Protocol-Server", "SmbDirect", "TFTP", "TelnetClient", "TIFFIFilter", "VirtualMachinePlatform", "WAS-ConfigurationAPI",
|
|
"WAS-NetFxEnvironment", "WAS-ProcessModel", "WAS-WindowsActivationService", "WCF-HTTP-Activation", "WCF-HTTP-Activation45",
|
|
"WCF-MSMQ-Activation45", "WCF-MSMQ-Activation", "WCF-NonHTTP-Activation", "WCF-Pipe-Activation45", "WCF-Services45",
|
|
"WCF-TCP-Activation45", "WCF-TCP-PortSharing45", "Windows-Defender-ApplicationGuard",
|
|
"Windows-Defender-Default-Definitions", "Windows-Identity-Foundation", "WindowsMediaPlayer", "WorkFolders-Client"
|
|
)
|
|
|
|
$script:skuList = @(
|
|
'Home',
|
|
'Home N',
|
|
'Home Single Language',
|
|
'Education',
|
|
'Education N',
|
|
'Pro',
|
|
'Pro N',
|
|
'Pro Education',
|
|
'Pro Education N',
|
|
'Pro for Workstations',
|
|
'Pro N for Workstations',
|
|
'Enterprise',
|
|
'Enterprise N',
|
|
'Enterprise 2016 LTSB',
|
|
'Enterprise N 2016 LTSB',
|
|
'Enterprise LTSC',
|
|
'Enterprise N LTSC',
|
|
'IoT Enterprise LTSC',
|
|
'IoT Enterprise N LTSC',
|
|
'Standard',
|
|
'Standard (Desktop Experience)',
|
|
'Datacenter',
|
|
'Datacenter (Desktop Experience)'
|
|
)
|
|
|
|
$script:allowedLangs = @(
|
|
'ar-sa', 'bg-bg', 'cs-cz', 'da-dk', 'de-de', 'el-gr', 'en-gb', 'en-us', 'es-es', 'es-mx', 'et-ee',
|
|
'fi-fi', 'fr-ca', 'fr-fr', 'he-il', 'hr-hr', 'hu-hu', 'it-it', 'ja-jp', 'ko-kr', 'lt-lt', 'lv-lv',
|
|
'nb-no', 'nl-nl', 'pl-pl', 'pt-br', 'pt-pt', 'ro-ro', 'ru-ru', 'sk-sk', 'sl-si', 'sr-latn-rs',
|
|
'sv-se', 'th-th', 'tr-tr', 'uk-ua', 'zh-cn', 'zh-tw'
|
|
)
|
|
|
|
$script:allWindowsReleases = @(
|
|
[PSCustomObject]@{ Display = "Windows 10"; Value = 10 },
|
|
[PSCustomObject]@{ Display = "Windows 11"; Value = 11 },
|
|
[PSCustomObject]@{ Display = "Windows Server 2016"; Value = 2016 },
|
|
[PSCustomObject]@{ Display = "Windows Server 2019"; Value = 2019 },
|
|
[PSCustomObject]@{ Display = "Windows Server 2022"; Value = 2022 },
|
|
[PSCustomObject]@{ Display = "Windows Server 2025"; Value = 2025 },
|
|
[PSCustomObject]@{ Display = "Windows 10 LTSB 2016"; Value = 2016 }, # Changed Value from 1607
|
|
[PSCustomObject]@{ Display = "Windows 10 LTSC 2019"; Value = 2019 }, # Changed Value from 1809
|
|
[PSCustomObject]@{ Display = "Windows 10 LTSC 2021"; Value = 2021 },
|
|
[PSCustomObject]@{ Display = "Windows 10 LTSC 2024"; Value = 2024 }
|
|
)
|
|
|
|
$script:mctWindowsReleases = @(
|
|
[PSCustomObject]@{ Display = "Windows 10"; Value = 10 },
|
|
[PSCustomObject]@{ Display = "Windows 11"; Value = 11 }
|
|
)
|
|
|
|
$script:windowsVersionMap = @{
|
|
10 = @("22H2")
|
|
11 = @("22H2", "23H2", "24H2")
|
|
2016 = @("1607") # Windows 10 LTSB 2016 & Server 2016
|
|
2019 = @("1809") # Windows 10 LTSC 2019 & Server 2019
|
|
# Note: Server 2016 and LTSB 2016 now share the key 2016, mapping to version "1607"
|
|
# Note: Server 2019 and LTSC 2019 now share the key 2019, mapping to version "1809"
|
|
2021 = @("21H2") # LTSC 2021
|
|
2022 = @("21H2") # Server 2022
|
|
2024 = @("24H2") # LTSC 2024
|
|
2025 = @("24H2") # Server 2025
|
|
}
|
|
|
|
# SKU Groups
|
|
$script:clientSKUs = @(
|
|
'Home',
|
|
'Home N',
|
|
'Home Single Language',
|
|
'Education',
|
|
'Education N',
|
|
'Pro',
|
|
'Pro N',
|
|
'Pro Education',
|
|
'Pro Education N',
|
|
'Pro for Workstations',
|
|
'Pro N for Workstations',
|
|
'Enterprise',
|
|
'Enterprise N'
|
|
)
|
|
|
|
$script:serverSKUs = @(
|
|
'Standard',
|
|
'Standard (Desktop Experience)',
|
|
'Datacenter',
|
|
'Datacenter (Desktop Experience)'
|
|
)
|
|
|
|
$script:ltsc2016SKUs = @(
|
|
'Enterprise 2016 LTSB',
|
|
'Enterprise N 2016 LTSB'
|
|
)
|
|
|
|
$script:ltscGenericSKUs = @( # For LTSC 2019, 2021, 2024
|
|
'Enterprise LTSC',
|
|
'Enterprise N LTSC'
|
|
)
|
|
|
|
$script:iotLtscSKUs = @(
|
|
'IoT Enterprise LTSC',
|
|
'IoT Enterprise N LTSC'
|
|
# Note: IoT SKUs are often specialized and might have different edition IDs.
|
|
# This list is a general representation. Actual ISOs might be needed for specific IoT LTSC editions.
|
|
)
|
|
|
|
# Map Windows Release Values to their corresponding SKU lists
|
|
$script:windowsReleaseSkuMap = @{
|
|
10 = $script:clientSKUs # Windows 10 Client
|
|
11 = $script:clientSKUs # Windows 11 Client
|
|
2016 = $script:serverSKUs # Windows Server 2016 (LTSB 2016 handled by Get-AvailableSkusForRelease)
|
|
2019 = $script:serverSKUs # Windows Server 2019 (LTSC 2019 handled by Get-AvailableSkusForRelease)
|
|
2022 = $script:serverSKUs # Windows Server 2022
|
|
2025 = $script:serverSKUs # Windows Server 2025
|
|
2021 = $script:ltscGenericSKUs + $script:iotLtscSKUs # Windows 10 LTSC 2021
|
|
2024 = $script:ltscGenericSKUs + $script:iotLtscSKUs # Windows 10 LTSC 2024
|
|
# Note: LTSC 2016 and LTSC 2019 SKUs are now conditionally returned by Get-AvailableSkusForRelease
|
|
}
|
|
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 the default settings and static lists (Moved from UI_Helpers)
|
|
function Get-WindowsSettingsDefaults {
|
|
[CmdletBinding()]
|
|
param()
|
|
|
|
return [PSCustomObject]@{
|
|
DefaultISOPath = ""
|
|
DefaultWindowsArch = "x64"
|
|
DefaultWindowsLang = "en-us"
|
|
DefaultWindowsSKU = "Pro"
|
|
DefaultMediaType = "Consumer"
|
|
DefaultOptionalFeatures = ""
|
|
DefaultProductKey = ""
|
|
AllowedFeatures = $script:allowedFeatures
|
|
AllowedLanguages = $script:allowedLangs
|
|
AllowedArchitectures = @('x86', 'x64', 'arm64')
|
|
AllowedMediaTypes = @('Consumer', 'Business')
|
|
}
|
|
}
|
|
|
|
# Function to get the appropriate list of Windows Releases based on ISO path (Moved from UI_Helpers)
|
|
function Get-AvailableWindowsReleases {
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$IsoPath,
|
|
[Parameter(Mandatory = $true)]
|
|
[psobject]$State
|
|
)
|
|
|
|
if ([string]::IsNullOrEmpty($IsoPath)) {
|
|
return $State.Defaults.GeneralDefaults.MctWindowsReleases
|
|
}
|
|
else {
|
|
return $State.Defaults.GeneralDefaults.AllWindowsReleases
|
|
}
|
|
}
|
|
|
|
# Function to get available Windows Versions for a given release and ISO path (Moved from UI_Helpers)
|
|
function Get-AvailableWindowsVersions {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[int]$SelectedRelease,
|
|
|
|
[string]$IsoPath,
|
|
[Parameter(Mandatory = $true)]
|
|
[psobject]$State
|
|
)
|
|
|
|
$result = [PSCustomObject]@{
|
|
Versions = @()
|
|
DefaultVersion = $null
|
|
IsEnabled = $false
|
|
}
|
|
|
|
if (-not $State.Defaults.GeneralDefaults.WindowsVersionMap.ContainsKey($SelectedRelease)) {
|
|
return $result
|
|
}
|
|
|
|
$validVersions = $State.Defaults.GeneralDefaults.WindowsVersionMap[$SelectedRelease]
|
|
|
|
if ([string]::IsNullOrEmpty($IsoPath)) {
|
|
# Logic for when no ISO is specified (MCT scenario)
|
|
switch ($SelectedRelease) {
|
|
10 { $result.DefaultVersion = "22H2" }
|
|
11 { $result.DefaultVersion = "24H2" }
|
|
# Server versions typically require an ISO, but handle just in case
|
|
2016 { $result.DefaultVersion = "1607" }
|
|
2019 { $result.DefaultVersion = "1809" }
|
|
2022 { $result.DefaultVersion = "21H2" }
|
|
2025 { $result.DefaultVersion = "24H2" }
|
|
default { $result.DefaultVersion = $validVersions[0] }
|
|
}
|
|
$result.Versions = @($result.DefaultVersion) # Only the default is available/relevant
|
|
$result.IsEnabled = $false # Combo should be disabled
|
|
}
|
|
else {
|
|
# Logic for when an ISO is specified
|
|
$result.Versions = $validVersions
|
|
# Set default selection logic (e.g., latest for Win11)
|
|
if ($SelectedRelease -eq 11 -and $validVersions -contains "24H2") {
|
|
$result.DefaultVersion = "24H2"
|
|
}
|
|
elseif ($validVersions.Count -gt 0) {
|
|
$result.DefaultVersion = $validVersions[0]
|
|
}
|
|
$result.IsEnabled = $true
|
|
}
|
|
|
|
return $result
|
|
}
|
|
|
|
# Function to get available SKUs for a given Windows Release value and display name
|
|
function Get-AvailableSkusForRelease {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[int]$SelectedReleaseValue,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string]$SelectedReleaseDisplayName,
|
|
[Parameter(Mandatory = $true)]
|
|
[psobject]$State
|
|
)
|
|
|
|
WriteLog "Get-AvailableSkusForRelease: Getting SKUs for Release Value '$SelectedReleaseValue', Display Name '$SelectedReleaseDisplayName'."
|
|
|
|
# Handle LTSC 2016 specifically
|
|
if ($SelectedReleaseValue -eq 2016 -and $SelectedReleaseDisplayName -like '*LTSB*') {
|
|
WriteLog "Get-AvailableSkusForRelease: Matched LTSB 2016. Returning LTSC 2016 SKUs."
|
|
return $State.Defaults.GeneralDefaults.Ltsc2016SKUs
|
|
}
|
|
# Handle LTSC 2019 specifically
|
|
# Ensure "Server" is not in the display name to avoid matching "Windows Server 2019"
|
|
elseif ($SelectedReleaseValue -eq 2019 -and $SelectedReleaseDisplayName -like '*LTSC*' -and $SelectedReleaseDisplayName -notlike '*Server*') {
|
|
WriteLog "Get-AvailableSkusForRelease: Matched LTSC 2019. Returning generic LTSC SKUs (including IoT)."
|
|
# Assuming LTSC 2019 uses the generic LTSC SKUs + IoT LTSC SKUs
|
|
return ($State.Defaults.GeneralDefaults.LtscGenericSKUs + $State.Defaults.GeneralDefaults.IotLtscSKUs | Select-Object -Unique)
|
|
}
|
|
# For all other cases, use the main SKU map
|
|
elseif ($State.Defaults.GeneralDefaults.WindowsReleaseSkuMap.ContainsKey($SelectedReleaseValue)) {
|
|
$availableSkus = $State.Defaults.GeneralDefaults.WindowsReleaseSkuMap[$SelectedReleaseValue]
|
|
WriteLog "Get-AvailableSkusForRelease: Found $($availableSkus.Count) SKUs for Release '$SelectedReleaseValue' using standard map."
|
|
return $availableSkus
|
|
}
|
|
else {
|
|
WriteLog "Get-AvailableSkusForRelease: Warning - Release Value '$SelectedReleaseValue' not found in SKU map. Returning default client SKUs."
|
|
# Fallback to a default list (e.g., client SKUs) or an empty list
|
|
return $State.Defaults.GeneralDefaults.ClientSKUs
|
|
}
|
|
}
|
|
|
|
# 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
|
|
# Static Data Lists/Maps
|
|
AllowedFeatures = $script:allowedFeatures
|
|
SkuList = $script:skuList
|
|
AllowedLanguages = $script:allowedLangs
|
|
AllWindowsReleases = $script:allWindowsReleases
|
|
MctWindowsReleases = $script:mctWindowsReleases
|
|
WindowsVersionMap = $script:windowsVersionMap
|
|
ClientSKUs = $script:clientSKUs
|
|
ServerSKUs = $script:serverSKUs
|
|
Ltsc2016SKUs = $script:ltsc2016SKUs
|
|
LtscGenericSKUs = $script:ltscGenericSKUs
|
|
IotLtscSKUs = $script:iotLtscSKUs
|
|
WindowsReleaseSkuMap = $script:windowsReleaseSkuMap
|
|
}
|
|
}
|
|
|
|
# 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
|
|
}
|
|
}
|
|
}
|
|
|
|
# --------------------------------------------------------------------------
|
|
# SECTION: Winget Management Functions
|
|
# --------------------------------------------------------------------------
|
|
function Search-WingetPackagesPublic {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Query
|
|
)
|
|
|
|
WriteLog "Searching Winget packages with query: '$Query'"
|
|
try {
|
|
# Call the shared Find-WinGetPackage function
|
|
$results = Find-WinGetPackage -Query $Query -ErrorAction Stop
|
|
WriteLog "Found $($results.Count) packages matching query '$Query'."
|
|
return $results
|
|
}
|
|
catch {
|
|
WriteLog "Error during Winget search: $($_.Exception.Message)"
|
|
# Return an empty array or throw, depending on desired UI behavior
|
|
return @()
|
|
}
|
|
}
|
|
|
|
function Test-WingetCLI {
|
|
[CmdletBinding()]
|
|
param()
|
|
|
|
$minVersion = [version]"1.8.1911"
|
|
|
|
# Check Winget CLI
|
|
$wingetCmd = Get-Command -Name winget -ErrorAction SilentlyContinue
|
|
if (-not $wingetCmd) {
|
|
return @{
|
|
Version = "Not installed"
|
|
Status = "Not installed - Install from Microsoft Store"
|
|
}
|
|
}
|
|
|
|
# Get and check version
|
|
$wingetVersion = & winget.exe --version
|
|
if ($wingetVersion -match 'v?(\d+\.\d+.\d+)') {
|
|
$version = [version]$matches[1]
|
|
if ($version -lt $minVersion) {
|
|
return @{
|
|
Version = $version.ToString()
|
|
Status = "Update required - Install from Microsoft Store"
|
|
}
|
|
}
|
|
return @{
|
|
Version = $version.ToString()
|
|
Status = $version.ToString()
|
|
}
|
|
}
|
|
|
|
return @{
|
|
Version = "Unknown"
|
|
Status = "Version check failed"
|
|
}
|
|
}
|
|
|
|
|
|
function Install-WingetComponents {
|
|
[CmdletBinding()]
|
|
param(
|
|
# Add parameter to accept a script block for UI updates
|
|
[Parameter(Mandatory)]
|
|
[scriptblock]$UiUpdateCallback
|
|
)
|
|
|
|
$minVersion = [version]"1.8.1911"
|
|
$module = $null
|
|
|
|
try {
|
|
# Check and update PowerShell Module
|
|
$module = Get-InstalledModule -Name Microsoft.WinGet.Client -ErrorAction SilentlyContinue
|
|
if (-not $module -or $module.Version -lt $minVersion) {
|
|
WriteLog "Winget module needs install/update. Attempting..."
|
|
# Invoke the callback provided by the UI script to update status
|
|
# Note: We don't have the CLI version readily available here, pass a placeholder or adjust if needed.
|
|
& $UiUpdateCallback "Checking..." "Installing..."
|
|
|
|
# Store and modify PSGallery trust setting temporarily if needed
|
|
$PSGalleryTrust = (Get-PSRepository -Name 'PSGallery').InstallationPolicy
|
|
if ($PSGalleryTrust -eq 'Untrusted') {
|
|
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
|
|
}
|
|
|
|
# Install/Update the module
|
|
Install-Module -Name Microsoft.WinGet.Client -Force -Repository 'PSGallery'
|
|
|
|
# Restore original PSGallery trust setting
|
|
if ($PSGalleryTrust -eq 'Untrusted') {
|
|
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Untrusted
|
|
}
|
|
|
|
$module = Get-InstalledModule -Name Microsoft.WinGet.Client -ErrorAction Stop
|
|
}
|
|
|
|
return $module
|
|
}
|
|
catch {
|
|
Write-Error "Failed to install/update Winget PowerShell module: $_"
|
|
throw
|
|
}
|
|
}
|
|
|
|
# Winget Module Check Function (UI Version)
|
|
# Performs checks, triggers install if needed, and reports status back to the UI.
|
|
function Confirm-WingetInstallationUI {
|
|
[CmdletBinding()]
|
|
param(
|
|
# Callback for intermediate UI updates (e.g., "Installing...")
|
|
[Parameter(Mandatory)]
|
|
[scriptblock]$UiUpdateCallback
|
|
)
|
|
|
|
$minVersion = [version]"1.8.1911"
|
|
$result = [PSCustomObject]@{
|
|
Success = $false
|
|
Message = ""
|
|
CliVersion = "Unknown"
|
|
ModuleVersion = "Unknown"
|
|
NeedsUpdate = $false
|
|
UpdateAttempted = $false
|
|
}
|
|
|
|
try {
|
|
# Initial Check
|
|
WriteLog "Confirm-WingetInstallationUI: Starting checks..."
|
|
$cliStatus = Test-WingetCLI
|
|
$module = Get-InstalledModule -Name Microsoft.WinGet.Client -ErrorAction SilentlyContinue
|
|
|
|
$result.CliVersion = $cliStatus.Version
|
|
$result.ModuleVersion = if ($null -ne $module) { $module.Version.ToString() } else { "Not installed" }
|
|
|
|
# Use callback for initial status display
|
|
& $UiUpdateCallback $result.CliVersion $result.ModuleVersion
|
|
|
|
# Determine if install/update is needed
|
|
$needsCliUpdate = $cliStatus.Status -notmatch '^\d+\.\d+\.\d+$' -or ([version]$cliStatus.Version -lt $minVersion)
|
|
$needsModuleUpdate = ($null -eq $module) -or ([version]$module.Version -lt $minVersion)
|
|
$result.NeedsUpdate = $needsCliUpdate -or $needsModuleUpdate
|
|
|
|
if ($result.NeedsUpdate) {
|
|
WriteLog "Confirm-WingetInstallationUI: Update needed. CLI Needs Update: $needsCliUpdate, Module Needs Update: $needsModuleUpdate"
|
|
$result.UpdateAttempted = $true
|
|
|
|
# Use callback to indicate installation attempt
|
|
& $UiUpdateCallback $result.CliVersion "Installing/Updating..."
|
|
|
|
# Attempt to install/update Winget CLI and module
|
|
$installedModule = Install-WingetComponents -UiUpdateCallback $UiUpdateCallback
|
|
|
|
# Re-check status after attempt
|
|
WriteLog "Confirm-WingetInstallationUI: Re-checking status after update attempt..."
|
|
$cliStatus = Test-WingetCLI
|
|
$result.CliVersion = $cliStatus.Version
|
|
$result.ModuleVersion = if ($null -ne $installedModule) { $installedModule.Version } else { "Install Failed" }
|
|
# Use callback for final status display after update attempt
|
|
& $UiUpdateCallback $result.CliVersion $result.ModuleVersion
|
|
|
|
# Check if update was successful
|
|
$cliOk = $cliStatus.Status -match '^\d+\.\d+\.\d+$' -and ([version]$cliStatus.Version -ge $minVersion)
|
|
$moduleOk = ($null -ne $installedModule) -and ([version]$installedModule.Version -ge $minVersion)
|
|
$result.Success = $cliOk -and $moduleOk
|
|
$result.Message = if ($result.Success) { "Winget components installed/updated successfully." } else { "Winget component installation/update failed or is incomplete." }
|
|
WriteLog "Confirm-WingetInstallationUI: Update attempt finished. Success: $($result.Success). Message: $($result.Message)"
|
|
}
|
|
else {
|
|
# Already up-to-date
|
|
$result.Success = $true
|
|
$result.Message = "Winget components are up-to-date."
|
|
WriteLog "Confirm-WingetInstallationUI: Components already up-to-date."
|
|
}
|
|
}
|
|
catch {
|
|
$result.Success = $false
|
|
$result.Message = "Error during Winget check/install: $($_.Exception.Message)"
|
|
WriteLog "Confirm-WingetInstallationUI: Error - $($result.Message)"
|
|
# Use callback to show error state
|
|
& $UiUpdateCallback $result.CliVersion "Error"
|
|
}
|
|
|
|
return $result
|
|
}
|
|
# Function to handle downloading a winget application (Modified for ForEach-Object -Parallel)
|
|
function Start-WingetAppDownloadTask {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[PSCustomObject]$ApplicationItemData, # Pass data, not the UI object
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AppListJsonPath,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AppsPath, # Pass necessary paths
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$WindowsArch,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$OrchestrationPath,
|
|
[Parameter(Mandatory = $true)]
|
|
[System.Collections.Concurrent.ConcurrentQueue[hashtable]]$ProgressQueue # Add queue parameter
|
|
)
|
|
|
|
$appName = $ApplicationItemData.Name
|
|
$appId = $ApplicationItemData.Id
|
|
$source = $ApplicationItemData.Source
|
|
$status = "Checking..." # Initial local status
|
|
$resultCode = -1 # Default to error/unknown
|
|
|
|
# Initial status update
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
|
|
WriteLog "Starting download task for $($appName) with ID $($appId) from source $($source)."
|
|
# WriteLog "Apps Path: $($AppsPath)"
|
|
# WriteLog "AppList JSON Path: $($AppListJsonPath)"
|
|
# WriteLog "Windows Architecture: $($WindowsArch)"
|
|
# WriteLog "Orchestration Path: $($OrchestrationPath)"
|
|
|
|
try {
|
|
# Define paths
|
|
$userAppListPath = Join-Path -Path $AppsPath -ChildPath "UserAppList.json"
|
|
$appFound = $false # Flag to track if the app is found locally
|
|
# WriteLog "UserAppList Path: $($userAppListPath)"
|
|
# WriteLog "Checking for existing app in UserAppList.json and content folder."
|
|
|
|
# 1. Check UserAppList.json and content
|
|
if (Test-Path -Path $userAppListPath) {
|
|
# WriteLog "UserAppList.json found at $($userAppListPath). Checking for app entry."
|
|
try {
|
|
$userAppListContent = Get-Content -Path $userAppListPath -Raw | ConvertFrom-Json
|
|
$userAppEntry = $userAppListContent | Where-Object { $_.Name -eq $appName }
|
|
|
|
if ($userAppEntry) {
|
|
$appFolder = Join-Path -Path "$AppsPath\Win32" -ChildPath $appName
|
|
if (Test-Path -Path $appFolder -PathType Container) {
|
|
$folderSize = (Get-ChildItem -Path $appFolder -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
|
|
if ($folderSize -gt 1MB) {
|
|
$appFound = $true
|
|
$status = "Not Downloaded: App in $userAppListPath and found in $appFolder"
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
WriteLog "Found '$appName' in $userAppListPath and content exists in '$appFolder'."
|
|
return [PSCustomObject]@{ Id = $appId; Status = $status; ResultCode = 0 }
|
|
}
|
|
else {
|
|
$appFound = $true
|
|
$status = "Error: App in '$userAppListPath' but content missing/small in '$appFolder'. Copy content or remove from UserAppList.json."
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
WriteLog $status
|
|
return [PSCustomObject]@{ Id = $appId; Status = $status; ResultCode = 1 }
|
|
}
|
|
}
|
|
else {
|
|
$appFound = $true
|
|
$status = "Error: App in '$userAppListPath' but content folder '$appFolder' not found. Copy content or remove from UserAppList.json."
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
WriteLog $status
|
|
return [PSCustomObject]@{ Id = $appId; Status = $status; ResultCode = 1 }
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
WriteLog "Warning: Could not read or parse '$userAppListPath'. Error: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# 2. Check previous Winget download
|
|
if (-not $appFound) {
|
|
if (-not $appFound) {
|
|
$wingetWin32jsonFile = Join-Path -Path $OrchestrationPath -ChildPath "WinGetWin32Apps.json"
|
|
if (Test-Path -Path $wingetWin32jsonFile) {
|
|
try {
|
|
$wingetAppsJson = Get-Content -Path $wingetWin32jsonFile -Raw | ConvertFrom-Json
|
|
# Check if app already exists in WinGetWin32Apps.json
|
|
$existingWin32Entry = $wingetAppsJson | Where-Object { $_.Name -eq $appName }
|
|
if ($existingWin32Entry) {
|
|
$appFolder = Join-Path -Path "$AppsPath\Win32" -ChildPath $appName
|
|
if (Test-Path -Path $appFolder -PathType Container) {
|
|
$folderSize = (Get-ChildItem -Path $appFolder -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
|
|
if ($folderSize -gt 1MB) {
|
|
$appFound = $true
|
|
$status = "Not Downloaded: App already in $wingetWin32jsonFile and found in $appFolder"
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
WriteLog "Found '$appName' in WinGetWin32Apps.json and content exists in '$appFolder'. Skipping download to prevent duplicate entry."
|
|
return [PSCustomObject]@{ Id = $appId; Status = $status; ResultCode = 0 }
|
|
}
|
|
}
|
|
else {
|
|
# App entry exists in WinGetWin32Apps.json but folder is missing
|
|
$appFound = $true
|
|
$status = "Error: App in '$wingetWin32jsonFile' but content folder '$appFolder' not found. Remove entry from WinGetWin32Apps.json or restore content."
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
WriteLog $status
|
|
return [PSCustomObject]@{ Id = $appId; Status = $status; ResultCode = 1 }
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
WriteLog "Warning: Could not read or parse '$wingetWin32jsonFile'. Error: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
}
|
|
# For now, assuming Get-Application uses $global variables set in the main script or $using: scope.
|
|
# $global:AppsPath = $AppsPath # Potentially redundant if set globally before parallel call
|
|
# $global:WindowsArch = $WindowsArch # Potentially redundant
|
|
# $global:orchestrationPath = $OrchestrationPath # Potentially redundant
|
|
|
|
$wingetWin32jsonFile = Join-Path -Path $OrchestrationPath -ChildPath "WinGetWin32Apps.json"
|
|
if (Test-Path -Path $wingetWin32jsonFile) {
|
|
try {
|
|
$wingetAppsJson = Get-Content -Path $wingetWin32jsonFile -Raw | ConvertFrom-Json
|
|
$wingetApp = $wingetAppsJson | Where-Object { $_.Name -eq $appName }
|
|
if ($wingetApp) {
|
|
$appFolder = Join-Path -Path "$AppsPath\Win32" -ChildPath $appName
|
|
if (Test-Path -Path $appFolder -PathType Container) {
|
|
$folderSize = (Get-ChildItem -Path $appFolder -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
|
|
if ($folderSize -gt 1MB) {
|
|
$appFound = $true
|
|
$status = "Not Downloaded: App in $wingetWin32jsonFile and found in $appFolder"
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
WriteLog "Found '$appName' via WinGetWin32Apps.json and content exists in '$appFolder'."
|
|
return [PSCustomObject]@{ Id = $appId; Status = $status; ResultCode = 0 }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
WriteLog "Warning: Could not read or parse '$wingetWin32jsonFile'. Error: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Check MSStore folder
|
|
if (-not $appFound -and (Test-Path -Path "$AppsPath\MSStore" -PathType Container)) {
|
|
$appFolder = Join-Path -Path "$AppsPath\MSStore" -ChildPath $appName
|
|
if (Test-Path -Path $appFolder -PathType Container) {
|
|
$folderSize = (Get-ChildItem -Path $appFolder -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
|
|
if ($folderSize -gt 1MB) {
|
|
$appFound = $true
|
|
$status = "Already downloaded (MSStore)"
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
WriteLog "Found '$appName' content in '$appFolder'."
|
|
return [PSCustomObject]@{ Id = $appId; Status = $status; ResultCode = 0 }
|
|
}
|
|
}
|
|
}
|
|
|
|
# 3. If not found locally, add to AppList.json and download
|
|
if (-not $appFound) {
|
|
# Add to AppList.json
|
|
$appListContent = $null
|
|
$appListDir = Split-Path -Path $AppListJsonPath -Parent
|
|
if (-not (Test-Path -Path $appListDir -PathType Container)) {
|
|
New-Item -Path $appListDir -ItemType Directory -Force | Out-Null
|
|
}
|
|
if (Test-Path -Path $AppListJsonPath) {
|
|
try {
|
|
$appListContent = Get-Content -Path $AppListJsonPath -Raw | ConvertFrom-Json
|
|
if (-not $appListContent.PSObject.Properties['apps']) {
|
|
$appListContent = @{ apps = @() }
|
|
}
|
|
}
|
|
catch {
|
|
WriteLog "Warning: Could not read or parse '$AppListJsonPath'. Creating new structure. Error: $($_.Exception.Message)"
|
|
$appListContent = @{ apps = @() }
|
|
}
|
|
}
|
|
else {
|
|
$appListContent = @{ apps = @() }
|
|
}
|
|
|
|
$appExistsInAppList = $false
|
|
if ($appListContent.apps) {
|
|
foreach ($app in $appListContent.apps) {
|
|
if ($app.id -eq $appId) {
|
|
$appExistsInAppList = $true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if (-not $appExistsInAppList) {
|
|
$newApp = @{ name = $appName; id = $appId; source = $source }
|
|
if (-not ($appListContent.apps -is [array])) { $appListContent.apps = @() }
|
|
$appListContent.apps += $newApp
|
|
try {
|
|
# Use a lock to prevent race conditions when writing to the same file
|
|
$lockName = "AppListJsonLock"
|
|
$lock = New-Object System.Threading.Mutex($false, $lockName)
|
|
try {
|
|
$lock.WaitOne() | Out-Null
|
|
# Re-read content inside lock to ensure latest version
|
|
if (Test-Path -Path $AppListJsonPath) {
|
|
$currentAppListContent = Get-Content -Path $AppListJsonPath -Raw | ConvertFrom-Json
|
|
if (-not ($currentAppListContent.apps | Where-Object { $_.id -eq $appId })) {
|
|
$currentAppListContent.apps += $newApp
|
|
$currentAppListContent | ConvertTo-Json -Depth 10 | Set-Content -Path $AppListJsonPath -Encoding UTF8
|
|
WriteLog "Added '$appName' to '$AppListJsonPath'."
|
|
}
|
|
else {
|
|
WriteLog "'$appName' already exists in '$AppListJsonPath' (checked inside lock)."
|
|
}
|
|
}
|
|
else {
|
|
# File doesn't exist, write the initial content
|
|
$appListContent | ConvertTo-Json -Depth 10 | Set-Content -Path $AppListJsonPath -Encoding UTF8
|
|
WriteLog "Created '$AppListJsonPath' and added '$appName'."
|
|
}
|
|
}
|
|
finally {
|
|
$lock.ReleaseMutex()
|
|
$lock.Dispose()
|
|
}
|
|
}
|
|
catch {
|
|
WriteLog "Error saving '$AppListJsonPath'. Error: $($_.Exception.Message)"
|
|
$status = "Error saving AppList.json"
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
return [PSCustomObject]@{ Id = $appId; Status = $status; ResultCode = 1 }
|
|
}
|
|
}
|
|
else {
|
|
WriteLog "'$appName' already exists in '$AppListJsonPath'."
|
|
}
|
|
|
|
# Proceed with download
|
|
$status = "Downloading..."
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
|
|
# Ensure variables needed by Get-Application are accessible
|
|
# (Assuming they are available via $using: scope or global scope from main script)
|
|
# $global:AppsPath = $AppsPath # Potentially redundant
|
|
# $global:WindowsArch = $WindowsArch # Potentially redundant
|
|
# $global:orchestrationPath = $OrchestrationPath # Potentially redundant"
|
|
WriteLog "Orchestration Path: $($OrchestrationPath)"
|
|
if (-not (Test-Path -Path $OrchestrationPath -PathType Container)) {
|
|
New-Item -Path $OrchestrationPath -ItemType Directory -Force | Out-Null
|
|
}
|
|
$win32Folder = Join-Path -Path $AppsPath -ChildPath "Win32"
|
|
if ($source -eq "winget" -and -not (Test-Path -Path $win32Folder -PathType Container)) {
|
|
New-Item -Path $win32Folder -ItemType Directory -Force | Out-Null
|
|
}
|
|
$storeAppsFolder = Join-Path -Path $AppsPath -ChildPath "MSStore"
|
|
if ($source -eq "msstore" -and -not (Test-Path -Path $storeAppsFolder -PathType Container)) {
|
|
New-Item -Path $storeAppsFolder -ItemType Directory -Force | Out-Null
|
|
}
|
|
|
|
try {
|
|
# Call Get-Application (ensure it's available via dot-sourcing and uses $global:LogFile)
|
|
$resultCode = Get-Application -AppName $appName -AppId $appId -Source $source -ErrorAction Stop
|
|
|
|
# Determine status based on result code
|
|
switch ($resultCode) {
|
|
0 { $status = "Downloaded successfully" }
|
|
1 { $status = "Error: No win32 app installers were found" }
|
|
2 { $status = "Silent install switch could not be found. Did not download." }
|
|
default { $status = "Downloaded with status: $resultCode" } # Should not happen with current Get-Application
|
|
}
|
|
|
|
# Remove app from AppList.json if silent install switch could not be found (resultCode 2)
|
|
if ($resultCode -eq 2) {
|
|
try {
|
|
if (Test-Path -Path $AppListJsonPath) {
|
|
$appListContent = Get-Content -Path $AppListJsonPath -Raw | ConvertFrom-Json
|
|
if ($appListContent.apps) {
|
|
$filteredApps = @($appListContent.apps | Where-Object { $_.id -ne $appId })
|
|
$appListContent.apps = $filteredApps
|
|
$appListContent | ConvertTo-Json -Depth 10 | Set-Content -Path $AppListJsonPath -Encoding UTF8
|
|
WriteLog "Removed '$appName' ($appId) from '$AppListJsonPath' due to missing silent install switch."
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
WriteLog "Failed to remove '$appName' from '$AppListJsonPath': $($_.Exception.Message)"
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
$status = "Error: $($_.Exception.Message)"
|
|
WriteLog "Download error for $($appName): $($_.Exception.Message)"
|
|
$resultCode = 1 # Indicate error
|
|
# Enqueue error status
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
|
|
# Remove app from AppList.json if publisher does not support download
|
|
if ($_.Exception.Message -match "does not support downloads by the publisher") {
|
|
try {
|
|
if (Test-Path -Path $AppListJsonPath) {
|
|
$appListContent = Get-Content -Path $AppListJsonPath -Raw | ConvertFrom-Json
|
|
if ($appListContent.apps) {
|
|
$filteredApps = @($appListContent.apps | Where-Object { $_.id -ne $appId })
|
|
$appListContent.apps = $filteredApps
|
|
$appListContent | ConvertTo-Json -Depth 10 | Set-Content -Path $AppListJsonPath -Encoding UTF8
|
|
WriteLog "Removed '$appName' ($appId) from '$AppListJsonPath' due to publisher download restriction."
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
WriteLog "Failed to remove '$appName' from '$AppListJsonPath': $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
}
|
|
} # End if (-not $appFound)
|
|
|
|
}
|
|
catch {
|
|
$status = "Error: $($_.Exception.Message)"
|
|
WriteLog "Unexpected error in Start-WingetAppDownloadTask for $($appName): $($_.Exception.Message)"
|
|
$resultCode = 1 # Indicate error
|
|
# Enqueue error status
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
}
|
|
finally {
|
|
# Ensure status is not empty before returning
|
|
if ([string]::IsNullOrEmpty($status)) {
|
|
$status = "Error: Unknown failure" # Provide a default error status
|
|
WriteLog "Status was empty for $appName ($appId), setting to default error."
|
|
if ($resultCode -ne 0 -and $resultCode -ne 1 -and $resultCode -ne 2) {
|
|
$resultCode = -1 # Ensure resultCode reflects an error if status was empty
|
|
}
|
|
# Enqueue the final (error) status if it was previously empty
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
}
|
|
elseif ($resultCode -ne 0) {
|
|
# Enqueue the final status if it's an error (already set in try/catch)
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
}
|
|
else {
|
|
# Enqueue the final success status
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appId -Status $status
|
|
}
|
|
}
|
|
|
|
# Prepare the return object as a Hashtable
|
|
$returnObject = @{ Id = $appId; Status = $status; ResultCode = $resultCode }
|
|
|
|
# Return the final status and result code as a Hashtable
|
|
return $returnObject
|
|
}
|
|
|
|
# Function to copy a single BYO application (Modified for ForEach-Object -Parallel)
|
|
function Start-CopyBYOApplicationTask {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[PSCustomObject]$ApplicationItemData, # Pass data, not the UI object
|
|
[Parameter(Mandatory)]
|
|
[string]$AppsPath, # Pass necessary path
|
|
[Parameter(Mandatory = $true)]
|
|
[System.Collections.Concurrent.ConcurrentQueue[hashtable]]$ProgressQueue # Add queue parameter
|
|
# REMOVED: UI-related parameters
|
|
)
|
|
|
|
$priority = $ApplicationItemData.Priority
|
|
$appName = $ApplicationItemData.Name
|
|
$commandLine = $ApplicationItemData.CommandLine
|
|
$arguments = $ApplicationItemData.Arguments
|
|
$sourcePath = $ApplicationItemData.Source
|
|
$status = "Starting..." # Initial local status
|
|
$success = $false
|
|
|
|
# Initial status update
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appName -Status $status
|
|
|
|
if ([string]::IsNullOrWhiteSpace($AppsPath)) {
|
|
$status = "Error: Apps Path not set"
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appName -Status $status
|
|
WriteLog "Copy error for $($appName): Apps Path not set."
|
|
return [PSCustomObject]@{ Name = $appName; Status = $status; Success = $success }
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($sourcePath)) {
|
|
$status = "No source specified"
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appName -Status $status
|
|
# This isn't an error, just nothing to do. Consider it success.
|
|
$success = $true
|
|
return [PSCustomObject]@{ Name = $appName; Status = $status; Success = $success }
|
|
}
|
|
|
|
if (-not (Test-Path -Path $sourcePath -PathType Container)) {
|
|
$status = "Error: Source path not found"
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appName -Status $status
|
|
WriteLog "Copy error for $($appName): Source path '$sourcePath' not found."
|
|
return [PSCustomObject]@{ Name = $appName; Status = $status; Success = $success }
|
|
}
|
|
|
|
$win32BasePath = Join-Path -Path $AppsPath -ChildPath "Win32"
|
|
$destinationPath = Join-Path -Path $win32BasePath -ChildPath $appName
|
|
|
|
try {
|
|
# Check destination
|
|
if (Test-Path -Path $destinationPath -PathType Container) {
|
|
$folderSize = (Get-ChildItem -Path $destinationPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
|
|
if ($folderSize -gt 1MB) {
|
|
$status = "Already copied"
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appName -Status $status
|
|
WriteLog "Skipping copy for $($appName): Destination '$destinationPath' exists and has content."
|
|
$success = $true
|
|
return [PSCustomObject]@{ Name = $appName; Status = $status; Success = $success }
|
|
}
|
|
else {
|
|
WriteLog "Destination '$destinationPath' exists but is empty/small. Proceeding with copy."
|
|
}
|
|
}
|
|
|
|
# Ensure base directory exists
|
|
if (-not (Test-Path -Path $win32BasePath -PathType Container)) {
|
|
New-Item -Path $win32BasePath -ItemType Directory -Force | Out-Null
|
|
WriteLog "Created directory: $win32BasePath"
|
|
}
|
|
|
|
# Perform the copy
|
|
$status = "Copying..."
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appName -Status $status
|
|
WriteLog "Copying '$sourcePath' to '$destinationPath'..."
|
|
Copy-Item -Path $sourcePath -Destination $destinationPath -Recurse -Force -ErrorAction Stop
|
|
$status = "Copied successfully"
|
|
$success = $true
|
|
WriteLog "Successfully copied '$appName' to '$destinationPath'."
|
|
|
|
# ------------------------------------------------------------------
|
|
# Update (or create) UserAppList.json with the copied application
|
|
# ------------------------------------------------------------------
|
|
try {
|
|
WriteLog "Updating UserAppList.json for '$appName'..."
|
|
$userAppListPath = Join-Path -Path $AppsPath -ChildPath 'UserAppList.json'
|
|
|
|
# Build the new entry
|
|
$newEntry = [pscustomobject]@{
|
|
Priority = $priority
|
|
Name = $appName
|
|
CommandLine = $commandLine
|
|
Arguments = $arguments
|
|
Source = $sourcePath
|
|
}
|
|
|
|
# Load existing list if present, ensuring it's always an array
|
|
if (Test-Path -Path $userAppListPath) {
|
|
try {
|
|
# Attempt to load and ensure it's an array
|
|
$appList = @(Get-Content -Path $userAppListPath -Raw | ConvertFrom-Json -ErrorAction Stop)
|
|
}
|
|
catch {
|
|
WriteLog "Warning: Could not parse '$userAppListPath' or it's not a valid JSON array. Initializing as empty array. Error: $($_.Exception.Message)"
|
|
$appList = @() # Initialize as empty array on error
|
|
}
|
|
}
|
|
else {
|
|
$appList = @() # Initialize as empty array if file doesn't exist
|
|
}
|
|
|
|
# Ensure $appList is an array even if ConvertFrom-Json returned $null or a single object somehow
|
|
if ($null -eq $appList -or $appList -isnot [array]) {
|
|
# If it was a single object, wrap it in an array. Otherwise, start fresh.
|
|
$appList = if ($null -ne $appList) { @($appList) } else { @() }
|
|
}
|
|
|
|
# Skip adding if an entry with the same Name already exists
|
|
if (-not ($appList | Where-Object { $_.Name -eq $newEntry.Name })) {
|
|
# Now $appList is guaranteed to be an array, so += is safe
|
|
$appList += $newEntry
|
|
# Sort by Priority before saving
|
|
$sortedAppList = $appList | Sort-Object Priority
|
|
$sortedAppList | ConvertTo-Json -Depth 10 | Set-Content -Path $userAppListPath -Encoding UTF8
|
|
WriteLog "Added '$($newEntry.Name)' to '$userAppListPath'."
|
|
}
|
|
else {
|
|
WriteLog "'$appName' already exists in '$userAppListPath'."
|
|
}
|
|
}
|
|
catch {
|
|
WriteLog "Failed to update UserAppList.json for '$appName': $($_.Exception.Message)"
|
|
}
|
|
|
|
}
|
|
catch {
|
|
$errorMessage = $_.Exception.Message
|
|
$status = "Error: $($errorMessage)"
|
|
WriteLog "Copy error for $($appName): $($errorMessage)"
|
|
$success = $false
|
|
# Enqueue error status
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appName -Status $status
|
|
}
|
|
|
|
# Enqueue final success status if applicable
|
|
if ($success) {
|
|
Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $appName -Status $status
|
|
}
|
|
|
|
# Return the final status
|
|
return [PSCustomObject]@{ Name = $appName; Status = $status; Success = $success }
|
|
}
|
|
|
|
# --------------------------------------------------------------------------
|
|
# SECTION: UI Configuration
|
|
# --------------------------------------------------------------------------
|
|
function Get-UIConfig {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[psobject]$State
|
|
)
|
|
# Create hash to store configuration
|
|
$config = [ordered]@{
|
|
AllowExternalHardDiskMedia = $State.Controls.chkAllowExternalHardDiskMedia.IsChecked
|
|
AllowVHDXCaching = $State.Controls.chkAllowVHDXCaching.IsChecked
|
|
AppListPath = $State.Controls.txtAppListJsonPath.Text
|
|
AppsPath = $State.Controls.txtApplicationPath.Text
|
|
AppsScriptVariables = if ($State.Controls.chkDefineAppsScriptVariables.IsChecked) {
|
|
$vars = @{}
|
|
foreach ($item in $State.Data.appsScriptVariablesDataList) {
|
|
$vars[$item.Key] = $item.Value
|
|
}
|
|
if ($vars.Count -gt 0) { $vars } else { $null }
|
|
}
|
|
else { $null }
|
|
BuildUSBDrive = $State.Controls.chkBuildUSBDriveEnable.IsChecked
|
|
CleanupAppsISO = $State.Controls.chkCleanupAppsISO.IsChecked
|
|
CleanupCaptureISO = $State.Controls.chkCleanupCaptureISO.IsChecked
|
|
CleanupDeployISO = $State.Controls.chkCleanupDeployISO.IsChecked
|
|
CleanupDrivers = $State.Controls.chkCleanupDrivers.IsChecked
|
|
CompactOS = $State.Controls.chkCompactOS.IsChecked
|
|
CompressDownloadedDriversToWim = $State.Controls.chkCompressDriversToWIM.IsChecked
|
|
CopyAutopilot = $State.Controls.chkCopyAutopilot.IsChecked
|
|
CopyDrivers = $State.Controls.chkCopyDrivers.IsChecked
|
|
CopyOfficeConfigXML = $State.Controls.chkCopyOfficeConfigXML.IsChecked
|
|
CopyPEDrivers = $State.Controls.chkCopyPEDrivers.IsChecked
|
|
CopyPPKG = $State.Controls.chkCopyPPKG.IsChecked
|
|
CopyUnattend = $State.Controls.chkCopyUnattend.IsChecked
|
|
CreateCaptureMedia = $State.Controls.chkCreateCaptureMedia.IsChecked
|
|
CreateDeploymentMedia = $State.Controls.chkCreateDeploymentMedia.IsChecked
|
|
CustomFFUNameTemplate = $State.Controls.txtCustomFFUNameTemplate.Text
|
|
Disksize = [int64]$State.Controls.txtDiskSize.Text * 1GB
|
|
DownloadDrivers = $State.Controls.chkDownloadDrivers.IsChecked
|
|
DriversFolder = $State.Controls.txtDriversFolder.Text
|
|
DriversJsonPath = $State.Controls.txtDriversJsonPath.Text
|
|
FFUCaptureLocation = $State.Controls.txtFFUCaptureLocation.Text
|
|
FFUDevelopmentPath = $State.Controls.txtFFUDevPath.Text
|
|
FFUPrefix = $State.Controls.txtVMNamePrefix.Text
|
|
InstallApps = $State.Controls.chkInstallApps.IsChecked
|
|
InstallDrivers = $State.Controls.chkInstallDrivers.IsChecked
|
|
InstallOffice = $State.Controls.chkInstallOffice.IsChecked
|
|
InstallWingetApps = $State.Controls.chkInstallWingetApps.IsChecked
|
|
ISOPath = $State.Controls.txtISOPath.Text
|
|
LogicalSectorSizeBytes = [int]$State.Controls.cmbLogicalSectorSize.SelectedItem.Content
|
|
Make = $State.Controls.cmbMake.SelectedItem
|
|
MediaType = $State.Controls.cmbMediaType.SelectedItem
|
|
Memory = [int64]$State.Controls.txtMemory.Text * 1GB
|
|
Model = if ($State.Controls.chkDownloadDrivers.IsChecked) {
|
|
$selectedModels = $State.Controls.lstDriverModels.Items | Where-Object { $_.IsSelected }
|
|
if ($selectedModels.Count -ge 1) {
|
|
$selectedModels[0].Model
|
|
}
|
|
else {
|
|
$null
|
|
}
|
|
}
|
|
else {
|
|
$null
|
|
}
|
|
OfficeConfigXMLFile = $State.Controls.txtOfficeConfigXMLFilePath.Text
|
|
OfficePath = $State.Controls.txtOfficePath.Text
|
|
Optimize = $State.Controls.chkOptimize.IsChecked
|
|
OptionalFeatures = $State.Controls.txtOptionalFeatures.Text
|
|
OrchestrationPath = "$($State.Controls.txtApplicationPath.Text)\Orchestration"
|
|
PEDriversFolder = $State.Controls.txtPEDriversFolder.Text
|
|
Processors = [int]$State.Controls.txtProcessors.Text
|
|
ProductKey = $State.Controls.txtProductKey.Text
|
|
PromptExternalHardDiskMedia = $State.Controls.chkPromptExternalHardDiskMedia.IsChecked
|
|
RemoveApps = $State.Controls.chkRemoveApps.IsChecked
|
|
RemoveFFU = $State.Controls.chkRemoveFFU.IsChecked
|
|
RemoveUpdates = $State.Controls.chkRemoveUpdates.IsChecked
|
|
ShareName = $State.Controls.txtShareName.Text
|
|
UpdateADK = $State.Controls.chkUpdateADK.IsChecked
|
|
UpdateEdge = $State.Controls.chkUpdateEdge.IsChecked
|
|
UpdateLatestCU = $State.Controls.chkUpdateLatestCU.IsChecked
|
|
UpdateLatestDefender = $State.Controls.chkUpdateLatestDefender.IsChecked
|
|
UpdateLatestMicrocode = $State.Controls.chkUpdateLatestMicrocode.IsChecked
|
|
UpdateLatestMSRT = $State.Controls.chkUpdateLatestMSRT.IsChecked
|
|
UpdateLatestNet = $State.Controls.chkUpdateLatestNet.IsChecked
|
|
UpdateOneDrive = $State.Controls.chkUpdateOneDrive.IsChecked
|
|
UpdatePreviewCU = $State.Controls.chkUpdatePreviewCU.IsChecked
|
|
UserAppListPath = "$($State.Controls.txtApplicationPath.Text)\UserAppList.json"
|
|
USBDriveList = @{}
|
|
Username = $State.Controls.txtUsername.Text
|
|
VMHostIPAddress = $State.Controls.txtVMHostIPAddress.Text
|
|
VMLocation = $State.Controls.txtVMLocation.Text
|
|
VMSwitchName = if ($State.Controls.cmbVMSwitchName.SelectedItem -eq 'Other') {
|
|
$State.Controls.txtCustomVMSwitchName.Text
|
|
}
|
|
else {
|
|
$State.Controls.cmbVMSwitchName.SelectedItem
|
|
}
|
|
WindowsArch = $State.Controls.cmbWindowsArch.SelectedItem
|
|
WindowsLang = $State.Controls.cmbWindowsLang.SelectedItem
|
|
WindowsRelease = [int]$State.Controls.cmbWindowsRelease.SelectedItem.Value
|
|
WindowsSKU = $State.Controls.cmbWindowsSKU.SelectedItem
|
|
WindowsVersion = $State.Controls.cmbWindowsVersion.SelectedItem
|
|
}
|
|
|
|
$State.Controls.lstUSBDrives.Items | Where-Object { $_.IsSelected } | ForEach-Object {
|
|
$config.USBDriveList[$_.Model] = $_.SerialNumber
|
|
}
|
|
|
|
return $config
|
|
}
|
|
|
|
# --------------------------------------------------------------------------
|
|
# SECTION: UI Initialization Functions
|
|
# --------------------------------------------------------------------------
|
|
|
|
function Initialize-UIControls {
|
|
param([PSCustomObject]$State)
|
|
WriteLog "Initializing UI control references..."
|
|
$window = $State.Window
|
|
# Find all controls ONCE and store them in the state object
|
|
$State.Controls.cmbWindowsRelease = $window.FindName('cmbWindowsRelease')
|
|
$State.Controls.cmbWindowsVersion = $window.FindName('cmbWindowsVersion')
|
|
$State.Controls.txtISOPath = $window.FindName('txtISOPath')
|
|
$State.Controls.btnBrowseISO = $window.FindName('btnBrowseISO')
|
|
$State.Controls.cmbWindowsArch = $window.FindName('cmbWindowsArch')
|
|
$State.Controls.cmbWindowsLang = $window.FindName('cmbWindowsLang')
|
|
$State.Controls.cmbWindowsSKU = $window.FindName('cmbWindowsSKU')
|
|
$State.Controls.cmbMediaType = $window.FindName('cmbMediaType')
|
|
$State.Controls.txtOptionalFeatures = $window.FindName('txtOptionalFeatures')
|
|
$State.Controls.featuresPanel = $window.FindName('stackFeaturesContainer')
|
|
$State.Controls.chkDownloadDrivers = $window.FindName('chkDownloadDrivers')
|
|
$State.Controls.cmbMake = $window.FindName('cmbMake')
|
|
$State.Controls.spMakeSection = $window.FindName('spMakeSection')
|
|
$State.Controls.btnGetModels = $window.FindName('btnGetModels')
|
|
$State.Controls.spModelFilterSection = $window.FindName('spModelFilterSection')
|
|
$State.Controls.txtModelFilter = $window.FindName('txtModelFilter')
|
|
$State.Controls.lstDriverModels = $window.FindName('lstDriverModels')
|
|
$State.Controls.spDriverActionButtons = $window.FindName('spDriverActionButtons')
|
|
$State.Controls.btnSaveDriversJson = $window.FindName('btnSaveDriversJson')
|
|
$State.Controls.btnImportDriversJson = $window.FindName('btnImportDriversJson')
|
|
$State.Controls.btnDownloadSelectedDrivers = $window.FindName('btnDownloadSelectedDrivers')
|
|
$State.Controls.btnClearDriverList = $window.FindName('btnClearDriverList')
|
|
$State.Controls.chkInstallOffice = $window.FindName('chkInstallOffice')
|
|
$State.Controls.chkInstallApps = $window.FindName('chkInstallApps')
|
|
$State.Controls.OfficePathStackPanel = $window.FindName('OfficePathStackPanel')
|
|
$State.Controls.OfficePathGrid = $window.FindName('OfficePathGrid')
|
|
$State.Controls.CopyOfficeConfigXMLStackPanel = $window.FindName('CopyOfficeConfigXMLStackPanel')
|
|
$State.Controls.OfficeConfigurationXMLFileStackPanel = $window.FindName('OfficeConfigurationXMLFileStackPanel')
|
|
$State.Controls.OfficeConfigurationXMLFileGrid = $window.FindName('OfficeConfigurationXMLFileGrid')
|
|
$State.Controls.chkCopyOfficeConfigXML = $window.FindName('chkCopyOfficeConfigXML')
|
|
$State.Controls.chkLatestCU = $window.FindName('chkUpdateLatestCU')
|
|
$State.Controls.chkPreviewCU = $window.FindName('chkUpdatePreviewCU')
|
|
$State.Controls.btnCheckUSBDrives = $window.FindName('btnCheckUSBDrives')
|
|
$State.Controls.lstUSBDrives = $window.FindName('lstUSBDrives')
|
|
$State.Controls.chkSelectAllUSBDrives = $window.FindName('chkSelectAllUSBDrives')
|
|
$State.Controls.chkBuildUSBDriveEnable = $window.FindName('chkBuildUSBDriveEnable')
|
|
$State.Controls.usbSection = $window.FindName('usbDriveSection')
|
|
$State.Controls.chkSelectSpecificUSBDrives = $window.FindName('chkSelectSpecificUSBDrives')
|
|
$State.Controls.usbSelectionPanel = $window.FindName('usbDriveSelectionPanel')
|
|
$State.Controls.chkAllowExternalHardDiskMedia = $window.FindName('chkAllowExternalHardDiskMedia')
|
|
$State.Controls.chkPromptExternalHardDiskMedia = $window.FindName('chkPromptExternalHardDiskMedia')
|
|
$State.Controls.chkInstallWingetApps = $window.FindName('chkInstallWingetApps')
|
|
$State.Controls.wingetPanel = $window.FindName('wingetPanel')
|
|
$State.Controls.btnCheckWingetModule = $window.FindName('btnCheckWingetModule')
|
|
$State.Controls.txtWingetVersion = $window.FindName('txtWingetVersion')
|
|
$State.Controls.txtWingetModuleVersion = $window.FindName('txtWingetModuleVersion')
|
|
$State.Controls.applicationPathPanel = $window.FindName('applicationPathPanel')
|
|
$State.Controls.appListJsonPathPanel = $window.FindName('appListJsonPathPanel')
|
|
$State.Controls.btnBrowseApplicationPath = $window.FindName('btnBrowseApplicationPath')
|
|
$State.Controls.btnBrowseAppListJsonPath = $window.FindName('btnBrowseAppListJsonPath')
|
|
$State.Controls.chkBringYourOwnApps = $window.FindName('chkBringYourOwnApps')
|
|
$State.Controls.byoApplicationPanel = $window.FindName('byoApplicationPanel')
|
|
$State.Controls.wingetSearchPanel = $window.FindName('wingetSearchPanel')
|
|
$State.Controls.txtWingetSearch = $window.FindName('txtWingetSearch')
|
|
$State.Controls.btnWingetSearch = $window.FindName('btnWingetSearch')
|
|
$State.Controls.lstWingetResults = $window.FindName('lstWingetResults')
|
|
$State.Controls.btnSaveWingetList = $window.FindName('btnSaveWingetList')
|
|
$State.Controls.btnImportWingetList = $window.FindName('btnImportWingetList')
|
|
$State.Controls.btnClearWingetList = $window.FindName('btnClearWingetList')
|
|
$State.Controls.btnDownloadSelected = $window.FindName('btnDownloadSelected')
|
|
$State.Controls.btnBrowseAppSource = $window.FindName('btnBrowseAppSource')
|
|
$State.Controls.btnBrowseFFUDevPath = $window.FindName('btnBrowseFFUDevPath')
|
|
$State.Controls.btnBrowseFFUCaptureLocation = $window.FindName('btnBrowseFFUCaptureLocation')
|
|
$State.Controls.btnBrowseOfficePath = $window.FindName('btnBrowseOfficePath')
|
|
$State.Controls.btnBrowseDriversFolder = $window.FindName('btnBrowseDriversFolder')
|
|
$State.Controls.btnBrowsePEDriversFolder = $window.FindName('btnBrowsePEDriversFolder')
|
|
$State.Controls.txtAppName = $window.FindName('txtAppName')
|
|
$State.Controls.txtAppCommandLine = $window.FindName('txtAppCommandLine')
|
|
$State.Controls.txtAppArguments = $window.FindName('txtAppArguments')
|
|
$State.Controls.txtAppSource = $window.FindName('txtAppSource')
|
|
$State.Controls.btnAddApplication = $window.FindName('btnAddApplication')
|
|
$State.Controls.btnSaveBYOApplications = $window.FindName('btnSaveBYOApplications')
|
|
$State.Controls.btnLoadBYOApplications = $window.FindName('btnLoadBYOApplications')
|
|
$State.Controls.btnClearBYOApplications = $window.FindName('btnClearBYOApplications')
|
|
$State.Controls.btnCopyBYOApps = $window.FindName('btnCopyBYOApps')
|
|
$State.Controls.lstApplications = $window.FindName('lstApplications')
|
|
$State.Controls.btnMoveTop = $window.FindName('btnMoveTop')
|
|
$State.Controls.btnMoveUp = $window.FindName('btnMoveUp')
|
|
$State.Controls.btnMoveDown = $window.FindName('btnMoveDown')
|
|
$State.Controls.btnMoveBottom = $window.FindName('btnMoveBottom')
|
|
$State.Controls.txtStatus = $window.FindName('txtStatus')
|
|
$State.Controls.pbOverallProgress = $window.FindName('progressBar')
|
|
$State.Controls.txtOverallStatus = $window.FindName('txtStatus')
|
|
$State.Controls.cmbVMSwitchName = $window.FindName('cmbVMSwitchName')
|
|
$State.Controls.txtVMHostIPAddress = $window.FindName('txtVMHostIPAddress')
|
|
$State.Controls.txtCustomVMSwitchName = $window.FindName('txtCustomVMSwitchName')
|
|
$State.Controls.txtFFUDevPath = $window.FindName('txtFFUDevPath')
|
|
$State.Controls.txtCustomFFUNameTemplate = $window.FindName('txtCustomFFUNameTemplate')
|
|
$State.Controls.txtFFUCaptureLocation = $window.FindName('txtFFUCaptureLocation')
|
|
$State.Controls.txtShareName = $window.FindName('txtShareName')
|
|
$State.Controls.txtUsername = $window.FindName('txtUsername')
|
|
$State.Controls.chkCompactOS = $window.FindName('chkCompactOS')
|
|
$State.Controls.chkOptimize = $window.FindName('chkOptimize')
|
|
$State.Controls.chkAllowVHDXCaching = $window.FindName('chkAllowVHDXCaching')
|
|
$State.Controls.chkCreateCaptureMedia = $window.FindName('chkCreateCaptureMedia')
|
|
$State.Controls.chkCreateDeploymentMedia = $window.FindName('chkCreateDeploymentMedia')
|
|
$State.Controls.chkCopyAutopilot = $window.FindName('chkCopyAutopilot')
|
|
$State.Controls.chkCopyUnattend = $window.FindName('chkCopyUnattend')
|
|
$State.Controls.chkCopyPPKG = $window.FindName('chkCopyPPKG')
|
|
$State.Controls.chkCleanupAppsISO = $window.FindName('chkCleanupAppsISO')
|
|
$State.Controls.chkCleanupCaptureISO = $window.FindName('chkCleanupCaptureISO')
|
|
$State.Controls.chkCleanupDeployISO = $window.FindName('chkCleanupDeployISO')
|
|
$State.Controls.chkCleanupDrivers = $window.FindName('chkCleanupDrivers')
|
|
$State.Controls.chkRemoveFFU = $window.FindName('chkRemoveFFU')
|
|
$State.Controls.txtDiskSize = $window.FindName('txtDiskSize')
|
|
$State.Controls.txtMemory = $window.FindName('txtMemory')
|
|
$State.Controls.txtProcessors = $window.FindName('txtProcessors')
|
|
$State.Controls.txtVMLocation = $window.FindName('txtVMLocation')
|
|
$State.Controls.txtVMNamePrefix = $window.FindName('txtVMNamePrefix')
|
|
$State.Controls.cmbLogicalSectorSize = $window.FindName('cmbLogicalSectorSize')
|
|
$State.Controls.txtProductKey = $window.FindName('txtProductKey')
|
|
$State.Controls.txtOfficePath = $window.FindName('txtOfficePath')
|
|
$State.Controls.txtOfficeConfigXMLFilePath = $window.FindName('txtOfficeConfigXMLFilePath')
|
|
$State.Controls.txtDriversFolder = $window.FindName('txtDriversFolder')
|
|
$State.Controls.txtPEDriversFolder = $window.FindName('txtPEDriversFolder')
|
|
$State.Controls.chkCopyPEDrivers = $window.FindName('chkCopyPEDrivers')
|
|
$State.Controls.chkUpdateLatestCU = $window.FindName('chkUpdateLatestCU')
|
|
$State.Controls.chkUpdateLatestNet = $window.FindName('chkUpdateLatestNet')
|
|
$State.Controls.chkUpdateLatestDefender = $window.FindName('chkUpdateLatestDefender')
|
|
$State.Controls.chkUpdateEdge = $window.FindName('chkUpdateEdge')
|
|
$State.Controls.chkUpdateOneDrive = $window.FindName('chkUpdateOneDrive')
|
|
$State.Controls.chkUpdateLatestMSRT = $window.FindName('chkUpdateLatestMSRT')
|
|
$State.Controls.chkUpdatePreviewCU = $window.FindName('chkUpdatePreviewCU')
|
|
$State.Controls.txtApplicationPath = $window.FindName('txtApplicationPath')
|
|
$State.Controls.txtAppListJsonPath = $window.FindName('txtAppListJsonPath')
|
|
$State.Controls.chkInstallDrivers = $window.FindName('chkInstallDrivers')
|
|
$State.Controls.chkCopyDrivers = $window.FindName('chkCopyDrivers')
|
|
$State.Controls.chkCompressDriversToWIM = $window.FindName('chkCompressDriversToWIM')
|
|
$State.Controls.chkRemoveApps = $window.FindName('chkRemoveApps')
|
|
$State.Controls.chkRemoveUpdates = $window.FindName('chkRemoveUpdates')
|
|
$State.Controls.chkUpdateLatestMicrocode = $window.FindName('chkUpdateLatestMicrocode')
|
|
$State.Controls.chkDefineAppsScriptVariables = $window.FindName('chkDefineAppsScriptVariables')
|
|
$State.Controls.appsScriptVariablesPanel = $window.FindName('appsScriptVariablesPanel')
|
|
$State.Controls.txtAppsScriptKey = $window.FindName('txtAppsScriptKey')
|
|
$State.Controls.txtAppsScriptValue = $window.FindName('txtAppsScriptValue')
|
|
$State.Controls.btnAddAppsScriptVariable = $window.FindName('btnAddAppsScriptVariable')
|
|
$State.Controls.lstAppsScriptVariables = $window.FindName('lstAppsScriptVariables')
|
|
$State.Controls.btnRemoveSelectedAppsScriptVariables = $window.FindName('btnRemoveSelectedAppsScriptVariables')
|
|
$State.Controls.btnClearAppsScriptVariables = $window.FindName('btnClearAppsScriptVariables')
|
|
$State.Controls.txtDriversJsonPath = $window.FindName('txtDriversJsonPath')
|
|
$State.Controls.btnBrowseDriversJsonPath = $window.FindName('btnBrowseDriversJsonPath')
|
|
$State.Controls.chkUpdateADK = $window.FindName('chkUpdateADK')
|
|
}
|
|
|
|
# --------------------------------------------------------------------------
|
|
# SECTION: Module Export
|
|
# --------------------------------------------------------------------------
|
|
|
|
# Export only the functions intended for public use by the UI script
|
|
Export-ModuleMember -Function *
|