workflow/vNext/ui/MainWindow.ps1
|
<#!
.SYNOPSIS Interactive picker for OSDCloud operating system catalog entries using WPF UI (Fluent Design). #> [CmdletBinding()] param() #================================================ Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase # Load Wpf.Ui.dll if not already loaded (handles the case where the module did not pre-load it) try { if (!([System.Management.Automation.PSTypeName]'Wpf.Ui.Controls.Button').Type) { $moduleBase = Split-Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) -Parent if ($PSVersionTable.PSEdition -eq 'Desktop') { Add-Type -Path (Join-Path $moduleBase 'types\wpfui\net481\Wpf.Ui.dll') } else { Add-Type -Path (Join-Path $moduleBase 'types\wpfui\net6.0-windows\Wpf.Ui.dll') } } } catch { Write-Warning "[MainWindow.ps1] Could not load Wpf.Ui.dll: $_" } #================================================ # Variables $deviceBiosReleaseDate = $global:OSDCloudDevice.BiosReleaseDate $deviceBiosVersion = $global:OSDCloudDevice.BiosVersion $deviceOSDManufacturer = $global:OSDCloudDevice.OSDManufacturer $deviceOSDModel = $global:OSDCloudDevice.OSDModel $deviceOSDProduct = $global:OSDCloudDevice.OSDProduct $deviceComputerSystemSKU = $global:OSDCloudDevice.ComputerSystemSKU $deviceIsAutopilotSpec = $global:OSDCloudDevice.IsAutopilotSpec $deviceIsTpmSpec = $global:OSDCloudDevice.IsTpmSpec $deviceSerialNumber = $global:OSDCloudDevice.SerialNumber $deviceUUID = $global:OSDCloudDevice.UUID $deviceHardwareHash = $global:OSDCloudDevice.HardwareHash $getOSDCloudModuleVersion = Get-OSDCloudModuleVersion #================================================ # WPFUI Fluent theme initialization # Ensure a WPF Application singleton exists (WPFUI requires it for theme resource resolution) if (-not [System.Windows.Application]::Current) { try { $null = [System.Windows.Application]::new() } catch { Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] Could not create WPF Application: $_" } } # Merge WPFUI theme and control resource dictionaries into the Application resources. # ThemesDictionary and ControlsDictionary are WPFUI markup types that resolve their # own pack URIs, so no hard-coded Source URI is required here. if ([System.Windows.Application]::Current) { try { $themeDict = [Wpf.Ui.Markup.ThemesDictionary]::new() $themeDict.Theme = [Wpf.Ui.Appearance.ThemeType]::Light $controlsDict = [Wpf.Ui.Markup.ControlsDictionary]::new() $appResources = [System.Windows.Application]::Current.Resources $appResources.MergedDictionaries.Add($themeDict) $appResources.MergedDictionaries.Add($controlsDict) Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] WPFUI theme merged successfully" } catch { Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] WPFUI theme merge skipped: $_" } } #================================================ # XAML $xamlfile = Get-Item -Path "$PSScriptRoot\MainWindow.xaml" $xaml = Get-Content $xamlfile.FullName $stringReader = [System.IO.StringReader]::new($xaml) $xmlReader = [System.Xml.XmlReader]::Create($stringReader) $window = [Windows.Markup.XamlReader]::Load($xmlReader) #================================================ # Window Title $deviceTitleParts = @() if (-not [string]::IsNullOrWhiteSpace($getOSDCloudModuleVersion)) { $deviceTitleParts += $getOSDCloudModuleVersion } if ($deviceTitleParts.Count -gt 0) { $window.Title = "OSDCloud version $($deviceTitleParts -join ' - ')" } #================================================ # Logo $logoImage = $window.FindName('LogoImage') if ($logoImage) { $logoImage.Source = "$PSScriptRoot\logo.png" } #================================================ # Navigation panel switching $NavListBox = $window.FindName('NavListBox') $DevicePanel = $window.FindName('DevicePanel') $DriversPanel = $window.FindName('DriversPanel') $DiskPanel = $window.FindName('DiskPanel') $DeploymentPanel = $window.FindName('DeploymentPanel') $StepsPanel = $window.FindName('StepsPanel') $PrivacyPanel = $window.FindName('PrivacyPanel') $NavListBox.SelectedIndex = 0 $NavListBox.Add_SelectionChanged({ $tag = if ($NavListBox.SelectedItem) { [string]$NavListBox.SelectedItem.Tag } else { 'Deployment' } $DevicePanel.Visibility = if ($tag -eq 'Device') { [System.Windows.Visibility]::Visible } else { [System.Windows.Visibility]::Collapsed } $DriversPanel.Visibility = if ($tag -eq 'Drivers') { [System.Windows.Visibility]::Visible } else { [System.Windows.Visibility]::Collapsed } $DiskPanel.Visibility = if ($tag -eq 'Disk') { [System.Windows.Visibility]::Visible } else { [System.Windows.Visibility]::Collapsed } $DeploymentPanel.Visibility = if ($tag -eq 'Deployment') { [System.Windows.Visibility]::Visible } else { [System.Windows.Visibility]::Collapsed } $StepsPanel.Visibility = if ($tag -eq 'Steps') { [System.Windows.Visibility]::Visible } else { [System.Windows.Visibility]::Collapsed } $PrivacyPanel.Visibility = if ($tag -eq 'Privacy') { [System.Windows.Visibility]::Visible } else { [System.Windows.Visibility]::Collapsed } }) #================================================ # Menu Items $RunCmdPrompt = $window.FindName("RunCmdPrompt") $RunPowerShell = $window.FindName("RunPowerShell") $RunPwsh = $window.FindName("RunPwsh") $LogsMenuItem = $window.FindName("LogsMenuItem") $WMIMenuItem = $window.FindName("WMIMenuItem") $RunCmdPrompt.Add_Click({ try { Start-Process -FilePath "cmd.exe" } catch { [System.Windows.MessageBox]::Show("Failed to open CMD Prompt: $($_.Exception.Message)", "Error", "OK", "Error") | Out-Null } }) $RunPowerShell.Add_Click({ try { Start-Process -FilePath "powershell.exe" } catch { [System.Windows.MessageBox]::Show("Failed to open PowerShell: $($_.Exception.Message)", "Error", "OK", "Error") | Out-Null } }) if ($RunPwsh) { $pwshCommand = Get-Command -Name 'pwsh.exe' -ErrorAction SilentlyContinue if ($pwshCommand) { $script:PwshPath = $pwshCommand.Source $RunPwsh.Visibility = [System.Windows.Visibility]::Visible $RunPwsh.Add_Click({ try { Start-Process -FilePath $script:PwshPath } catch { [System.Windows.MessageBox]::Show("Failed to open PowerShell 7: $($_.Exception.Message)", "Error", "OK", "Error") | Out-Null } }) } else { $RunPwsh.Visibility = [System.Windows.Visibility]::Collapsed } } function Add-NoLogsMenuEntry { param( [Parameter(Mandatory)] [System.Windows.Controls.MenuItem]$MenuItem ) $noLogsItem = [System.Windows.Controls.MenuItem]::new() $noLogsItem.Header = 'No logs found' $noLogsItem.IsEnabled = $false $MenuItem.Items.Add($noLogsItem) | Out-Null } function Set-LogsMenuItems { $LogsMenuItem.Items.Clear() $logsRoot = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath 'osdcloud-logs' if (-not (Test-Path -LiteralPath $logsRoot)) { Add-NoLogsMenuEntry -MenuItem $LogsMenuItem return } $logFiles = Get-ChildItem -LiteralPath $logsRoot -File -ErrorAction SilentlyContinue | Sort-Object -Property Name $logFiles = $logFiles | Where-Object { $_.Name -NotLike "Win32_*.txt" } if (-not $logFiles) { Add-NoLogsMenuEntry -MenuItem $LogsMenuItem return } foreach ($logFile in $logFiles) { $logMenuItem = [System.Windows.Controls.MenuItem]::new() $logMenuItem.Header = $logFile.Name -replace '_', '__' $logMenuItem.Tag = $logFile.FullName $logMenuItem.Add_Click({ param($menuItem, $clickArgs) $logPath = [string]$menuItem.Tag if (-not (Test-Path -LiteralPath $logPath)) { [System.Windows.MessageBox]::Show('Log file not found.', 'Open Log', 'OK', 'Warning') | Out-Null return } try { Start-Process -FilePath 'notepad.exe' -ArgumentList @("`"$logPath`"") -ErrorAction Stop } catch { [System.Windows.MessageBox]::Show("Failed to open log: $($_.Exception.Message)", 'Open Log', 'OK', 'Error') | Out-Null } }) $LogsMenuItem.Items.Add($logMenuItem) | Out-Null } } Set-LogsMenuItems function Set-WMIMenuItems { $WMIMenuItem.Items.Clear() $logsRoot = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath 'osdcloud-logs' if (-not (Test-Path -LiteralPath $logsRoot)) { Add-NoLogsMenuEntry -MenuItem $WMIMenuItem return } $logFiles = Get-ChildItem -LiteralPath $logsRoot -File -ErrorAction SilentlyContinue | Sort-Object -Property Name $logFiles = $logFiles | Where-Object { $_.Name -Like "Win32_*.txt" } if (-not $logFiles) { Add-NoLogsMenuEntry -MenuItem $WMIMenuItem return } foreach ($logFile in $logFiles) { $logMenuItem = [System.Windows.Controls.MenuItem]::new() $logMenuItem.Header = $logFile.Name -replace '_', '__' $logMenuItem.Tag = $logFile.FullName $logMenuItem.Add_Click({ param($menuItem, $clickArgs) $logPath = [string]$menuItem.Tag if (-not (Test-Path -LiteralPath $logPath)) { [System.Windows.MessageBox]::Show('Log file not found.', 'Open Log', 'OK', 'Warning') | Out-Null return } try { Start-Process -FilePath 'notepad.exe' -ArgumentList @("`"$logPath`"") -ErrorAction Stop } catch { [System.Windows.MessageBox]::Show("Failed to open log: $($_.Exception.Message)", 'Open Log', 'OK', 'Error') | Out-Null } }) $WMIMenuItem.Items.Add($logMenuItem) | Out-Null } } Set-WMIMenuItems #================================================ # Task Sequence $TaskSequenceCombo = $window.FindName("TaskSequenceCombo") $taskSequenceFlows = $global:OSDCloudDeploy.Flows.Name if ($null -eq $taskSequenceFlows) { $taskSequenceFlows = @() } $TaskSequenceCombo.ItemsSource = $taskSequenceFlows $TaskSequenceCombo.SelectedIndex = 0 $TaskSequenceStepsGrid = $window.FindName("TaskSequenceStepsGrid") $script:CurrentTaskSequenceName = [string]$TaskSequenceCombo.SelectedItem $TaskSequenceStepsGrid.CanUserSortColumns = $false $TaskSequenceStepsGrid.Add_Sorting({ param($eventSource, $sortArgs) $sortArgs.Handled = $true $eventSource.Items.SortDescriptions.Clear() }) function New-StepCheckBoxColumn { param( [Parameter(Mandatory)] [string]$PropertyName ) $binding = [System.Windows.Data.Binding]::new($PropertyName) $binding.Mode = [System.Windows.Data.BindingMode]::TwoWay $binding.UpdateSourceTrigger = [System.Windows.Data.UpdateSourceTrigger]::PropertyChanged $binding.TargetNullValue = $false $binding.FallbackValue = $false $elementStyle = [System.Windows.Style]::new([System.Windows.Controls.CheckBox]) $elementStyle.Setters.Add([System.Windows.Setter]::new([System.Windows.Controls.CheckBox]::HorizontalAlignmentProperty, [System.Windows.HorizontalAlignment]::Center)) $elementStyle.Setters.Add([System.Windows.Setter]::new([System.Windows.Controls.CheckBox]::VerticalAlignmentProperty, [System.Windows.VerticalAlignment]::Center)) $elementStyle.Setters.Add([System.Windows.Setter]::new([System.Windows.Controls.CheckBox]::IsThreeStateProperty, $false)) $checkboxColumn = [System.Windows.Controls.DataGridCheckBoxColumn]::new() $checkboxColumn.Header = $PropertyName $checkboxColumn.Binding = $binding $checkboxColumn.ElementStyle = $elementStyle $checkboxColumn.EditingElementStyle = $elementStyle $checkboxColumn.IsReadOnly = $false $checkboxColumn.CanUserSort = $false $checkboxColumn.Width = [System.Windows.Controls.DataGridLength]::new(44) return $checkboxColumn } function Save-TaskSequenceSteps { param( [Parameter(Mandatory)] [string]$TaskSequenceName, [Parameter()] $Steps ) if ([string]::IsNullOrWhiteSpace($TaskSequenceName)) { return } $workflowTaskObject = $global:OSDCloudDeploy.Flows | Where-Object { $_.Name -eq $TaskSequenceName } | Select-Object -First 1 if (-not $workflowTaskObject) { return } if ($Steps) { $stepsArray = @($Steps) foreach ($step in $stepsArray) { if (-not (Get-Member -InputObject $step -Name 'skip' -ErrorAction SilentlyContinue)) { $step | Add-Member -MemberType NoteProperty -Name 'skip' -Value $false } if (-not (Get-Member -InputObject $step -Name 'debug' -ErrorAction SilentlyContinue)) { $step | Add-Member -MemberType NoteProperty -Name 'debug' -Value $false } if (-not (Get-Member -InputObject $step -Name 'pause' -ErrorAction SilentlyContinue)) { $step | Add-Member -MemberType NoteProperty -Name 'pause' -Value $false } if (-not (Get-Member -InputObject $step -Name 'verbose' -ErrorAction SilentlyContinue)) { $step | Add-Member -MemberType NoteProperty -Name 'verbose' -Value $false } } $workflowTaskObject.steps = $stepsArray } else { $workflowTaskObject.steps = @() } } $TaskSequenceStepsGrid.Add_AutoGeneratingColumn({ param($eventSource, $columnArgs) $columnArgs.Column.CanUserSort = $false if (@('debug', 'verbose', 'testinfullos') -icontains $columnArgs.PropertyName) { $columnArgs.Cancel = $true return } if ($columnArgs.PropertyName -ieq 'skip' -or $columnArgs.PropertyName -ieq 'pause') { $columnArgs.Column = New-StepCheckBoxColumn -PropertyName $columnArgs.PropertyName return } if ($columnArgs.PropertyName -ieq 'command') { $binding = [System.Windows.Data.Binding]::new($columnArgs.PropertyName) $binding.Mode = [System.Windows.Data.BindingMode]::TwoWay $binding.UpdateSourceTrigger = [System.Windows.Data.UpdateSourceTrigger]::PropertyChanged $columnArgs.Column.IsReadOnly = $false $columnArgs.Column.CanUserSort = $false if ($columnArgs.Column -is [System.Windows.Controls.DataGridTextColumn]) { $columnArgs.Column.Binding = $binding } return } if ($columnArgs.PropertyName -ieq 'name') { $binding = [System.Windows.Data.Binding]::new($columnArgs.PropertyName) $binding.Mode = [System.Windows.Data.BindingMode]::TwoWay $binding.UpdateSourceTrigger = [System.Windows.Data.UpdateSourceTrigger]::PropertyChanged $columnArgs.Column.IsReadOnly = $false $columnArgs.Column.CanUserSort = $false if ($columnArgs.Column -is [System.Windows.Controls.DataGridTextColumn]) { $columnArgs.Column.Binding = $binding } return } $columnArgs.Column.IsReadOnly = $true $columnArgs.Column.CanUserSort = $false }) $TaskSequenceStepsGrid.Add_AutoGeneratedColumns({ param($eventSource, $autoGeneratedColumnsArgs) $skipColumn = $eventSource.Columns | Where-Object { [string]$_.Header -ieq 'skip' } | Select-Object -First 1 if ($skipColumn) { $skipColumn.DisplayIndex = 0 } }) function Update-TaskSequenceSteps { $selectedTaskSequence = [string]$TaskSequenceCombo.SelectedItem if ([string]::IsNullOrWhiteSpace($selectedTaskSequence)) { $TaskSequenceStepsGrid.ItemsSource = $null return } $workflowTaskObject = $global:OSDCloudDeploy.Flows | Where-Object { $_.Name -eq $selectedTaskSequence } | Select-Object -First 1 if ($workflowTaskObject -and $workflowTaskObject.steps) { $steps = $workflowTaskObject.steps foreach ($step in $steps) { if (-not (Get-Member -InputObject $step -Name 'skip' -ErrorAction SilentlyContinue)) { $step | Add-Member -MemberType NoteProperty -Name 'skip' -Value $false } if (-not (Get-Member -InputObject $step -Name 'debug' -ErrorAction SilentlyContinue)) { $step | Add-Member -MemberType NoteProperty -Name 'debug' -Value $false } if (-not (Get-Member -InputObject $step -Name 'pause' -ErrorAction SilentlyContinue)) { $step | Add-Member -MemberType NoteProperty -Name 'pause' -Value $false } if (-not (Get-Member -InputObject $step -Name 'verbose' -ErrorAction SilentlyContinue)) { $step | Add-Member -MemberType NoteProperty -Name 'verbose' -Value $false } } $TaskSequenceStepsGrid.Items.SortDescriptions.Clear() $TaskSequenceStepsGrid.ItemsSource = $steps $TaskSequenceStepsGrid.Items.SortDescriptions.Clear() } else { $TaskSequenceStepsGrid.ItemsSource = $null } } $TaskSequenceCombo.Add_SelectionChanged({ if ($SummaryTaskSequenceText) { $value = [string]$TaskSequenceCombo.SelectedItem $SummaryTaskSequenceText.Text = if (-not [string]::IsNullOrWhiteSpace($value)) { $value } else { 'Not selected' } } Save-TaskSequenceSteps -TaskSequenceName $script:CurrentTaskSequenceName -Steps $TaskSequenceStepsGrid.ItemsSource $script:CurrentTaskSequenceName = [string]$TaskSequenceCombo.SelectedItem Update-TaskSequenceSteps }) Update-TaskSequenceSteps #================================================ # Local ISO Values function Get-LocalIsoFile { $localIsoFiles = @( Get-OSDCloudCache -Type ISO -ErrorAction SilentlyContinue | Where-Object { $_.Type -eq 'ISO' -and -not [string]::IsNullOrWhiteSpace([string]$_.FullName) } ) return @($localIsoFiles | Sort-Object -Property FullName -Unique) } function Get-DriverPackCacheFile { param( [Parameter()] $DriverPackObject, [Parameter()] [System.Object[]]$CacheItems ) if (-not $DriverPackObject -or -not $CacheItems) { return $null } $candidateFileNames = @() $fileName = [string]$DriverPackObject.FileName if (-not [string]::IsNullOrWhiteSpace($fileName)) { $candidateFileNames += [System.IO.Path]::GetFileName($fileName) } $url = [string]$DriverPackObject.Url if (-not [string]::IsNullOrWhiteSpace($url)) { try { $candidateFileNames += [System.IO.Path]::GetFileName(([System.Uri]$url).AbsolutePath) } catch { $candidateFileNames += [System.IO.Path]::GetFileName($url) } } $candidateFileNames = @( $candidateFileNames | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) } | Sort-Object -Unique ) if (-not $candidateFileNames) { return $null } $exactFileName = [string]$DriverPackObject.FileName if (-not [string]::IsNullOrWhiteSpace($exactFileName)) { $matchedExact = $CacheItems | Where-Object { [System.IO.Path]::GetFileName([string]$_.FullName) -ieq [System.IO.Path]::GetFileName($exactFileName) } | Select-Object -First 1 if ($matchedExact) { return $matchedExact } } return $CacheItems | Where-Object { $cacheFileName = [System.IO.Path]::GetFileName([string]$_.FullName) $candidateFileNames -icontains $cacheFileName } | Select-Object -First 1 } function Get-DriverPackCacheItems { return @( Get-OSDCloudCache -Type DriverPacks -ErrorAction SilentlyContinue | Where-Object { $_.Type -eq 'DriverPacks' -and -not [string]::IsNullOrWhiteSpace([string]$_.FullName) } ) } function Test-IsUsbDriveRoot { param( [Parameter(Mandatory)] [string]$DriveRoot ) if ($DriveRoot -notmatch '^[A-Z]:\\$') { return $false } $driveLetter = $DriveRoot.Substring(0, 1) try { $partition = Get-Partition -DriveLetter $driveLetter -ErrorAction Stop | Select-Object -First 1 if ($partition) { $disk = Get-Disk -Number $partition.DiskNumber -ErrorAction SilentlyContinue if ($disk -and [string]$disk.BusType -ieq 'USB') { return $true } } } catch { } try { $volume = Get-Volume -DriveLetter $driveLetter -ErrorAction Stop if ($volume -and ([string]$volume.DriveType -ieq 'Removable')) { return $true } } catch { } return $false } function Get-DriverPackUsbCacheTarget { param( [Parameter()] [Int64]$MinimumFreeBytes = 10GB ) $cacheRoots = @( Get-OSDCloudCache -ErrorAction SilentlyContinue | Where-Object { $_.Type -eq 'Cache' -and -not [string]::IsNullOrWhiteSpace([string]$_.DriveRoot) } ) if (-not $cacheRoots) { return $null } $eligibleTargets = foreach ($cacheRoot in $cacheRoots) { $driveRoot = [string]$cacheRoot.DriveRoot if (-not (Test-IsUsbDriveRoot -DriveRoot $driveRoot)) { continue } $driveLetter = $driveRoot.TrimEnd('\\').TrimEnd(':') $drive = Get-PSDrive -Name $driveLetter -PSProvider FileSystem -ErrorAction SilentlyContinue if (-not $drive) { continue } $freeBytes = [Int64]$drive.Free if ($freeBytes -gt $MinimumFreeBytes) { [PSCustomObject]@{ DriveRoot = $driveRoot CachePath = [string]$cacheRoot.FullName FreeBytes = $freeBytes VolumeLabel = [string]$cacheRoot.VolumeLabel } } } return $eligibleTargets | Sort-Object -Property FreeBytes -Descending | Select-Object -First 1 } function Convert-ToSafePathSegment { param( [Parameter()] [string]$Value, [Parameter()] [string]$DefaultValue = 'Unknown' ) if ([string]::IsNullOrWhiteSpace($Value)) { return $DefaultValue } $safeValue = $Value foreach ($invalidChar in [System.IO.Path]::GetInvalidFileNameChars()) { $safeValue = $safeValue.Replace([string]$invalidChar, '_') } if ([string]::IsNullOrWhiteSpace($safeValue)) { return $DefaultValue } return $safeValue } function Convert-ToSingleQuotedPowerShellLiteral { param( [Parameter(Mandatory)] [string]$Value ) return "'" + $Value.Replace("'", "''") + "'" } function Get-DriverPackFileName { param( [Parameter()] $DriverPackObject ) if (-not $DriverPackObject) { return $null } $fileName = [string]$DriverPackObject.FileName if (-not [string]::IsNullOrWhiteSpace($fileName)) { return [System.IO.Path]::GetFileName($fileName) } $url = [string]$DriverPackObject.Url if ([string]::IsNullOrWhiteSpace($url)) { return $null } try { return [System.IO.Path]::GetFileName(([System.Uri]$url).AbsolutePath) } catch { return [System.IO.Path]::GetFileName($url) } } function Get-CloudOperatingSystemFileName { param( [Parameter()] $OperatingSystemObject ) if (-not $OperatingSystemObject) { return $null } $fileName = [string]$OperatingSystemObject.FileName if (-not [string]::IsNullOrWhiteSpace($fileName)) { return [System.IO.Path]::GetFileName($fileName) } $filePath = [string]$OperatingSystemObject.FilePath if ([string]::IsNullOrWhiteSpace($filePath)) { return $null } try { return [System.IO.Path]::GetFileName(([System.Uri]$filePath).AbsolutePath) } catch { return [System.IO.Path]::GetFileName($filePath) } } function Get-CloudOperatingSystemCacheItems { return @( Get-OSDCloudCache -Type ESD, WIM -ErrorAction SilentlyContinue | Where-Object { $_.Type -in @('ESD', 'WIM') -and -not [string]::IsNullOrWhiteSpace([string]$_.FullName) } ) } function Get-CloudOperatingSystemCacheFile { param( [Parameter()] $OperatingSystemObject, [Parameter()] [System.Object[]]$CacheItems ) if (-not $OperatingSystemObject -or -not $CacheItems) { return $null } $candidateFileNames = @() $fileName = [string]$OperatingSystemObject.FileName if (-not [string]::IsNullOrWhiteSpace($fileName)) { $candidateFileNames += [System.IO.Path]::GetFileName($fileName) } $filePath = [string]$OperatingSystemObject.FilePath if (-not [string]::IsNullOrWhiteSpace($filePath)) { try { $candidateFileNames += [System.IO.Path]::GetFileName(([System.Uri]$filePath).AbsolutePath) } catch { $candidateFileNames += [System.IO.Path]::GetFileName($filePath) } } $candidateFileNames = @( $candidateFileNames | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) } | Sort-Object -Unique ) if (-not $candidateFileNames) { return $null } return $CacheItems | Where-Object { $cacheFileName = [System.IO.Path]::GetFileName([string]$_.FullName) $candidateFileNames -icontains $cacheFileName } | Select-Object -First 1 } function Get-DriverFolderRelativePath { param( [Parameter(Mandatory)] [string]$Path ) $relativePathMatch = [System.Text.RegularExpressions.Regex]::Match($Path, '(?i)(OSDCloud\\Drivers(?:\\.*)?)$') if ($relativePathMatch.Success) { return $relativePathMatch.Groups[1].Value } return $null } function Get-DriveVolumeMetadata { param( [Parameter(Mandatory)] [string]$DriveRoot ) $volume = $null if ($DriveRoot -match '^[A-Z]:\\$') { try { $volume = Get-Volume -DriveLetter $DriveRoot.Substring(0, 1) -ErrorAction Stop } catch { $volume = $null } } return [PSCustomObject]@{ DriveRoot = $DriveRoot VolumeLabel = if ($volume) { [string]$volume.FileSystemLabel } else { $null } VolumeUniqueId = if ($volume) { [string]$volume.UniqueId } else { $null } } } function Get-DriverFolderItem { $driverFolders = Get-OSDCloudCache -Type Drivers -ErrorAction SilentlyContinue | Where-Object { $_.Type -eq 'Drivers' -and -not [string]::IsNullOrWhiteSpace([string]$_.FullName) } | ForEach-Object { $folderPath = [string]$_.FullName $folderName = [System.IO.Path]::GetFileName($folderPath) $isAutoDefaultMatch = @('Auto', 'Default') -contains $folderName $isManufacturerMatch = (-not [string]::IsNullOrWhiteSpace($deviceOSDManufacturer)) -and ($folderName -ieq [string]$deviceOSDManufacturer) $isModelMatch = (-not [string]::IsNullOrWhiteSpace($deviceOSDModel)) -and ($folderName -like "*$deviceOSDModel*") $isProductMatch = (-not [string]::IsNullOrWhiteSpace($deviceOSDProduct)) -and ($folderName -like "*$deviceOSDProduct*") [PSCustomObject]@{ Name = $folderPath Path = $folderPath RelativePath = Get-DriverFolderRelativePath -Path $folderPath DriveRoot = [string]$_.DriveRoot VolumeLabel = [string]$_.VolumeLabel VolumeUniqueId = [string]$_.VolumeUniqueId IsSelected = ($isAutoDefaultMatch -or $isManufacturerMatch -or $isModelMatch -or $isProductMatch) } } return @($driverFolders | Sort-Object -Property Path -Unique) } $LocalIsoCard = $window.FindName("LocalIsoCard") $UseLocalIsoToggle = $window.FindName("UseLocalIsoToggle") $LocalIsoCombo = $window.FindName("LocalIsoCombo") $CloudOperatingSystemCard = $window.FindName("CloudOperatingSystemCard") $LocalIsoFiles = @(Get-LocalIsoFile) $DriverPackCacheItems = @(Get-DriverPackCacheItems) if ($LocalIsoFiles.Count -gt 0) { $LocalIsoCard.Visibility = [System.Windows.Visibility]::Visible $LocalIsoCombo.ItemsSource = $LocalIsoFiles $LocalIsoCombo.DisplayMemberPath = 'FullName' $LocalIsoCombo.SelectedIndex = 0 } $DriverFolderPanel = $window.FindName("DriverFolderPanel") $DriverFolderExpander = $window.FindName("DriverFolderExpander") $DriverFolderGridBorder = $window.FindName("DriverFolderGridBorder") $DriverFolderGrid = $window.FindName("DriverFolderGrid") $DriverFolderPathText = $window.FindName("DriverFolderPathText") $DriverFolderEmptyHelpText = $window.FindName("DriverFolderEmptyHelpText") $DriverFolderItems = @(Get-DriverFolderItem) if ($DriverFolderItems.Count -gt 0) { $selectedDriverFolderPaths = @() $selectedDriverFolderSelections = @() if ($global:OSDCloudDeploy.DriverFolderSelections) { $selectedDriverFolderSelections = @($global:OSDCloudDeploy.DriverFolderSelections) } if ($global:OSDCloudDeploy.DriverFolderPaths) { $selectedDriverFolderPaths = @($global:OSDCloudDeploy.DriverFolderPaths) } elseif ($global:OSDCloudDeploy.DriverFolderPath) { $selectedDriverFolderPaths = @([string]$global:OSDCloudDeploy.DriverFolderPath) } foreach ($driverFolderItem in $DriverFolderItems) { $selectionMatch = $selectedDriverFolderSelections | Where-Object { ((-not [string]::IsNullOrWhiteSpace([string]$_.Path)) -and ([string]$_.Path -eq [string]$driverFolderItem.Path)) -or ((-not [string]::IsNullOrWhiteSpace([string]$_.RelativePath)) -and ([string]$_.RelativePath -eq [string]$driverFolderItem.RelativePath) -and ([string]$_.VolumeLabel -eq [string]$driverFolderItem.VolumeLabel)) -or ((-not [string]::IsNullOrWhiteSpace([string]$_.RelativePath)) -and ([string]$_.RelativePath -eq [string]$driverFolderItem.RelativePath) -and ([string]$_.VolumeUniqueId -eq [string]$driverFolderItem.VolumeUniqueId)) } | Select-Object -First 1 if ($selectionMatch -or ($selectedDriverFolderPaths -contains [string]$driverFolderItem.Path)) { $driverFolderItem.IsSelected = $true } } $DriverFolderGrid.ItemsSource = $DriverFolderItems $DriverFolderPanel.IsEnabled = $true $DriverFolderGrid.IsEnabled = $true if ($DriverFolderGridBorder) { $DriverFolderGridBorder.Visibility = [System.Windows.Visibility]::Visible } if ($DriverFolderExpander) { $DriverFolderExpander.IsExpanded = $true } if ($DriverFolderEmptyHelpText) { $DriverFolderEmptyHelpText.Visibility = [System.Windows.Visibility]::Collapsed } } else { $DriverFolderGrid.ItemsSource = @() # Keep panel enabled so the collapsed card can still be expanded for guidance text. $DriverFolderPanel.IsEnabled = $true $DriverFolderGrid.IsEnabled = $false if ($DriverFolderGridBorder) { $DriverFolderGridBorder.Visibility = [System.Windows.Visibility]::Collapsed } if ($DriverFolderExpander) { $DriverFolderExpander.IsExpanded = $false } if ($DriverFolderEmptyHelpText) { $DriverFolderEmptyHelpText.Visibility = [System.Windows.Visibility]::Visible } $global:OSDCloudDeploy.DriverFolderName = $null $global:OSDCloudDeploy.DriverFolderPath = $null $global:OSDCloudDeploy.DriverFolderNames = @() $global:OSDCloudDeploy.DriverFolderPaths = @() $global:OSDCloudDeploy.DriverFolderSelections = @() } #================================================ # Operating System Values if ($global:OSDCloudDeploy.OperatingSystemValues) { $OperatingSystemValues = $global:OSDCloudDeploy.OperatingSystemValues Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] OperatingSystemValues = $OperatingSystemValues" } else { $OperatingSystemValues = $global:DeployOSDCloudOperatingSystems.OperatingSystem | Sort-Object -Unique | Sort-Object -Descending Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] Catalog OperatingSystemValues = $OperatingSystemValues" } $OperatingSystemCombo = $window.FindName("OperatingSystemCombo") $OperatingSystemCombo.ItemsSource = $OperatingSystemValues #================================================ # OperatingSystem Default if ($global:OSDCloudDeploy.OperatingSystem) { $OperatingSystemDefault = $global:OSDCloudDeploy.OperatingSystem Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] OperatingSystem = $OperatingSystemDefault" } if ($OperatingSystemDefault -and ($OperatingSystemValues -contains $OperatingSystemDefault)) { $OperatingSystemCombo.SelectedItem = $OperatingSystemDefault } elseif ($OperatingSystemValues) { $OperatingSystemCombo.SelectedIndex = 0 } else { $OperatingSystemCombo.SelectedIndex = -1 } #================================================ # OS Edition Values if ($global:OSDCloudDeploy.OSEditionValues.Edition) { $OSEditionValues = $global:OSDCloudDeploy.OSEditionValues.Edition Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] OSEditionValues = $OSEditionValues" } else { $OSEditionValues = @() } $OSEditionCombo = $window.FindName("OSEditionCombo") $OSEditionCombo.ItemsSource = $OSEditionValues #================================================ # OS Edition Default if ($global:OSDCloudDeploy.OSEdition) { $OSEditionDefault = $global:OSDCloudDeploy.OSEdition Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] OSEdition = $OSEditionDefault" } if ($OSEditionDefault) { $OSEditionCombo.SelectedItem = $OSEditionDefault } elseif ($OperatingSystemValues) { $OSEditionCombo.SelectedIndex = 0 } else { $OSEditionCombo.SelectedIndex = -1 } #================================================ # OS Activation Values if ($global:OSDCloudDeploy.OSActivationValues) { $OSActivationValues = $global:OSDCloudDeploy.OSActivationValues Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] OSActivationValues = $OSActivationValues" } else { $OSActivationValues = @() } $OSActivationCombo = $window.FindName("OSActivationCombo") $OSActivationCombo.ItemsSource = $OSActivationValues #================================================ # OS Activation Default if ($global:OSDCloudDeploy.OSActivation) { $OSActivationDefault = $global:OSDCloudDeploy.OSActivation Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] OSActivation = $OSActivationDefault" } if ($OSActivationDefault -and ($OSActivationValues -contains $OSActivationDefault)) { $OSActivationCombo.SelectedItem = $OSActivationDefault } elseif ($OSActivationValues) { $OSActivationCombo.SelectedIndex = 0 } else { $OSActivationCombo.SelectedIndex = -1 } #================================================ # OS Language Code Values if ($global:OSDCloudDeploy.OSLanguageCodeValues) { $OSLanguageCodeValues = $global:OSDCloudDeploy.OSLanguageCodeValues Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] OSLanguageCodeValues = $OSLanguageCodeValues" } else { $OSLanguageCodeValues = $global:DeployOSDCloudOperatingSystems.OSLanguageCode | Sort-Object -Unique | Sort-Object -Descending Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] Catalog OSLanguageCodeValues = $OSLanguageCodeValues" } $OSLanguageCodeCombo = $window.FindName("OSLanguageCodeCombo") $OSLanguageCodeCombo.ItemsSource = $OSLanguageCodeValues #================================================ # OS Language Code Default if ($global:OSDCloudDeploy.OSLanguageCode) { $OSLanguageCodeDefault = $global:OSDCloudDeploy.OSLanguageCode Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] OSLanguage = $OSLanguageCodeDefault" } if ($OSLanguageCodeDefault -and ($OSLanguageCodeValues -contains $OSLanguageCodeDefault)) { $OSLanguageCodeCombo.SelectedItem = $OSLanguageCodeDefault } elseif ($OSLanguageCodeValues) { $OSLanguageCodeCombo.SelectedIndex = 0 } else { $OSLanguageCodeCombo.SelectedIndex = -1 } #================================================ # Driver Pack Combo $DriverPackCatalog = @('None', 'Microsoft Update Catalog') if ($global:OSDCloudDeploy.DriverPackValues) { $DriverPackCatalog += $global:OSDCloudDeploy.DriverPackValues | ForEach-Object { $_.Name } } $DriverPackCombo = $window.FindName("DriverPackCombo") $DriverPackCombo.ItemsSource = $DriverPackCatalog if ($global:OSDCloudDeploy.DriverPackName) { $DriverPackCombo.SelectedValue = $global:OSDCloudDeploy.DriverPackName } else { $DriverPackCombo.SelectedIndex = 0 } #================================================ # Device Info Text Blocks $deviceBiosReleaseDateText = $window.FindName("deviceBiosReleaseDateText") $deviceBiosReleaseDateText.Text = $deviceBiosReleaseDate $deviceBiosVersionText = $window.FindName("deviceBiosVersionText") $deviceBiosVersionText.Text = $deviceBiosVersion $deviceOSDManufacturerText = $window.FindName("deviceOSDManufacturerText") $deviceOSDManufacturerText.Text = $deviceOSDManufacturer $deviceOSDModelText = $window.FindName("deviceOSDModelText") $deviceOSDModelText.Text = $deviceOSDModel $deviceOSDProductText = $window.FindName("deviceOSDProductText") $deviceOSDProductText.Text = $deviceOSDProduct $deviceComputerSystemSKUText = $window.FindName("deviceComputerSystemSKUText") $deviceComputerSystemSKUText.Text = $deviceComputerSystemSKU function Set-ClipboardText { param([string]$Text) $maxRetries = 5 for ($i = 0; $i -lt $maxRetries; $i++) { try { [System.Windows.Clipboard]::SetText($Text) return } catch { if ($i -lt ($maxRetries - 1)) { Start-Sleep -Milliseconds 100 } else { Write-Warning "Failed to copy to clipboard: $_" } } } } $deviceSerialNumberText = $window.FindName("deviceSerialNumberText") $deviceSerialNumberText.Text = $deviceSerialNumber $deviceSerialNumberText.Add_MouseLeftButtonUp({ $serialNumberValue = [string]$deviceSerialNumberText.Text if ([string]::IsNullOrWhiteSpace($serialNumberValue)) { return } Set-ClipboardText -Text $serialNumberValue }) $deviceIsAutopilotSpecText = $window.FindName("deviceIsAutopilotSpecText") $deviceIsAutopilotSpecText.Text = $deviceIsAutopilotSpec $deviceIsTpmSpecText = $window.FindName("deviceIsTpmSpecText") $deviceIsTpmSpecText.Text = $deviceIsTpmSpec $deviceUUIDText = $window.FindName("deviceUUIDText") $deviceUUIDText.Text = $deviceUUID $deviceUUIDText.Add_MouseLeftButtonUp({ $uuidValue = [string]$deviceUUIDText.Text if ([string]::IsNullOrWhiteSpace($uuidValue)) { return } Set-ClipboardText -Text $uuidValue }) $deviceHardwareHashLabelText = $window.FindName("deviceHardwareHashLabelText") $deviceHardwareHashText = $window.FindName("deviceHardwareHashText") if (-not [string]::IsNullOrWhiteSpace([string]$deviceHardwareHash)) { $deviceHardwareHashLabelText.Visibility = [System.Windows.Visibility]::Visible $deviceHardwareHashText.Text = 'Copy to Clipboard' $deviceHardwareHashText.Visibility = [System.Windows.Visibility]::Visible $deviceHardwareHashText.Add_MouseLeftButtonUp({ Set-ClipboardText -Text ([string]$deviceHardwareHash) }) } $OSDCloudDeviceGrid = $window.FindName('OSDCloudDeviceGrid') function Convert-OSDCloudDeviceValueToString { param($Value) if ($null -eq $Value) { return '' } if ($Value -is [string]) { return $Value } if ($Value -is [System.Collections.IDictionary]) { return (($Value.GetEnumerator() | ForEach-Object { "{0}={1}" -f $_.Key, $_.Value }) -join '; ') } if ($Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])) { return (($Value | ForEach-Object { [string]$_ }) -join ', ') } return [string]$Value } function Get-OSDCloudDeviceItems { $deviceObject = $global:OSDCloudDevice if (-not $deviceObject) { return @() } if ($deviceObject -is [System.Collections.IDictionary]) { return @( $deviceObject.GetEnumerator() | Sort-Object -Property Key | ForEach-Object { [PSCustomObject]@{ Key = [string]$_.Key Value = Convert-OSDCloudDeviceValueToString -Value $_.Value } } ) } return @( $deviceObject.PSObject.Properties | Where-Object { $_.MemberType -in @('NoteProperty', 'Property') -and $_.IsGettable -and $_.Name -notlike 'PS*' } | Sort-Object -Property Name | ForEach-Object { [PSCustomObject]@{ Key = [string]$_.Name Value = Convert-OSDCloudDeviceValueToString -Value $_.Value } } ) } if ($OSDCloudDeviceGrid) { $OSDCloudDeviceGrid.ItemsSource = Get-OSDCloudDeviceItems } #================================================ # Summary / Selected detail text blocks $SelectedOSLanguageText = $window.FindName("SelectedOSLanguageText") $SelectedIdText = $window.FindName("SelectedIdText") $SelectedFileNameText = $window.FindName("SelectedFileNameText") $CloudOperatingSystemDownloadButton = $window.FindName("CloudOperatingSystemDownloadButton") $DriverPackUrlText = $window.FindName("DriverPackUrlText") $DriverPackCacheLabelText = $window.FindName("DriverPackCacheLabelText") $DriverPackCachePathText = $window.FindName("DriverPackCachePathText") $DriverPackDownloadButton = $window.FindName("DriverPackDownloadButton") $DriverPackUrlText.Text = [string]$global:OSDCloudDeploy.DriverPackObject.Url if ($global:OSDCloudDeploy.DriverFolderPaths.Count -gt 0) { $DriverFolderPathText.Text = ($global:OSDCloudDeploy.DriverFolderPaths -join '; ') } else { $DriverFolderPathText.Text = [string]$global:OSDCloudDeploy.DriverFolderPath } #================================================ # Start Button $StartButton = $window.FindName("StartButton") $StartButton.IsEnabled = $false function Get-ComboValue { param( [Parameter(Mandatory)] [System.Windows.Controls.ComboBox]$ComboBox ) $value = $ComboBox.SelectedItem if ($null -eq $value) { return $null } $text = [string]$value if ([string]::IsNullOrWhiteSpace($text)) { return $null } return $text } function Set-StartButtonState { $useLocalIso = $UseLocalIsoToggle -and $UseLocalIsoToggle.IsChecked -eq $true if ($useLocalIso) { $StartButton.IsEnabled = ($null -ne $LocalIsoCombo.SelectedItem) } else { $StartButton.IsEnabled = ($null -ne $global:OSDCloudDeploy.OperatingSystemObject) } } function Update-OperatingSystemSourceVisibility { $useLocalIso = $UseLocalIsoToggle -and $UseLocalIsoToggle.IsChecked -eq $true if ($useLocalIso) { $LocalIsoCombo.Visibility = [System.Windows.Visibility]::Visible $CloudOperatingSystemCard.Visibility = [System.Windows.Visibility]::Collapsed } else { $LocalIsoCombo.Visibility = [System.Windows.Visibility]::Collapsed $CloudOperatingSystemCard.Visibility = [System.Windows.Visibility]::Visible } Set-StartButtonState } function Update-SelectedDetails { param( [Parameter()] $Item ) if (-not $Item) { $SelectedIdText.Text = 'No matching catalog entry.' $SelectedOSLanguageText.Text = '-' $SelectedFileNameText.Text = '-' return } $SelectedIdText.Text = [string]$Item.Id $SelectedOSLanguageText.Text = if ($Item.OSLanguage) { [string]$Item.OSLanguage } elseif ($Item.OSLanguageCode) { [string]$Item.OSLanguageCode } else { '-' } $SelectedFileNameText.Text = [string]$Item.FileName } function Update-CloudOperatingSystemDownloadState { if (-not $CloudOperatingSystemDownloadButton) { return } $useLocalIso = $UseLocalIsoToggle -and $UseLocalIsoToggle.IsChecked -eq $true $operatingSystemObject = $global:OSDCloudDeploy.OperatingSystemObject $operatingSystemUrl = if ($operatingSystemObject) { [string]$operatingSystemObject.FilePath } else { $null } $operatingSystemFileName = Get-CloudOperatingSystemFileName -OperatingSystemObject $operatingSystemObject $cloudCacheItems = @(Get-CloudOperatingSystemCacheItems) $matchedCloudCacheFile = Get-CloudOperatingSystemCacheFile -OperatingSystemObject $operatingSystemObject -CacheItems $cloudCacheItems $script:CloudOperatingSystemUsbTarget = Get-DriverPackUsbCacheTarget $hasDownloadableCloudOperatingSystem = -not $useLocalIso -and ($null -ne $operatingSystemObject) -and -not [string]::IsNullOrWhiteSpace($operatingSystemUrl) -and -not [string]::IsNullOrWhiteSpace($operatingSystemFileName) if ($hasDownloadableCloudOperatingSystem -and -not $matchedCloudCacheFile -and $script:CloudOperatingSystemUsbTarget) { $CloudOperatingSystemDownloadButton.Visibility = [System.Windows.Visibility]::Visible $targetDescription = [string]$script:CloudOperatingSystemUsbTarget.DriveRoot if (-not [string]::IsNullOrWhiteSpace([string]$script:CloudOperatingSystemUsbTarget.VolumeLabel)) { $targetDescription = "$targetDescription ($([string]$script:CloudOperatingSystemUsbTarget.VolumeLabel))" } $CloudOperatingSystemDownloadButton.ToolTip = "Download selected Cloud Operating System to $targetDescription" } else { $CloudOperatingSystemDownloadButton.Visibility = [System.Windows.Visibility]::Collapsed $CloudOperatingSystemDownloadButton.ToolTip = $null } } function Update-OsResults { $updateOperatingSystem = Get-ComboValue -ComboBox $OperatingSystemCombo $updateOSEdition = Get-ComboValue -ComboBox $OSEditionCombo $updateOSActivation = Get-ComboValue -ComboBox $OSActivationCombo $updateOSLanguageCode = Get-ComboValue -ComboBox $OSLanguageCodeCombo Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] updateOperatingSystem = $updateOperatingSystem" Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] updateOSEdition = $updateOSEdition" Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] updateOSActivation = $updateOSActivation" Write-Verbose "[$(Get-Date -format s)] [MainWindow.ps1] updateOSLanguageCode = $updateOSLanguageCode" $global:OSDCloudDeploy.OperatingSystemObject = $global:DeployOSDCloudOperatingSystems | ` Where-Object { $_.OperatingSystem -match $updateOperatingSystem } | ` Where-Object { $_.OSActivation -eq $updateOSActivation } | ` Where-Object { $_.OSLanguageCode -eq $updateOSLanguageCode } | Select-Object -First 1 if (-not $global:OSDCloudDeploy.OperatingSystemObject) { throw "No Operating System found for OperatingSystem: $updateOperatingSystem, OSActivation: $updateOSActivation, OSLanguageCode: $updateOSLanguageCode. Please check your OSDCloud OperatingSystems." } $script:SelectedImage = $global:OSDCloudDeploy.OperatingSystemObject if ($updateOSEdition -match 'Home') { $OSActivationCombo.SelectedValue = 'Retail' $OSActivationCombo.IsEnabled = $false } if ($updateOSEdition -match 'Education') { $OSActivationCombo.IsEnabled = $true } if ($updateOSEdition -match 'Enterprise') { $OSActivationCombo.SelectedValue = 'Volume' $OSActivationCombo.IsEnabled = $false } if ($updateOSEdition -match 'Pro') { $OSActivationCombo.IsEnabled = $true } Update-SelectedDetails -Item $script:SelectedImage Update-CloudOperatingSystemDownloadState Set-StartButtonState } function Update-DriverPackResults { Update-CloudOperatingSystemDownloadState $selectedDriverPackName = Get-ComboValue -ComboBox $DriverPackCombo $global:OSDCloudDeploy.DriverPackName = $selectedDriverPackName $global:OSDCloudDeploy.DriverPackObject = $global:OSDCloudDeploy.DriverPackValues | Where-Object { $_.Name -eq $selectedDriverPackName } | Select-Object -First 1 $DriverPackUrlText.Text = [string]$global:OSDCloudDeploy.DriverPackObject.Url $DriverPackCacheItems = @(Get-DriverPackCacheItems) $matchedCacheFile = Get-DriverPackCacheFile -DriverPackObject $global:OSDCloudDeploy.DriverPackObject -CacheItems $DriverPackCacheItems $script:DriverPackUsbTarget = Get-DriverPackUsbCacheTarget if ($matchedCacheFile) { $DriverPackCachePathText.Text = [string]$matchedCacheFile.FullName $DriverPackCachePathText.Visibility = [System.Windows.Visibility]::Visible $DriverPackCacheLabelText.Visibility = [System.Windows.Visibility]::Visible } else { $DriverPackCachePathText.Text = '' $DriverPackCachePathText.Visibility = [System.Windows.Visibility]::Collapsed $DriverPackCacheLabelText.Visibility = [System.Windows.Visibility]::Collapsed } if ($DriverPackDownloadButton) { $hasDownloadableDriverPack = ($null -ne $global:OSDCloudDeploy.DriverPackObject) -and -not [string]::IsNullOrWhiteSpace([string]$global:OSDCloudDeploy.DriverPackObject.Url) -and -not [string]::IsNullOrWhiteSpace((Get-DriverPackFileName -DriverPackObject $global:OSDCloudDeploy.DriverPackObject) ) if ($hasDownloadableDriverPack -and -not $matchedCacheFile -and $script:DriverPackUsbTarget) { $DriverPackDownloadButton.Visibility = [System.Windows.Visibility]::Visible $targetDescription = [string]$script:DriverPackUsbTarget.DriveRoot if (-not [string]::IsNullOrWhiteSpace([string]$script:DriverPackUsbTarget.VolumeLabel)) { $targetDescription = "$targetDescription ($([string]$script:DriverPackUsbTarget.VolumeLabel))" } $DriverPackDownloadButton.ToolTip = "Download selected Driver Pack to $targetDescription" } else { $DriverPackDownloadButton.Visibility = [System.Windows.Visibility]::Collapsed $DriverPackDownloadButton.ToolTip = $null } } } function Update-DriverFolderResults { $selectedDriverFolderItems = @($DriverFolderGrid.ItemsSource | Where-Object { $_.IsSelected -eq $true -and -not [string]::IsNullOrWhiteSpace([string]$_.Path) }) if ($selectedDriverFolderItems.Count -gt 0) { $global:OSDCloudDeploy.DriverFolderNames = @($selectedDriverFolderItems | ForEach-Object { [string]$_.Name }) $global:OSDCloudDeploy.DriverFolderPaths = @($selectedDriverFolderItems | ForEach-Object { [string]$_.Path }) $global:OSDCloudDeploy.DriverFolderSelections = @($selectedDriverFolderItems | ForEach-Object { [PSCustomObject]@{ Path = [string]$_.Path RelativePath = [string]$_.RelativePath DriveRoot = [string]$_.DriveRoot VolumeLabel = [string]$_.VolumeLabel VolumeUniqueId = [string]$_.VolumeUniqueId Name = [string]$_.Name } }) # Backward compatibility for existing single-folder consumers. $global:OSDCloudDeploy.DriverFolderName = $global:OSDCloudDeploy.DriverFolderNames | Select-Object -First 1 $global:OSDCloudDeploy.DriverFolderPath = $global:OSDCloudDeploy.DriverFolderPaths | Select-Object -First 1 } else { $global:OSDCloudDeploy.DriverFolderName = $null $global:OSDCloudDeploy.DriverFolderPath = $null $global:OSDCloudDeploy.DriverFolderNames = @() $global:OSDCloudDeploy.DriverFolderPaths = @() $global:OSDCloudDeploy.DriverFolderSelections = @() } if ($global:OSDCloudDeploy.DriverFolderPaths.Count -gt 0) { $DriverFolderPathText.Text = ($global:OSDCloudDeploy.DriverFolderPaths -join '; ') } else { $DriverFolderPathText.Text = '' } } function Request-DriverFolderResultsRefresh { if ($window -and $window.Dispatcher) { $window.Dispatcher.InvokeAsync({ $DriverFolderGrid.CommitEdit([System.Windows.Controls.DataGridEditingUnit]::Cell, $true) | Out-Null $DriverFolderGrid.CommitEdit([System.Windows.Controls.DataGridEditingUnit]::Row, $true) | Out-Null Update-DriverFolderResults }, [System.Windows.Threading.DispatcherPriority]::Background) | Out-Null } else { Update-DriverFolderResults } } $DriverPackCombo.Add_SelectionChanged({ Update-DriverPackResults }) $DriverFolderGrid.Add_CurrentCellChanged({ Request-DriverFolderResultsRefresh }) $DriverFolderGrid.Add_CellEditEnding({ Request-DriverFolderResultsRefresh }) $DriverFolderGrid.Add_PreviewMouseLeftButtonUp({ Request-DriverFolderResultsRefresh }) $DriverFolderGrid.Add_PreviewKeyUp({ param($eventSource, $keyEvent) if ($keyEvent.Key -eq [System.Windows.Input.Key]::Space -or $keyEvent.Key -eq [System.Windows.Input.Key]::Enter) { Request-DriverFolderResultsRefresh } }) $UseLocalIsoToggle.Add_Checked({ Update-OperatingSystemSourceVisibility }) $UseLocalIsoToggle.Add_Unchecked({ Update-OperatingSystemSourceVisibility }) $LocalIsoCombo.Add_SelectionChanged({ Set-StartButtonState }) $OperatingSystemCombo.Add_SelectionChanged({ Update-OsResults }) $OSActivationCombo.Add_SelectionChanged({ Update-OsResults }) $OSEditionCombo.Add_SelectionChanged({ Update-OsResults }) $OSLanguageCodeCombo.Add_SelectionChanged({ Update-OsResults }) $script:SelectionConfirmed = $false if ($DriverPackDownloadButton) { $DriverPackDownloadButton.Add_Click({ $driverPackObject = $global:OSDCloudDeploy.DriverPackObject if (-not $driverPackObject) { [System.Windows.MessageBox]::Show('Select a Driver Pack before downloading.', 'Driver Pack Download', 'OK', 'Warning') | Out-Null return } $driverPackUrl = [string]$driverPackObject.Url $driverPackFileName = Get-DriverPackFileName -DriverPackObject $driverPackObject if ([string]::IsNullOrWhiteSpace($driverPackUrl) -or [string]::IsNullOrWhiteSpace($driverPackFileName)) { [System.Windows.MessageBox]::Show('The selected Driver Pack does not provide a downloadable URL or filename.', 'Driver Pack Download', 'OK', 'Warning') | Out-Null return } $usbTarget = Get-DriverPackUsbCacheTarget if (-not $usbTarget) { [System.Windows.MessageBox]::Show('No USB OSDCloud cache target with more than 10 GB free space is available.', 'Driver Pack Download', 'OK', 'Warning') | Out-Null Update-DriverPackResults return } $manufacturerFolderName = Convert-ToSafePathSegment -Value ([string]$deviceOSDManufacturer) if ([string]::IsNullOrWhiteSpace($manufacturerFolderName)) { $manufacturerFolderName = Convert-ToSafePathSegment -Value ([string]$driverPackObject.Manufacturer) } $destinationDirectory = Join-Path -Path ([string]$usbTarget.DriveRoot) -ChildPath ("OSDCloud\\DriverPacks\\$manufacturerFolderName") $destinationPath = Join-Path -Path $destinationDirectory -ChildPath $driverPackFileName $cacheMatch = Get-DriverPackCacheFile -DriverPackObject $driverPackObject -CacheItems (Get-DriverPackCacheItems) if ($cacheMatch) { Update-DriverPackResults [System.Windows.MessageBox]::Show("Driver Pack is already cached at $([string]$cacheMatch.FullName).", 'Driver Pack Download', 'OK', 'Information') | Out-Null return } $destinationDirectoryLiteral = Convert-ToSingleQuotedPowerShellLiteral -Value $destinationDirectory $destinationPathLiteral = Convert-ToSingleQuotedPowerShellLiteral -Value $destinationPath $driverPackUrlLiteral = Convert-ToSingleQuotedPowerShellLiteral -Value $driverPackUrl $downloadCommand = "`$ProgressPreference = 'SilentlyContinue'; New-Item -Path $destinationDirectoryLiteral -ItemType Directory -Force | Out-Null; & curl.exe --location --fail --output $destinationPathLiteral $driverPackUrlLiteral" try { Start-Process -FilePath 'powershell.exe' -ArgumentList @('-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', $downloadCommand) -ErrorAction Stop # [System.Windows.MessageBox]::Show("Started download to $destinationPath", 'Driver Pack Download', 'OK', 'Information') | Out-Null } catch { [System.Windows.MessageBox]::Show("Failed to start Driver Pack download: $($_.Exception.Message)", 'Driver Pack Download', 'OK', 'Error') | Out-Null } Update-DriverPackResults }) } if ($CloudOperatingSystemDownloadButton) { $CloudOperatingSystemDownloadButton.Add_Click({ $useLocalIso = $UseLocalIsoToggle -and $UseLocalIsoToggle.IsChecked -eq $true if ($useLocalIso) { [System.Windows.MessageBox]::Show('Cloud Operating System download is unavailable while local ISO is selected.', 'Cloud Operating System Download', 'OK', 'Warning') | Out-Null Update-CloudOperatingSystemDownloadState return } $operatingSystemObject = $global:OSDCloudDeploy.OperatingSystemObject if (-not $operatingSystemObject) { [System.Windows.MessageBox]::Show('Select a Cloud Operating System before downloading.', 'Cloud Operating System Download', 'OK', 'Warning') | Out-Null return } $operatingSystemUrl = [string]$operatingSystemObject.FilePath $operatingSystemFileName = Get-CloudOperatingSystemFileName -OperatingSystemObject $operatingSystemObject if ([string]::IsNullOrWhiteSpace($operatingSystemUrl) -or [string]::IsNullOrWhiteSpace($operatingSystemFileName)) { [System.Windows.MessageBox]::Show('The selected Cloud Operating System does not provide a downloadable URL or filename.', 'Cloud Operating System Download', 'OK', 'Warning') | Out-Null return } $usbTarget = Get-DriverPackUsbCacheTarget if (-not $usbTarget) { [System.Windows.MessageBox]::Show('No USB OSDCloud cache target with more than 10 GB free space is available.', 'Cloud Operating System Download', 'OK', 'Warning') | Out-Null Update-CloudOperatingSystemDownloadState return } $destinationDirectory = Join-Path -Path ([string]$usbTarget.DriveRoot) -ChildPath 'OSDCloud\OS' $destinationPath = Join-Path -Path $destinationDirectory -ChildPath $operatingSystemFileName $cacheMatch = Get-CloudOperatingSystemCacheFile -OperatingSystemObject $operatingSystemObject -CacheItems (Get-CloudOperatingSystemCacheItems) if ($cacheMatch) { Update-CloudOperatingSystemDownloadState [System.Windows.MessageBox]::Show("Cloud Operating System is already cached at $([string]$cacheMatch.FullName).", 'Cloud Operating System Download', 'OK', 'Information') | Out-Null return } $destinationDirectoryLiteral = Convert-ToSingleQuotedPowerShellLiteral -Value $destinationDirectory $destinationPathLiteral = Convert-ToSingleQuotedPowerShellLiteral -Value $destinationPath $operatingSystemUrlLiteral = Convert-ToSingleQuotedPowerShellLiteral -Value $operatingSystemUrl $downloadCommand = "`$ProgressPreference = 'SilentlyContinue'; New-Item -Path $destinationDirectoryLiteral -ItemType Directory -Force | Out-Null; & curl.exe --location --fail --output $destinationPathLiteral $operatingSystemUrlLiteral" try { Start-Process -FilePath 'powershell.exe' -ArgumentList @('-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', $downloadCommand) -ErrorAction Stop } catch { [System.Windows.MessageBox]::Show("Failed to start Cloud Operating System download: $($_.Exception.Message)", 'Cloud Operating System Download', 'OK', 'Error') | Out-Null } Update-CloudOperatingSystemDownloadState }) } $StartButton.Add_Click({ $TaskSequenceStepsGrid.CommitEdit([System.Windows.Controls.DataGridEditingUnit]::Cell, $true) | Out-Null $TaskSequenceStepsGrid.CommitEdit([System.Windows.Controls.DataGridEditingUnit]::Row, $true) | Out-Null $DriverFolderGrid.CommitEdit([System.Windows.Controls.DataGridEditingUnit]::Cell, $true) | Out-Null $DriverFolderGrid.CommitEdit([System.Windows.Controls.DataGridEditingUnit]::Row, $true) | Out-Null Update-DriverFolderResults Save-TaskSequenceSteps -TaskSequenceName $script:CurrentTaskSequenceName -Steps $TaskSequenceStepsGrid.ItemsSource $script:SelectionConfirmed = $true $window.DialogResult = $true $window.Close() }) Update-OsResults Update-OperatingSystemSourceVisibility Update-CloudOperatingSystemDownloadState Update-DriverPackResults Update-DriverFolderResults # Initialize task sequence summary if present if ($SummaryTaskSequenceText) { $value = [string]$TaskSequenceCombo.SelectedItem $SummaryTaskSequenceText.Text = if (-not [string]::IsNullOrWhiteSpace($value)) { $value } else { 'Not selected' } } $null = $window.ShowDialog() if ($script:SelectionConfirmed) { #================================================ # Local Variables $OSDCloudWorkflowTaskName = $TaskSequenceCombo.SelectedValue $OSDCloudWorkflowTaskObject = $global:OSDCloudDeploy.Flows | Where-Object { $_.Name -eq $OSDCloudWorkflowTaskName } | Select-Object -First 1 $useLocalIso = $UseLocalIsoToggle -and $UseLocalIsoToggle.IsChecked -eq $true if ($useLocalIso) { $LocalImageFileInfo = $LocalIsoCombo.SelectedItem $LocalImageFilePath = [string]$LocalImageFileInfo.FullName $LocalImageName = $null $OSEditionId = $null $OperatingSystemObject = [PSCustomObject]@{ FileName = $LocalImageFileInfo.Name FilePath = $LocalImageFilePath OperatingSystem = 'Local ISO' OSName = 'Local ISO' OSActivation = $null OSBuild = $null OSBuildVersion = $null OSLanguageCode = $null OSVersion = $null } } else { $OperatingSystemObject = $global:OSDCloudDeploy.OperatingSystemObject $OSEditionId = $global:OSDCloudDeploy.OSEditionValues | Where-Object { $_.Edition -eq $OSEditionCombo.SelectedValue } | Select-Object -ExpandProperty EditionId } #================================================ # Global Variables $global:OSDCloudDeploy.WorkflowTaskName = $OSDCloudWorkflowTaskName $global:OSDCloudDeploy.WorkflowTaskObject = $OSDCloudWorkflowTaskObject $global:OSDCloudDeploy.ImageFileName = $OperatingSystemObject.FileName $global:OSDCloudDeploy.ImageFileUrl = $OperatingSystemObject.FilePath $global:OSDCloudDeploy.OperatingSystemObject = $OperatingSystemObject $global:OSDCloudDeploy.OperatingSystem = $OperatingSystemObject.OSName $global:OSDCloudDeploy.OSActivation = $OperatingSystemObject.OSActivation $global:OSDCloudDeploy.OSBuild = $OperatingSystemObject.OSBuild $global:OSDCloudDeploy.OSEdition = if ($useLocalIso) { $null } else { Get-ComboValue -ComboBox $OSEditionCombo } $global:OSDCloudDeploy.OSEditionId = $OSEditionId $global:OSDCloudDeploy.OSLanguageCode = $OperatingSystemObject.OSLanguageCode $global:OSDCloudDeploy.OperatingSystem = $OperatingSystemObject.OperatingSystem $global:OSDCloudDeploy.OSVersion = $OperatingSystemObject.OSVersion $global:OSDCloudDeploy.TimeStart = (Get-Date) $global:OSDCloudDeploy.LocalImageFileInfo = $LocalImageFileInfo $global:OSDCloudDeploy.LocalImageFilePath = $LocalImageFilePath $global:OSDCloudDeploy.LocalImageName = $LocalImageName Update-DriverFolderResults $LogsPath = "$env:TEMP\osdcloud-logs" if (-not (Test-Path -Path $LogsPath)) { New-Item -Path $LogsPath -ItemType Directory -Force | Out-Null } $global:OSDCloudDeploy | ConvertTo-Json | Out-File -FilePath "$LogsPath\OSDCloudDeploy.json" -Encoding utf8 -Width 2000 -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue } |