From 4709177bc319d10cce85dff0c96b962afd48e419 Mon Sep 17 00:00:00 2001 From: HedgeComp <94635857+HedgeComp@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:16:08 -0500 Subject: [PATCH 1/6] Fix HP Driver Windows Version case sensitivity --- FFUDevelopment/BuildFFUVM.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1 index efff889..39f9da0 100644 --- a/FFUDevelopment/BuildFFUVM.ps1 +++ b/FFUDevelopment/BuildFFUVM.ps1 @@ -1003,7 +1003,9 @@ function Get-HPDrivers { $Arch = $WindowsArch -replace "^x", "" # Construct the URL to download the driver XML cab for the model - $ModelRelease = $SystemID + "_$Arch" + "_$WindowsRelease" + ".0.$WindowsVersion" + # The HPcloud reference site is case sensitve so we must convert the Windowsversion to lower 'h' first + $WindowsVersionHP = $WindowsVersion -replace 'H', 'h' + $ModelRelease = $SystemID + "_$Arch" + "_$WindowsRelease" + ".0.$WindowsVersionHP" $DriverCabUrl = "https://hpia.hpcloud.hp.com/ref/$SystemID/$ModelRelease.cab" $DriverCabFile = "$DriversFolder\$ModelRelease.cab" $DriverXmlFile = "$DriversFolder\$ModelRelease.xml" From db62e052752e62efae8cfef2353f527787cef606 Mon Sep 17 00:00:00 2001 From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com> Date: Fri, 10 Jan 2025 13:50:00 -0800 Subject: [PATCH 2/6] Bug fixes - Fixed an issue with WinPE Drivers not being added to Deployment media - Fixed an issue where Windows SKUs that include spaces in their names (e.g. Pro Education) and `$InstallApps $false`, FFU creation would fail due to the name including a space. Added a new function `Get-ShortenedWindowsSKU` to truncate the SKU for FFU name creation purposes. This required various changes throughout the script that relied on the Windows SKU for naming. - Updated version 2412.3 --- FFUDevelopment/BuildFFUVM.ps1 | 116 +++++++++++++----- .../WinPEDeployFFUFiles/ApplyFFU.ps1 | 2 +- 2 files changed, 84 insertions(+), 34 deletions(-) diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1 index efff889..2cd1f15 100644 --- a/FFUDevelopment/BuildFFUVM.ps1 +++ b/FFUDevelopment/BuildFFUVM.ps1 @@ -361,7 +361,7 @@ param( [Parameter(Mandatory = $false)] [string]$ExportConfigFile ) -$version = '2412.1' +$version = '2412.3' # If a config file is specified and it exists, load it if ($ConfigFile -and (Test-Path -Path $ConfigFile)) { @@ -557,7 +557,7 @@ function Invoke-Process { [Parameter()] [ValidateNotNullOrEmpty()] - [string]$ArgumentList, + [string[]]$ArgumentList, [Parameter()] [ValidateNotNullOrEmpty()] @@ -3051,7 +3051,7 @@ function New-PEMedia { if ($CopyPEDrivers) { WriteLog "Adding drivers to WinPE media" try { - Add-WindowsDriver -Path "$WinPEFFUPath\Mount" -Driver "$FFUDevelopmentPath\$PEDriversFolder" -Recurse -ErrorAction SilentlyContinue | Out-null + Add-WindowsDriver -Path "$WinPEFFUPath\Mount" -Driver "$PEDriversFolder" -Recurse -ErrorAction SilentlyContinue | Out-null } catch { WriteLog 'Some drivers failed to be added to the FFU. This can be expected. Continuing.' @@ -3124,14 +3124,57 @@ function Optimize-FFUCaptureDrive { throw $_ } } + +function Get-ShortenedWindowsSKU { + param ( + [string]$WindowsSKU + ) + $shortenedWindowsSKU = switch ($WindowsSKU) { + 'Core' { 'Home' } + 'CoreN' { 'Home_N' } + 'CoreSingleLanguage' { 'Home_SL' } + 'Education' { 'Edu' } + 'EducationN' { 'Edu_N' } + 'Professional' { 'Pro' } + 'ProfessionalN' { 'Pro_N' } + 'ProfessionalEducation' { 'Pro_Edu' } + 'ProfessionalEducationN' { 'Pro_Edu_N' } + 'ProfessionalWorkstation' { 'Pro_WKS' } + 'ProfessionalWorkstationN' { 'Pro_WKS_N' } + 'Enterprise' { 'Ent' } + 'EnterpriseN' { 'Ent_N' } + 'ServerStandard' { 'Srv_Std' } + 'ServerDatacenter' { 'Srv_Dtc' } + 'Home' { 'Home' } + 'Home N' { 'Home_N' } + 'Home Single Language' { 'Home_SL' } + 'Education' { 'Edu' } + 'Education N' { 'Edu_N' } + 'Professional' { 'Pro' } + 'Pro N' { 'Pro_N' } + 'Pro Education' { 'Pro_Edu' } + 'Pro Education N' { 'Pro_Edu_N' } + 'Pro for Workstations' { 'Pro_WKS' } + 'Pro N for Workstations' { 'Pro_WKS_N' } + 'Enterprise' { 'Ent' } + 'Enterprise N' { 'Ent_N' } + 'Standard' { 'Srv_Std' } + 'Standard (Desktop Experience)' { 'Srv_Std_DE' } + 'Datacenter' { 'Srv_Dtc' } + 'Datacenter (Desktop Experience)' { 'Srv_Dtc_DE' } + } + return $shortenedWindowsSKU + +} function New-FFUFileName { + $BuildDate = Get-Date -uformat %b%Y # Replace '{WindowsRelease}' with the Windows release (e.g., 10, 11, 2016, 2019, 2022, 2025) $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{WindowsRelease}', $WindowsRelease # Replace '{WindowsVersion}' with the Windows version (e.g., 1607, 1809, 21h2, 22h2, 23h2, 24h2, etc) $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{WindowsVersion}', $WindowsVersion # Replace '{SKU}' with the SKU of the Windows image (e.g., Pro, Enterprise, etc.) - $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{SKU}', $SKU + $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{SKU}', $shortenedWindowsSKU # Replace '{BuildDate}' with the current month and year (e.g., Jan2023) $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{BuildDate}', $BuildDate # Replace '{yyyy}' with the current year in 4-digit format (e.g., 2023) @@ -3197,42 +3240,44 @@ function New-FFU { } } elseif (-not $InstallApps -and (-not $AllowVHDXCaching)) { + #Get Windows Version Information from the VHDX + $winverinfo = Get-WindowsVersionInfo + WriteLog 'Creating FFU File Name' if ($CustomFFUNameTemplate) { $FFUFileName = New-FFUFileName } else{ - #Get Windows Version Information from the VHDX - $winverinfo = Get-WindowsVersionInfo - $FFUFileName = "$($winverinfo.Name)`_$($winverinfo.DisplayVersion)`_$($winverinfo.SKU)`_$($winverinfo.BuildDate).ffu" + $FFUFileName = "$($winverinfo.Name)`_$($winverinfo.DisplayVersion)`_$($shortenedWindowsSKU)`_$($winverinfo.BuildDate).ffu" } WriteLog "FFU file name: $FFUFileName" $FFUFile = "$FFUCaptureLocation\$FFUFileName" #Capture the FFU - Invoke-Process cmd "/c ""$DandIEnv"" && dism /Capture-FFU /ImageFile:$FFUFile /CaptureDrive:\\.\PhysicalDrive$($vhdxDisk.DiskNumber) /Name:$($winverinfo.Name)$($winverinfo.DisplayVersion)$($winverinfo.SKU) /Compress:Default" | Out-Null - # Invoke-Process cmd "/c dism /Capture-FFU /ImageFile:$FFUFile /CaptureDrive:\\.\PhysicalDrive$($vhdxDisk.DiskNumber) /Name:$($winverinfo.Name)$($winverinfo.DisplayVersion)$($winverinfo.SKU) /Compress:Default" | Out-Null + WriteLog 'Capturing FFU' + Invoke-Process cmd "/c ""$DandIEnv"" && dism /Capture-FFU /ImageFile:$FFUFile /CaptureDrive:\\.\PhysicalDrive$($vhdxDisk.DiskNumber) /Name:$($winverinfo.Name)$($winverinfo.DisplayVersion)$($shortenedWindowsSKU) /Compress:Default" | Out-Null WriteLog 'FFU Capture complete' Dismount-ScratchVhdx -VhdxPath $VHDXPath } elseif (-not $InstallApps -and $AllowVHDXCaching) { # Make $FFUFileName based on values in the config.json file + WriteLog 'Creating FFU File Name' if ($CustomFFUNameTemplate) { $FFUFileName = New-FFUFileName - } else { + } + else { $BuildDate = Get-Date -UFormat %b%Y # Get Windows Information to make the FFU file name from the cachedVHDXInfo file if ($installationType -eq 'Client') { - $FFUFileName = "Win$($cachedVHDXInfo.WindowsRelease)`_$($cachedVHDXInfo.WindowsVersion)`_$($cachedVHDXInfo.WindowsSKU)`_$BuildDate.ffu" - } else { - $FFUFileName = "Server$($cachedVHDXInfo.WindowsRelease)`_$($cachedVHDXInfo.WindowsVersion)`_$($cachedVHDXInfo.WindowsSKU)`_$BuildDate.ffu" + $FFUFileName = "Win$($cachedVHDXInfo.WindowsRelease)`_$($cachedVHDXInfo.WindowsVersion)`_$($shortenedWindowsSKU)`_$BuildDate.ffu" + } + else { + $FFUFileName = "Server$($cachedVHDXInfo.WindowsRelease)`_$($cachedVHDXInfo.WindowsVersion)`_$($shortenedWindowsSKU)`_$BuildDate.ffu" } } WriteLog "FFU file name: $FFUFileName" $FFUFile = "$FFUCaptureLocation\$FFUFileName" - #Dismount the VHDX - #Capture the FFU - Invoke-Process cmd "/c ""$DandIEnv"" && dism /Capture-FFU /ImageFile:$FFUFile /CaptureDrive:\\.\PhysicalDrive$($vhdxDisk.DiskNumber) /Name:$($cachedVHDXInfo.WindowsRelease)$($cachedVHDXInfo.WindowsVersion)$($cachedVHDXInfo.WindowsSKU) /Compress:Default" | Out-Null - # Invoke-Process cmd "/c dism /Capture-FFU /ImageFile:$FFUFile /CaptureDrive:\\.\PhysicalDrive$($vhdxDisk.DiskNumber) /Name:$($winverinfo.Name)$($winverinfo.DisplayVersion)$($winverinfo.SKU) /Compress:Default" | Out-Null + WriteLog 'Capturing FFU' + Invoke-Process cmd "/c ""$DandIEnv"" && dism /Capture-FFU /ImageFile:$FFUFile /CaptureDrive:\\.\PhysicalDrive$($vhdxDisk.DiskNumber) /Name:$($cachedVHDXInfo.WindowsRelease)$($cachedVHDXInfo.WindowsVersion)$($shortenedWindowsSKU) /Compress:Default" | Out-Null WriteLog 'FFU Capture complete' Dismount-ScratchVhdx -VhdxPath $VHDXPath } @@ -3354,8 +3399,8 @@ Function Get-WindowsVersionInfo { Invoke-Process reg "load HKLM\FFU $Software" | Out-Null #Find Windows version values - $SKU = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'EditionID' - WriteLog "Windows SKU: $SKU" + # $WindowsSKU = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'EditionID' + # WriteLog "Windows SKU: $WindowsSKU" [int]$CurrentBuild = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'CurrentBuild' WriteLog "Windows Build: $CurrentBuild" #DisplayVersion does not exist for 1607 builds (RS1 and Server 2016) and Server 2019 @@ -3366,19 +3411,18 @@ Function Get-WindowsVersionInfo { $BuildDate = Get-Date -uformat %b%Y - $SKU = switch ($SKU) { - Core { 'Home' } - Professional { 'Pro' } - ProfessionalEducation { 'Pro_Edu' } - Enterprise { 'Ent' } - Education { 'Edu' } - ProfessionalWorkstation { 'Pro_Wks' } - ServerStandard { 'Srv_Std' } - ServerDatacenter { 'Srv_Dtc' } - } - WriteLog "Windows SKU Modified to: $SKU" + # $WindowsSKU = switch ($WindowsSKU) { + # Core { 'Home' } + # Professional { 'Pro' } + # ProfessionalEducation { 'Pro_Edu' } + # Enterprise { 'Ent' } + # Education { 'Edu' } + # ProfessionalWorkstation { 'Pro_Wks' } + # ServerStandard { 'Srv_Std' } + # ServerDatacenter { 'Srv_Dtc' } + # } - if ($SKU -notmatch "Srv") { + if ($shortenedWindowsSKU -notmatch "Srv") { if ($CurrentBuild -ge 22000) { $Name = 'Win11' } @@ -3407,7 +3451,7 @@ Function Get-WindowsVersionInfo { DisplayVersion = $DisplayVersion BuildDate = $buildDate Name = $Name - SKU = $SKU + # SKU = $WindowsSKU } } Function Get-USBDrive { @@ -4242,7 +4286,8 @@ if (($make -and $model) -and ($installdrivers -or $copydrivers)) { try { $adkPath = Get-ADK #Need to use the Deployment and Imaging tools environment to use dism from the Sept 2023 ADK to optimize FFU - $DandIEnv = "$adkPath`Assessment and Deployment Kit\Deployment Tools\DandISetEnv.bat" + $DandIEnv = Join-Path $adkPath "Assessment and Deployment Kit\Deployment Tools\DandISetEnv.bat" + } catch { WriteLog 'ADK not found' @@ -4894,6 +4939,11 @@ try { New-FFU $FFUVM.Name } else { + #Shorten Windows SKU for use in FFU file name to remove spaces and long names + WriteLog 'Shortening Windows SKU for FFU file name' + $shortenedWindowsSKU = Get-ShortenedWindowsSKU -WindowsSKU $WindowsSKU + WriteLog "Shortened Windows SKU: $shortenedWindowsSKU" + #Create FFU file New-FFU } } diff --git a/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 b/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 index e11260d..74746a7 100644 --- a/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 +++ b/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 @@ -135,7 +135,7 @@ $LogFileName = 'ScriptLog.txt' $USBDrive = Get-USBDrive New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null $LogFile = $USBDrive + $LogFilename -$version = '2412.1' +$version = '2412.3' WriteLog 'Begin Logging' WriteLog "Script version: $version" From 77ef15494163f517a60587824224c71415f39007 Mon Sep 17 00:00:00 2001 From: JonasKloseBW Date: Mon, 13 Jan 2025 12:51:32 +0100 Subject: [PATCH 3/6] Add $AppListPath parameter - Add $AppListPath parameter - Set default value to "$AppsPath\AppList.json" - Modify Get-Apps call to use the $AppListPath parameter --- FFUDevelopment/BuildFFUVM.ps1 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1 index d788a05..352d315 100644 --- a/FFUDevelopment/BuildFFUVM.ps1 +++ b/FFUDevelopment/BuildFFUVM.ps1 @@ -230,6 +230,7 @@ param( [string]$FFUDevelopmentPath = $PSScriptRoot, [bool]$InstallApps, + [string]$AppListPath, [hashtable]$AppsScriptVariables, [bool]$InstallOffice, @@ -471,6 +472,7 @@ if ($installationType -eq 'Server'){ 2025 { $WindowsVersion = '24H2' } } } +if (-not $AppListPath) { $AppListPath = "$AppsPath\AppList.json" } #FUNCTIONS function WriteLog($LogText) { @@ -4307,9 +4309,9 @@ if ($InstallApps) { exit } WriteLog "$AppsPath\InstallAppsandSysprep.cmd found" - If (Test-Path -Path "$AppsPath\AppList.json"){ - WriteLog "$AppsPath\AppList.json found, checking for winget apps to install" - Get-Apps -AppList "$AppsPath\AppList.json" + If (Test-Path -Path $AppListPath){ + WriteLog "$AppListPath found, checking for winget apps to install" + Get-Apps -AppList "$AppListPath" } if (-not $InstallOffice) { From 7c9f24f6958d4f149be646cd60704af1ca52147b Mon Sep 17 00:00:00 2001 From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com> Date: Thu, 16 Jan 2025 18:00:08 -0800 Subject: [PATCH 4/6] Fixed issues - Fixed an issue where if AppsScriptVariables was configured in a config file, the hashtable wasn't being created by the script when setting the variable. - Fixed a crash where shortening the Windows SKU was creating duplicate shortened names for certain SKUs. --- FFUDevelopment/BuildFFUVM.ps1 | 36 +++++++++---------- .../WinPEDeployFFUFiles/ApplyFFU.ps1 | 2 +- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1 index 352d315..fe2107c 100644 --- a/FFUDevelopment/BuildFFUVM.ps1 +++ b/FFUDevelopment/BuildFFUVM.ps1 @@ -384,12 +384,12 @@ if ($ConfigFile -and (Test-Path -Path $ConfigFile)) { } # If this is the Headers parameter, convert PSCustomObject to hashtable - if ($key -eq 'Headers' -and $value -is [System.Management.Automation.PSCustomObject]) { - $headers = [hashtable]::new() + if ((($key -eq 'Headers') -or ($key -eq 'AppsScriptVariables')) -and ($value -is [System.Management.Automation.PSCustomObject])) { + $hashtableValue = [hashtable]::new() foreach ($prop in $value.psobject.Properties) { - $headers[$prop.Name] = $prop.Value + $hashtableValue[$prop.Name] = $prop.Value } - $value = $headers + $value = $hashtableValue } # Check if this key matches a parameter in the script @@ -3135,36 +3135,34 @@ function Get-ShortenedWindowsSKU { ) $shortenedWindowsSKU = switch ($WindowsSKU) { 'Core' { 'Home' } - 'CoreN' { 'Home_N' } - 'CoreSingleLanguage' { 'Home_SL' } - 'Education' { 'Edu' } - 'EducationN' { 'Edu_N' } - 'Professional' { 'Pro' } - 'ProfessionalN' { 'Pro_N' } - 'ProfessionalEducation' { 'Pro_Edu' } - 'ProfessionalEducationN' { 'Pro_Edu_N' } - 'ProfessionalWorkstation' { 'Pro_WKS' } - 'ProfessionalWorkstationN' { 'Pro_WKS_N' } - 'Enterprise' { 'Ent' } - 'EnterpriseN' { 'Ent_N' } - 'ServerStandard' { 'Srv_Std' } - 'ServerDatacenter' { 'Srv_Dtc' } 'Home' { 'Home' } + 'CoreN' { 'Home_N' } 'Home N' { 'Home_N' } + 'CoreSingleLanguage' { 'Home_SL' } 'Home Single Language' { 'Home_SL' } 'Education' { 'Edu' } + 'EducationN' { 'Edu_N' } 'Education N' { 'Edu_N' } 'Professional' { 'Pro' } + 'Pro' { 'Pro' } + 'ProfessionalN' { 'Pro_N' } 'Pro N' { 'Pro_N' } + 'ProfessionalEducation' { 'Pro_Edu' } 'Pro Education' { 'Pro_Edu' } + 'ProfessionalEducationN' { 'Pro_Edu_N' } 'Pro Education N' { 'Pro_Edu_N' } + 'ProfessionalWorkstation' { 'Pro_WKS' } 'Pro for Workstations' { 'Pro_WKS' } + 'ProfessionalWorkstationN' { 'Pro_WKS_N' } 'Pro N for Workstations' { 'Pro_WKS_N' } 'Enterprise' { 'Ent' } + 'EnterpriseN' { 'Ent_N' } 'Enterprise N' { 'Ent_N' } + 'ServerStandard' { 'Srv_Std' } 'Standard' { 'Srv_Std' } - 'Standard (Desktop Experience)' { 'Srv_Std_DE' } + 'ServerDatacenter' { 'Srv_Dtc' } 'Datacenter' { 'Srv_Dtc' } + 'Standard (Desktop Experience)' { 'Srv_Std_DE' } 'Datacenter (Desktop Experience)' { 'Srv_Dtc_DE' } } return $shortenedWindowsSKU diff --git a/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 b/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 index 74746a7..99b701a 100644 --- a/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 +++ b/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 @@ -135,7 +135,7 @@ $LogFileName = 'ScriptLog.txt' $USBDrive = Get-USBDrive New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null $LogFile = $USBDrive + $LogFilename -$version = '2412.3' +$version = '2412.4' WriteLog 'Begin Logging' WriteLog "Script version: $version" From 0606a1278c1876eec2dad6fbea6f64a7229a71c9 Mon Sep 17 00:00:00 2001 From: JonasKloseBW Date: Thu, 27 Feb 2025 18:37:29 +0100 Subject: [PATCH 5/6] Update ApplyFFU.ps1 - Allows setting the computer name with a predefined list (SerialComputerNames.csv) of serial numbers and matching computer names - Defaults to FFU-{Random} if no matching serial number is found in list so FFU deployment can continue without user input --- .../WinPEDeployFFUFiles/ApplyFFU.ps1 | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 b/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 index 99b701a..0d7b7b7 100644 --- a/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 +++ b/FFUDevelopment/WinPEDeployFFUFiles/ApplyFFU.ps1 @@ -222,6 +222,7 @@ if (Test-Path -Path $PPKGFolder){ $UnattendFolder = $USBDrive + "unattend\" $UnattendFilePath = $UnattendFolder + "unattend.xml" $UnattendPrefixPath = $UnattendFolder + "prefixes.txt" +$UnattendComputerNamePath = $UnattendFolder + "SerialComputerNames.csv" If (Test-Path -Path $UnattendFilePath){ $UnattendFile = Get-ChildItem -Path $UnattendFilePath If ($UnattendFile){ @@ -234,6 +235,12 @@ If (Test-Path -Path $UnattendPrefixPath){ $UnattendPrefix = $true } } +If (Test-Path -Path $UnattendComputerNamePath){ + $UnattendComputerNameFile = Get-ChildItem -Path $UnattendComputerNamePath + If ($UnattendComputerNameFile){ + $UnattendComputerName = $true + } +} #Ask for device name if unattend exists if ($Unattend -and $UnattendPrefix){ @@ -278,7 +285,25 @@ if ($Unattend -and $UnattendPrefix){ $computername = Set-Computername($computername) Writelog "Computer name set to $computername" } -elseif($Unattend){ +elseif($Unattend -and $UnattendComputerName){ + Writelog 'Unattend file found with SerialComputerNames.csv. Getting name for current computer.' + $SerialComputerNames = Import-Csv -Path $UnattendComputerNameFile.FullName -Delimiter "," + + $SerialNumber = (Get-CimInstance -Class Win32_Bios).SerialNumber + $SCName = $SerialComputerNames | Where-Object { $_.SerialNumber -eq $SerialNumber } + + If ($SCName) { + [string]$computername = $SCName.ComputerName + $computername = Set-Computername($computername) + Writelog "Computer name set to $computername" + } else { + Writelog 'No matching serial number found in SerialComputerNames.csv. Setting random computer name to complete setup.' + [string]$computername = ("FFU-" + (-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 11 | ForEach-Object { [char]$_ }))) + $computername = Set-Computername($computername) + Writelog "Computer name set to $computername" + } +} +elseif($Unattend) { Writelog 'Unattend file found with no prefixes.txt, asking for name' [string]$computername = Read-Host 'Enter device name' Set-Computername($computername) From 0d1d3a1ed549ec232f8a44b6c7d105f68b90cb18 Mon Sep 17 00:00:00 2001 From: rbalsleyMSFT Date: Wed, 14 May 2025 19:59:04 -0700 Subject: [PATCH 6/6] Fix issue with checkpoint CUs and May 2025-05B CU. Should future proof new checkpoint CUs in the future. --- FFUDevelopment/BuildFFUVM.ps1 | 88 +++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 14 deletions(-) diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1 index fe2107c..c73a46f 100644 --- a/FFUDevelopment/BuildFFUVM.ps1 +++ b/FFUDevelopment/BuildFFUVM.ps1 @@ -2431,23 +2431,27 @@ function Get-KBLink { $VerbosePreference = 'SilentlyContinue' $results = Invoke-WebRequest -Uri "http://www.catalog.update.microsoft.com/Search.aspx?q=$Name" -Headers $Headers -UserAgent $UserAgent $VerbosePreference = $OriginalVerbosePreference + + # Extract the first KB article ID from the HTML content and store it globally + if ($results.Content -match '>\s*([^\(<]+)\(KB(\d+)\)\s*<') { + $kbArticleID = "KB$($matches[2])" + $global:LastKBArticleID = $kbArticleID + WriteLog "Found KB article ID: $kbArticleID" + } + else { + WriteLog "No KB article ID found in search results." + $global:LastKBArticleID = $null + } + $kbids = $results.InputFields | Where-Object { $_.type -eq 'Button' -and $_.Value -eq 'Download' } | Select-Object -ExpandProperty ID - # Write-Verbose -Message "$kbids" - if (-not $kbids) { Write-Warning -Message "No results found for $Name" return } - # $guids = $results.Links | - # Where-Object ID -match '_link' | - # Where-Object { $_.OuterHTML -match ( "(?=.*" + ( $Filter -join ")(?=.*" ) + ")" ) } | - # ForEach-Object { $_.id.replace('_link', '') } | - # Where-Object { $_ -in $kbids } - $guids = $results.Links | Where-Object ID -match '_link' | Where-Object { $_.OuterHTML -match ( "(?=.*" + ( $Filter -join ")(?=.*" ) + ")" ) } | @@ -4568,8 +4572,27 @@ try { WriteLog "Latest SSU saved to $SSUFilePath" } WriteLog "Searching for $name from Microsoft Update Catalog and saving to $KBPath" - $KBFilePath = Save-KB -Name $Name -Path $KBPath - WriteLog "Latest CU saved to $KBPath\$KBFilePath" + $CUFileName = Save-KB -Name $Name -Path $KBPath + # Check if $CUFileName contains the string in $global:LastKBArticleID + # If it does not, look in $KBPath for the file that contains the string in $global:LastKBArticleID + # and set that as the $CUFileName + # This is because checkpoint CUs download indeterministically + WriteLog "Checking if $CUFileName contains $global:LastKBArticleID" + if ($CUFileName -notmatch $global:LastKBArticleID) { + WriteLog "$CUFileName does not contain $global:LastKBArticleID, searching for file that contains it" + $CUFileName = $null + # Get the file that contains the string in $global:LastKBArticleID + $CUFileName = (Get-ChildItem -Path $KBPath -Filter "*$global:LastKBArticleID*" | Select-Object -First 1).Name + if ($null -ne $CUFileName) { + WriteLog "Found $CUFileName" + } + else { + WriteLog "Could not find file that contains $global:LastKBArticleID" + throw "Could not find file that contains $global:LastKBArticleID" + } + } + $CUPath = "$KBPath\$CUFileName" + WriteLog "Latest CU saved to $CUPath" } #Update Latest Preview Cumlative Update for Client OS only @@ -4583,8 +4606,27 @@ try { New-Item -Path $KBPath -ItemType Directory -Force | Out-Null } WriteLog "Searching for $name from Microsoft Update Catalog and saving to $KBPath" - $KBFilePath = Save-KB -Name $Name -Path $KBPath - WriteLog "Latest Preview CU saved to $KBPath\$KBFilePath" + $CUPFileName = Save-KB -Name $Name -Path $KBPath + # Check if $CUPFileName contains the string in $global:LastKBArticleID + # If it does not, look in $KBPath for the file that contains the string in $global:LastKBArticleID + # and set that as the $CUPFileName + # This is because checkpoint CUs download indeterministically + WriteLog "Checking if $CUPFileName contains $global:LastKBArticleID" + if ($CUPFileName -notmatch $global:LastKBArticleID) { + WriteLog "$CUPFileName does not contain $global:LastKBArticleID, searching for file that contains it" + $CUPFileName = $null + # Get the file that contains the string in $global:LastKBArticleID + $CUPFileName = (Get-ChildItem -Path $KBPath -Filter "*$global:LastKBArticleID*" | Select-Object -First 1).Name + if ($null -ne $CUPFileName) { + WriteLog "Found $CUPFileName" + } + else { + WriteLog "Could not find file that contains $global:LastKBArticleID" + throw "Could not find file that contains $global:LastKBArticleID" + } + } + $CUPPath = "$KBPath\$CUPFileName" + WriteLog "Latest CU saved to $CUPPath" } #Update Latest .NET Framework @@ -4611,8 +4653,26 @@ try { New-Item -Path $KBPath -ItemType Directory -Force | Out-Null } WriteLog "Searching for $name from Microsoft Update Catalog and saving to $KBPath" - $KBFilePath = Save-KB -Name $Name -Path $KBPath - WriteLog "Latest .NET saved to $KBPath\$KBFilePath" + $NETFileName = Save-KB -Name $Name -Path $KBPath + # Check if $NETFileName contains the string in $global:LastKBArticleID + # If it does not, look in $KBPath for the file that contains the string in $global:LastKBArticleID + # and set that as the $NETFileName + WriteLog "Checking if $NETFileName contains $global:LastKBArticleID" + if ($NETFileName -notmatch $global:LastKBArticleID) { + WriteLog "$NETFileName does not contain $global:LastKBArticleID, searching for file that contains it" + $NETFileName = $null + # Get the file that contains the string in $global:LastKBArticleID + $NETFileName = (Get-ChildItem -Path $KBPath -Filter "*$global:LastKBArticleID*" | Select-Object -First 1).Name + if ($null -ne $NETFileName) { + WriteLog "Found $NETFileName" + } + else { + WriteLog "Could not find file that contains $global:LastKBArticleID" + throw "Could not find file that contains $global:LastKBArticleID" + } + } + $NETPath = "$KBPath\$NETFileName" + WriteLog "Latest CU saved to $NETPath" } #Search for cached VHDX and skip VHDX creation if there's a cached version