From c8ef42ab2115a068e46f3d0ce1275b3417180771 Mon Sep 17 00:00:00 2001
From: w0 <33212583+w0@users.noreply.github.com>
Date: Thu, 12 Sep 2024 13:54:58 -0500
Subject: [PATCH 01/50] Update USBImagingToolCreator.ps1
added support for relative paths. formatted script and removed functions from inside if statement.
---
FFUDevelopment/USBImagingToolCreator.ps1 | 298 ++++++++++++-----------
1 file changed, 158 insertions(+), 140 deletions(-)
diff --git a/FFUDevelopment/USBImagingToolCreator.ps1 b/FFUDevelopment/USBImagingToolCreator.ps1
index dd3f36d..d390896 100644
--- a/FFUDevelopment/USBImagingToolCreator.ps1
+++ b/FFUDevelopment/USBImagingToolCreator.ps1
@@ -1,20 +1,18 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $True, Position = 0)]
- $DeployISOPath,
+ [io.fileinfo] $DeployISOPath,
[Switch]$DisableAutoPlay
)
-$Host.UI.RawUI.WindowTitle = 'USB Imaging Tool Creator'
-if($DeployISOPath){
-$DevelopmentPath = $DeployISOPath | Split-Path
function WriteLog($LogText) {
-$LogFileName = '\Script.log'
-$LogFile = $DevelopmentPath + $LogFilename
+ $LogFileName = '\Script.log'
+ $LogFile = $DevelopmentPath + $LogFilename
Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText" -Force -ErrorAction SilentlyContinue
Write-Verbose $LogText
}
-Function Get-USBDrive {
+
+function Get-USBDrive {
$USBDrives = (Get-WmiObject -Class Win32_DiskDrive -Filter "MediaType='Removable Media'")
If ($USBDrives -and ($null -eq $USBDrives.count)) {
$USBDrivesCount = 1
@@ -31,26 +29,27 @@ Function Get-USBDrive {
}
return $USBDrives, $USBDrivesCount
}
-Function Build-DeploymentUSB{
- param(
- [Array]$Drives
- )
- writelog "Checking if ffu files are present in the ffu folder"
- $Images = Get-ChildItem -Path $FFUPath -Filter "*.ffu" -File -Recurse
- writelog "Checking if drivers are present in the drivers folder"
- $Drivers = Get-ChildItem -Path $DriversPath -Recurse
- $DrivesCount = $Drives.Count
- Writelog "Creating partitions..."
- foreach ($USBDrive in $Drives) {
- $DriveNumber = $USBDrive.DeviceID.Replace("\\.\PHYSICALDRIVE", "")
- $Model = $USBDrive.model
- $ScriptBlock = {
+
+function Build-DeploymentUSB {
+ param(
+ [Array]$Drives
+ )
+ writelog "Checking if ffu files are present in the ffu folder"
+ $Images = Get-ChildItem -Path $FFUPath -Filter "*.ffu" -File -Recurse
+ writelog "Checking if drivers are present in the drivers folder"
+ $Drivers = Get-ChildItem -Path $DriversPath -Recurse
+ $DrivesCount = $Drives.Count
+ Writelog "Creating partitions..."
+ foreach ($USBDrive in $Drives) {
+ $DriveNumber = $USBDrive.DeviceID.Replace("\\.\PHYSICALDRIVE", "")
+ $Model = $USBDrive.model
+ $ScriptBlock = {
param($DriveNumber)
Clear-Disk -Number $DriveNumber -RemoveData -RemoveOEM -Confirm:$false
$Disk = Get-Disk -Number $DriveNumber
$PartitionStyle = $Disk.PartitionStyle
- if($PartitionStyle -ne 'MBR'){
- $Disk | Set-Disk -PartitionStyle MBR
+ if ($PartitionStyle -ne 'MBR') {
+ $Disk | Set-Disk -PartitionStyle MBR
}
$BootPartition = New-Partition -DiskNumber $DriveNumber -Size 2GB -IsActive -AssignDriveLetter
$DeployPartition = New-Partition -DiskNumber $DriveNumber -UseMaximumSize -AssignDriveLetter
@@ -62,127 +61,131 @@ Function Build-DeploymentUSB{
}
writelog "Wait for partitioning jobs to complete"
Get-Job | Wait-Job | Out-Null
- if($DrivesCount -gt 1){
- writelog "Get file system information for all drives"
- $Partitions = Get-Partition | Get-Volume
- }else{
- writelog "Get file system information for drive number $DiskNumber"
- $Partitions = Get-Partition -DiskNumber $DriveNumber | Get-Volume
+ if ($DrivesCount -gt 1) {
+ writelog "Get file system information for all drives"
+ $Partitions = Get-Partition | Get-Volume
}
-writelog "Get drive letter for all volumes labeled:BOOT"
-$BootDrives = ($Partitions | Where-Object { $_.FileSystemLabel -eq "BOOT"}).DriveLetter
-writelog "Get drive letter for all volumes labeled:Deploy"
-$DeployDrives = ($Partitions | Where-Object { $_.FileSystemLabel -eq "Deploy"}).DriveLetter
-writelog "Mount Deployment .iso image"
-$ISOMountPoint = (Mount-DiskImage -ImagePath "$DeployISOPath" -PassThru | Get-Volume).DriveLetter + ":\"
-writelog "Copying boot files to all drives labeled BOOT concurrently"
-foreach ($Drive in $BootDrives) {
-$Destination = $Drive + ":\"
- $jobScriptBlock = {
- param (
- [string]$SFolder,
- [string]$DFolder
- )
- Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
+ else {
+ writelog "Get file system information for drive number $DiskNumber"
+ $Partitions = Get-Partition -DiskNumber $DriveNumber | Get-Volume
}
- WriteLog "Start job to copy all boot files to $Destination"
- Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $ISOMountPoint, $Destination | Out-Null
-}
-if($Images){
-writelog "Copying FFU image files to all drives labeled deploy concurrently"
-foreach ($Drive in $DeployDrives) {
-$Destination = $Drive + ":\"
- $jobScriptBlock = {
- param (
- [string]$SFolder,
- [string]$DFolder
- )
- Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
- }
-
- WriteLog "Start job to copy all FFU files to $Destination"
- Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $FFUPath, $Destination | Out-Null
- }
-}
-if($Drivers){
-writelog "Copying driver files to all drives labeled deploy concurrently"
-foreach ($Drive in $DeployDrives) {
-$Destination = $Drive + ":\Drivers"
- $jobScriptBlock = {
- param (
- [string]$SFolder,
- [string]$DFolder
- )
- New-Item -Path $DFolder -ItemType Directory -Force -Confirm: $false | Out-Null
- Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
- }
- WriteLog "Start job to copy all drivers to $Destination"
- Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $DriversPath, $Destination | Out-Null
-}
-}
-if(!($Drivers)){
- foreach ($Drive in $DeployDrives) {
- WriteLog "Create drivers directory"
- $drivepath = $Drive + ":\"
- New-Item -Path "$drivepath" -Name Drivers -ItemType Directory -Force -Confirm: $false | Out-Null
+ writelog "Get drive letter for all volumes labeled:BOOT"
+ $BootDrives = ($Partitions | Where-Object { $_.FileSystemLabel -eq "BOOT" }).DriveLetter
+ writelog "Get drive letter for all volumes labeled:Deploy"
+ $DeployDrives = ($Partitions | Where-Object { $_.FileSystemLabel -eq "Deploy" }).DriveLetter
+ writelog "Mount Deployment .iso image"
+ $ISOMountPoint = (Mount-DiskImage -ImagePath "$DeployISOPath" -PassThru | Get-Volume).DriveLetter + ":\"
+ writelog "Copying boot files to all drives labeled BOOT concurrently"
+ foreach ($Drive in $BootDrives) {
+ $Destination = $Drive + ":\"
+ $jobScriptBlock = {
+ param (
+ [string]$SFolder,
+ [string]$DFolder
+ )
+ Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
}
-}
-if($DrivesCount -gt 1){
-Writelog "Building $DrivesCount drives concurrently...Please be patient..."
-}else{
-Writelog "Building the imaging tool on $model...Please be patient..."
-}
-Get-Job | Wait-Job | Out-Null
+ WriteLog "Start job to copy all boot files to $Destination"
+ Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $ISOMountPoint, $Destination | Out-Null
+ }
+ if ($Images) {
+ writelog "Copying FFU image files to all drives labeled deploy concurrently"
+ foreach ($Drive in $DeployDrives) {
+ $Destination = $Drive + ":\"
+ $jobScriptBlock = {
+ param (
+ [string]$SFolder,
+ [string]$DFolder
+ )
+ Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
+ }
-Dismount-DiskImage -ImagePath $DeployISOPath | Out-Null
-Writelog "Drive creation jobs completed..."
+ WriteLog "Start job to copy all FFU files to $Destination"
+ Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $FFUPath, $Destination | Out-Null
+ }
+ }
+ if ($Drivers) {
+ writelog "Copying driver files to all drives labeled deploy concurrently"
+ foreach ($Drive in $DeployDrives) {
+ $Destination = $Drive + ":\Drivers"
+ $jobScriptBlock = {
+ param (
+ [string]$SFolder,
+ [string]$DFolder
+ )
+ New-Item -Path $DFolder -ItemType Directory -Force -Confirm: $false | Out-Null
+ Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
+ }
+ WriteLog "Start job to copy all drivers to $Destination"
+ Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $DriversPath, $Destination | Out-Null
+ }
+ }
+ if (!($Drivers)) {
+ foreach ($Drive in $DeployDrives) {
+ WriteLog "Create drivers directory"
+ $drivepath = $Drive + ":\"
+ New-Item -Path "$drivepath" -Name Drivers -ItemType Directory -Force -Confirm: $false | Out-Null
+ }
+ }
+ if ($DrivesCount -gt 1) {
+ Writelog "Building $DrivesCount drives concurrently...Please be patient..."
+ }
+ else {
+ Writelog "Building the imaging tool on $model...Please be patient..."
+ }
+ Get-Job | Wait-Job | Out-Null
+
+ Dismount-DiskImage -ImagePath $DeployISOPath | Out-Null
+ Writelog "Drive creation jobs completed..."
}
-Function New-DeploymentUSB {
+function New-DeploymentUSB {
param(
[Array]$Drives,
[int]$Count,
[String]$FFUPath = "$DevelopmentPath\FFU",
[String]$DriversPath = "$DevelopmentPath\Drivers"
-
+
)
-
+
$Drivelist = @()
writelog "Creating a USB drive selection list"
- for($i=0;$i -le $Count -1;$i++){
+ for ($i = 0; $i -le $Count - 1; $i++) {
$DriveModel = $Drives[$i].Model
- $DriveSize = [math]::round($Drives[$i].size/1GB, 2)
+ $DriveSize = [math]::round($Drives[$i].size / 1GB, 2)
$DiskNumber = $Drives[$i].DeviceID.Replace("\\.\PHYSICALDRIVE", "")
- $Properties = [ordered]@{Number = $i + 1 ; DriveNumber = $DiskNumber ; DriveModel = $driveModel ; 'Size (GB)' = $DriveSize}
+ $Properties = [ordered]@{Number = $i + 1 ; DriveNumber = $DiskNumber ; DriveModel = $driveModel ; 'Size (GB)' = $DriveSize }
$Drivelist += New-Object PSObject -Property $Properties
- }
- if($Count -gt 1){
- $Last = $Count+1
+ }
+ if ($Count -gt 1) {
+ $Last = $Count + 1
$Drivelist += New-Object -TypeName PSObject -Property @{ Number = "$last"; DriveModel = "Select this option to use all ($count) inserted USB Drives" }
- }
- $Drivelist | Format-Table -AutoSize -Property Number, DriveModel , 'Size (GB)'
- do {
+ }
+ $Drivelist | Format-Table -AutoSize -Property Number, DriveModel , 'Size (GB)'
+ do {
try {
$var = $true
$DriveSelected = Read-Host 'Enter the drive number to apply the .iso to'
- $DriveSelected = ($DriveSelected -as [int]) -1
- if($Last){
- writelog "All drives selected"
- }else{
- writelog "Drive $DriveSelected selected"}
+ $DriveSelected = ($DriveSelected -as [int]) - 1
+ if ($Last) {
+ writelog "All drives selected"
}
- catch {
+ else {
+ writelog "Drive $DriveSelected selected"
+ }
+ }
+ catch {
Write-Host 'Input was not in correct format. Please enter a valid FFU number'
$var = $false
}
- } until (($DriveSelected -le $Count -1 -or $last) -and $var)
+ } until (($DriveSelected -le $Count - 1 -or $last) -and $var)
$DisableAutoPlayCurrentSetting = (Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name DisableAutoplay).DisableAutoplay
- if($DisableAutoPlay -and $DisableAutoPlayCurrentSetting -ne 1){
- writelog "Disable autoPlay current setting is $DisableAutoPlayCurrentSetting"
- WriteLog "Setting the registry key to disable autoplay for all drives"
- Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name "DisableAutoplay" -Value 1 -Type DWORD
+ if ($DisableAutoPlay -and $DisableAutoPlayCurrentSetting -ne 1) {
+ writelog "Disable autoPlay current setting is $DisableAutoPlayCurrentSetting"
+ WriteLog "Setting the registry key to disable autoplay for all drives"
+ Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name "DisableAutoplay" -Value 1 -Type DWORD
}
WriteLog "Closing all MMC windows to prevent drive lock errors"
Stop-Process -Name mmc -ErrorAction SilentlyContinue
@@ -190,34 +193,49 @@ Function New-DeploymentUSB {
Stop-Process -Name diskpart -ErrorAction SilentlyContinue
$Selection = $Drivelist[$DriveSelected].Number
$totalSteps = 5
- if($Selection -eq $last){
- Read-Host -Prompt "ALL DRIVES SELECTED! WILL ERASE ALL CURRENTLY CONNECTED USB DRIVES!! Press ENTER to continue"
- Build-DeploymentUSB -Drives $Drives
- }else{
- Read-Host -Prompt "Drive number $Selection was selected. Press ENTER to continue"
- Build-DeploymentUSB -Drives $Drives[$DriveSelected]
+ if ($Selection -eq $last) {
+ Read-Host -Prompt "ALL DRIVES SELECTED! WILL ERASE ALL CURRENTLY CONNECTED USB DRIVES!! Press ENTER to continue"
+ Build-DeploymentUSB -Drives $Drives
+ }
+ else {
+ Read-Host -Prompt "Drive number $Selection was selected. Press ENTER to continue"
+ Build-DeploymentUSB -Drives $Drives[$DriveSelected]
}
WriteLog "Setting the registry key to re-enable autoplay for all drives"
- if($DisableAutoPlay){
- Writelog "Setting disable autoplay setting back to $DisableAutoPlayCurrentSetting"
- Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name "DisableAutoplay" -Value $DisableAutoPlayCurrentSetting -Type DWORD
+ if ($DisableAutoPlay) {
+ Writelog "Setting disable autoplay setting back to $DisableAutoPlayCurrentSetting"
+ Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name "DisableAutoplay" -Value $DisableAutoPlayCurrentSetting -Type DWORD
}
Writelog "Completed!"
-}
-#Get USB Drive and create log file
-if(Test-Path "$DevelopmentPath\Script.log"){
-Remove-Item -Path "$DevelopmentPath\Script.log" -Force -Confirm:$false
-New-item -Path $DevelopmentPath -Name 'Script.log' -ItemType "file" -Force | Out-Null
}
-WriteLog 'Begin Logging'
-WriteLog 'Getting USB drive information and usb drive count'
-$USBDrives,$USBDrivesCount = Get-USBDrive
-New-DeploymentUSB -Drives $USBDrives -Count $USBDrivesCount
-read-host -Prompt "USB drive creation complete. Press ENTER to exit"
-Exit
-}else{
-Write-Host "No .ISO file selected..."
-read-host "Press ENTER to Exit..."
-Exit
+$Host.UI.RawUI.WindowTitle = 'USB Imaging Tool Creator'
+
+# Check if path is relative
+if (-not [System.IO.Path]::IsPathRooted($DeployISOPath)) {
+ # Path is relative, Build full path
+ [io.fileinfo] $DeployISOPath = (Resolve-Path $DeployISOPath).Path
+ Write-Verbose "Path to ISO was relative. Attempting to resolve at: $($DeployISOPath.FullName)"
+}
+
+if ($DeployISOPath.Exists) {
+ $DevelopmentPath = $DeployISOPath.Directory.FullName
+
+ #Get USB Drive and create log file
+ if (Test-Path "$DevelopmentPath\Script.log") {
+ Remove-Item -Path "$DevelopmentPath\Script.log" -Force -Confirm:$false
+ New-item -Path $DevelopmentPath -Name 'Script.log' -ItemType "file" -Force | Out-Null
+ }
+
+ WriteLog 'Begin Logging'
+ WriteLog 'Getting USB drive information and usb drive count'
+
+ $USBDrives, $USBDrivesCount = Get-USBDrive
+
+ New-DeploymentUSB -Drives $USBDrives -Count $USBDrivesCount
+
+ Read-Host -Prompt "USB drive creation complete. Press ENTER to exit"
+ Exit
+} else {
+ Write-Error "Unable to locate ISO file at: $($DeployISOPath.FullName)"
}
From 807456de86edb7075e791205ff6b5bf4021802a7 Mon Sep 17 00:00:00 2001
From: w0 <33212583+w0@users.noreply.github.com>
Date: Thu, 12 Sep 2024 13:57:58 -0500
Subject: [PATCH 02/50] removed unused variable. more format.
---
FFUDevelopment/USBImagingToolCreator.ps1 | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/FFUDevelopment/USBImagingToolCreator.ps1 b/FFUDevelopment/USBImagingToolCreator.ps1
index d390896..c5c28ab 100644
--- a/FFUDevelopment/USBImagingToolCreator.ps1
+++ b/FFUDevelopment/USBImagingToolCreator.ps1
@@ -16,8 +16,7 @@ function Get-USBDrive {
$USBDrives = (Get-WmiObject -Class Win32_DiskDrive -Filter "MediaType='Removable Media'")
If ($USBDrives -and ($null -eq $USBDrives.count)) {
$USBDrivesCount = 1
- }
- else {
+ } else {
$USBDrivesCount = $USBDrives.Count
}
WriteLog "Found $USBDrivesCount USB drives"
@@ -64,8 +63,7 @@ function Build-DeploymentUSB {
if ($DrivesCount -gt 1) {
writelog "Get file system information for all drives"
$Partitions = Get-Partition | Get-Volume
- }
- else {
+ } else {
writelog "Get file system information for drive number $DiskNumber"
$Partitions = Get-Partition -DiskNumber $DriveNumber | Get-Volume
}
@@ -129,8 +127,7 @@ function Build-DeploymentUSB {
}
if ($DrivesCount -gt 1) {
Writelog "Building $DrivesCount drives concurrently...Please be patient..."
- }
- else {
+ } else {
Writelog "Building the imaging tool on $model...Please be patient..."
}
Get-Job | Wait-Job | Out-Null
@@ -192,12 +189,11 @@ function New-DeploymentUSB {
WriteLog "Closing all Diskpart windows to prevent drive lock errors"
Stop-Process -Name diskpart -ErrorAction SilentlyContinue
$Selection = $Drivelist[$DriveSelected].Number
- $totalSteps = 5
+
if ($Selection -eq $last) {
Read-Host -Prompt "ALL DRIVES SELECTED! WILL ERASE ALL CURRENTLY CONNECTED USB DRIVES!! Press ENTER to continue"
Build-DeploymentUSB -Drives $Drives
- }
- else {
+ } else {
Read-Host -Prompt "Drive number $Selection was selected. Press ENTER to continue"
Build-DeploymentUSB -Drives $Drives[$DriveSelected]
}
From 1da28024cc79917b0a0c47e152f64876c880c00b Mon Sep 17 00:00:00 2001
From: Zehadi Alam <63765084+zehadialam@users.noreply.github.com>
Date: Sun, 15 Sep 2024 00:41:04 -0400
Subject: [PATCH 03/50] Add support for updating latest Windows Malicious
Software Removal Tool
---
FFUDevelopment/Apps/InstallAppsandSysprep.cmd | 1 +
FFUDevelopment/BuildFFUVM.ps1 | 90 ++++++++++++++++++-
2 files changed, 89 insertions(+), 2 deletions(-)
diff --git a/FFUDevelopment/Apps/InstallAppsandSysprep.cmd b/FFUDevelopment/Apps/InstallAppsandSysprep.cmd
index 2178783..fc3477b 100644
--- a/FFUDevelopment/Apps/InstallAppsandSysprep.cmd
+++ b/FFUDevelopment/Apps/InstallAppsandSysprep.cmd
@@ -5,6 +5,7 @@ REM d:\Office\setup.exe /configure d:\office\DeployFFU.xml
REM Install Defender Platform Update
REM Install Defender Definitions
REM Install Windows Security Platform Update
+REM Install Windows Malicious Software Removal Tool
REM Install OneDrive Per Machine
REM Install Edge Stable
REM Winget Win32 Apps
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 68cc570..bf01154 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -115,6 +115,9 @@ When set to $true, will download and install the latest .NET Framework for Windo
.PARAMETER UpdateLatestDefender
When set to $true, will download and install the latest Windows Defender definitions and Defender platform update. Default is $false.
+.PARAMETER UpdateLatestMSRT
+When set to $true, will download and install the latest Windows Malicious Software Removal Tool. Default is $false
+
.PARAMETER UpdateEdge
When set to $true, will download and install the latest Microsoft Edge for Windows 10/11. Default is $false.
@@ -306,6 +309,7 @@ param(
[bool]$UpdatePreviewCU,
[bool]$UpdateLatestNet,
[bool]$UpdateLatestDefender,
+ [bool]$UpdateLatestMSRT,
[bool]$UpdateEdge,
[bool]$UpdateOneDrive,
[bool]$CopyPPKG,
@@ -372,6 +376,7 @@ if (-not $FFUCaptureLocation) { $FFUCaptureLocation = "$FFUDevelopmentPath\FFU"
if (-not $LogFile) { $LogFile = "$FFUDevelopmentPath\FFUDevelopment.log" }
if (-not $KBPath) { $KBPath = "$FFUDevelopmentPath\KB" }
if (-not $DefenderPath) { $DefenderPath = "$AppsPath\Defender" }
+if (-not $MSRTPath) { $MSRTPath = "$AppsPath\MSRT" }
if (-not $OneDrivePath) { $OneDrivePath = "$AppsPath\OneDrive" }
if (-not $EdgePath) { $EdgePath = "$AppsPath\Edge" }
if (-not $DriversFolder) { $DriversFolder = "$FFUDevelopmentPath\Drivers" }
@@ -2309,6 +2314,39 @@ function Save-KB {
return $fileName
}
+function Get-MSRTUrl {
+ param (
+ [ValidateSet("x86", "x64")]
+ [string]$WindowsArch
+ )
+ try {
+ # Retrieve content of Windows Malicious Software Removal Tool page
+ $pattern = '\"url\":\"(https://download\.microsoft\.com/download/[^"]+)\"'
+ $OriginalVerbosePreference = $VerbosePreference
+ $VerbosePreference = 'SilentlyContinue'
+ if ($WindowsArch -eq "x64") {
+ $MSRTWebPage = Invoke-RestMethod "https://www.microsoft.com/en-us/download/details.aspx?id=9905" -Headers $Headers -UserAgent $UserAgent
+ }
+ if ($WindowsArch -eq "x86") {
+ $MSRTWebPage = Invoke-RestMethod "https://www.microsoft.com/en-us/download/details.aspx?id=16" -Headers $Headers -UserAgent $UserAgent
+ }
+ $VerbosePreference = $OriginalVerbosePreference
+ # Extract download URL based on specified pattern
+ $MSRTMatch = [regex]::Match($MSRTWebPage, $pattern)
+ if (-not $MSRTMatch.Success) {
+ WriteLog "Failed to retrieve Malicious Software Removal Tool URL. Pattern match failed"
+ return
+ }
+ $MSRTUrl = $MSRTMatch.Groups[1].Value
+ return $MSRTUrl
+ }
+ catch {
+ WriteLog $_
+ Write-Error "Error occurred while retrieving Windows Malicious Software Removal Tool link"
+ throw $_
+ }
+}
+
function New-AppsISO {
#Create Apps ISO file
$OSCDIMG = "$adkpath`Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe"
@@ -3511,6 +3549,12 @@ function Get-FFUEnvironment {
Remove-Item -Path $DefenderPath -Recurse -Force -ErrorAction SilentlyContinue
WriteLog 'Removal complete'
}
+ #Clean up $MSRTPath
+ if (Test-Path -Path $MSRTPath) {
+ WriteLog "Removing $MSRTPath"
+ Remove-Item -Path $MSRTPath -Recurse -Force -ErrorAction SilentlyContinue
+ WriteLog 'Removal complete'
+ }
#Clean up $OneDrivePath
If (Test-Path -Path $OneDrivePath) {
WriteLog "Removing $OneDrivePath"
@@ -3561,6 +3605,17 @@ function Clear-InstallAppsandSysprep {
WriteLog 'Removal complete'
}
}
+ if ($UpdateLatestMSRT) {
+ WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to remove Windows Malicious Software Removal Tool"
+ $CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
+ $CmdContent -notmatch 'd:\\MSRT*' | Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
+ #Clean up $MSRTPath
+ If (Test-Path -Path $MSRTPath) {
+ WriteLog "Removing $MSRTPath"
+ Remove-Item -Path $MSRTPath -Recurse -Force -ErrorAction SilentlyContinue
+ WriteLog 'Removal complete'
+ }
+ }
if ($UpdateOneDrive) {
WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to remove OneDrive install"
$CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
@@ -3727,8 +3782,8 @@ if (($LogicalSectorSizeBytes -eq 4096) -and ($installdrivers -eq $true)) {
if ($BuildUSBDrive -eq $true) {
$USBDrives, $USBDrivesCount = Get-USBDrive
}
-if (($InstallApps -eq $false) -and (($UpdateLatestDefender -eq $true) -or ($UpdateOneDrive -eq $true) -or ($UpdateEdge -eq $true))) {
- WriteLog 'You have selected to update Defender, OneDrive, or Edge, however you are setting InstallApps to false. These updates require the InstallApps variable to be set to true. Please set InstallApps to true and try again.'
+if (($InstallApps -eq $false) -and (($UpdateLatestDefender -eq $true) -or ($UpdateOneDrive -eq $true) -or ($UpdateEdge -eq $true) -or ($UpdateLatestMSRT -eq $true))) {
+ WriteLog 'You have selected to update Defender, Malicious Software Removal Tool, OneDrive, or Edge, however you are setting InstallApps to false. These updates require the InstallApps variable to be set to true. Please set InstallApps to true and try again.'
throw "InstallApps variable must be set to `$true to update Defender, OneDrive, or Edge"
}
if (($WindowsArch -eq 'ARM64') -and ($InstallOffice -eq $true)) {
@@ -3741,6 +3796,11 @@ if (($WindowsArch -eq 'ARM64') -and ($UpdateOneDrive -eq $true)) {
WriteLog 'OneDrive currently fails to install on ARM64 VMs (even with the OneDrive ARM setup files). Setting UpdateOneDrive to false'
}
+if (($WindowsArch -eq 'ARM64') -and ($UpdateLatestMSRT -eq $true)) {
+ $UpdateLatestMSRT = $false
+ WriteLog 'Windows Malicious Software Removal Tool is not available for the ARM64 architecture.'
+}
+
###END PARAMETER VALIDATION
#Get script variable values
@@ -3887,6 +3947,32 @@ if ($InstallApps) {
Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $UpdatedcmdContent
WriteLog "Update complete"
}
+ if ($UpdateLatestMSRT) {
+ WriteLog "`$UpdateLatestMSRT is set to true."
+ #Check if $MSRTPath exists, if not, create it
+ if (-not (Test-Path -Path $MSRTPath)) {
+ WriteLog "Creating $MSRTPath"
+ New-Item -Path $MSRTPath -ItemType Directory -Force | Out-Null
+ }
+ WriteLog "Getting Windows Malicious Software Removal Tool URL"
+ $MSRTUrl = Get-MSRTUrl -WindowsArch $WindowsArch
+ $MSRTFileName = Split-Path -Path $MSRTUrl -Leaf
+ try {
+ WriteLog "Windows Malicious Software Removal Tool URL for $WindowsArch is $MSRTUrl"
+ Start-BitsTransferWithRetry -Source $MSRTUrl -Destination "$MSRTPath\$MSRTFileName"
+ WriteLog "Windows Malicious Software Removal Tool downloaded to $MSRTPath\$MSRTFileName"
+ }
+ catch {
+ Write-Error "Downloading Windows Malicious Software Removal Tool failed"
+ WriteLog "Downloading Windows Malicious Software Removal Tool failed"
+ throw $_
+ }
+ WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to include Windows Malicious Software Removal Tool"
+ $CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
+ $UpdatedcmdContent = $CmdContent -replace '^(REM Install Windows Malicious Software Removal Tool)', ("REM Install Windows Malicious Software Removal Tool`r`nStart /wait d:\MSRT\$MSRTFileName /quiet")
+ Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $UpdatedcmdContent
+ WriteLog "Update complete"
+ }
#Download and Install OneDrive Per Machine
if ($UpdateOneDrive) {
WriteLog "`$UpdateOneDrive is set to true, checking for latest OneDrive client"
From 5dcdd2c36f5a3a15d1a9bb160dd8f638eed7376b Mon Sep 17 00:00:00 2001
From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com>
Date: Tue, 17 Sep 2024 14:03:43 -0700
Subject: [PATCH 04/50] Clean up the Get-MicrosoftDrivers old code and version
change
---
FFUDevelopment/BuildFFUVM.ps1 | 159 +---------------------------------
1 file changed, 1 insertion(+), 158 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 7829a3f..d09359b 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -336,7 +336,7 @@ param(
[bool]$AllowExternalHardDiskMedia,
[bool]$PromptExternalHardDiskMedia = $true
)
-$version = '2409.2'
+$version = '2410.1'
#Check if Hyper-V feature is installed (requires only checks the module)
$osInfo = Get-WmiObject -Class Win32_OperatingSystem
@@ -541,163 +541,6 @@ function Start-BitsTransferWithRetry {
return $false
}
-# function Get-MicrosoftDrivers {
-# param (
-# [string]$Make,
-# [string]$Model,
-# [int]$WindowsRelease
-# )
-
-# $url = "https://support.microsoft.com/en-us/surface/download-drivers-and-firmware-for-surface-09bb2e09-2a4b-cb69-0951-078a7739e120"
-
-# # Download the webpage content
-# WriteLog "Getting Surface driver information from $url"
-# $OriginalVerbosePreference = $VerbosePreference
-# $VerbosePreference = 'SilentlyContinue'
-# $webContent = Invoke-WebRequest -Uri $url -UseBasicParsing -Headers $Headers -UserAgent $UserAgent
-# $VerbosePreference = $OriginalVerbosePreference
-# WriteLog "Complete"
-
-# # Parse the content of the relevant nested divs
-# WriteLog "Parsing web content for models and download links"
-# $html = $webContent.Content
-# $nestedDivPattern = '
(.*?)
'
-# $nestedDivMatches = [regex]::Matches($html, $nestedDivPattern, [System.Text.RegularExpressions.RegexOptions]::Singleline)
-
-# $models = @()
-# $modelPattern = '(.*?)
\s*\s*\s* \s*window.__DLCDetails__={(.*?)}'
-# $scriptMatch = [regex]::Match($downloadPageContent.Content, $scriptPattern)
-
-# if ($scriptMatch.Success) {
-# $scriptContent = $scriptMatch.Groups[1].Value
-
-# # Extract the download file information from the script tag
-# $downloadFilePattern = '"name":"(.*?)",.*?"url":"(.*?)"'
-# $downloadFileMatches = [regex]::Matches($scriptContent, $downloadFilePattern)
-
-# $downloadLink = $null
-# foreach ($downloadFile in $downloadFileMatches) {
-# $fileName = $downloadFile.Groups[1].Value
-# $fileUrl = $downloadFile.Groups[2].Value
-
-# if ($fileName -match "Win$WindowsRelease") {
-# $downloadLink = $fileUrl
-# break
-# }
-# }
-
-# if ($downloadLink) {
-# WriteLog "Download Link for Windows ${WindowsRelease}: $downloadLink"
-
-# # Create directory structure
-# if (-not (Test-Path -Path $DriversFolder)) {
-# WriteLog "Creating Drivers folder: $DriversFolder"
-# New-Item -Path $DriversFolder -ItemType Directory -Force | Out-Null
-# WriteLog "Drivers folder created"
-# }
-# $surfaceDriversPath = Join-Path -Path $DriversFolder -ChildPath $Make
-# $modelPath = Join-Path -Path $surfaceDriversPath -ChildPath $Model
-# if (-Not (Test-Path -Path $modelPath)) {
-# WriteLog "Creating model folder: $modelPath"
-# New-Item -Path $modelPath -ItemType Directory | Out-Null
-# WriteLog "Complete"
-# }
-
-# # Download the file
-# $filePath = Join-Path -Path $surfaceDriversPath -ChildPath ($fileName)
-# WriteLog "Downloading $Model driver file to $filePath"
-# Start-BitsTransferWithRetry -Source $downloadLink -Destination $filePath
-# WriteLog "Download complete"
-
-# # Determine file extension
-# $fileExtension = [System.IO.Path]::GetExtension($filePath).ToLower()
-
-# if ($fileExtension -eq ".msi") {
-# # Extract the MSI file using an administrative install
-# WriteLog "Extracting MSI file to $modelPath"
-# $arguments = "/a `"$($filePath)`" /qn TARGETDIR=`"$($modelPath)`""
-# Invoke-Process -FilePath "msiexec.exe" -ArgumentList $arguments
-# WriteLog "Extraction complete"
-# } elseif ($fileExtension -eq ".zip") {
-# # Extract the ZIP file
-# WriteLog "Extracting ZIP file to $modelPath"
-# $ProgressPreference = 'SilentlyContinue'
-# Expand-Archive -Path $filePath -DestinationPath $modelPath -Force
-# $ProgressPreference = 'Continue'
-# WriteLog "Extraction complete"
-# } else {
-# WriteLog "Unsupported file type: $fileExtension"
-# }
-# # Remove the downloaded file
-# WriteLog "Removing $filePath"
-# Remove-Item -Path $filePath -Force
-# WriteLog "Complete"
-# } else {
-# WriteLog "No download link found for Windows $WindowsRelease."
-# }
-# } else {
-# WriteLog "Failed to parse the download page for the MSI file."
-# }
-# }
function Get-MicrosoftDrivers {
param (
[string]$Make,
From 7a2aab3204976e044f8981c9dcbb07a40d6af9d4 Mon Sep 17 00:00:00 2001
From: Zehadi Alam <63765084+zehadialam@users.noreply.github.com>
Date: Tue, 17 Sep 2024 20:36:22 -0400
Subject: [PATCH 05/50] Adjust install command for MSRT
---
FFUDevelopment/BuildFFUVM.ps1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index bf01154..4c2cec4 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -3969,7 +3969,7 @@ if ($InstallApps) {
}
WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to include Windows Malicious Software Removal Tool"
$CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
- $UpdatedcmdContent = $CmdContent -replace '^(REM Install Windows Malicious Software Removal Tool)', ("REM Install Windows Malicious Software Removal Tool`r`nStart /wait d:\MSRT\$MSRTFileName /quiet")
+ $UpdatedcmdContent = $CmdContent -replace '^(REM Install Windows Malicious Software Removal Tool)', ("REM Install Windows Malicious Software Removal Tool`r`nd:\MSRT\$MSRTFileName /quiet")
Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $UpdatedcmdContent
WriteLog "Update complete"
}
From ec7e9a546cc7da79ee4b12325953c08ca768a0f1 Mon Sep 17 00:00:00 2001
From: Zehadi Alam <63765084+zehadialam@users.noreply.github.com>
Date: Tue, 17 Sep 2024 21:08:09 -0400
Subject: [PATCH 06/50] Adjust pattern for URL scraping
---
FFUDevelopment/BuildFFUVM.ps1 | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 4c2cec4..101df9c 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -2321,7 +2321,7 @@ function Get-MSRTUrl {
)
try {
# Retrieve content of Windows Malicious Software Removal Tool page
- $pattern = '\"url\":\"(https://download\.microsoft\.com/download/[^"]+)\"'
+ $pattern = 'https:\/\/download\.microsoft\.com\/download\/[^\s]*\.exe'
$OriginalVerbosePreference = $VerbosePreference
$VerbosePreference = 'SilentlyContinue'
if ($WindowsArch -eq "x64") {
@@ -2337,7 +2337,7 @@ function Get-MSRTUrl {
WriteLog "Failed to retrieve Malicious Software Removal Tool URL. Pattern match failed"
return
}
- $MSRTUrl = $MSRTMatch.Groups[1].Value
+ $MSRTUrl = $MSRTMatch.Value
return $MSRTUrl
}
catch {
From e3cbcab6b2944404aec397c76078bd4453177a13 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 23 Sep 2024 03:41:40 +0200
Subject: [PATCH 07/50] Update BuildFFUVM.ps1
- Add script parameters to allow Windows Server image creation
---
FFUDevelopment/BuildFFUVM.ps1 | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 7829a3f..0c3011b 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -201,7 +201,11 @@ param(
[Parameter(Mandatory = $false, Position = 0)]
[ValidateScript({ Test-Path $_ })]
[string]$ISOPath,
- [ValidateSet('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')]
+ [ValidateScript({
+ $allowedSKUs = @('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', 'Standard', 'Standard (Desktop Experience)', 'Datacenter', 'Datacenter (Desktop Experience)')
+ if ($allowedSKUs -contains $_) { $true } else { throw "Invalid WindowsSKU value. Allowed values: $($allowedSKUs -join ', ')" }
+ return $true
+ })]
[string]$WindowsSKU = 'Pro',
[ValidateScript({ Test-Path $_ })]
[string]$FFUDevelopmentPath = $PSScriptRoot,
@@ -267,7 +271,7 @@ param(
[string]$ProductKey,
[bool]$BuildUSBDrive,
[Parameter(Mandatory = $false)]
- [ValidateSet(10, 11)]
+ [ValidateSet(10, 11, 2016, 2019, 2022, 2025)]
[int]$WindowsRelease = 11,
[Parameter(Mandatory = $false)]
[string]$WindowsVersion = '23h2',
From cafc45dbba49948b96eff8ff31dc8b34cbbcb11f Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 23 Sep 2024 03:53:45 +0200
Subject: [PATCH 08/50] Update BuildFFUVM.ps1
- Add Dell driver download support for Windows Server 2016, 2022 and fall back to 2022 drivers for other Server versions.
---
FFUDevelopment/BuildFFUVM.ps1 | 33 +++++++++++++++++++++++++++------
1 file changed, 27 insertions(+), 6 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 0c3011b..aa1a1e6 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -1325,10 +1325,21 @@ function Get-DellDrivers {
[string]$Model,
[Parameter(Mandatory = $true)]
[ValidateSet("x64", "x86", "ARM64")]
- [string]$WindowsArch
+ [string]$WindowsArch,
+ [Parameter(Mandatory = $true)]
+ [string]$WindowsRelease
)
- $catalogUrl = "http://downloads.dell.com/catalog/CatalogPC.cab"
+ if ($WindowsRelease -le 11) {
+ $catalogUrl = "http://downloads.dell.com/catalog/CatalogPC.cab"
+ $DellCabFile = "$DriversFolder\CatalogPC.cab"
+ $DellCatalogXML = "$DriversFolder\CatalogPC.XML"
+ } else {
+ $catalogUrl = "https://downloads.dell.com/catalog/Catalog.cab"
+ $DellCabFile = "$DriversFolder\Catalog.cab"
+ $DellCatalogXML = "$DriversFolder\Catalog.xml"
+ }
+
if (-not (Test-Url -Url $catalogUrl)) {
WriteLog "Dell Catalog cab URL is not accessible: $catalogUrl Exiting"
if ($VerbosePreference -ne 'Continue') {
@@ -1348,12 +1359,10 @@ function Get-DellDrivers {
New-Item -Path $DriversFolder -ItemType Directory -Force | Out-Null
WriteLog "Dell Drivers folder created"
- $DellCabFile = "$DriversFolder\CatalogPC.cab"
WriteLog "Downloading Dell Catalog cab file: $catalogUrl to $DellCabFile"
Start-BitsTransferWithRetry -Source $catalogUrl -Destination $DellCabFile
WriteLog "Dell Catalog cab file downloaded"
- $DellCatalogXML = "$DriversFolder\CatalogPC.XML"
WriteLog "Extracting Dell Catalog cab file to $DellCatalogXML"
Invoke-Process -FilePath Expand.exe -ArgumentList "$DellCabFile $DellCatalogXML"
WriteLog "Dell Catalog cab file extracted"
@@ -1367,7 +1376,19 @@ function Get-DellDrivers {
$models = $component.SupportedSystems.Brand.Model
foreach ($item in $models) {
if ($item.Display.'#cdata-section' -match $Model) {
- $validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { $_.osArch -eq $WindowsArch }
+
+ if ($WindowsRelease -le 11) {
+ $validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { $_.osArch -eq $WindowsArch }
+ } elseif ($WindowsRelease -eq 2016) {
+ $validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { ($_.osArch -eq $WindowsArch) -and ($_.osCode -match "W14") }
+ } elseif ($WindowsRelease -eq 2019) {
+ $validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { ($_.osArch -eq $WindowsArch) -and ($_.osCode -match "W19") }
+ } elseif ($WindowsRelease -eq 2022) {
+ $validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { ($_.osArch -eq $WindowsArch) -and ($_.osCode -match "W22") }
+ } else {
+ $validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { ($_.osArch -eq $WindowsArch) -and ($_.osCode -match "W22") }
+ }
+
if ($validOS) {
$driverPath = $component.path
$downloadUrl = $baseLocation + $driverPath
@@ -3952,7 +3973,7 @@ if (($make -and $model) -and ($installdrivers -or $copydrivers)) {
if ($make -eq 'Dell'){
WriteLog 'Getting Dell drivers'
#Dell mixes Win10 and 11 drivers, hence no WindowsRelease parameter
- Get-DellDrivers -Model $Model -WindowsArch $WindowsArch
+ Get-DellDrivers -Model $Model -WindowsArch $WindowsArch -WindowsRelease $WindowsRelease
WriteLog 'Getting Dell drivers completed successfully'
}
}
From 0b151f90546b6b19d986f80164e3535c2aba9787 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 23 Sep 2024 03:55:37 +0200
Subject: [PATCH 09/50] Update BuildFFUVM.ps1
- Throw error when trying to download Windows Server as it's not possible
---
FFUDevelopment/BuildFFUVM.ps1 | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index aa1a1e6..73f8a08 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -1767,8 +1767,10 @@ function Get-WindowsESD {
$cabFileUrl = if ($WindowsRelease -eq 10) {
'https://go.microsoft.com/fwlink/?LinkId=841361'
}
- else {
+ elseif ($WindowsRelease -eq 11) {
'https://go.microsoft.com/fwlink/?LinkId=2156292'
+ } else {
+ throw "Can't download Windows Server. Please download the Windows setup media from your subscription homepage."
}
# Download cab file
From 5acac4ba5bddbed139b8c5f06d36909e0f5f5eea Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 23 Sep 2024 04:02:28 +0200
Subject: [PATCH 10/50] Update BuildFFUVM.ps1
- Update Get-LatestWindowsKB to support searching for Windows Server updates
- Improve the HTML regex to return a more precise match by using the $WindowsRelease variable for lookbehind based searching
---
FFUDevelopment/BuildFFUVM.ps1 | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 73f8a08..3fc1b59 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -2414,16 +2414,21 @@ function Get-KBLink {
}
function Get-LatestWindowsKB {
param (
- [ValidateSet(10, 11)]
- [int]$WindowsRelease
+ [Parameter(Mandatory)]
+ [ValidateSet(10, 11, 2016, 2019, 2022, 2025)]
+ [int]$WindowsRelease,
+ [Parameter(Mandatory)]
+ [string]$WindowsVersion
)
# Define the URL of the update history page based on the Windows release
if ($WindowsRelease -eq 11) {
$updateHistoryUrl = 'https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information'
}
- else {
+ elseif ($WindowsRelease -eq 10) {
$updateHistoryUrl = 'https://learn.microsoft.com/en-us/windows/release-health/release-information'
+ } else {
+ $updateHistoryUrl = 'https://learn.microsoft.com/en-us/windows/release-health/windows-server-release-info'
}
# Use Invoke-WebRequest to fetch the content of the page
@@ -2433,7 +2438,11 @@ function Get-LatestWindowsKB {
$VerbosePreference = $OriginalVerbosePreference
# Use a regular expression to find the KB article number
- $kbArticleRegex = 'KB\d+'
+ if ($WindowsRelease -le 11) {
+ $kbArticleRegex = "(?:Version $WindowsRelease \(OS build d+\)(?!(KB)).)*?KB\d+"
+ } else {
+ $kbArticleRegex = "(?:Windows Server $WindowsRelease \(OS build d+\)(?!(KB)).)*?KB\d+"
+ }
$kbArticle = [regex]::Match($response.Content, $kbArticleRegex).Value
return $kbArticle
From 3457aedf5d760b67da6039293c2953fb280184cd Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 23 Sep 2024 04:04:13 +0200
Subject: [PATCH 11/50] Update BuildFFUVM.ps1
- Add support for searching Windows Server SKUs in images in Get-WimIndex
---
FFUDevelopment/BuildFFUVM.ps1 | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 3fc1b59..0035aa9 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -2563,9 +2563,13 @@ function Get-WimIndex {
If ($ISOPath) {
$wimindex = switch ($WindowsSKU) {
'Home' { 1 }
+ 'Standard' { 1 }
'Home_N' { 2 }
+ 'Standard (Desktop Experience)' { 1 }
'Home_SL' { 3 }
+ 'Datacenter' { 3 }
'EDU' { 4 }
+ 'Datacenter (Desktop Experience)' { 4 }
'EDU_N' { 5 }
'Pro' { 6 }
'Pro_N' { 7 }
From af28624e2d4f5eeb6cd775ee39a07a799416d1fd Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 23 Sep 2024 04:08:01 +0200
Subject: [PATCH 12/50] Update BuildFFUVM.ps1
- Update Get-Index to support processing Windows Server images
---
FFUDevelopment/BuildFFUVM.ps1 | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 0035aa9..cf9bfc3 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -2600,8 +2600,13 @@ function Get-Index {
# Get the ImageName of ImageIndex 1 if an ISO was specified, else use ImageIndex 4 - this is usually Home or Education SKU on ESD MCT media
if($ISOPath){
- $imageIndex = $imageIndexes | Where-Object ImageIndex -eq 1
- $WindowsImage = $imageIndex.ImageName.Substring(0, 10)
+ if ($WindowsSKU -notmatch "Standard|Datacenter") {
+ $imageIndex = $imageIndexes | Where-Object ImageIndex -eq 1
+ $WindowsImage = $imageIndex.ImageName.Substring(0, 10)
+ } else {
+ $imageIndex = $imageIndexes | Where-Object ImageIndex -eq 1
+ $WindowsImage = $imageIndex.ImageName.Substring(0, 19)
+ }
}
else{
$imageIndex = $imageIndexes | Where-Object ImageIndex -eq 4
@@ -2619,8 +2624,8 @@ function Get-Index {
return $matchingImageIndex.ImageIndex
}
else {
- # Look for either the number 10 or 11 in the ImageName
- $relevantImageIndexes = $imageIndexes | Where-Object { ($_.ImageName -like "*10*") -or ($_.ImageName -like "*11*") }
+ # Look for the numbers 10, 11, 2016, 2019, 2022+ in the ImageName
+ $relevantImageIndexes = $imageIndexes | Where-Object { ($_.ImageName -match "(10|11|2016|2019|202\d)") }
while ($true) {
# Present list of ImageNames to the end user if no matching ImageIndex is found
From 4b33627d197fd5153e9f9a3a2d4d8aea92f2c22e Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 23 Sep 2024 04:11:15 +0200
Subject: [PATCH 13/50] Update BuildFFUVM.ps1
- Fix bug in New-OSPartition
- CompactOS is now avoided on Windows Server
---
FFUDevelopment/BuildFFUVM.ps1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index cf9bfc3..832949e 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -2737,7 +2737,7 @@ function New-OSPartition {
if ((Get-CimInstance Win32_OperatingSystem).Caption -match "Server") {
WriteLog (Expand-WindowsImage -ImagePath $WimPath -Index $WimIndex -ApplyPath "$($osPartition.DriveLetter):\")
}
- if ($CompactOS) {
+ elseif ($CompactOS) {
WriteLog '$CompactOS is set to true, using -Compact switch to apply the WIM file to the OS partition.'
WriteLog (Expand-WindowsImage -ImagePath $WimPath -Index $WimIndex -ApplyPath "$($osPartition.DriveLetter):\" -Compact)
}
From ac485f9c871695f90f7f2d6fb2ecc77978b71630 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 23 Sep 2024 04:16:57 +0200
Subject: [PATCH 14/50] Update BuildFFUVM.ps1
- Update Get-WindowsVersionInfo
- Server Standard is now called 'Srv_Std'
- Server Datacenter is now called 'Srv_Dtc'
- 17763 is matched as Windows Server 2019
- 20348 is matched as Windows Server 2022
- 26100 is matched as Windows Server 2025
- other versions are matched as $DisplayVersion as a fallback
Please remember to update the Windows Server version of 2025 in case it changes until release.
---
FFUDevelopment/BuildFFUVM.ps1 | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 832949e..48bd118 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -3265,14 +3265,25 @@ Function Get-WindowsVersionInfo {
Enterprise { 'Ent' }
Education { 'Edu' }
ProfessionalWorkstation { 'Pro_Wks' }
+ ServerStandard { 'Srv_Std' }
+ ServerDatacenter { 'Srv_Dtc' }
}
WriteLog "Windows SKU Modified to: $SKU"
- if ($CurrentBuild -ge 22000) {
- $Name = 'Win11'
- }
- else {
- $Name = 'Win10'
+ if ($SKU -notmatch "Srv") {
+ if ($CurrentBuild -ge 22000) {
+ $Name = 'Win11'
+ }
+ else {
+ $Name = 'Win10'
+ }
+ } else {
+ $Name = switch ($CurrentBuild) {
+ 26100 { '2025' }
+ 20348 { '2022' }
+ 17763 { '2019' }
+ Default { $DisplayVersion }
+ }
}
WriteLog "Unloading registry"
From b8bda93e8df60ef00f1f48028231f91c504e161d Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 23 Sep 2024 04:26:30 +0200
Subject: [PATCH 15/50] Update BuildFFUVM.ps1
- Updated MU Catalog search
- Search now includes searching for Windows Server updates
Has been verified to work for Windows Server 2022 and Windows Server 2025 only.
---
FFUDevelopment/BuildFFUVM.ps1 | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 48bd118..0dba935 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -4233,7 +4233,15 @@ try {
#The Windows release info page is updated later than the MU Catalog
if ($UpdateLatestCU -and -not $UpdatePreviewCU) {
Writelog "`$UpdateLatestCU is set to true, checking for latest CU"
- $Name = """Cumulative update for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
+ if ($WindowsRelease -le 11) {
+ $Name = """Cumulative update for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
+ } elseif ($WindowsRelease -eq 2022) {
+ $Name = """Cumulative Update for Microsoft server operating system, version $WindowsVersion for $WindowsArch"""
+ } elseif ($WindowsRelease -lt 2022) {
+ $Name = """Cumulative update for Windows 10 Version $WindowsVersion for $WindowsArch"""
+ } else {
+ $Name = """Cumulative update for Windows 11 Version $WindowsVersion for $WindowsArch"""
+ }
#Check if $KBPath exists, if not, create it
If (-not (Test-Path -Path $KBPath)) {
WriteLog "Creating $KBPath"
@@ -4248,7 +4256,15 @@ try {
#will take Precendence over $UpdateLastestCU if both were set to $true
if ($UpdatePreviewCU) {
Writelog "`$UpdatePreviewCU is set to true, checking for latest Preview CU"
- $Name = """Cumulative update Preview for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
+ if ($WindowsRelease -le 11) {
+ $Name = """Cumulative update Preview for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
+ } elseif ($WindowsRelease -eq 2022) {
+ $Name = """Cumulative Update Preview for Microsoft server operating system, version $WindowsVersion for $WindowsArch"""
+ } elseif ($WindowsRelease -lt 2022) {
+ $Name = """Cumulative update Preview for Windows 10 Version $WindowsVersion for $WindowsArch"""
+ } else {
+ $Name = """Cumulative update Preview for Windows 11 Version $WindowsVersion for $WindowsArch"""
+ }
#Check if $KBPath exists, if not, create it
If (-not (Test-Path -Path $KBPath)) {
WriteLog "Creating $KBPath"
From b6dda55a827306b1b5b619175ef342cf335af355 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 23 Sep 2024 04:29:04 +0200
Subject: [PATCH 16/50] Update BuildFFUVM.ps1
- Updated the .NET Framework download code
- Download the appropriate .NET Framework for Windows Server
---
FFUDevelopment/BuildFFUVM.ps1 | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 0dba935..67552ad 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -4278,7 +4278,13 @@ try {
#Update Latest .NET Framework
if ($UpdateLatestNet) {
Writelog "`$UpdateLatestNet is set to true, checking for latest .NET Framework"
- $Name = "Cumulative update for .net framework windows $WindowsRelease $WindowsVersion $WindowsArch -preview"
+ if ($WindowsRelease -le 11) {
+ $Name = "Cumulative update for .net framework windows $WindowsRelease $WindowsVersion $WindowsArch -preview"
+ } elseif ($WindowsRelease -le 2022) {
+ $Name = "Cumulative update for .net framework windows 10 $WindowsVersion for $WindowsArch -preview"
+ } else {
+ $Name = "Cumulative update for .net framework windows 11 $WindowsVersion for $WindowsArch -preview"
+ }
#Check if $KBPath exists, if not, create it
If (-not (Test-Path -Path $KBPath)) {
WriteLog "Creating $KBPath"
From 412e3a078c7422975edb472bc007b1f163ea0be7 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 23 Sep 2024 04:37:09 +0200
Subject: [PATCH 17/50] Update CaptureFFU.ps1
- Add Windows Server support like in BuildFFUVM.ps1
---
.../WinPECaptureFFUFiles/CaptureFFU.ps1 | 21 ++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1 b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
index fb4b964..8a5cee8 100644
--- a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
+++ b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
@@ -28,13 +28,24 @@ $SKU = switch ($SKU) {
EducationN { 'EduN'}
ProfessionalWorkstation { 'Pro_Wks' }
ProfessionalWorkstationN { 'Pro_WksN' }
+ ServerStandard { 'Srv_Std' }
+ ServerDatacenter { 'Srv_Dtc' }
}
-if($CurrentBuild -ge 22000){
- $Name = 'Win11'
-}
-else{
- $Name = 'Win10'
+if ($SKU -notmatch "Srv") {
+ if ($CurrentBuild -ge 22000) {
+ $Name = 'Win11'
+ }
+ else {
+ $Name = 'Win10'
+ }
+} else {
+ $Name = switch ($CurrentBuild) {
+ 26100 { '2025' }
+ 20348 { '2022' }
+ 17763 { '2019' }
+ Default { $DisplayVersion }
+ }
}
#If Office is installed, modify the file name of the FFU
From c93c417dba2afb27c3b2e4753598a8d498a7bb3e Mon Sep 17 00:00:00 2001
From: Zehadi Alam <63765084+zehadialam@users.noreply.github.com>
Date: Mon, 23 Sep 2024 23:42:09 -0400
Subject: [PATCH 18/50] Removed Get-MSRTUrl function and use Microsoft Update
Catalog to retrieve latest MSRT
---
FFUDevelopment/BuildFFUVM.ps1 | 48 +++--------------------------------
1 file changed, 3 insertions(+), 45 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 101df9c..6fa9984 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -2314,39 +2314,6 @@ function Save-KB {
return $fileName
}
-function Get-MSRTUrl {
- param (
- [ValidateSet("x86", "x64")]
- [string]$WindowsArch
- )
- try {
- # Retrieve content of Windows Malicious Software Removal Tool page
- $pattern = 'https:\/\/download\.microsoft\.com\/download\/[^\s]*\.exe'
- $OriginalVerbosePreference = $VerbosePreference
- $VerbosePreference = 'SilentlyContinue'
- if ($WindowsArch -eq "x64") {
- $MSRTWebPage = Invoke-RestMethod "https://www.microsoft.com/en-us/download/details.aspx?id=9905" -Headers $Headers -UserAgent $UserAgent
- }
- if ($WindowsArch -eq "x86") {
- $MSRTWebPage = Invoke-RestMethod "https://www.microsoft.com/en-us/download/details.aspx?id=16" -Headers $Headers -UserAgent $UserAgent
- }
- $VerbosePreference = $OriginalVerbosePreference
- # Extract download URL based on specified pattern
- $MSRTMatch = [regex]::Match($MSRTWebPage, $pattern)
- if (-not $MSRTMatch.Success) {
- WriteLog "Failed to retrieve Malicious Software Removal Tool URL. Pattern match failed"
- return
- }
- $MSRTUrl = $MSRTMatch.Value
- return $MSRTUrl
- }
- catch {
- WriteLog $_
- Write-Error "Error occurred while retrieving Windows Malicious Software Removal Tool link"
- throw $_
- }
-}
-
function New-AppsISO {
#Create Apps ISO file
$OSCDIMG = "$adkpath`Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe"
@@ -3949,24 +3916,15 @@ if ($InstallApps) {
}
if ($UpdateLatestMSRT) {
WriteLog "`$UpdateLatestMSRT is set to true."
+ $Name = "Windows Malicious Software Removal Tool"
#Check if $MSRTPath exists, if not, create it
if (-not (Test-Path -Path $MSRTPath)) {
WriteLog "Creating $MSRTPath"
New-Item -Path $MSRTPath -ItemType Directory -Force | Out-Null
}
WriteLog "Getting Windows Malicious Software Removal Tool URL"
- $MSRTUrl = Get-MSRTUrl -WindowsArch $WindowsArch
- $MSRTFileName = Split-Path -Path $MSRTUrl -Leaf
- try {
- WriteLog "Windows Malicious Software Removal Tool URL for $WindowsArch is $MSRTUrl"
- Start-BitsTransferWithRetry -Source $MSRTUrl -Destination "$MSRTPath\$MSRTFileName"
- WriteLog "Windows Malicious Software Removal Tool downloaded to $MSRTPath\$MSRTFileName"
- }
- catch {
- Write-Error "Downloading Windows Malicious Software Removal Tool failed"
- WriteLog "Downloading Windows Malicious Software Removal Tool failed"
- throw $_
- }
+ $MSRTFileName = Save-KB -Name $Name -Path $MSRTPath
+ WriteLog "Latest Windows Malicious Software Removal Tool saved to $MSRTPath\$MSRTFileName"
WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to include Windows Malicious Software Removal Tool"
$CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
$UpdatedcmdContent = $CmdContent -replace '^(REM Install Windows Malicious Software Removal Tool)', ("REM Install Windows Malicious Software Removal Tool`r`nd:\MSRT\$MSRTFileName /quiet")
From 15c0478710dc463b3c1bf3e0b6ee94a2ac476956 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Thu, 26 Sep 2024 17:15:17 +0200
Subject: [PATCH 19/50] Update BuildFFUVM.ps1 1/2
- Add parameter $AppsScriptVariablesvariable
- Allow value based behavior changes during the app install phase in InstallAppsandSysprep.cmd
---
FFUDevelopment/BuildFFUVM.ps1 | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 3d98155..d97fd16 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -210,6 +210,7 @@ param(
[ValidateScript({ Test-Path $_ })]
[string]$FFUDevelopmentPath = $PSScriptRoot,
[bool]$InstallApps,
+ [hashtable]$AppsScriptVariables,
[bool]$InstallOffice,
[ValidateSet('Microsoft', 'Dell', 'HP', 'Lenovo')]
[string]$Make,
@@ -4028,6 +4029,31 @@ if ($InstallApps) {
Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $UpdatedcmdContent
WriteLog "Update complete"
}
+
+ if (-not $AppsScriptVariables) {
+ #Modify InstallAppsandSysprep.cmd to remove the script variables
+ $CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
+ $StartIndex = $CmdContent.IndexOf("REM START Batch variables placeholder")
+ $EndIndex = $CmdContent.IndexOf("REM END Batch variables placeholder")
+ if (($StartIndex + 1) -lt $EndIndex) {
+ for ($i = ($StartIndex + 1); $i -lt $EndIndex; $i++) {
+ $CmdContent[$i] = $null
+ }
+ }
+ Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $CmdContent
+ }
+
+ if ($AppsScriptVariables) {
+ #Modify InstallAppsandSysprep.cmd to add the script variables
+ $CmdContent = [System.Collections.ArrayList](Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd")
+ $ScriptIndex = $CmdContent.IndexOf("REM START Batch variables placeholder") + 1
+ foreach ($VariableKey in $AppsScriptVariables.Keys) {
+ $CmdContent.Insert($ScriptIndex, ("set {0}={1}" -f $VariableKey, $AppsScriptVariables[$VariableKey]))
+ $ScriptIndex++
+ }
+ Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $CmdContent
+ }
+
#Create Apps ISO
WriteLog "Creating $AppsISO file"
New-AppsISO
From 6a0faa958ec8dc9674abaade6b2a1446629af271 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Thu, 26 Sep 2024 17:16:22 +0200
Subject: [PATCH 20/50] Update InstallAppsandSysprep.cmd 2/2
- Add parameter $AppsScriptVariablesvariable
- Allow value based behavior changes during the app install phase in InstallAppsandSysprep.cmd
---
FFUDevelopment/Apps/InstallAppsandSysprep.cmd | 2 ++
1 file changed, 2 insertions(+)
diff --git a/FFUDevelopment/Apps/InstallAppsandSysprep.cmd b/FFUDevelopment/Apps/InstallAppsandSysprep.cmd
index aba7da8..ba69335 100644
--- a/FFUDevelopment/Apps/InstallAppsandSysprep.cmd
+++ b/FFUDevelopment/Apps/InstallAppsandSysprep.cmd
@@ -8,6 +8,8 @@ REM Install Windows Security Platform Update
REM Install OneDrive Per Machine
REM Install Edge Stable
REM Winget Win32 Apps
+REM START Batch variables placeholder
+REM END Batch variables placeholder
REM Add additional apps below here
REM Contoso App (Example)
REM msiexec /i d:\Contoso\setup.msi /qn /norestart
From 5545554d7e07a57e2cdd2e9e57a782c2ba558bc4 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Thu, 26 Sep 2024 20:08:47 +0200
Subject: [PATCH 21/50] Update BuildFFUVM.ps1
- Fix accidental variable accumulation in InstallAppsandSysprep.cmd
---
FFUDevelopment/BuildFFUVM.ps1 | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index d97fd16..5e2f5b6 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -4030,18 +4030,16 @@ if ($InstallApps) {
WriteLog "Update complete"
}
- if (-not $AppsScriptVariables) {
- #Modify InstallAppsandSysprep.cmd to remove the script variables
- $CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
- $StartIndex = $CmdContent.IndexOf("REM START Batch variables placeholder")
- $EndIndex = $CmdContent.IndexOf("REM END Batch variables placeholder")
- if (($StartIndex + 1) -lt $EndIndex) {
- for ($i = ($StartIndex + 1); $i -lt $EndIndex; $i++) {
- $CmdContent[$i] = $null
- }
+ #Modify InstallAppsandSysprep.cmd to remove old script variables
+ $CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
+ $StartIndex = $CmdContent.IndexOf("REM START Batch variables placeholder")
+ $EndIndex = $CmdContent.IndexOf("REM END Batch variables placeholder")
+ if (($StartIndex + 1) -lt $EndIndex) {
+ for ($i = ($StartIndex + 1); $i -lt $EndIndex; $i++) {
+ $CmdContent[$i] = $null
}
- Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $CmdContent
}
+ Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $CmdContent
if ($AppsScriptVariables) {
#Modify InstallAppsandSysprep.cmd to add the script variables
From 6e5d634af65796aad3ec7dbfcd99103184d41bc5 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Fri, 27 Sep 2024 02:15:25 +0200
Subject: [PATCH 22/50] Update BuildFFUVM.ps1
- Bypass VMSwitchIPAddress to VMHostAddress check on systems with a configured NAT setup
---
FFUDevelopment/BuildFFUVM.ps1 | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index dadc8cf..2e6e681 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -3805,7 +3805,19 @@ if (($VMHostIPAddress) -and ($VMSwitchName)){
throw "IP address for -VMSwitchName $VMSwitchName not found. Please check the -VMSwitchName parameter and try again."
}
if ($VMSwitchIPAddress -ne $VMHostIPAddress) {
- throw "IP address for -VMSwitchName $VMSwitchName is $VMSwitchIPAddress, which does not match the -VMHostIPAddress $VMHostIPAddress. Please check the -VMHostIPAddress parameter and try again."
+ try {
+ # Bypass the check for systems that could have a Hyper-V NAT switch
+ $null = Get-NetNat -ErrorAction Stop
+ $NetNat = @(Get-NetNat -ErrorAction Stop);
+ } catch {
+ throw "IP address for -VMSwitchName $VMSwitchName is $VMSwitchIPAddress, which does not match the -VMHostIPAddress $VMHostIPAddress. Please check the -VMHostIPAddress parameter and try again."
+ }
+ if ($NetNat.Count -gt 0) {
+ WriteLog "IP address for -VMSwitchName $VMSwitchName is $VMSwitchIPAddress, which does not match the -VMHostIPAddress $VMHostIPAddress!"
+ WriteLog "NAT setup detected, remember to configure NATing if the FFU image can't be captured to the network share on the host."
+ } else {
+ throw "IP address for -VMSwitchName $VMSwitchName is $VMSwitchIPAddress, which does not match the -VMHostIPAddress $VMHostIPAddress. Please check the -VMHostIPAddress parameter and try again."
+ }
}
WriteLog '-VMSwitchName and -VMHostIPAddress validation complete'
}
From 5194133a78eaafc0f4e6aa626f680651e9f2133a Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Fri, 27 Sep 2024 18:34:41 +0200
Subject: [PATCH 23/50] Update BuildFFUVM.ps1
- Add help for parameter AppsScriptVariables
---
FFUDevelopment/BuildFFUVM.ps1 | 3 +++
1 file changed, 3 insertions(+)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 2e6e681..3dcf517 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -169,6 +169,9 @@ Make of the device to download drivers. Accepted values are: 'Microsoft', 'Dell'
.PARAMETER Model
Model of the device to download drivers. This is required if Make is set.
+.PARAMETER AppsScriptVariables
+When passed a hashtable, the script will alter the $FFUDevelopmentPath\Apps\InstallAppsandSysprep.cmd file to set variables with the hashtable keys as variable names and the hashtable values their content.
+
.EXAMPLE
Command line for most people who want to download the latest Windows 11 Pro x64 media in English (US) with the latest Windows Cumulative Update, .NET Framework, Defender platform and definition updates, Edge, OneDrive, and Office/M365 Apps. It will also copy drivers to the FFU. This can take about 40 minutes to create the FFU due to the time it takes to download and install the updates.
.\BuildFFUVM.ps1 -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -VMSwitchName 'Name of your VM Switch in Hyper-V' -VMHostIPAddress 'Your IP Address' -CreateCaptureMedia $true -CreateDeploymentMedia $true -BuildUSBDrive $true -UpdateLatestCU $true -UpdateLatestNet $true -UpdateLatestDefender $true -UpdateEdge $true -UpdateOneDrive $true -verbose
From d45b6dc8dcc80e3ae165c1065a4effcf6d6bd39a Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 30 Sep 2024 14:14:33 +0200
Subject: [PATCH 24/50] Update BuildFFUVM.ps1
- improve Optimize-FFUCaptureDrive
- use the drive letter that the Windows partition gets after mounting the vhdx file
---
FFUDevelopment/BuildFFUVM.ps1 | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 40a45c0..dda476f 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -2863,11 +2863,12 @@ function Optimize-FFUCaptureDrive {
)
try {
WriteLog 'Mounting VHDX for volume optimization'
- Mount-VHD -Path $VhdxPath
+ $mountedDisk = Mount-VHD -Path $VhdxPath -Passthru | Get-Disk
+ $osPartition = $mountedDisk | Get-Partition | Where-Object { $_.GptType -eq "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}" }
WriteLog 'Defragmenting Windows partition...'
- Optimize-Volume -DriveLetter W -Defrag -NormalPriority -Verbose
+ Optimize-Volume -DriveLetter $osPartition.DriveLetter -Defrag -NormalPriority -Verbose
WriteLog 'Performing slab consolidation on Windows partition...'
- Optimize-Volume -DriveLetter W -SlabConsolidate -NormalPriority -Verbose
+ Optimize-Volume -DriveLetter $osPartition.DriveLetter -SlabConsolidate -NormalPriority -Verbose
WriteLog 'Dismounting VHDX'
Dismount-ScratchVhdx -VhdxPath $VhdxPath
WriteLog 'Mounting VHDX as read-only for optimization'
From 3694e1a6e408465c42d4a79fca097b868275b9a5 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 30 Sep 2024 15:44:40 +0200
Subject: [PATCH 25/50] Update BuildFFUVM.ps1
- Add parameter $AllowVHDXCaching
- Adds the ability to cache VHDX files together with their configuration
- This should roughly cut the build time in half as long as there are no new patches released since the last cached VHDX file
---
FFUDevelopment/BuildFFUVM.ps1 | 258 ++++++++++++++++++++++++++--------
1 file changed, 201 insertions(+), 57 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index dda476f..94ebe9d 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -312,6 +312,7 @@ param(
[bool]$UpdateLatestMSRT,
[bool]$UpdateEdge,
[bool]$UpdateOneDrive,
+ [bool]$AllowVHDXCaching,
[bool]$CopyPPKG,
[bool]$CopyUnattend,
[bool]$CopyAutopilot,
@@ -342,6 +343,23 @@ param(
)
$version = '2410.1'
+#Class definition for vhdx cache
+class VhdxCacheUpdateItem {
+ [string]$Name
+ VhdxCacheUpdateItem([string]$Name) {
+ $this.Name = $Name
+ }
+}
+
+class VhdxCacheItem {
+ [string]$VhdxFileName = ""
+ [string]$WindowsSKU = ""
+ [string]$WindowsRelease = ""
+ [string]$WindowsVersion = ""
+ [string]$OptionalFeatures = ""
+ [VhdxCacheUpdateItem[]]$IncludedUpdates = @()
+}
+
#Check if Hyper-V feature is installed (requires only checks the module)
$osInfo = Get-WmiObject -Class Win32_OperatingSystem
$isServer = $osInfo.Caption -match 'server'
@@ -384,6 +402,7 @@ if (-not $PPKGFolder) { $PPKGFolder = "$FFUDevelopmentPath\PPKG" }
if (-not $UnattendFolder) { $UnattendFolder = "$FFUDevelopmentPath\Unattend" }
if (-not $AutopilotFolder) { $AutopilotFolder = "$FFUDevelopmentPath\Autopilot" }
if (-not $PEDriversFolder) { $PEDriversFolder = "$FFUDevelopmentPath\PEDrivers" }
+if (-not $VHDXCacheFolder) { $VHDXCacheFolder = "$FFUDevelopmentPath\VHDXCache" }
#FUNCTIONS
@@ -4032,6 +4051,127 @@ if ($InstallApps) {
#Create VHDX
try {
+ #Update latest Cumulative Update if both $UpdateLatestCU is $true and $UpdatePreviewCU is $false
+ #Changed to use MU Catalog instead of using Get-LatestWindowsKB
+ #The Windows release info page is updated later than the MU Catalog
+ if ($UpdateLatestCU -and -not $UpdatePreviewCU) {
+ Writelog "`$UpdateLatestCU is set to true, checking for latest CU"
+ if ($WindowsRelease -le 11) {
+ $Name = """Cumulative update for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
+ } elseif ($WindowsRelease -eq 2022) {
+ $Name = """Cumulative Update for Microsoft server operating system, version $WindowsVersion for $WindowsArch"""
+ } elseif ($WindowsRelease -lt 2022) {
+ $Name = """Cumulative update for Windows 10 Version $WindowsVersion for $WindowsArch"""
+ } else {
+ $Name = """Cumulative update for Windows 11 Version $WindowsVersion for $WindowsArch"""
+ }
+ 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"
+ }
+
+ #Update Latest Preview Cumlative Update
+ #will take Precendence over $UpdateLastestCU if both were set to $true
+ if ($UpdatePreviewCU) {
+ Writelog "`$UpdatePreviewCU is set to true, checking for latest Preview CU"
+ if ($WindowsRelease -le 11) {
+ $Name = """Cumulative update Preview for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
+ } elseif ($WindowsRelease -eq 2022) {
+ $Name = """Cumulative Update Preview for Microsoft server operating system, version $WindowsVersion for $WindowsArch"""
+ } elseif ($WindowsRelease -lt 2022) {
+ $Name = """Cumulative update Preview for Windows 10 Version $WindowsVersion for $WindowsArch"""
+ } else {
+ $Name = """Cumulative update Preview for Windows 11 Version $WindowsVersion for $WindowsArch"""
+ }
+ 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"
+ }
+
+ #Update Latest .NET Framework
+ if ($UpdateLatestNet) {
+ Writelog "`$UpdateLatestNet is set to true, checking for latest .NET Framework"
+ if ($WindowsRelease -le 11) {
+ $Name = "Cumulative update for .net framework windows $WindowsRelease $WindowsVersion $WindowsArch -preview"
+ } elseif ($WindowsRelease -le 2022) {
+ $Name = "Cumulative update for .net framework windows 10 $WindowsVersion for $WindowsArch -preview"
+ } else {
+ $Name = "Cumulative update for .net framework windows 11 $WindowsVersion for $WindowsArch -preview"
+ }
+ 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"
+ }
+ # #Update Latest Security Platform Update
+ # if ($UpdateSecurityPlatform) {
+ # WriteLog "`$UpdateSecurityPlatform is set to true, checking for latest Security Platform Update"
+ # $Name = "Windows Security platform definition updates"
+ # #Check if $KBPath exists, if not, create it
+ # If (-not (Test-Path -Path $KBPath)) {
+ # WriteLog "Creating $KBPath"
+ # 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 Security Platform Update saved to $KBPath\$KBFilePath"
+ # }
+
+ #Search for cached VHDX and skip VHDX creation if there's a cached version
+ if ($AllowVHDXCaching) {
+ if (Test-Path -Path $VHDXCacheFolder) {
+ $vhdxJsons = @(Get-ChildItem -File -Path $VHDXCacheFolder -Filter "*_config.json" | Sort-Object -Property CreationTime -Descending)
+ $downloadedKBs = @(Get-ChildItem -File -Path $KBPath)
+ #$jsonDeserializer = [System.Web.Script.Serialization.JavaScriptSerializer]::new()
+
+ foreach ($vhdxJson in $vhdxJsons) {
+ try {
+ #$vhdxCacheItem = $jsonDeserializer.Deserialize((Get-Content -Path $vhdxJson.FullName -Raw), [VhdxCacheItem])
+ $vhdxCacheItem = Get-Content -Path $vhdxJson.FullName -Raw | ConvertFrom-Json
+
+ if ((($vhdxCacheItem.WindowsSKU -ne $WindowsSKU) -or
+ ([string]::IsNullOrEmpty($vhdxCacheItem.WindowsSKU) -xor [string]::IsNullOrEmpty($WindowsSKU)))) {
+ WriteLog "WindowsSKU not equal"
+ continue
+ }
+
+ if ((($vhdxCacheItem.WindowsRelease -ne $WindowsRelease) -or
+ ([string]::IsNullOrEmpty($vhdxCacheItem.WindowsRelease) -xor [string]::IsNullOrEmpty($WindowsRelease)))) {
+ WriteLog "WindowsRelease not equal"
+ continue
+ }
+
+ if ((($vhdxCacheItem.WindowsVersion -ne $WindowsVersion) -or
+ ([string]::IsNullOrEmpty($vhdxCacheItem.WindowsVersion) -xor [string]::IsNullOrEmpty($WindowsVersion)))) {
+ WriteLog "WindowsVersion not equal"
+ continue
+ }
+
+ if ((($vhdxCacheItem.OptionalFeatures -ne $OptionalFeatures) -or
+ ([string]::IsNullOrEmpty($vhdxCacheItem.OptionalFeatures) -xor [string]::IsNullOrEmpty($OptionalFeatures)))) {
+ WriteLog "OptionalFeatures not equal"
+ continue
+ }
+
+ if ((Compare-Object -ReferenceObject $downloadedKBs -DifferenceObject $vhdxCacheItem.IncludedUpdates -Property Name).Length -gt 0) {
+ (Compare-Object -ReferenceObject $downloadedKBs -DifferenceObject $vhdxCacheItem.IncludedUpdates -Property Name)
+ $downloadedKBs.Name
+ $vhdxCacheItem.IncludedUpdates.Name
+ WriteLog "Updates not equal"
+ continue
+ }
+
+ WriteLog "Found cached VHDX file with same parameters and patches"
+ $cachedVHDXFileFound = $true
+ $cachedVHDXInfo = $vhdxCacheItem
+ break
+ } catch {
+ WriteLog "Reading $vhdxJson Failed with error $_"
+ }
+ }
+ }
+ }
+
+ if (-Not $cachedVHDXFileFound) {
if ($ISOPath) {
$wimPath = Get-WimFromISO
}
@@ -4060,63 +4200,16 @@ try {
Add-BootFiles -OsPartitionDriveLetter $osPartitionDriveLetter -SystemPartitionDriveLetter $systemPartitionDriveLetter[1]
- #Update latest Cumulative Update if both $UpdateLatestCU is $true and $UpdatePreviewCU is $false
- #Changed to use MU Catalog instead of using Get-LatestWindowsKB
- #The Windows release info page is updated later than the MU Catalog
- if ($UpdateLatestCU -and -not $UpdatePreviewCU) {
- Writelog "`$UpdateLatestCU is set to true, checking for latest CU"
- $Name = """Cumulative update for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
- #Check if $KBPath exists, if not, create it
- If (-not (Test-Path -Path $KBPath)) {
- WriteLog "Creating $KBPath"
- New-Item -Path $KBPath -ItemType Directory -Force | Out-Null
+ if ($UpdateLatestCU -or $UpdateLatestNet -or $UpdatePreviewCU ) {
+ #Check if $KBCachePath exists, if not, create it
+ if ($AllowUpdateCaching) {
+ if (-not (Test-Path -Path $KBCachePath)) {
+ WriteLog "Creating $KBCachePath"
+ New-Item -Path $KBCachePath -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 CU saved to $KBPath\$KBFilePath"
}
- #Update Latest Preview Cumlative Update
- #will take Precendence over $UpdateLastestCU if both were set to $true
- if ($UpdatePreviewCU) {
- Writelog "`$UpdatePreviewCU is set to true, checking for latest Preview CU"
- $Name = """Cumulative update Preview for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
- #Check if $KBPath exists, if not, create it
- If (-not (Test-Path -Path $KBPath)) {
- WriteLog "Creating $KBPath"
- 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"
- }
-
- #Update Latest .NET Framework
- if ($UpdateLatestNet) {
- Writelog "`$UpdateLatestNet is set to true, checking for latest .NET Framework"
- $Name = "Cumulative update for .net framework windows $WindowsRelease $WindowsVersion $WindowsArch -preview"
- #Check if $KBPath exists, if not, create it
- If (-not (Test-Path -Path $KBPath)) {
- WriteLog "Creating $KBPath"
- 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"
- }
- # #Update Latest Security Platform Update
- # if ($UpdateSecurityPlatform) {
- # WriteLog "`$UpdateSecurityPlatform is set to true, checking for latest Security Platform Update"
- # $Name = "Windows Security platform definition updates"
- # #Check if $KBPath exists, if not, create it
- # If (-not (Test-Path -Path $KBPath)) {
- # WriteLog "Creating $KBPath"
- # 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 Security Platform Update saved to $KBPath\$KBFilePath"
- # }
#Add Windows packages
@@ -4126,9 +4219,17 @@ try {
WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath -PreventPending | Out-Null
WriteLog "KBs added to $WindowsPartition"
+ if ($AllowVHDXCaching) {
+ $cachedVHDXInfo = [VhdxCacheItem]::new()
+ $includedUpdates = Get-ChildItem -Path $KBPath -File
+
+ foreach ($includedUpdate in $includedUpdates) {
+ $cachedVHDXInfo.IncludedUpdates += ([VhdxCacheUpdateItem]::new($includedUpdate.Name))
+ }
+ }
WriteLog "Removing $KBPath"
Remove-Item -Path $KBPath -Recurse -Force | Out-Null
- WriteLog "Clean Up the WinSxS Folder"
+ WriteLog "Clean Up the WinSxS Folder"
Dism /Image:$WindowsPartition /Cleanup-Image /StartComponentCleanup /ResetBase | Out-Null
WriteLog "Clean Up the WinSxS Folder completed"
}
@@ -4139,11 +4240,25 @@ try {
}
}
-
#Enable Windows Optional Features (e.g. .Net3, etc)
If ($OptionalFeatures) {
$Source = Join-Path (Split-Path $wimpath) "sxs"
Enable-WindowsFeaturesByName -FeatureNames $OptionalFeatures -Source $Source
+ }
+
+ } else {
+ #Use cached vhdx file
+ WriteLog "Using cached VHDX file to speed up build proces"
+ WriteLog "VHDX file is: $($cachedVHDXInfo.VhdxFileName)"
+
+ Robocopy.exe $($VHDXCacheFolder) $($VMPath) $($cachedVHDXInfo.VhdxFileName) /E /COPY:DAT /R:5 /W:5 /J
+ $VHDXPath = Join-Path $($VMPath) $($cachedVHDXInfo.VhdxFileName)
+
+ $vhdxDisk = Get-VHD -Path $VHDXPath | Mount-VHD -Passthru | Get-Disk
+ $osPartition = $vhdxDisk | Get-Partition | Where-Object { $_.GptType -eq "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}" }
+ $osPartitionDriveLetter = $osPartition.DriveLetter
+ $WindowsPartition = $osPartitionDriveLetter + ":\"
+
}
#Set Product key
@@ -4165,7 +4280,7 @@ try {
If ($InstallApps) {
#Copy Unattend file so VM Boots into Audit Mode
WriteLog 'Copying unattend file to boot to audit mode'
- New-Item -Path "$($osPartitionDriveLetter):\Windows\Panther\unattend" -ItemType Directory | Out-Null
+ New-Item -Path "$($osPartitionDriveLetter):\Windows\Panther\unattend" -ItemType Directory -Force | Out-Null
if($WindowsArch -eq 'x64'){
Copy-Item -Path "$FFUDevelopmentPath\BuildFFUUnattend\unattend_x64.xml" -Destination "$($osPartitionDriveLetter):\Windows\Panther\Unattend\Unattend.xml" -Force | Out-Null
}
@@ -4173,6 +4288,35 @@ try {
Copy-Item -Path "$FFUDevelopmentPath\BuildFFUUnattend\unattend_arm64.xml" -Destination "$($osPartitionDriveLetter):\Windows\Panther\Unattend\Unattend.xml" -Force | Out-Null
}
WriteLog 'Copy completed'
+ }
+
+ if ($AllowVHDXCaching -and !$cachedVHDXFileFound) {
+ WriteLog 'New cachabe VHDX created'
+
+ WriteLog 'Defragmenting Windows partition...'
+ Optimize-Volume -DriveLetter $osPartition.DriveLetter -Defrag -NormalPriority -Verbose
+ WriteLog 'Performing slab consolidation on Windows partition...'
+ Optimize-Volume -DriveLetter $osPartition.DriveLetter -SlabConsolidate -NormalPriority -Verbose
+ WriteLog 'Dismounting VHDX'
+ Dismount-ScratchVhdx -VhdxPath $VHDXPath
+
+ WriteLog 'Copying to cache dir'
+
+ #Assuming there are now name collisons
+ Robocopy.exe $($VMPath) $($VHDXCacheFolder) $("$VMName.vhdx") /E /COPY:DAT /R:5 /W:5 /J
+
+ #Only create new instance if not created during patching
+ if ($null -eq $cachedVHDXInfo) {
+ $cachedVHDXInfo = [VhdxCacheItem]::new()
+ }
+ $cachedVHDXInfo.VhdxFileName = $("$VMName.vhdx")
+ $cachedVHDXInfo.WindowsSKU = $WindowsSKU
+ $cachedVHDXInfo.WindowsRelease = $WindowsRelease
+ $cachedVHDXInfo.WindowsVersion = $WindowsVersion
+ $cachedVHDXInfo.OptionalFeatures = $OptionalFeatures
+
+ $cachedVHDXInfo | ConvertTo-Json | Out-File -FilePath ("{0}\{1}_config.json" -f $($VHDXCacheFolder), $VMName)
+ } else {
Dismount-ScratchVhdx -VhdxPath $VHDXPath
}
}
From f144f1d71c64f4a4561548202f1750120df3c7aa Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 30 Sep 2024 16:52:15 +0200
Subject: [PATCH 26/50] Update CaptureFFU.ps1
- Add support for Server Core in SKU name
---
FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1 | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1 b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
index 8a5cee8..81a7907 100644
--- a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
+++ b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
@@ -12,6 +12,7 @@ reg load "HKLM\FFU" $Software
$SKU = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'EditionID'
[int]$CurrentBuild = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'CurrentBuild'
$DisplayVersion = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'DisplayVersion'
+$InstallationType = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'InstallationType'
$BuildDate = Get-Date -uformat %b%Y
$SKU = switch ($SKU) {
@@ -32,7 +33,7 @@ $SKU = switch ($SKU) {
ServerDatacenter { 'Srv_Dtc' }
}
-if ($SKU -notmatch "Srv") {
+if ($InstallationType -eq "Client") {
if ($CurrentBuild -ge 22000) {
$Name = 'Win11'
}
@@ -46,6 +47,9 @@ if ($SKU -notmatch "Srv") {
17763 { '2019' }
Default { $DisplayVersion }
}
+ if ($InstallationType -eq "Server Core") {
+ $SKU += "_Core"
+ }
}
#If Office is installed, modify the file name of the FFU
From fbe8eca26367c5b0a3eb2b7a8139f0f40ff8e03d Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Wed, 2 Oct 2024 15:05:41 +0200
Subject: [PATCH 27/50] Update Search Strings: Update BuildFFUVM.ps1
- Update search strings for Windows / .NET Framework updates to get more consistent and reliable results
---
FFUDevelopment/BuildFFUVM.ps1 | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 3dcf517..b068b96 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -4166,7 +4166,7 @@ try {
} elseif ($WindowsRelease -lt 2022) {
$Name = """Cumulative update for Windows 10 Version $WindowsVersion for $WindowsArch"""
} else {
- $Name = """Cumulative update for Windows 11 Version $WindowsVersion for $WindowsArch"""
+ $Name = """Cumulative update for Windows 11 Version $WindowsVersion for $WindowsArch"" ""Security Updates"""
}
#Check if $KBPath exists, if not, create it
If (-not (Test-Path -Path $KBPath)) {
@@ -4206,10 +4206,14 @@ try {
Writelog "`$UpdateLatestNet is set to true, checking for latest .NET Framework"
if ($WindowsRelease -le 11) {
$Name = "Cumulative update for .net framework windows $WindowsRelease $WindowsVersion $WindowsArch -preview"
- } elseif ($WindowsRelease -le 2022) {
- $Name = "Cumulative update for .net framework windows 10 $WindowsVersion for $WindowsArch -preview"
+ } elseif ($WindowsRelease -eq 2016) {
+ $Name = '"Cumulative Update for .NET Framework" "4.8" for Windows 10 Version ' + $WindowsVersion + ' x64 -preview'
+ } elseif ($WindowsRelease -eq 2019) {
+ $Name = '"Cumulative Update for .NET Framework" "3.5, 4.7.2 and 4.8" for Windows 10 Version ' + $WindowsVersion + ' x64 -preview'
+ } elseif ($WindowsRelease -eq 2022) {
+ $Name = '"Cumulative Update" ".NET Framework" "3.5, 4.8 and 4.8.1" "server operating system " ' + $WindowsVersion + ' x64 -preview'
} else {
- $Name = "Cumulative update for .net framework windows 11 $WindowsVersion for $WindowsArch -preview"
+ $Name = '"Cumulative Update for .NET Framework" "3.5 and 4.8.1" for Windows 11 ' + $WindowsVersion + ' x64 -preview'
}
#Check if $KBPath exists, if not, create it
If (-not (Test-Path -Path $KBPath)) {
From f09c404f65e805e6ca227c5190102cf69e77803b Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Thu, 3 Oct 2024 03:47:33 +0200
Subject: [PATCH 28/50] Update BuildFFUVM.ps1
- Find better results for Windows Server 2019 .NET Framework updates
---
FFUDevelopment/BuildFFUVM.ps1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index b068b96..0fc53bc 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -4209,7 +4209,7 @@ try {
} elseif ($WindowsRelease -eq 2016) {
$Name = '"Cumulative Update for .NET Framework" "4.8" for Windows 10 Version ' + $WindowsVersion + ' x64 -preview'
} elseif ($WindowsRelease -eq 2019) {
- $Name = '"Cumulative Update for .NET Framework" "3.5, 4.7.2 and 4.8" for Windows 10 Version ' + $WindowsVersion + ' x64 -preview'
+ $Name = '"Cumulative Update for .NET Framework" "3.5, 4.7.2 and 4.8" Server ' + $WindowsRelease + ' x64 -preview'
} elseif ($WindowsRelease -eq 2022) {
$Name = '"Cumulative Update" ".NET Framework" "3.5, 4.8 and 4.8.1" "server operating system " ' + $WindowsVersion + ' x64 -preview'
} else {
From 658d2d7af454160e380cec45ae3671a94421cb7b Mon Sep 17 00:00:00 2001
From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com>
Date: Fri, 4 Oct 2024 14:58:21 -0700
Subject: [PATCH 29/50] - Added server skus to validateset for $WindowsSKU -
Added new variable $installationType which uses $WindowsRelease to determine
Server or Client. If $installationType is Server, $WindowsRelease version is
used to set $WindowsVersion to the appropriate version (1607, 1809, 21H2) -
Fixed an issue where the recovery partition wouldn't be created on server
OSes due to winre.wim being hidden. Never saw this on client OSes even though
it also was hidden IIRC. - Removed verbosity for Optimize-Volume as it was
outputting when -verbose was not specified. - Modified some search strings
for .NET CUs when installing on Server OS - Included SSU for Windows Server
2016 as it's mandatory - Added some error checking for Server 2019 and 2022
CU installations when CU fails due to lack of SSU. If you run into this
error, you're using old media and should use the latest. Always use the
latest ISO if you can.
---
FFUDevelopment/BuildFFUVM.ps1 | 140 ++++++++++++------
.../WinPECaptureFFUFiles/CaptureFFU.ps1 | 22 ++-
2 files changed, 106 insertions(+), 56 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 3dcf517..a057493 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -207,15 +207,13 @@ param(
[Parameter(Mandatory = $false, Position = 0)]
[ValidateScript({ Test-Path $_ })]
[string]$ISOPath,
- [ValidateScript({
- $allowedSKUs = @('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', 'Standard', 'Standard (Desktop Experience)', 'Datacenter', 'Datacenter (Desktop Experience)')
- if ($allowedSKUs -contains $_) { $true } else { throw "Invalid WindowsSKU value. Allowed values: $($allowedSKUs -join ', ')" }
- return $true
- })]
+ [ValidateSet('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', 'Standard', 'Standard (Desktop Experience)', 'Datacenter', 'Datacenter (Desktop Experience)')]
[string]$WindowsSKU = 'Pro',
[ValidateScript({ Test-Path $_ })]
[string]$FFUDevelopmentPath = $PSScriptRoot,
+
[bool]$InstallApps,
+
[hashtable]$AppsScriptVariables,
[bool]$InstallOffice,
[ValidateSet('Microsoft', 'Dell', 'HP', 'Lenovo')]
@@ -392,7 +390,15 @@ if (-not $PPKGFolder) { $PPKGFolder = "$FFUDevelopmentPath\PPKG" }
if (-not $UnattendFolder) { $UnattendFolder = "$FFUDevelopmentPath\Unattend" }
if (-not $AutopilotFolder) { $AutopilotFolder = "$FFUDevelopmentPath\Autopilot" }
if (-not $PEDriversFolder) { $PEDriversFolder = "$FFUDevelopmentPath\PEDrivers" }
-
+if (-not $installationType) { $installationType = if ($WindowsRelease.ToString().Length -eq 2) { 'Client' } else { 'Server' } }
+if ($installationType -eq 'Server'){
+ #Map $WindowsRelease to $WindowsVersion for Windows Server
+ switch ($WindowsRelease) {
+ 2016 { $WindowsVersion = '1607' }
+ 2019 { $WindowsVersion = '1809' }
+ 2022 { $WindowsVersion = '21H2' }
+ }
+}
#FUNCTIONS
function WriteLog($LogText) {
@@ -1179,9 +1185,10 @@ function Get-DellDrivers {
[ValidateSet("x64", "x86", "ARM64")]
[string]$WindowsArch,
[Parameter(Mandatory = $true)]
- [string]$WindowsRelease
+ [int]$WindowsRelease
)
+ #CatalogPC.cab is the catalog for Windows client PCs, Catalog.cab is the catalog for Windows Server
if ($WindowsRelease -le 11) {
$catalogUrl = "http://downloads.dell.com/catalog/CatalogPC.cab"
$DellCabFile = "$DriversFolder\CatalogPC.cab"
@@ -1622,7 +1629,7 @@ function Get-WindowsESD {
elseif ($WindowsRelease -eq 11) {
'https://go.microsoft.com/fwlink/?LinkId=2156292'
} else {
- throw "Can't download Windows Server. Please download the Windows setup media from your subscription homepage."
+ throw "Downloading Windows Server is not supported. Please use the -ISOPath parameter to specify the path to the Windows Server ISO file."
}
# Download cab file
@@ -2620,7 +2627,7 @@ function New-RecoveryPartition {
$calculatedRecoverySize = $RecoveryPartitionSize
}
else {
- $winReWim = Get-ChildItem "$($OsPartition.DriveLetter):\Windows\System32\Recovery\Winre.wim"
+ $winReWim = Get-ChildItem "$($OsPartition.DriveLetter):\Windows\System32\Recovery\Winre.wim" -Attributes Hidden -ErrorAction SilentlyContinue
if (($null -ne $winReWim) -and ($winReWim.Count -eq 1)) {
# Wim size + 100MB is minimum WinRE partition size.
@@ -2914,9 +2921,9 @@ function Optimize-FFUCaptureDrive {
WriteLog 'Mounting VHDX for volume optimization'
Mount-VHD -Path $VhdxPath
WriteLog 'Defragmenting Windows partition...'
- Optimize-Volume -DriveLetter W -Defrag -NormalPriority -Verbose
+ Optimize-Volume -DriveLetter W -Defrag -NormalPriority
WriteLog 'Performing slab consolidation on Windows partition...'
- Optimize-Volume -DriveLetter W -SlabConsolidate -NormalPriority -Verbose
+ Optimize-Volume -DriveLetter W -SlabConsolidate -NormalPriority
WriteLog 'Dismounting VHDX'
Dismount-ScratchVhdx -VhdxPath $VhdxPath
WriteLog 'Mounting VHDX as read-only for optimization'
@@ -3106,8 +3113,12 @@ Function Get-WindowsVersionInfo {
WriteLog "Windows SKU: $SKU"
[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
+ if($CurrentBuild -notin (14393, 17763)) {
$DisplayVersion = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'DisplayVersion'
WriteLog "Windows Version: $DisplayVersion"
+ }
+
$BuildDate = Get-Date -uformat %b%Y
$SKU = switch ($SKU) {
@@ -3117,7 +3128,7 @@ Function Get-WindowsVersionInfo {
Enterprise { 'Ent' }
Education { 'Edu' }
ProfessionalWorkstation { 'Pro_Wks' }
- ServerStandard { 'Srv_Std' }
+ ServerStandard { 'Srv_Std' }
ServerDatacenter { 'Srv_Dtc' }
}
WriteLog "Windows SKU Modified to: $SKU"
@@ -3129,11 +3140,13 @@ Function Get-WindowsVersionInfo {
else {
$Name = 'Win10'
}
- } else {
+ }
+ else {
$Name = switch ($CurrentBuild) {
26100 { '2025' }
20348 { '2022' }
17763 { '2019' }
+ 14393 { '2016' }
Default { $DisplayVersion }
}
}
@@ -3809,18 +3822,20 @@ if (($VMHostIPAddress) -and ($VMSwitchName)){
}
if ($VMSwitchIPAddress -ne $VMHostIPAddress) {
try {
- # Bypass the check for systems that could have a Hyper-V NAT switch
- $null = Get-NetNat -ErrorAction Stop
- $NetNat = @(Get-NetNat -ErrorAction Stop);
- } catch {
- throw "IP address for -VMSwitchName $VMSwitchName is $VMSwitchIPAddress, which does not match the -VMHostIPAddress $VMHostIPAddress. Please check the -VMHostIPAddress parameter and try again."
+ # Bypass the check for systems that could have a Hyper-V NAT switch
+ $null = Get-NetNat -ErrorAction Stop
+ $NetNat = @(Get-NetNat -ErrorAction Stop)
}
- if ($NetNat.Count -gt 0) {
+ catch {
+ throw "IP address for -VMSwitchName $VMSwitchName is $VMSwitchIPAddress, which does not match the -VMHostIPAddress $VMHostIPAddress. Please check the -VMHostIPAddress parameter and try again."
+ }
+ if ($NetNat.Count -gt 0) {
WriteLog "IP address for -VMSwitchName $VMSwitchName is $VMSwitchIPAddress, which does not match the -VMHostIPAddress $VMHostIPAddress!"
- WriteLog "NAT setup detected, remember to configure NATing if the FFU image can't be captured to the network share on the host."
- } else {
- throw "IP address for -VMSwitchName $VMSwitchName is $VMSwitchIPAddress, which does not match the -VMHostIPAddress $VMHostIPAddress. Please check the -VMHostIPAddress parameter and try again."
- }
+ WriteLog "NAT setup detected, remember to configure NATing if the FFU image can't be captured to the network share on the host."
+ }
+ else {
+ throw "IP address for -VMSwitchName $VMSwitchName is $VMSwitchIPAddress, which does not match the -VMHostIPAddress $VMHostIPAddress. Please check the -VMHostIPAddress parameter and try again."
+ }
}
WriteLog '-VMSwitchName and -VMHostIPAddress validation complete'
}
@@ -4159,38 +4174,38 @@ try {
#The Windows release info page is updated later than the MU Catalog
if ($UpdateLatestCU -and -not $UpdatePreviewCU) {
Writelog "`$UpdateLatestCU is set to true, checking for latest CU"
- if ($WindowsRelease -le 11) {
+ if ($installationType -eq 'Client') {
$Name = """Cumulative update for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
- } elseif ($WindowsRelease -eq 2022) {
- $Name = """Cumulative Update for Microsoft server operating system, version $WindowsVersion for $WindowsArch"""
- } elseif ($WindowsRelease -lt 2022) {
- $Name = """Cumulative update for Windows 10 Version $WindowsVersion for $WindowsArch"""
- } else {
- $Name = """Cumulative update for Windows 11 Version $WindowsVersion for $WindowsArch"""
+ }
+ if ($WindowsRelease -eq 2022) {
+ $Name = """Cumulative Update for Microsoft server operating system, version 22h2 for $WindowsArch"""
+ }
+ if ($WindowsRelease -in 2016, 2019) {
+ $Name = """Cumulative update for Windows Server $WindowsRelease for $WindowsArch"""
}
#Check if $KBPath exists, if not, create it
If (-not (Test-Path -Path $KBPath)) {
WriteLog "Creating $KBPath"
New-Item -Path $KBPath -ItemType Directory -Force | Out-Null
}
+ #Get latest Servicing Stack Update for Windows Server 2016
+ if ($WindowsRelease -eq 2016) {
+ $SSUName = """Servicing stack update for Windows Server $WindowsRelease for $WindowsArch"""
+ WriteLog "Searching for $SSUName from Microsoft Update Catalog and saving to $KBPath"
+ $SSUFile = Save-KB -Name $SSUName -Path $KBPath
+ $SSUFilePath = "$KBPath\$SSUFile"
+ 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"
}
- #Update Latest Preview Cumlative Update
+ #Update Latest Preview Cumlative Update for Client OS only
#will take Precendence over $UpdateLastestCU if both were set to $true
- if ($UpdatePreviewCU) {
+ if ($UpdatePreviewCU -and $installationType -eq 'Client') {
Writelog "`$UpdatePreviewCU is set to true, checking for latest Preview CU"
- if ($WindowsRelease -le 11) {
- $Name = """Cumulative update Preview for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
- } elseif ($WindowsRelease -eq 2022) {
- $Name = """Cumulative Update Preview for Microsoft server operating system, version $WindowsVersion for $WindowsArch"""
- } elseif ($WindowsRelease -lt 2022) {
- $Name = """Cumulative update Preview for Windows 10 Version $WindowsVersion for $WindowsArch"""
- } else {
- $Name = """Cumulative update Preview for Windows 11 Version $WindowsVersion for $WindowsArch"""
- }
+ $Name = """Cumulative update Preview for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
#Check if $KBPath exists, if not, create it
If (-not (Test-Path -Path $KBPath)) {
WriteLog "Creating $KBPath"
@@ -4204,12 +4219,17 @@ try {
#Update Latest .NET Framework
if ($UpdateLatestNet) {
Writelog "`$UpdateLatestNet is set to true, checking for latest .NET Framework"
- if ($WindowsRelease -le 11) {
- $Name = "Cumulative update for .net framework windows $WindowsRelease $WindowsVersion $WindowsArch -preview"
- } elseif ($WindowsRelease -le 2022) {
- $Name = "Cumulative update for .net framework windows 10 $WindowsVersion for $WindowsArch -preview"
- } else {
- $Name = "Cumulative update for .net framework windows 11 $WindowsVersion for $WindowsArch -preview"
+ if ($installationType -eq 'Client') {
+ $Name = "Cumulative update for .net framework windows $WindowsRelease $WindowsVersion $WindowsArch -preview"
+ }
+ if ($WindowsRelease -eq 2022) {
+ $Name = """Cumulative Update for .NET Framework 3.5, 4.8 and 4.8.1"" " + """operating system version 21H2 for x64"""
+ }
+ if ($WindowsRelease -eq 2019) {
+ $Name = """Cumulative Update for .NET Framework 3.5, 4.7.2 and 4.8 for Windows Server 2019 for x64"""
+ }
+ if ($WindowsRelease -eq 2016) {
+ $Name = """Cumulative Update for .NET Framework 4.8 for Windows Server 2016 for x64"""
}
#Check if $KBPath exists, if not, create it
If (-not (Test-Path -Path $KBPath)) {
@@ -4240,17 +4260,41 @@ try {
try {
WriteLog "Adding KBs to $WindowsPartition"
WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
- Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath -PreventPending | Out-Null
+ # If WindowsRelease is 2016, we need to add the SSU first
+ if ($WindowsRelease -eq 2016) {
+ WriteLog "WindowsRelease is 2016, adding SSU first"
+ WriteLog "Adding SSU to $WindowsPartition"
+ # Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath -PreventPending | Out-Null
+ # Commenting out -preventpending as it causes an issue with the SSU being applied
+ # Seems to be because of the registry being mounted per dism.log
+ Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath | Out-Null
+ WriteLog "SSU added to $WindowsPartition"
+ WriteLog "Removing $SSUFilePath"
+ Remove-Item -Path $SSUFilePath -Force | Out-Null
+ WriteLog 'SSU removed'
+ WriteLog "Adding CU to $WindowsPartition"
+ }
+ # Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath -PreventPending | Out-Null
+ Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath | Out-Null
WriteLog "KBs added to $WindowsPartition"
WriteLog "Removing $KBPath"
Remove-Item -Path $KBPath -Recurse -Force | Out-Null
WriteLog "Clean Up the WinSxS Folder"
+ WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
Dism /Image:$WindowsPartition /Cleanup-Image /StartComponentCleanup /ResetBase | Out-Null
WriteLog "Clean Up the WinSxS Folder completed"
}
catch {
Write-Host "Adding KB to VHDX failed with error $_"
WriteLog "Adding KB to VHDX failed with error $_"
+ if ($_.Exception.HResult -eq -2146498525){
+ Write-Host 'Missing latest Servicing Stack Update'
+ Write-Host 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
+ Write-Host 'Recommended to use the latest media'
+ WriteLog 'Missing latest Servicing Stack Update'
+ WriteLog 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
+ WriteLog 'Recommended to use the latest media'
+ }
throw $_
}
}
diff --git a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1 b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
index 81a7907..1a8fdd7 100644
--- a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
+++ b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
@@ -11,22 +11,24 @@ reg load "HKLM\FFU" $Software
$SKU = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'EditionID'
[int]$CurrentBuild = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'CurrentBuild'
-$DisplayVersion = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'DisplayVersion'
+if ($CurrentBuild -notin 14393, 17763) {
+ $DisplayVersion = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'DisplayVersion'
+}
$InstallationType = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'InstallationType'
$BuildDate = Get-Date -uformat %b%Y
$SKU = switch ($SKU) {
Core { 'Home' }
- CoreN { 'HomeN'}
- CoreSingleLanguage { 'HomeSL'}
+ CoreN { 'HomeN' }
+ CoreSingleLanguage { 'HomeSL' }
Professional { 'Pro' }
- ProfessionalN { 'ProN'}
+ ProfessionalN { 'ProN' }
ProfessionalEducation { 'Pro_Edu' }
ProfessionalEducationN { 'Pro_EduN' }
Enterprise { 'Ent' }
- EnterpriseN { 'EntN'}
+ EnterpriseN { 'EntN' }
Education { 'Edu' }
- EducationN { 'EduN'}
+ EducationN { 'EduN' }
ProfessionalWorkstation { 'Pro_Wks' }
ProfessionalWorkstationN { 'Pro_WksN' }
ServerStandard { 'Srv_Std' }
@@ -40,11 +42,13 @@ if ($InstallationType -eq "Client") {
else {
$Name = 'Win10'
}
-} else {
+}
+else {
$Name = switch ($CurrentBuild) {
26100 { '2025' }
20348 { '2022' }
17763 { '2019' }
+ 14393 { '2016' }
Default { $DisplayVersion }
}
if ($InstallationType -eq "Server Core") {
@@ -70,8 +74,10 @@ else{
#Unload Registry
Set-Location X:\
Remove-Variable SKU
+if ($CurrentBuild -notin 14393, 17763) {
+ Remove-Variable DisplayVersion
+}
Remove-Variable CurrentBuild
-Remove-Variable DisplayVersion
Remove-Variable Office
reg unload "HKLM\FFU"
#This prevents Critical Process Died errors you can have during deployment of the FFU - may not happen during capture from WinPE, but adding here to be consistent with VHDX capture
From 1f65198803d730c897198436edfd17c3eaf10564 Mon Sep 17 00:00:00 2001
From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com>
Date: Wed, 9 Oct 2024 16:17:11 -0700
Subject: [PATCH 30/50] - $WindowsVersion set to 24h2, can override by using
-WindowsVersion 23H2 if you want the old behavior - Removed the "Downloading
information GUID" messages when downloading content from the Microsoft Update
Catalog while -verbose was specified in the command line - In Get-KBLink,
made a change to just grab the first result returned instead of the entire
results page. This removes the need to use a break statement in Save-KB when
downloading updates. This fixed an issue with the new 24H2 Checkpoint
Cumulative Updates in Win11 and Server 2025. - Changed Windows MSRT search
string for x64 and x86. This was mainly to get x64 to the top of the search
results. x86 won't actually download since the code isn't in place for
content from the MU Catalog to download x86 content (no idea if anyone
actually builds x86 FFUs for Win10 - I hope not)
---
FFUDevelopment/BuildFFUVM.ps1 | 74 +++++++++++++++++++++--------------
1 file changed, 44 insertions(+), 30 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index a057493..056ead9 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -279,7 +279,7 @@ param(
[ValidateSet(10, 11, 2016, 2019, 2022, 2025)]
[int]$WindowsRelease = 11,
[Parameter(Mandatory = $false)]
- [string]$WindowsVersion = '23h2',
+ [string]$WindowsVersion = '24h2',
[Parameter(Mandatory = $false)]
[ValidateSet('x86', 'x64', 'arm64')]
[string]$WindowsArch = 'x64',
@@ -2238,9 +2238,16 @@ function Get-KBLink {
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 ")(?=.*" ) + ")" ) } |
+ Select-Object -First 1 |
ForEach-Object { $_.id.replace('_link', '') } |
Where-Object { $_ -in $kbids }
@@ -2250,7 +2257,7 @@ function Get-KBLink {
}
foreach ($guid in $guids) {
- Write-Verbose -Message "Downloading information for $guid"
+ # Write-Verbose -Message "Downloading information for $guid"
$post = @{ size = 0; updateID = $guid; uidInfo = $guid } | ConvertTo-Json -Compress
$body = @{ updateIDs = "[$post]" }
$OriginalVerbosePreference = $VerbosePreference
@@ -2321,35 +2328,35 @@ function Save-KB {
$links = Get-KBLink -Name $kb
foreach ($link in $links) {
if (!($link -match 'x64' -or $link -match 'amd64' -or $link -match 'x86' -or $link -match 'arm64')) {
- WriteLog "No architecture found in $link, assume this is for all architectures"
+ WriteLog "No architecture found in $link, skipping"
#FIX: 3/22/2024 - the SecurityHealthSetup fix was updated and now includes two files (one is x64 and the other is arm64)
#Unfortunately there is no easy way to determine the architecture from the file name
#There is a support doc that include links to download, but it's out of date (n-1)
#https://support.microsoft.com/en-us/topic/windows-security-update-a6ac7d2e-b1bf-44c0-a028-41720a242da3
#These files don't change that often, so will check the link above to see when it updates and may use that
#For now this is hard-coded for these specific file names
- if ($link -match 'security') {
- #Make sure we're getting the correct architecture for the Security Health Setup update
- WriteLog "Link: $link matches security"
- if ($WindowsArch -eq 'x64') {
- if ($link -match 'securityhealthsetup_e1') {
- Writelog "Downloading $Link for $WindowsArch to $Path"
- Start-BitsTransferWithRetry -Source $link -Destination $Path
- $fileName = ($link -split '/')[-1]
- Writelog "Returning $fileName"
- break
- }
- }
- if ($WindowsArch -eq 'arm64') {
- if ($link -match 'securityhealthsetup_25') {
- Writelog "Downloading $Link for $WindowsArch to $Path"
- Start-BitsTransferWithRetry -Source $link -Destination $Path
- $fileName = ($link -split '/')[-1]
- Writelog "Returning $fileName"
- break
- }
- }
- }
+ # if ($link -match 'security') {
+ # #Make sure we're getting the correct architecture for the Security Health Setup update
+ # WriteLog "Link: $link matches security"
+ # if ($WindowsArch -eq 'x64') {
+ # if ($link -match 'securityhealthsetup_e1') {
+ # Writelog "Downloading $Link for $WindowsArch to $Path"
+ # Start-BitsTransferWithRetry -Source $link -Destination $Path
+ # $fileName = ($link -split '/')[-1]
+ # Writelog "Returning $fileName"
+ # break
+ # }
+ # }
+ # if ($WindowsArch -eq 'arm64') {
+ # if ($link -match 'securityhealthsetup_25') {
+ # Writelog "Downloading $Link for $WindowsArch to $Path"
+ # Start-BitsTransferWithRetry -Source $link -Destination $Path
+ # $fileName = ($link -split '/')[-1]
+ # Writelog "Returning $fileName"
+ # break
+ # }
+ # }
+ # }
}
if ($link -match 'x64' -or $link -match 'amd64') {
@@ -2359,7 +2366,8 @@ function Save-KB {
Start-BitsTransferWithRetry -Source $link -Destination $Path
$fileName = ($link -split '/')[-1]
Writelog "Returning $fileName"
- break
+ #With Windows 11 24H2 and Checkpoint CUs, there are multiple files that are downloaded
+ # break
}
}
@@ -2370,7 +2378,8 @@ function Save-KB {
Start-BitsTransferWithRetry -Source $link -Destination $Path
$fileName = ($link -split '/')[-1]
Writelog "Returning $fileName"
- break
+ #With Windows 11 24H2 and Checkpoint CUs, there are multiple files that are downloaded
+ # break
}
}
}
@@ -3265,12 +3274,12 @@ Function Get-USBDrive {
if ($VerbosePreference -ne 'Continue') {
Write-Host "Found $USBDrivesCount total USB drives"
If ($ExternalCount -gt 0) {
- Write-Host "$ExternalCount are external drives"
+ Write-Host "$ExternalCount external drives"
}
}
WriteLog "Found $USBDrivesCount total USB drives"
If ($ExternalCount -gt 0) {
- WriteLog "$ExternalCount are external drives"
+ WriteLog "$ExternalCount external drives"
}
}
}
@@ -4020,7 +4029,12 @@ if ($InstallApps) {
}
if ($UpdateLatestMSRT) {
WriteLog "`$UpdateLatestMSRT is set to true."
- $Name = "Windows Malicious Software Removal Tool"
+ if ($WindowsArch -eq 'x64') {
+ $Name = """Windows Malicious Software Removal Tool x64""" + " " + """Windows $WindowsRelease"""
+ }
+ if ($WindowsArch -eq 'x86') {
+ $Name = """Windows Malicious Software Removal Tool""" + " " + """Windows $WindowsRelease"""
+ }
#Check if $MSRTPath exists, if not, create it
if (-not (Test-Path -Path $MSRTPath)) {
WriteLog "Creating $MSRTPath"
From eae619e7e83976c26fadbab71c0bdba2c9d81569 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Mon, 4 Nov 2024 18:23:57 +0100
Subject: [PATCH 31/50] Update BuildFFUVM.ps1
- Fix update search for Windows Server 2025 release
---
FFUDevelopment/BuildFFUVM.ps1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index e4f1ae7..7ff37ad 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -4192,7 +4192,7 @@ try {
$Name = """Cumulative update for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
}
if ($WindowsRelease -eq 2025) {
- $Name = """Cumulative update for Windows 11 Version 24h2 for $WindowsArch"" ""Security Updates"""
+ $Name = """Cumulative Update for Microsoft server operating system, version 24h2 for $WindowsArch"""
}
if ($WindowsRelease -eq 2022) {
$Name = """Cumulative Update for Microsoft server operating system, version 21h2 for $WindowsArch"""
From db788c3c3045586a8be4d7b8fc5e69fe2bf6ff5d Mon Sep 17 00:00:00 2001
From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com>
Date: Wed, 27 Nov 2024 13:13:29 -0800
Subject: [PATCH 32/50] =?UTF-8?q?-=20If=20not=20passing=20an=20ISO,=20hard?=
=?UTF-8?q?coded=20WindowsVersion=20of=2022H2=20for=20Windows=2010=20or=20?=
=?UTF-8?q?24H2=20for=20Windows=2011=20since=20the=20ESD=20media=20only=20?=
=?UTF-8?q?provides=20those=20two=20versions.=20Not=20doing=20this=20allow?=
=?UTF-8?q?ed=20for=20unnecessary=20VHDX=20creation=20since=20it=20checks?=
=?UTF-8?q?=20the=20WindowsVersion=20via=20the=20json=20file.=20This=20als?=
=?UTF-8?q?o=20fixes=20an=20issue=20where=20CUs=20could=20be=20searched=20?=
=?UTF-8?q?for=20that=20didn=E2=80=99t=20exist,=20but=20the=20media=20woul?=
=?UTF-8?q?d=20still=20download=20-=20Added=20some=20additional=20logging?=
=?UTF-8?q?=20entries=20-=20Removed=20verbose=20output=20of=20the=20Optimi?=
=?UTF-8?q?ze-Volume=20command=20-=20Fixed=20an=20issue=20where=20not=20pa?=
=?UTF-8?q?ssing=20an=20ISO=20caused=20the=20script=20to=20fail=20-=20Clea?=
=?UTF-8?q?ned=20up=20the=20KBPath=20folder=20at=20the=20end=20of=20the=20?=
=?UTF-8?q?run=20-=20Changed=20some=20minor=20formatting=20items?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
FFUDevelopment/BuildFFUVM.ps1 | 235 ++++++++++++++++++----------------
1 file changed, 124 insertions(+), 111 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index f0ced96..1148ae2 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -3900,6 +3900,17 @@ if (($WindowsArch -eq 'ARM64') -and ($UpdateLatestMSRT -eq $true)) {
$UpdateLatestMSRT = $false
WriteLog 'Windows Malicious Software Removal Tool is not available for the ARM64 architecture.'
}
+#If downloading ESD from MCT, hardcode WindowsVersion to 22H2 for Windows 10 and 24H2 for Windows 11
+#MCT media only provides 22H2 and 24H2 media
+#This prevents issues with VHDX Caching unecessarily and with searching for CUs
+if ($ISOPath -eq '') {
+ if ($WindowsRelease -eq '10') {
+ $WindowsVersion = '22H2'
+ }
+ if ($WindowsRelease -eq '11') {
+ $WindowsVersion = '24H2'
+ }
+}
###END PARAMETER VALIDATION
@@ -4208,7 +4219,7 @@ try {
}
#Update Latest Preview Cumlative Update for Client OS only
- #will take Precendence over $UpdateLastestCU if both were set to $true
+ #will take Precendence over $UpdateLatestCU if both were set to $true
if ($UpdatePreviewCU -and $installationType -eq 'Client') {
Writelog "`$UpdatePreviewCU is set to true, checking for latest Preview CU"
$Name = """Cumulative update Preview for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
@@ -4262,37 +4273,41 @@ try {
#Search for cached VHDX and skip VHDX creation if there's a cached version
if ($AllowVHDXCaching) {
+ WriteLog 'AllowVHDXCaching is true, checking for cached VHDX file'
if (Test-Path -Path $VHDXCacheFolder) {
- $vhdxJsons = @(Get-ChildItem -File -Path $VHDXCacheFolder -Filter "*_config.json" | Sort-Object -Property CreationTime -Descending)
+ WriteLog "Found $VHDXCacheFolder"
+ $vhdxJsons = @(Get-ChildItem -File -Path $VHDXCacheFolder -Filter '*_config.json' | Sort-Object -Property CreationTime -Descending)
+ WriteLog "Found $($vhdxJsons.Count) cached VHDX files"
$downloadedKBs = @(Get-ChildItem -File -Path $KBPath)
#$jsonDeserializer = [System.Web.Script.Serialization.JavaScriptSerializer]::new()
foreach ($vhdxJson in $vhdxJsons) {
try {
+ WriteLog "Processing $($vhdxJson.FullName)"
#$vhdxCacheItem = $jsonDeserializer.Deserialize((Get-Content -Path $vhdxJson.FullName -Raw), [VhdxCacheItem])
$vhdxCacheItem = Get-Content -Path $vhdxJson.FullName -Raw | ConvertFrom-Json
if ((($vhdxCacheItem.WindowsSKU -ne $WindowsSKU) -or
([string]::IsNullOrEmpty($vhdxCacheItem.WindowsSKU) -xor [string]::IsNullOrEmpty($WindowsSKU)))) {
- WriteLog "WindowsSKU not equal"
+ WriteLog 'WindowsSKU mismatch, continuing'
continue
}
if ((($vhdxCacheItem.WindowsRelease -ne $WindowsRelease) -or
([string]::IsNullOrEmpty($vhdxCacheItem.WindowsRelease) -xor [string]::IsNullOrEmpty($WindowsRelease)))) {
- WriteLog "WindowsRelease not equal"
+ WriteLog 'WindowsRelease mismatch, continuing'
continue
}
if ((($vhdxCacheItem.WindowsVersion -ne $WindowsVersion) -or
([string]::IsNullOrEmpty($vhdxCacheItem.WindowsVersion) -xor [string]::IsNullOrEmpty($WindowsVersion)))) {
- WriteLog "WindowsVersion not equal"
+ Writelog 'WindowsVersion mismatch, continuing'
continue
}
if ((($vhdxCacheItem.OptionalFeatures -ne $OptionalFeatures) -or
([string]::IsNullOrEmpty($vhdxCacheItem.OptionalFeatures) -xor [string]::IsNullOrEmpty($OptionalFeatures)))) {
- WriteLog "OptionalFeatures not equal"
+ WriteLog 'OptionalFeatures mismatch, continuing'
continue
}
@@ -4300,11 +4315,11 @@ try {
(Compare-Object -ReferenceObject $downloadedKBs -DifferenceObject $vhdxCacheItem.IncludedUpdates -Property Name)
$downloadedKBs.Name
$vhdxCacheItem.IncludedUpdates.Name
- WriteLog "Updates not equal"
+ WriteLog 'IncludedUpdates mismatch, continuing'
continue
}
- WriteLog "Found cached VHDX file with same parameters and patches"
+ WriteLog "Found cached VHDX file $vhdxCacheFolder\$($vhdxCacheItem.VhdxFileName) with matching parameters and included updates"
$cachedVHDXFileFound = $true
$cachedVHDXInfo = $vhdxCacheItem
break
@@ -4316,116 +4331,102 @@ try {
}
if (-Not $cachedVHDXFileFound) {
- if ($ISOPath) {
- $wimPath = Get-WimFromISO
- }
- else {
- $wimPath = Get-WindowsESD -WindowsRelease $WindowsRelease -WindowsArch $WindowsArch -WindowsLang $WindowsLang -MediaType $mediaType
- }
- #If index not specified by user, try and find based on WindowsSKU
- if (-not($index) -and ($WindowsSKU)) {
- $index = Get-Index -WindowsImagePath $wimPath -WindowsSKU $WindowsSKU
- }
-
- $vhdxDisk = New-ScratchVhdx -VhdxPath $VHDXPath -SizeBytes $disksize -LogicalSectorSizeBytes $LogicalSectorSizeBytes
-
- $systemPartitionDriveLetter = New-SystemPartition -VhdxDisk $vhdxDisk
-
- New-MSRPartition -VhdxDisk $vhdxDisk
-
- $osPartition = New-OSPartition -VhdxDisk $vhdxDisk -OSPartitionSize $OSPartitionSize -WimPath $WimPath -WimIndex $index
- $osPartitionDriveLetter = $osPartition[1].DriveLetter
- $WindowsPartition = $osPartitionDriveLetter + ":\"
-
- #$recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
- $recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
-
- WriteLog "All necessary partitions created."
-
- Add-BootFiles -OsPartitionDriveLetter $osPartitionDriveLetter -SystemPartitionDriveLetter $systemPartitionDriveLetter[1]
-
- if ($UpdateLatestCU -or $UpdateLatestNet -or $UpdatePreviewCU ) {
- #Check if $KBCachePath exists, if not, create it
- if ($AllowUpdateCaching) {
- if (-not (Test-Path -Path $KBCachePath)) {
- WriteLog "Creating $KBCachePath"
- New-Item -Path $KBCachePath -ItemType Directory -Force | Out-Null
- }
+ if ($ISOPath) {
+ $wimPath = Get-WimFromISO
+ } else {
+ $wimPath = Get-WindowsESD -WindowsRelease $WindowsRelease -WindowsArch $WindowsArch -WindowsLang $WindowsLang -MediaType $mediaType
+ }
+ #If index not specified by user, try and find based on WindowsSKU
+ if (-not($index) -and ($WindowsSKU)) {
+ $index = Get-Index -WindowsImagePath $wimPath -WindowsSKU $WindowsSKU
}
- }
+ $vhdxDisk = New-ScratchVhdx -VhdxPath $VHDXPath -SizeBytes $disksize -LogicalSectorSizeBytes $LogicalSectorSizeBytes
+
+ $systemPartitionDriveLetter = New-SystemPartition -VhdxDisk $vhdxDisk
+ New-MSRPartition -VhdxDisk $vhdxDisk
- #Add Windows packages
- if ($UpdateLatestCU -or $UpdateLatestNet -or $UpdatePreviewCU ) {
- try {
- WriteLog "Adding KBs to $WindowsPartition"
- WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
- # If WindowsRelease is 2016, we need to add the SSU first
- if ($WindowsRelease -eq 2016) {
- WriteLog "WindowsRelease is 2016, adding SSU first"
- WriteLog "Adding SSU to $WindowsPartition"
- # Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath -PreventPending | Out-Null
- # Commenting out -preventpending as it causes an issue with the SSU being applied
- # Seems to be because of the registry being mounted per dism.log
- Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath | Out-Null
- WriteLog "SSU added to $WindowsPartition"
- WriteLog "Removing $SSUFilePath"
- Remove-Item -Path $SSUFilePath -Force | Out-Null
- WriteLog 'SSU removed'
- WriteLog "Adding CU to $WindowsPartition"
- }
- # Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath -PreventPending | Out-Null
- Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath | Out-Null
- WriteLog "KBs added to $WindowsPartition"
- if ($AllowVHDXCaching) {
- $cachedVHDXInfo = [VhdxCacheItem]::new()
- $includedUpdates = Get-ChildItem -Path $KBPath -File
-
- foreach ($includedUpdate in $includedUpdates) {
- $cachedVHDXInfo.IncludedUpdates += ([VhdxCacheUpdateItem]::new($includedUpdate.Name))
+ $osPartition = New-OSPartition -VhdxDisk $vhdxDisk -OSPartitionSize $OSPartitionSize -WimPath $WimPath -WimIndex $index
+ $osPartitionDriveLetter = $osPartition[1].DriveLetter
+ $WindowsPartition = $osPartitionDriveLetter + ':\'
+
+ #$recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
+ $recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
+
+ WriteLog 'All necessary partitions created.'
+
+ Add-BootFiles -OsPartitionDriveLetter $osPartitionDriveLetter -SystemPartitionDriveLetter $systemPartitionDriveLetter[1]
+
+ #Add Windows packages
+ if ($UpdateLatestCU -or $UpdateLatestNet -or $UpdatePreviewCU ) {
+ try {
+ WriteLog "Adding KBs to $WindowsPartition"
+ WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
+ # If WindowsRelease is 2016, we need to add the SSU first
+ if ($WindowsRelease -eq 2016) {
+ WriteLog 'WindowsRelease is 2016, adding SSU first'
+ WriteLog "Adding SSU to $WindowsPartition"
+ # Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath -PreventPending | Out-Null
+ # Commenting out -preventpending as it causes an issue with the SSU being applied
+ # Seems to be because of the registry being mounted per dism.log
+ Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath | Out-Null
+ WriteLog "SSU added to $WindowsPartition"
+ WriteLog "Removing $SSUFilePath"
+ Remove-Item -Path $SSUFilePath -Force | Out-Null
+ WriteLog 'SSU removed'
+ WriteLog "Adding CU to $WindowsPartition"
}
- }
- WriteLog "Removing $KBPath"
- Remove-Item -Path $KBPath -Recurse -Force | Out-Null
- WriteLog "Clean Up the WinSxS Folder"
- WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
- Dism /Image:$WindowsPartition /Cleanup-Image /StartComponentCleanup /ResetBase | Out-Null
- WriteLog "Clean Up the WinSxS Folder completed"
+ # Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath -PreventPending | Out-Null
+ Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath | Out-Null
+ WriteLog "KBs added to $WindowsPartition"
+ if ($AllowVHDXCaching) {
+ $cachedVHDXInfo = [VhdxCacheItem]::new()
+ $includedUpdates = Get-ChildItem -Path $KBPath -File
+
+ foreach ($includedUpdate in $includedUpdates) {
+ $cachedVHDXInfo.IncludedUpdates += ([VhdxCacheUpdateItem]::new($includedUpdate.Name))
+ }
+ }
+ WriteLog "Removing $KBPath"
+ Remove-Item -Path $KBPath -Recurse -Force | Out-Null
+ WriteLog 'Clean Up the WinSxS Folder'
+ WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
+ Dism /Image:$WindowsPartition /Cleanup-Image /StartComponentCleanup /ResetBase | Out-Null
+ WriteLog 'Clean Up the WinSxS Folder completed'
+ } catch {
+ Write-Host "Adding KB to VHDX failed with error $_"
+ WriteLog "Adding KB to VHDX failed with error $_"
+ if ($_.Exception.HResult -eq -2146498525) {
+ Write-Host 'Missing latest Servicing Stack Update'
+ Write-Host 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
+ Write-Host 'Recommended to use the latest media'
+ WriteLog 'Missing latest Servicing Stack Update'
+ WriteLog 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
+ WriteLog 'Recommended to use the latest media'
+ }
+ throw $_
+ }
}
- catch {
- Write-Host "Adding KB to VHDX failed with error $_"
- WriteLog "Adding KB to VHDX failed with error $_"
- if ($_.Exception.HResult -eq -2146498525){
- Write-Host 'Missing latest Servicing Stack Update'
- Write-Host 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
- Write-Host 'Recommended to use the latest media'
- WriteLog 'Missing latest Servicing Stack Update'
- WriteLog 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
- WriteLog 'Recommended to use the latest media'
- }
- throw $_
- }
- }
- #Enable Windows Optional Features (e.g. .Net3, etc)
- If ($OptionalFeatures) {
- $Source = Join-Path (Split-Path $wimpath) "sxs"
- Enable-WindowsFeaturesByName -FeatureNames $OptionalFeatures -Source $Source
- }
+ #Enable Windows Optional Features (e.g. .Net3, etc)
+ If ($OptionalFeatures) {
+ $Source = Join-Path (Split-Path $wimpath) 'sxs'
+ Enable-WindowsFeaturesByName -FeatureNames $OptionalFeatures -Source $Source
+ }
} else {
#Use cached vhdx file
- WriteLog "Using cached VHDX file to speed up build proces"
+ WriteLog 'Using cached VHDX file to speed up build proces'
WriteLog "VHDX file is: $($cachedVHDXInfo.VhdxFileName)"
Robocopy.exe $($VHDXCacheFolder) $($VMPath) $($cachedVHDXInfo.VhdxFileName) /E /COPY:DAT /R:5 /W:5 /J
$VHDXPath = Join-Path $($VMPath) $($cachedVHDXInfo.VhdxFileName)
$vhdxDisk = Get-VHD -Path $VHDXPath | Mount-VHD -Passthru | Get-Disk
- $osPartition = $vhdxDisk | Get-Partition | Where-Object { $_.GptType -eq "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}" }
+ $osPartition = $vhdxDisk | Get-Partition | Where-Object { $_.GptType -eq '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}' }
$osPartitionDriveLetter = $osPartition.DriveLetter
- $WindowsPartition = $osPartitionDriveLetter + ":\"
+ $WindowsPartition = $osPartitionDriveLetter + ':\'
}
@@ -4439,9 +4440,11 @@ try {
Dismount-DiskImage -ImagePath $ISOPath | Out-null
WriteLog 'Done'
}
- else {
- #Remove ESD file
+ #If $wimPath is an esd file, remove it
+ If ($wimPath -match '.esd') {
+ WriteLog "Deleting $wimPath file"
Remove-Item -Path $wimPath -Force
+ WriteLog "$wimPath deleted"
}
@@ -4459,12 +4462,12 @@ try {
}
if ($AllowVHDXCaching -and !$cachedVHDXFileFound) {
- WriteLog 'New cachabe VHDX created'
+ WriteLog 'New cached VHDX created'
WriteLog 'Defragmenting Windows partition...'
- Optimize-Volume -DriveLetter $osPartition.DriveLetter -Defrag -NormalPriority -Verbose
+ Optimize-Volume -DriveLetter $osPartition.DriveLetter -Defrag -NormalPriority
WriteLog 'Performing slab consolidation on Windows partition...'
- Optimize-Volume -DriveLetter $osPartition.DriveLetter -SlabConsolidate -NormalPriority -Verbose
+ Optimize-Volume -DriveLetter $osPartition.DriveLetter -SlabConsolidate -NormalPriority
WriteLog 'Dismounting VHDX'
Dismount-ScratchVhdx -VhdxPath $VHDXPath
@@ -4737,20 +4740,30 @@ If ($CleanupAppsISO) {
Writelog "Removing $AppsISO failed with error $_"
throw $_
}
-If ($CleanupDrivers){
+}
+If ($CleanupDrivers) {
try {
If (Test-Path -Path $Driversfolder\$Make) {
WriteLog "Removing $Driversfolder\$Make"
Remove-Item -Path $Driversfolder\$Make -Force -Recurse
- WriteLog "Removal complete"
+ WriteLog 'Removal complete'
}
- }
- catch {
+ } catch {
Writelog "Removing $Driversfolder\$Make failed with error $_"
throw $_
}
-
}
+if ($AllowVHDXCaching) {
+ try {
+ If (Test-Path -Path $KBPath) {
+ WriteLog "Removing $KBPath"
+ Remove-Item -Path $KBPath -Recurse -Force -ErrorAction SilentlyContinue
+ WriteLog 'Removal complete'
+ }
+ } catch {
+ Writelog "Removing $KBPath failed with error $_"
+ throw $_
+ }
}
#Clean up dirty.txt file
Remove-Item -Path .\dirty.txt -Force | out-null
From 802a225c3e1c5910625e08316a70b7126930cddc Mon Sep 17 00:00:00 2001
From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com>
Date: Thu, 28 Nov 2024 17:36:00 -0800
Subject: [PATCH 33/50] Revert "2410.1 Allow vhdx caching"
---
FFUDevelopment/BuildFFUVM.ps1 | 323 +++++++++-------------------------
1 file changed, 86 insertions(+), 237 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 1148ae2..056ead9 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -318,7 +318,6 @@ param(
[bool]$UpdateLatestMSRT,
[bool]$UpdateEdge,
[bool]$UpdateOneDrive,
- [bool]$AllowVHDXCaching,
[bool]$CopyPPKG,
[bool]$CopyUnattend,
[bool]$CopyAutopilot,
@@ -349,23 +348,6 @@ param(
)
$version = '2410.1'
-#Class definition for vhdx cache
-class VhdxCacheUpdateItem {
- [string]$Name
- VhdxCacheUpdateItem([string]$Name) {
- $this.Name = $Name
- }
-}
-
-class VhdxCacheItem {
- [string]$VhdxFileName = ""
- [string]$WindowsSKU = ""
- [string]$WindowsRelease = ""
- [string]$WindowsVersion = ""
- [string]$OptionalFeatures = ""
- [VhdxCacheUpdateItem[]]$IncludedUpdates = @()
-}
-
#Check if Hyper-V feature is installed (requires only checks the module)
$osInfo = Get-WmiObject -Class Win32_OperatingSystem
$isServer = $osInfo.Caption -match 'server'
@@ -408,7 +390,6 @@ if (-not $PPKGFolder) { $PPKGFolder = "$FFUDevelopmentPath\PPKG" }
if (-not $UnattendFolder) { $UnattendFolder = "$FFUDevelopmentPath\Unattend" }
if (-not $AutopilotFolder) { $AutopilotFolder = "$FFUDevelopmentPath\Autopilot" }
if (-not $PEDriversFolder) { $PEDriversFolder = "$FFUDevelopmentPath\PEDrivers" }
-if (-not $VHDXCacheFolder) { $VHDXCacheFolder = "$FFUDevelopmentPath\VHDXCache" }
if (-not $installationType) { $installationType = if ($WindowsRelease.ToString().Length -eq 2) { 'Client' } else { 'Server' } }
if ($installationType -eq 'Server'){
#Map $WindowsRelease to $WindowsVersion for Windows Server
@@ -2947,12 +2928,11 @@ function Optimize-FFUCaptureDrive {
)
try {
WriteLog 'Mounting VHDX for volume optimization'
- $mountedDisk = Mount-VHD -Path $VhdxPath -Passthru | Get-Disk
- $osPartition = $mountedDisk | Get-Partition | Where-Object { $_.GptType -eq "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}" }
+ Mount-VHD -Path $VhdxPath
WriteLog 'Defragmenting Windows partition...'
- Optimize-Volume -DriveLetter $osPartition.DriveLetter -Defrag -NormalPriority
+ Optimize-Volume -DriveLetter W -Defrag -NormalPriority
WriteLog 'Performing slab consolidation on Windows partition...'
- Optimize-Volume -DriveLetter $osPartition.DriveLetter -SlabConsolidate -NormalPriority
+ Optimize-Volume -DriveLetter W -SlabConsolidate -NormalPriority
WriteLog 'Dismounting VHDX'
Dismount-ScratchVhdx -VhdxPath $VhdxPath
WriteLog 'Mounting VHDX as read-only for optimization'
@@ -3900,17 +3880,6 @@ if (($WindowsArch -eq 'ARM64') -and ($UpdateLatestMSRT -eq $true)) {
$UpdateLatestMSRT = $false
WriteLog 'Windows Malicious Software Removal Tool is not available for the ARM64 architecture.'
}
-#If downloading ESD from MCT, hardcode WindowsVersion to 22H2 for Windows 10 and 24H2 for Windows 11
-#MCT media only provides 22H2 and 24H2 media
-#This prevents issues with VHDX Caching unecessarily and with searching for CUs
-if ($ISOPath -eq '') {
- if ($WindowsRelease -eq '10') {
- $WindowsVersion = '22H2'
- }
- if ($WindowsRelease -eq '11') {
- $WindowsVersion = '24H2'
- }
-}
###END PARAMETER VALIDATION
@@ -4186,6 +4155,34 @@ if ($InstallApps) {
#Create VHDX
try {
+ if ($ISOPath) {
+ $wimPath = Get-WimFromISO
+ }
+ else {
+ $wimPath = Get-WindowsESD -WindowsRelease $WindowsRelease -WindowsArch $WindowsArch -WindowsLang $WindowsLang -MediaType $mediaType
+ }
+ #If index not specified by user, try and find based on WindowsSKU
+ if (-not($index) -and ($WindowsSKU)) {
+ $index = Get-Index -WindowsImagePath $wimPath -WindowsSKU $WindowsSKU
+ }
+
+ $vhdxDisk = New-ScratchVhdx -VhdxPath $VHDXPath -SizeBytes $disksize -LogicalSectorSizeBytes $LogicalSectorSizeBytes
+
+ $systemPartitionDriveLetter = New-SystemPartition -VhdxDisk $vhdxDisk
+
+ New-MSRPartition -VhdxDisk $vhdxDisk
+
+ $osPartition = New-OSPartition -VhdxDisk $vhdxDisk -OSPartitionSize $OSPartitionSize -WimPath $WimPath -WimIndex $index
+ $osPartitionDriveLetter = $osPartition[1].DriveLetter
+ $WindowsPartition = $osPartitionDriveLetter + ":\"
+
+ #$recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
+ $recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
+
+ WriteLog "All necessary partitions created."
+
+ Add-BootFiles -OsPartitionDriveLetter $osPartitionDriveLetter -SystemPartitionDriveLetter $systemPartitionDriveLetter[1]
+
#Update latest Cumulative Update if both $UpdateLatestCU is $true and $UpdatePreviewCU is $false
#Changed to use MU Catalog instead of using Get-LatestWindowsKB
#The Windows release info page is updated later than the MU Catalog
@@ -4219,7 +4216,7 @@ try {
}
#Update Latest Preview Cumlative Update for Client OS only
- #will take Precendence over $UpdateLatestCU if both were set to $true
+ #will take Precendence over $UpdateLastestCU if both were set to $true
if ($UpdatePreviewCU -and $installationType -eq 'Client') {
Writelog "`$UpdatePreviewCU is set to true, checking for latest Preview CU"
$Name = """Cumulative update Preview for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
@@ -4270,164 +4267,57 @@ try {
# $KBFilePath = Save-KB -Name $Name -Path $KBPath
# WriteLog "Latest Security Platform Update saved to $KBPath\$KBFilePath"
# }
-
- #Search for cached VHDX and skip VHDX creation if there's a cached version
- if ($AllowVHDXCaching) {
- WriteLog 'AllowVHDXCaching is true, checking for cached VHDX file'
- if (Test-Path -Path $VHDXCacheFolder) {
- WriteLog "Found $VHDXCacheFolder"
- $vhdxJsons = @(Get-ChildItem -File -Path $VHDXCacheFolder -Filter '*_config.json' | Sort-Object -Property CreationTime -Descending)
- WriteLog "Found $($vhdxJsons.Count) cached VHDX files"
- $downloadedKBs = @(Get-ChildItem -File -Path $KBPath)
- #$jsonDeserializer = [System.Web.Script.Serialization.JavaScriptSerializer]::new()
-
- foreach ($vhdxJson in $vhdxJsons) {
- try {
- WriteLog "Processing $($vhdxJson.FullName)"
- #$vhdxCacheItem = $jsonDeserializer.Deserialize((Get-Content -Path $vhdxJson.FullName -Raw), [VhdxCacheItem])
- $vhdxCacheItem = Get-Content -Path $vhdxJson.FullName -Raw | ConvertFrom-Json
-
- if ((($vhdxCacheItem.WindowsSKU -ne $WindowsSKU) -or
- ([string]::IsNullOrEmpty($vhdxCacheItem.WindowsSKU) -xor [string]::IsNullOrEmpty($WindowsSKU)))) {
- WriteLog 'WindowsSKU mismatch, continuing'
- continue
- }
-
- if ((($vhdxCacheItem.WindowsRelease -ne $WindowsRelease) -or
- ([string]::IsNullOrEmpty($vhdxCacheItem.WindowsRelease) -xor [string]::IsNullOrEmpty($WindowsRelease)))) {
- WriteLog 'WindowsRelease mismatch, continuing'
- continue
- }
-
- if ((($vhdxCacheItem.WindowsVersion -ne $WindowsVersion) -or
- ([string]::IsNullOrEmpty($vhdxCacheItem.WindowsVersion) -xor [string]::IsNullOrEmpty($WindowsVersion)))) {
- Writelog 'WindowsVersion mismatch, continuing'
- continue
- }
-
- if ((($vhdxCacheItem.OptionalFeatures -ne $OptionalFeatures) -or
- ([string]::IsNullOrEmpty($vhdxCacheItem.OptionalFeatures) -xor [string]::IsNullOrEmpty($OptionalFeatures)))) {
- WriteLog 'OptionalFeatures mismatch, continuing'
- continue
- }
-
- if ((Compare-Object -ReferenceObject $downloadedKBs -DifferenceObject $vhdxCacheItem.IncludedUpdates -Property Name).Length -gt 0) {
- (Compare-Object -ReferenceObject $downloadedKBs -DifferenceObject $vhdxCacheItem.IncludedUpdates -Property Name)
- $downloadedKBs.Name
- $vhdxCacheItem.IncludedUpdates.Name
- WriteLog 'IncludedUpdates mismatch, continuing'
- continue
- }
-
- WriteLog "Found cached VHDX file $vhdxCacheFolder\$($vhdxCacheItem.VhdxFileName) with matching parameters and included updates"
- $cachedVHDXFileFound = $true
- $cachedVHDXInfo = $vhdxCacheItem
- break
- } catch {
- WriteLog "Reading $vhdxJson Failed with error $_"
- }
+
+
+ #Add Windows packages
+ if ($UpdateLatestCU -or $UpdateLatestNet -or $UpdatePreviewCU ) {
+ try {
+ WriteLog "Adding KBs to $WindowsPartition"
+ WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
+ # If WindowsRelease is 2016, we need to add the SSU first
+ if ($WindowsRelease -eq 2016) {
+ WriteLog "WindowsRelease is 2016, adding SSU first"
+ WriteLog "Adding SSU to $WindowsPartition"
+ # Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath -PreventPending | Out-Null
+ # Commenting out -preventpending as it causes an issue with the SSU being applied
+ # Seems to be because of the registry being mounted per dism.log
+ Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath | Out-Null
+ WriteLog "SSU added to $WindowsPartition"
+ WriteLog "Removing $SSUFilePath"
+ Remove-Item -Path $SSUFilePath -Force | Out-Null
+ WriteLog 'SSU removed'
+ WriteLog "Adding CU to $WindowsPartition"
}
+ # Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath -PreventPending | Out-Null
+ Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath | Out-Null
+ WriteLog "KBs added to $WindowsPartition"
+ WriteLog "Removing $KBPath"
+ Remove-Item -Path $KBPath -Recurse -Force | Out-Null
+ WriteLog "Clean Up the WinSxS Folder"
+ WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
+ Dism /Image:$WindowsPartition /Cleanup-Image /StartComponentCleanup /ResetBase | Out-Null
+ WriteLog "Clean Up the WinSxS Folder completed"
}
+ catch {
+ Write-Host "Adding KB to VHDX failed with error $_"
+ WriteLog "Adding KB to VHDX failed with error $_"
+ if ($_.Exception.HResult -eq -2146498525){
+ Write-Host 'Missing latest Servicing Stack Update'
+ Write-Host 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
+ Write-Host 'Recommended to use the latest media'
+ WriteLog 'Missing latest Servicing Stack Update'
+ WriteLog 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
+ WriteLog 'Recommended to use the latest media'
+ }
+ throw $_
+ }
}
-
- if (-Not $cachedVHDXFileFound) {
- if ($ISOPath) {
- $wimPath = Get-WimFromISO
- } else {
- $wimPath = Get-WindowsESD -WindowsRelease $WindowsRelease -WindowsArch $WindowsArch -WindowsLang $WindowsLang -MediaType $mediaType
- }
- #If index not specified by user, try and find based on WindowsSKU
- if (-not($index) -and ($WindowsSKU)) {
- $index = Get-Index -WindowsImagePath $wimPath -WindowsSKU $WindowsSKU
- }
- $vhdxDisk = New-ScratchVhdx -VhdxPath $VHDXPath -SizeBytes $disksize -LogicalSectorSizeBytes $LogicalSectorSizeBytes
-
- $systemPartitionDriveLetter = New-SystemPartition -VhdxDisk $vhdxDisk
-
- New-MSRPartition -VhdxDisk $vhdxDisk
-
- $osPartition = New-OSPartition -VhdxDisk $vhdxDisk -OSPartitionSize $OSPartitionSize -WimPath $WimPath -WimIndex $index
- $osPartitionDriveLetter = $osPartition[1].DriveLetter
- $WindowsPartition = $osPartitionDriveLetter + ':\'
-
- #$recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
- $recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
-
- WriteLog 'All necessary partitions created.'
-
- Add-BootFiles -OsPartitionDriveLetter $osPartitionDriveLetter -SystemPartitionDriveLetter $systemPartitionDriveLetter[1]
-
- #Add Windows packages
- if ($UpdateLatestCU -or $UpdateLatestNet -or $UpdatePreviewCU ) {
- try {
- WriteLog "Adding KBs to $WindowsPartition"
- WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
- # If WindowsRelease is 2016, we need to add the SSU first
- if ($WindowsRelease -eq 2016) {
- WriteLog 'WindowsRelease is 2016, adding SSU first'
- WriteLog "Adding SSU to $WindowsPartition"
- # Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath -PreventPending | Out-Null
- # Commenting out -preventpending as it causes an issue with the SSU being applied
- # Seems to be because of the registry being mounted per dism.log
- Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath | Out-Null
- WriteLog "SSU added to $WindowsPartition"
- WriteLog "Removing $SSUFilePath"
- Remove-Item -Path $SSUFilePath -Force | Out-Null
- WriteLog 'SSU removed'
- WriteLog "Adding CU to $WindowsPartition"
- }
- # Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath -PreventPending | Out-Null
- Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath | Out-Null
- WriteLog "KBs added to $WindowsPartition"
- if ($AllowVHDXCaching) {
- $cachedVHDXInfo = [VhdxCacheItem]::new()
- $includedUpdates = Get-ChildItem -Path $KBPath -File
-
- foreach ($includedUpdate in $includedUpdates) {
- $cachedVHDXInfo.IncludedUpdates += ([VhdxCacheUpdateItem]::new($includedUpdate.Name))
- }
- }
- WriteLog "Removing $KBPath"
- Remove-Item -Path $KBPath -Recurse -Force | Out-Null
- WriteLog 'Clean Up the WinSxS Folder'
- WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
- Dism /Image:$WindowsPartition /Cleanup-Image /StartComponentCleanup /ResetBase | Out-Null
- WriteLog 'Clean Up the WinSxS Folder completed'
- } catch {
- Write-Host "Adding KB to VHDX failed with error $_"
- WriteLog "Adding KB to VHDX failed with error $_"
- if ($_.Exception.HResult -eq -2146498525) {
- Write-Host 'Missing latest Servicing Stack Update'
- Write-Host 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
- Write-Host 'Recommended to use the latest media'
- WriteLog 'Missing latest Servicing Stack Update'
- WriteLog 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
- WriteLog 'Recommended to use the latest media'
- }
- throw $_
- }
- }
-
- #Enable Windows Optional Features (e.g. .Net3, etc)
- If ($OptionalFeatures) {
- $Source = Join-Path (Split-Path $wimpath) 'sxs'
- Enable-WindowsFeaturesByName -FeatureNames $OptionalFeatures -Source $Source
- }
-
- } else {
- #Use cached vhdx file
- WriteLog 'Using cached VHDX file to speed up build proces'
- WriteLog "VHDX file is: $($cachedVHDXInfo.VhdxFileName)"
-
- Robocopy.exe $($VHDXCacheFolder) $($VMPath) $($cachedVHDXInfo.VhdxFileName) /E /COPY:DAT /R:5 /W:5 /J
- $VHDXPath = Join-Path $($VMPath) $($cachedVHDXInfo.VhdxFileName)
-
- $vhdxDisk = Get-VHD -Path $VHDXPath | Mount-VHD -Passthru | Get-Disk
- $osPartition = $vhdxDisk | Get-Partition | Where-Object { $_.GptType -eq '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}' }
- $osPartitionDriveLetter = $osPartition.DriveLetter
- $WindowsPartition = $osPartitionDriveLetter + ':\'
+ #Enable Windows Optional Features (e.g. .Net3, etc)
+ If ($OptionalFeatures) {
+ $Source = Join-Path (Split-Path $wimpath) "sxs"
+ Enable-WindowsFeaturesByName -FeatureNames $OptionalFeatures -Source $Source
}
#Set Product key
@@ -4440,18 +4330,16 @@ try {
Dismount-DiskImage -ImagePath $ISOPath | Out-null
WriteLog 'Done'
}
- #If $wimPath is an esd file, remove it
- If ($wimPath -match '.esd') {
- WriteLog "Deleting $wimPath file"
+ else {
+ #Remove ESD file
Remove-Item -Path $wimPath -Force
- WriteLog "$wimPath deleted"
}
If ($InstallApps) {
#Copy Unattend file so VM Boots into Audit Mode
WriteLog 'Copying unattend file to boot to audit mode'
- New-Item -Path "$($osPartitionDriveLetter):\Windows\Panther\unattend" -ItemType Directory -Force | Out-Null
+ New-Item -Path "$($osPartitionDriveLetter):\Windows\Panther\unattend" -ItemType Directory | Out-Null
if($WindowsArch -eq 'x64'){
Copy-Item -Path "$FFUDevelopmentPath\BuildFFUUnattend\unattend_x64.xml" -Destination "$($osPartitionDriveLetter):\Windows\Panther\Unattend\Unattend.xml" -Force | Out-Null
}
@@ -4459,35 +4347,6 @@ try {
Copy-Item -Path "$FFUDevelopmentPath\BuildFFUUnattend\unattend_arm64.xml" -Destination "$($osPartitionDriveLetter):\Windows\Panther\Unattend\Unattend.xml" -Force | Out-Null
}
WriteLog 'Copy completed'
- }
-
- if ($AllowVHDXCaching -and !$cachedVHDXFileFound) {
- WriteLog 'New cached VHDX created'
-
- WriteLog 'Defragmenting Windows partition...'
- Optimize-Volume -DriveLetter $osPartition.DriveLetter -Defrag -NormalPriority
- WriteLog 'Performing slab consolidation on Windows partition...'
- Optimize-Volume -DriveLetter $osPartition.DriveLetter -SlabConsolidate -NormalPriority
- WriteLog 'Dismounting VHDX'
- Dismount-ScratchVhdx -VhdxPath $VHDXPath
-
- WriteLog 'Copying to cache dir'
-
- #Assuming there are now name collisons
- Robocopy.exe $($VMPath) $($VHDXCacheFolder) $("$VMName.vhdx") /E /COPY:DAT /R:5 /W:5 /J
-
- #Only create new instance if not created during patching
- if ($null -eq $cachedVHDXInfo) {
- $cachedVHDXInfo = [VhdxCacheItem]::new()
- }
- $cachedVHDXInfo.VhdxFileName = $("$VMName.vhdx")
- $cachedVHDXInfo.WindowsSKU = $WindowsSKU
- $cachedVHDXInfo.WindowsRelease = $WindowsRelease
- $cachedVHDXInfo.WindowsVersion = $WindowsVersion
- $cachedVHDXInfo.OptionalFeatures = $OptionalFeatures
-
- $cachedVHDXInfo | ConvertTo-Json | Out-File -FilePath ("{0}\{1}_config.json" -f $($VHDXCacheFolder), $VMName)
- } else {
Dismount-ScratchVhdx -VhdxPath $VHDXPath
}
}
@@ -4740,30 +4599,20 @@ If ($CleanupAppsISO) {
Writelog "Removing $AppsISO failed with error $_"
throw $_
}
-}
-If ($CleanupDrivers) {
+If ($CleanupDrivers){
try {
If (Test-Path -Path $Driversfolder\$Make) {
WriteLog "Removing $Driversfolder\$Make"
Remove-Item -Path $Driversfolder\$Make -Force -Recurse
- WriteLog 'Removal complete'
+ WriteLog "Removal complete"
}
- } catch {
+ }
+ catch {
Writelog "Removing $Driversfolder\$Make failed with error $_"
throw $_
}
+
}
-if ($AllowVHDXCaching) {
- try {
- If (Test-Path -Path $KBPath) {
- WriteLog "Removing $KBPath"
- Remove-Item -Path $KBPath -Recurse -Force -ErrorAction SilentlyContinue
- WriteLog 'Removal complete'
- }
- } catch {
- Writelog "Removing $KBPath failed with error $_"
- throw $_
- }
}
#Clean up dirty.txt file
Remove-Item -Path .\dirty.txt -Force | out-null
From 39a9bc9022e992c3583e479b3f5019b407b71471 Mon Sep 17 00:00:00 2001
From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com>
Date: Thu, 28 Nov 2024 17:40:22 -0800
Subject: [PATCH 34/50] Revert "Revert "2410.1 Allow vhdx caching""
---
FFUDevelopment/BuildFFUVM.ps1 | 323 +++++++++++++++++++++++++---------
1 file changed, 237 insertions(+), 86 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 056ead9..1148ae2 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -318,6 +318,7 @@ param(
[bool]$UpdateLatestMSRT,
[bool]$UpdateEdge,
[bool]$UpdateOneDrive,
+ [bool]$AllowVHDXCaching,
[bool]$CopyPPKG,
[bool]$CopyUnattend,
[bool]$CopyAutopilot,
@@ -348,6 +349,23 @@ param(
)
$version = '2410.1'
+#Class definition for vhdx cache
+class VhdxCacheUpdateItem {
+ [string]$Name
+ VhdxCacheUpdateItem([string]$Name) {
+ $this.Name = $Name
+ }
+}
+
+class VhdxCacheItem {
+ [string]$VhdxFileName = ""
+ [string]$WindowsSKU = ""
+ [string]$WindowsRelease = ""
+ [string]$WindowsVersion = ""
+ [string]$OptionalFeatures = ""
+ [VhdxCacheUpdateItem[]]$IncludedUpdates = @()
+}
+
#Check if Hyper-V feature is installed (requires only checks the module)
$osInfo = Get-WmiObject -Class Win32_OperatingSystem
$isServer = $osInfo.Caption -match 'server'
@@ -390,6 +408,7 @@ if (-not $PPKGFolder) { $PPKGFolder = "$FFUDevelopmentPath\PPKG" }
if (-not $UnattendFolder) { $UnattendFolder = "$FFUDevelopmentPath\Unattend" }
if (-not $AutopilotFolder) { $AutopilotFolder = "$FFUDevelopmentPath\Autopilot" }
if (-not $PEDriversFolder) { $PEDriversFolder = "$FFUDevelopmentPath\PEDrivers" }
+if (-not $VHDXCacheFolder) { $VHDXCacheFolder = "$FFUDevelopmentPath\VHDXCache" }
if (-not $installationType) { $installationType = if ($WindowsRelease.ToString().Length -eq 2) { 'Client' } else { 'Server' } }
if ($installationType -eq 'Server'){
#Map $WindowsRelease to $WindowsVersion for Windows Server
@@ -2928,11 +2947,12 @@ function Optimize-FFUCaptureDrive {
)
try {
WriteLog 'Mounting VHDX for volume optimization'
- Mount-VHD -Path $VhdxPath
+ $mountedDisk = Mount-VHD -Path $VhdxPath -Passthru | Get-Disk
+ $osPartition = $mountedDisk | Get-Partition | Where-Object { $_.GptType -eq "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}" }
WriteLog 'Defragmenting Windows partition...'
- Optimize-Volume -DriveLetter W -Defrag -NormalPriority
+ Optimize-Volume -DriveLetter $osPartition.DriveLetter -Defrag -NormalPriority
WriteLog 'Performing slab consolidation on Windows partition...'
- Optimize-Volume -DriveLetter W -SlabConsolidate -NormalPriority
+ Optimize-Volume -DriveLetter $osPartition.DriveLetter -SlabConsolidate -NormalPriority
WriteLog 'Dismounting VHDX'
Dismount-ScratchVhdx -VhdxPath $VhdxPath
WriteLog 'Mounting VHDX as read-only for optimization'
@@ -3880,6 +3900,17 @@ if (($WindowsArch -eq 'ARM64') -and ($UpdateLatestMSRT -eq $true)) {
$UpdateLatestMSRT = $false
WriteLog 'Windows Malicious Software Removal Tool is not available for the ARM64 architecture.'
}
+#If downloading ESD from MCT, hardcode WindowsVersion to 22H2 for Windows 10 and 24H2 for Windows 11
+#MCT media only provides 22H2 and 24H2 media
+#This prevents issues with VHDX Caching unecessarily and with searching for CUs
+if ($ISOPath -eq '') {
+ if ($WindowsRelease -eq '10') {
+ $WindowsVersion = '22H2'
+ }
+ if ($WindowsRelease -eq '11') {
+ $WindowsVersion = '24H2'
+ }
+}
###END PARAMETER VALIDATION
@@ -4155,34 +4186,6 @@ if ($InstallApps) {
#Create VHDX
try {
- if ($ISOPath) {
- $wimPath = Get-WimFromISO
- }
- else {
- $wimPath = Get-WindowsESD -WindowsRelease $WindowsRelease -WindowsArch $WindowsArch -WindowsLang $WindowsLang -MediaType $mediaType
- }
- #If index not specified by user, try and find based on WindowsSKU
- if (-not($index) -and ($WindowsSKU)) {
- $index = Get-Index -WindowsImagePath $wimPath -WindowsSKU $WindowsSKU
- }
-
- $vhdxDisk = New-ScratchVhdx -VhdxPath $VHDXPath -SizeBytes $disksize -LogicalSectorSizeBytes $LogicalSectorSizeBytes
-
- $systemPartitionDriveLetter = New-SystemPartition -VhdxDisk $vhdxDisk
-
- New-MSRPartition -VhdxDisk $vhdxDisk
-
- $osPartition = New-OSPartition -VhdxDisk $vhdxDisk -OSPartitionSize $OSPartitionSize -WimPath $WimPath -WimIndex $index
- $osPartitionDriveLetter = $osPartition[1].DriveLetter
- $WindowsPartition = $osPartitionDriveLetter + ":\"
-
- #$recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
- $recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
-
- WriteLog "All necessary partitions created."
-
- Add-BootFiles -OsPartitionDriveLetter $osPartitionDriveLetter -SystemPartitionDriveLetter $systemPartitionDriveLetter[1]
-
#Update latest Cumulative Update if both $UpdateLatestCU is $true and $UpdatePreviewCU is $false
#Changed to use MU Catalog instead of using Get-LatestWindowsKB
#The Windows release info page is updated later than the MU Catalog
@@ -4216,7 +4219,7 @@ try {
}
#Update Latest Preview Cumlative Update for Client OS only
- #will take Precendence over $UpdateLastestCU if both were set to $true
+ #will take Precendence over $UpdateLatestCU if both were set to $true
if ($UpdatePreviewCU -and $installationType -eq 'Client') {
Writelog "`$UpdatePreviewCU is set to true, checking for latest Preview CU"
$Name = """Cumulative update Preview for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
@@ -4267,57 +4270,164 @@ try {
# $KBFilePath = Save-KB -Name $Name -Path $KBPath
# WriteLog "Latest Security Platform Update saved to $KBPath\$KBFilePath"
# }
-
-
- #Add Windows packages
- if ($UpdateLatestCU -or $UpdateLatestNet -or $UpdatePreviewCU ) {
- try {
- WriteLog "Adding KBs to $WindowsPartition"
- WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
- # If WindowsRelease is 2016, we need to add the SSU first
- if ($WindowsRelease -eq 2016) {
- WriteLog "WindowsRelease is 2016, adding SSU first"
- WriteLog "Adding SSU to $WindowsPartition"
- # Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath -PreventPending | Out-Null
- # Commenting out -preventpending as it causes an issue with the SSU being applied
- # Seems to be because of the registry being mounted per dism.log
- Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath | Out-Null
- WriteLog "SSU added to $WindowsPartition"
- WriteLog "Removing $SSUFilePath"
- Remove-Item -Path $SSUFilePath -Force | Out-Null
- WriteLog 'SSU removed'
- WriteLog "Adding CU to $WindowsPartition"
+
+ #Search for cached VHDX and skip VHDX creation if there's a cached version
+ if ($AllowVHDXCaching) {
+ WriteLog 'AllowVHDXCaching is true, checking for cached VHDX file'
+ if (Test-Path -Path $VHDXCacheFolder) {
+ WriteLog "Found $VHDXCacheFolder"
+ $vhdxJsons = @(Get-ChildItem -File -Path $VHDXCacheFolder -Filter '*_config.json' | Sort-Object -Property CreationTime -Descending)
+ WriteLog "Found $($vhdxJsons.Count) cached VHDX files"
+ $downloadedKBs = @(Get-ChildItem -File -Path $KBPath)
+ #$jsonDeserializer = [System.Web.Script.Serialization.JavaScriptSerializer]::new()
+
+ foreach ($vhdxJson in $vhdxJsons) {
+ try {
+ WriteLog "Processing $($vhdxJson.FullName)"
+ #$vhdxCacheItem = $jsonDeserializer.Deserialize((Get-Content -Path $vhdxJson.FullName -Raw), [VhdxCacheItem])
+ $vhdxCacheItem = Get-Content -Path $vhdxJson.FullName -Raw | ConvertFrom-Json
+
+ if ((($vhdxCacheItem.WindowsSKU -ne $WindowsSKU) -or
+ ([string]::IsNullOrEmpty($vhdxCacheItem.WindowsSKU) -xor [string]::IsNullOrEmpty($WindowsSKU)))) {
+ WriteLog 'WindowsSKU mismatch, continuing'
+ continue
+ }
+
+ if ((($vhdxCacheItem.WindowsRelease -ne $WindowsRelease) -or
+ ([string]::IsNullOrEmpty($vhdxCacheItem.WindowsRelease) -xor [string]::IsNullOrEmpty($WindowsRelease)))) {
+ WriteLog 'WindowsRelease mismatch, continuing'
+ continue
+ }
+
+ if ((($vhdxCacheItem.WindowsVersion -ne $WindowsVersion) -or
+ ([string]::IsNullOrEmpty($vhdxCacheItem.WindowsVersion) -xor [string]::IsNullOrEmpty($WindowsVersion)))) {
+ Writelog 'WindowsVersion mismatch, continuing'
+ continue
+ }
+
+ if ((($vhdxCacheItem.OptionalFeatures -ne $OptionalFeatures) -or
+ ([string]::IsNullOrEmpty($vhdxCacheItem.OptionalFeatures) -xor [string]::IsNullOrEmpty($OptionalFeatures)))) {
+ WriteLog 'OptionalFeatures mismatch, continuing'
+ continue
+ }
+
+ if ((Compare-Object -ReferenceObject $downloadedKBs -DifferenceObject $vhdxCacheItem.IncludedUpdates -Property Name).Length -gt 0) {
+ (Compare-Object -ReferenceObject $downloadedKBs -DifferenceObject $vhdxCacheItem.IncludedUpdates -Property Name)
+ $downloadedKBs.Name
+ $vhdxCacheItem.IncludedUpdates.Name
+ WriteLog 'IncludedUpdates mismatch, continuing'
+ continue
+ }
+
+ WriteLog "Found cached VHDX file $vhdxCacheFolder\$($vhdxCacheItem.VhdxFileName) with matching parameters and included updates"
+ $cachedVHDXFileFound = $true
+ $cachedVHDXInfo = $vhdxCacheItem
+ break
+ } catch {
+ WriteLog "Reading $vhdxJson Failed with error $_"
+ }
}
- # Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath -PreventPending | Out-Null
- Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath | Out-Null
- WriteLog "KBs added to $WindowsPartition"
- WriteLog "Removing $KBPath"
- Remove-Item -Path $KBPath -Recurse -Force | Out-Null
- WriteLog "Clean Up the WinSxS Folder"
- WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
- Dism /Image:$WindowsPartition /Cleanup-Image /StartComponentCleanup /ResetBase | Out-Null
- WriteLog "Clean Up the WinSxS Folder completed"
}
- catch {
- Write-Host "Adding KB to VHDX failed with error $_"
- WriteLog "Adding KB to VHDX failed with error $_"
- if ($_.Exception.HResult -eq -2146498525){
- Write-Host 'Missing latest Servicing Stack Update'
- Write-Host 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
- Write-Host 'Recommended to use the latest media'
- WriteLog 'Missing latest Servicing Stack Update'
- WriteLog 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
- WriteLog 'Recommended to use the latest media'
- }
- throw $_
- }
}
+
+ if (-Not $cachedVHDXFileFound) {
+ if ($ISOPath) {
+ $wimPath = Get-WimFromISO
+ } else {
+ $wimPath = Get-WindowsESD -WindowsRelease $WindowsRelease -WindowsArch $WindowsArch -WindowsLang $WindowsLang -MediaType $mediaType
+ }
+ #If index not specified by user, try and find based on WindowsSKU
+ if (-not($index) -and ($WindowsSKU)) {
+ $index = Get-Index -WindowsImagePath $wimPath -WindowsSKU $WindowsSKU
+ }
+ $vhdxDisk = New-ScratchVhdx -VhdxPath $VHDXPath -SizeBytes $disksize -LogicalSectorSizeBytes $LogicalSectorSizeBytes
+
+ $systemPartitionDriveLetter = New-SystemPartition -VhdxDisk $vhdxDisk
+
+ New-MSRPartition -VhdxDisk $vhdxDisk
+
+ $osPartition = New-OSPartition -VhdxDisk $vhdxDisk -OSPartitionSize $OSPartitionSize -WimPath $WimPath -WimIndex $index
+ $osPartitionDriveLetter = $osPartition[1].DriveLetter
+ $WindowsPartition = $osPartitionDriveLetter + ':\'
+
+ #$recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
+ $recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
+
+ WriteLog 'All necessary partitions created.'
+
+ Add-BootFiles -OsPartitionDriveLetter $osPartitionDriveLetter -SystemPartitionDriveLetter $systemPartitionDriveLetter[1]
+
+ #Add Windows packages
+ if ($UpdateLatestCU -or $UpdateLatestNet -or $UpdatePreviewCU ) {
+ try {
+ WriteLog "Adding KBs to $WindowsPartition"
+ WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
+ # If WindowsRelease is 2016, we need to add the SSU first
+ if ($WindowsRelease -eq 2016) {
+ WriteLog 'WindowsRelease is 2016, adding SSU first'
+ WriteLog "Adding SSU to $WindowsPartition"
+ # Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath -PreventPending | Out-Null
+ # Commenting out -preventpending as it causes an issue with the SSU being applied
+ # Seems to be because of the registry being mounted per dism.log
+ Add-WindowsPackage -Path $WindowsPartition -PackagePath $SSUFilePath | Out-Null
+ WriteLog "SSU added to $WindowsPartition"
+ WriteLog "Removing $SSUFilePath"
+ Remove-Item -Path $SSUFilePath -Force | Out-Null
+ WriteLog 'SSU removed'
+ WriteLog "Adding CU to $WindowsPartition"
+ }
+ # Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath -PreventPending | Out-Null
+ Add-WindowsPackage -Path $WindowsPartition -PackagePath $KBPath | Out-Null
+ WriteLog "KBs added to $WindowsPartition"
+ if ($AllowVHDXCaching) {
+ $cachedVHDXInfo = [VhdxCacheItem]::new()
+ $includedUpdates = Get-ChildItem -Path $KBPath -File
+
+ foreach ($includedUpdate in $includedUpdates) {
+ $cachedVHDXInfo.IncludedUpdates += ([VhdxCacheUpdateItem]::new($includedUpdate.Name))
+ }
+ }
+ WriteLog "Removing $KBPath"
+ Remove-Item -Path $KBPath -Recurse -Force | Out-Null
+ WriteLog 'Clean Up the WinSxS Folder'
+ WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
+ Dism /Image:$WindowsPartition /Cleanup-Image /StartComponentCleanup /ResetBase | Out-Null
+ WriteLog 'Clean Up the WinSxS Folder completed'
+ } catch {
+ Write-Host "Adding KB to VHDX failed with error $_"
+ WriteLog "Adding KB to VHDX failed with error $_"
+ if ($_.Exception.HResult -eq -2146498525) {
+ Write-Host 'Missing latest Servicing Stack Update'
+ Write-Host 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
+ Write-Host 'Recommended to use the latest media'
+ WriteLog 'Missing latest Servicing Stack Update'
+ WriteLog 'Media likely older than 2023-09 for Windows Server 2022 (KB5030216), or 2021-08 for Windows Server 2019 (KB5005112)'
+ WriteLog 'Recommended to use the latest media'
+ }
+ throw $_
+ }
+ }
+
+ #Enable Windows Optional Features (e.g. .Net3, etc)
+ If ($OptionalFeatures) {
+ $Source = Join-Path (Split-Path $wimpath) 'sxs'
+ Enable-WindowsFeaturesByName -FeatureNames $OptionalFeatures -Source $Source
+ }
+
+ } else {
+ #Use cached vhdx file
+ WriteLog 'Using cached VHDX file to speed up build proces'
+ WriteLog "VHDX file is: $($cachedVHDXInfo.VhdxFileName)"
+
+ Robocopy.exe $($VHDXCacheFolder) $($VMPath) $($cachedVHDXInfo.VhdxFileName) /E /COPY:DAT /R:5 /W:5 /J
+ $VHDXPath = Join-Path $($VMPath) $($cachedVHDXInfo.VhdxFileName)
+
+ $vhdxDisk = Get-VHD -Path $VHDXPath | Mount-VHD -Passthru | Get-Disk
+ $osPartition = $vhdxDisk | Get-Partition | Where-Object { $_.GptType -eq '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}' }
+ $osPartitionDriveLetter = $osPartition.DriveLetter
+ $WindowsPartition = $osPartitionDriveLetter + ':\'
- #Enable Windows Optional Features (e.g. .Net3, etc)
- If ($OptionalFeatures) {
- $Source = Join-Path (Split-Path $wimpath) "sxs"
- Enable-WindowsFeaturesByName -FeatureNames $OptionalFeatures -Source $Source
}
#Set Product key
@@ -4330,16 +4440,18 @@ try {
Dismount-DiskImage -ImagePath $ISOPath | Out-null
WriteLog 'Done'
}
- else {
- #Remove ESD file
+ #If $wimPath is an esd file, remove it
+ If ($wimPath -match '.esd') {
+ WriteLog "Deleting $wimPath file"
Remove-Item -Path $wimPath -Force
+ WriteLog "$wimPath deleted"
}
If ($InstallApps) {
#Copy Unattend file so VM Boots into Audit Mode
WriteLog 'Copying unattend file to boot to audit mode'
- New-Item -Path "$($osPartitionDriveLetter):\Windows\Panther\unattend" -ItemType Directory | Out-Null
+ New-Item -Path "$($osPartitionDriveLetter):\Windows\Panther\unattend" -ItemType Directory -Force | Out-Null
if($WindowsArch -eq 'x64'){
Copy-Item -Path "$FFUDevelopmentPath\BuildFFUUnattend\unattend_x64.xml" -Destination "$($osPartitionDriveLetter):\Windows\Panther\Unattend\Unattend.xml" -Force | Out-Null
}
@@ -4347,6 +4459,35 @@ try {
Copy-Item -Path "$FFUDevelopmentPath\BuildFFUUnattend\unattend_arm64.xml" -Destination "$($osPartitionDriveLetter):\Windows\Panther\Unattend\Unattend.xml" -Force | Out-Null
}
WriteLog 'Copy completed'
+ }
+
+ if ($AllowVHDXCaching -and !$cachedVHDXFileFound) {
+ WriteLog 'New cached VHDX created'
+
+ WriteLog 'Defragmenting Windows partition...'
+ Optimize-Volume -DriveLetter $osPartition.DriveLetter -Defrag -NormalPriority
+ WriteLog 'Performing slab consolidation on Windows partition...'
+ Optimize-Volume -DriveLetter $osPartition.DriveLetter -SlabConsolidate -NormalPriority
+ WriteLog 'Dismounting VHDX'
+ Dismount-ScratchVhdx -VhdxPath $VHDXPath
+
+ WriteLog 'Copying to cache dir'
+
+ #Assuming there are now name collisons
+ Robocopy.exe $($VMPath) $($VHDXCacheFolder) $("$VMName.vhdx") /E /COPY:DAT /R:5 /W:5 /J
+
+ #Only create new instance if not created during patching
+ if ($null -eq $cachedVHDXInfo) {
+ $cachedVHDXInfo = [VhdxCacheItem]::new()
+ }
+ $cachedVHDXInfo.VhdxFileName = $("$VMName.vhdx")
+ $cachedVHDXInfo.WindowsSKU = $WindowsSKU
+ $cachedVHDXInfo.WindowsRelease = $WindowsRelease
+ $cachedVHDXInfo.WindowsVersion = $WindowsVersion
+ $cachedVHDXInfo.OptionalFeatures = $OptionalFeatures
+
+ $cachedVHDXInfo | ConvertTo-Json | Out-File -FilePath ("{0}\{1}_config.json" -f $($VHDXCacheFolder), $VMName)
+ } else {
Dismount-ScratchVhdx -VhdxPath $VHDXPath
}
}
@@ -4599,20 +4740,30 @@ If ($CleanupAppsISO) {
Writelog "Removing $AppsISO failed with error $_"
throw $_
}
-If ($CleanupDrivers){
+}
+If ($CleanupDrivers) {
try {
If (Test-Path -Path $Driversfolder\$Make) {
WriteLog "Removing $Driversfolder\$Make"
Remove-Item -Path $Driversfolder\$Make -Force -Recurse
- WriteLog "Removal complete"
+ WriteLog 'Removal complete'
}
- }
- catch {
+ } catch {
Writelog "Removing $Driversfolder\$Make failed with error $_"
throw $_
}
-
}
+if ($AllowVHDXCaching) {
+ try {
+ If (Test-Path -Path $KBPath) {
+ WriteLog "Removing $KBPath"
+ Remove-Item -Path $KBPath -Recurse -Force -ErrorAction SilentlyContinue
+ WriteLog 'Removal complete'
+ }
+ } catch {
+ Writelog "Removing $KBPath failed with error $_"
+ throw $_
+ }
}
#Clean up dirty.txt file
Remove-Item -Path .\dirty.txt -Force | out-null
From ed6a5fc7f14404aeeaf02590fca91c3efaeed5e3 Mon Sep 17 00:00:00 2001
From: HedgeComp <94635857+HedgeComp@users.noreply.github.com>
Date: Tue, 10 Sep 2024 14:59:01 -0400
Subject: [PATCH 35/50] Create SampleUnattend_64.xml
Sample for Unattend File.
---
FFUDevelopment/unattend/SampleUnattend_64.xml | 35 +++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 FFUDevelopment/unattend/SampleUnattend_64.xml
diff --git a/FFUDevelopment/unattend/SampleUnattend_64.xml b/FFUDevelopment/unattend/SampleUnattend_64.xml
new file mode 100644
index 0000000..0a85e64
--- /dev/null
+++ b/FFUDevelopment/unattend/SampleUnattend_64.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+ MYCOMPUTER
+ Eastern Standard Time
+
+
+
+
+
+ 1
+ cmd.exe /c date 09-07-2024
+ Set system date to a specific date
+
+
+
+
+
+
+ 0409:00000409
+ en-US
+ en-US
+ en-US
+
+
+
+ 3
+ true
+ false
+
+
+
+
From 1c103b2db738d24a6a7f22a743e5d7f8810564a3 Mon Sep 17 00:00:00 2001
From: HedgeComp <94635857+HedgeComp@users.noreply.github.com>
Date: Tue, 10 Sep 2024 14:59:18 -0400
Subject: [PATCH 36/50] Rename SampleUnattend_64.xml to SampleUnattend_x64.xml
---
.../unattend/{SampleUnattend_64.xml => SampleUnattend_x64.xml} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename FFUDevelopment/unattend/{SampleUnattend_64.xml => SampleUnattend_x64.xml} (100%)
diff --git a/FFUDevelopment/unattend/SampleUnattend_64.xml b/FFUDevelopment/unattend/SampleUnattend_x64.xml
similarity index 100%
rename from FFUDevelopment/unattend/SampleUnattend_64.xml
rename to FFUDevelopment/unattend/SampleUnattend_x64.xml
From 9287464eb8bb3e06d370a47d21ea040950d64bcc Mon Sep 17 00:00:00 2001
From: HedgeComp <94635857+HedgeComp@users.noreply.github.com>
Date: Tue, 10 Sep 2024 14:59:44 -0400
Subject: [PATCH 37/50] Update unattend_x64.xml
---
FFUDevelopment/unattend/unattend_x64.xml | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/FFUDevelopment/unattend/unattend_x64.xml b/FFUDevelopment/unattend/unattend_x64.xml
index 9ce5316..4023587 100644
--- a/FFUDevelopment/unattend/unattend_x64.xml
+++ b/FFUDevelopment/unattend/unattend_x64.xml
@@ -1,8 +1,10 @@
-
+
+
MyComputer
+
-
\ No newline at end of file
+
From c26034a89cbed30b20e4ec46cef459f30849baa0 Mon Sep 17 00:00:00 2001
From: HedgeComp <94635857+HedgeComp@users.noreply.github.com>
Date: Tue, 10 Sep 2024 15:00:07 -0400
Subject: [PATCH 38/50] Update unattend_arm64.xml
---
FFUDevelopment/unattend/unattend_arm64.xml | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/FFUDevelopment/unattend/unattend_arm64.xml b/FFUDevelopment/unattend/unattend_arm64.xml
index 98b1e7f..ae61894 100644
--- a/FFUDevelopment/unattend/unattend_arm64.xml
+++ b/FFUDevelopment/unattend/unattend_arm64.xml
@@ -1,8 +1,10 @@
-
+
+
MyComputer
+
-
\ No newline at end of file
+
From add11b003774009336ad6c573bba93c2d412173e Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Tue, 1 Oct 2024 04:07:11 +0200
Subject: [PATCH 39/50] Update BuildFFUVM.ps1
- Add $CustomFFUNameTemplate and the required code into BuildFFUVM.ps1
---
FFUDevelopment/BuildFFUVM.ps1 | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 1148ae2..420d0ab 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -172,6 +172,9 @@ Model of the device to download drivers. This is required if Make is set.
.PARAMETER AppsScriptVariables
When passed a hashtable, the script will alter the $FFUDevelopmentPath\Apps\InstallAppsandSysprep.cmd file to set variables with the hashtable keys as variable names and the hashtable values their content.
+.PARAMETER CustomFFUNameTemplate
+Sets a custom FFU output name with placeholders. Allowed placeholders are: {Name}, {DisplayVersion}, {SKU}, {BuildDate}, {yyyy}, {MM}, {dd}, {H}, {hh}, {mm}, {tt}
+
.EXAMPLE
Command line for most people who want to download the latest Windows 11 Pro x64 media in English (US) with the latest Windows Cumulative Update, .NET Framework, Defender platform and definition updates, Edge, OneDrive, and Office/M365 Apps. It will also copy drivers to the FFU. This can take about 40 minutes to create the FFU due to the time it takes to download and install the updates.
.\BuildFFUVM.ps1 -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -VMSwitchName 'Name of your VM Switch in Hyper-V' -VMHostIPAddress 'Your IP Address' -CreateCaptureMedia $true -CreateDeploymentMedia $true -BuildUSBDrive $true -UpdateLatestCU $true -UpdateLatestNet $true -UpdateLatestDefender $true -UpdateEdge $true -UpdateOneDrive $true -verbose
@@ -237,8 +240,9 @@ param(
[string]$VMLocation,
[string]$FFUPrefix = '_FFU',
[string]$FFUCaptureLocation,
- [String]$ShareName = "FFUCaptureShare",
+ [string]$ShareName = "FFUCaptureShare",
[string]$Username = "ffu_user",
+ [string]$CustomFFUNameTemplate,
[Parameter(Mandatory = $false)]
[string]$VMHostIPAddress,
[bool]$CreateCaptureMedia = $true,
@@ -2807,6 +2811,11 @@ Function Set-CaptureFFU {
$ScriptContent = Get-Content -Path $CaptureFFUScriptPath
$UpdatedContent = $ScriptContent -replace '(net use).*', ("$SharePath")
WriteLog 'Updating share command in CaptureFFU.ps1 script with new share information'
+ $UpdatedContent = $UpdatedContent -replace '^\$CustomFFUNameTemplate \= .*#Custom naming', "#Custom naming placeholder"
+ if (![string]::IsNullOrEmpty($CustomFFUNameTemplate)) {
+ $UpdatedContent = $UpdatedContent -replace '#Custom naming placeholder', ("`$CustomFFUNameTemplate = '$CustomFFUNameTemplate' #Custom naming")
+ WriteLog 'Updating share command in CaptureFFU.ps1 script with new ffu name template information'
+ }
Set-Content -Path $CaptureFFUScriptPath -Value $UpdatedContent
WriteLog 'Update complete'
}
From a59210c559032457ae33aac857b1d6488f2f92b1 Mon Sep 17 00:00:00 2001
From: JonasKloseBW
Date: Tue, 1 Oct 2024 04:09:34 +0200
Subject: [PATCH 40/50] Update CaptureFFU.ps1
- Add $CustomFFUNameTemplate support in Update CaptureFFU.ps1
---
.../WinPECaptureFFUFiles/CaptureFFU.ps1 | 32 ++++++++++++++-----
1 file changed, 24 insertions(+), 8 deletions(-)
diff --git a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1 b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
index 1a8fdd7..17ff85f 100644
--- a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
+++ b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
@@ -1,5 +1,6 @@
#Modify the net use W: \\192.168.1.158\FFUCaptureShare /user:ffu_user ddb1f077-3eed-433c-b4d9-7b8cd54ce727
net use W: \\192.168.1.158\FFUCaptureShare /user:ffu_user ddb1f077-3eed-433c-b4d9-7b8cd54ce727
+#Custom naming placeholder
$AssignDriveLetter = 'x:\AssignDriveLetter.txt'
Start-Process -FilePath diskpart.exe -ArgumentList "/S $AssignDriveLetter" -Wait -ErrorAction Stop | Out-Null
@@ -59,16 +60,31 @@ else {
#If Office is installed, modify the file name of the FFU
#$Office = Get-childitem -Path 'M:\Program Files\Microsoft Office' -ErrorAction SilentlyContinue | Out-Null
$Office = Get-childitem -Path 'M:\Program Files\Microsoft Office' -ErrorAction SilentlyContinue
-if($Office){
+if ($Office) {
$ffuFilePath = "W:\$Name`_$DisplayVersion`_$SKU`_Office`_$BuildDate.ffu"
- $dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default"
-
-
-}
-else{
+} else {
$ffuFilePath = "W:\$Name`_$DisplayVersion`_$SKU`_Apps`_$BuildDate.ffu"
- $dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default"
-
+}
+$dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default"
+
+if (![string]::IsNullOrEmpty($CustomFFUNameTemplate)) {
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{Name}", $Name
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{DisplayVersion}", $DisplayVersion
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{SKU}", $SKU
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{BuildDate}", $BuildDate
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{yyyy}", (Get-Date -UFormat "%Y")
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{MM}", (Get-Date -UFormat "%m")
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{dd}", (Get-Date -UFormat "%d")
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{HH}", (Get-Date -UFormat "%H")
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{hh}", (Get-Date -UFormat "%I")
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{mm}", (Get-Date -UFormat "%M")
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{tt}", (Get-Date -UFormat "%p")
+
+ if (!$CustomFFUNameTemplate.EndsWith(".ffu")) {
+ $CustomFFUNameTemplate += ".ffu"
+ }
+
+ $dismArgs = "/capture-ffu /imagefile=W:\$CustomFFUNameTemplate /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default"
}
#Unload Registry
From db0fbfaaf48360b74721ee8ef8c6a637136f1e90 Mon Sep 17 00:00:00 2001
From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com>
Date: Mon, 2 Dec 2024 18:08:30 -0800
Subject: [PATCH 41/50] Refactor CaptureFFU.ps1 to use $WindowsVersion and
$WindowsRelease for improved clarity and consistency in FFU naming
---
.../WinPECaptureFFUFiles/CaptureFFU.ps1 | 65 +++++++++----------
1 file changed, 31 insertions(+), 34 deletions(-)
diff --git a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1 b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
index 17ff85f..986a417 100644
--- a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
+++ b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
@@ -13,7 +13,7 @@ reg load "HKLM\FFU" $Software
$SKU = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'EditionID'
[int]$CurrentBuild = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'CurrentBuild'
if ($CurrentBuild -notin 14393, 17763) {
- $DisplayVersion = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'DisplayVersion'
+ $WindowsVersion = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'DisplayVersion'
}
$InstallationType = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'InstallationType'
$BuildDate = Get-Date -uformat %b%Y
@@ -38,60 +38,58 @@ $SKU = switch ($SKU) {
if ($InstallationType -eq "Client") {
if ($CurrentBuild -ge 22000) {
- $Name = 'Win11'
+ $WindowsRelease = 'Win11'
}
else {
- $Name = 'Win10'
+ $WindowsRelease = 'Win10'
}
}
else {
- $Name = switch ($CurrentBuild) {
+ $WindowsRelease = switch ($CurrentBuild) {
26100 { '2025' }
20348 { '2022' }
17763 { '2019' }
14393 { '2016' }
- Default { $DisplayVersion }
+ Default { $WindowsVersion }
}
if ($InstallationType -eq "Server Core") {
$SKU += "_Core"
}
}
-#If Office is installed, modify the file name of the FFU
-#$Office = Get-childitem -Path 'M:\Program Files\Microsoft Office' -ErrorAction SilentlyContinue | Out-Null
-$Office = Get-childitem -Path 'M:\Program Files\Microsoft Office' -ErrorAction SilentlyContinue
-if ($Office) {
- $ffuFilePath = "W:\$Name`_$DisplayVersion`_$SKU`_Office`_$BuildDate.ffu"
-} else {
- $ffuFilePath = "W:\$Name`_$DisplayVersion`_$SKU`_Apps`_$BuildDate.ffu"
-}
-$dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default"
-
-if (![string]::IsNullOrEmpty($CustomFFUNameTemplate)) {
- $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{Name}", $Name
- $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{DisplayVersion}", $DisplayVersion
- $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{SKU}", $SKU
- $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{BuildDate}", $BuildDate
- $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{yyyy}", (Get-Date -UFormat "%Y")
- $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{MM}", (Get-Date -UFormat "%m")
- $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{dd}", (Get-Date -UFormat "%d")
- $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{HH}", (Get-Date -UFormat "%H")
- $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{hh}", (Get-Date -UFormat "%I")
- $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{mm}", (Get-Date -UFormat "%M")
- $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace "{tt}", (Get-Date -UFormat "%p")
-
- if (!$CustomFFUNameTemplate.EndsWith(".ffu")) {
- $CustomFFUNameTemplate += ".ffu"
+if ($CustomFFUNameTemplate) {
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{WindowsRelease}', $WindowsRelease
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{WindowsVersion}', $WindowsVersion
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{SKU}', $SKU
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{BuildDate}', $BuildDate
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{yyyy}', (Get-Date -UFormat '%Y')
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -creplace '{MM}', (Get-Date -UFormat '%m')
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{dd}', (Get-Date -UFormat '%d')
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -creplace '{HH}', (Get-Date -UFormat '%H')
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -creplace '{hh}', (Get-Date -UFormat '%I')
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -creplace '{mm}', (Get-Date -UFormat '%M')
+ $CustomFFUNameTemplate = $CustomFFUNameTemplate -replace '{tt}', (Get-Date -UFormat '%p')
+ if($CustomFFUNameTemplate -notlike '*.ffu') {
+ $CustomFFUNameTemplate += '.ffu'
}
-
- $dismArgs = "/capture-ffu /imagefile=W:\$CustomFFUNameTemplate /capturedrive=\\.\PhysicalDrive0 /name:$Name$DisplayVersion$SKU /Compress:Default"
+ $dismArgs = "/capture-ffu /imagefile=W:\$CustomFFUNameTemplate /capturedrive=\\.\PhysicalDrive0 /name:$WindowsRelease$WindowsVersion$SKU /Compress:Default"
+} else {
+ #If Office is installed, modify the file name of the FFU
+ #$Office = Get-childitem -Path 'M:\Program Files\Microsoft Office' -ErrorAction SilentlyContinue | Out-Null
+ $Office = Get-ChildItem -Path 'M:\Program Files\Microsoft Office' -ErrorAction SilentlyContinue
+ if ($Office) {
+ $ffuFilePath = "W:\$WindowsRelease`_$WindowsVersion`_$SKU`_Office`_$BuildDate.ffu"
+ } else {
+ $ffuFilePath = "W:\$WindowsRelease`_$WindowsVersion`_$SKU`_Apps`_$BuildDate.ffu"
+ }
+ $dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$WindowsRelease$WindowsVersion$SKU /Compress:Default"
}
#Unload Registry
Set-Location X:\
Remove-Variable SKU
if ($CurrentBuild -notin 14393, 17763) {
- Remove-Variable DisplayVersion
+ Remove-Variable WindowsVersion
}
Remove-Variable CurrentBuild
Remove-Variable Office
@@ -102,5 +100,4 @@ Start-sleep 60
Start-Process -FilePath dism.exe -ArgumentList $dismArgs -Wait -PassThru -ErrorAction Stop | Out-Null
#Copy DISM log to Host
xcopy X:\Windows\logs\dism\dism.log W:\ /Y | Out-Null
-
wpeutil Shutdown
From 6abc6f9d1a960f290fb0b14a042f8bddbccf2f07 Mon Sep 17 00:00:00 2001
From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com>
Date: Mon, 2 Dec 2024 18:34:58 -0800
Subject: [PATCH 42/50] Fixed an error with removing the office variable when
capturing the FFU
---
FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1 | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1 b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
index 986a417..b51ba5a 100644
--- a/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
+++ b/FFUDevelopment/WinPECaptureFFUFiles/CaptureFFU.ps1
@@ -88,11 +88,13 @@ if ($CustomFFUNameTemplate) {
#Unload Registry
Set-Location X:\
Remove-Variable SKU
+Remove-Variable CurrentBuild
if ($CurrentBuild -notin 14393, 17763) {
Remove-Variable WindowsVersion
}
-Remove-Variable CurrentBuild
-Remove-Variable Office
+if($Office) {
+ Remove-Variable Office
+}
reg unload "HKLM\FFU"
#This prevents Critical Process Died errors you can have during deployment of the FFU - may not happen during capture from WinPE, but adding here to be consistent with VHDX capture
Write-Host "Sleeping for 60 seconds to allow registry to unload prior to capture"
From a2c2b690264fb784edd7c04429faff635b2faa37 Mon Sep 17 00:00:00 2001
From: Mike Kelly <167896478+MKellyCBSD@users.noreply.github.com>
Date: Tue, 3 Dec 2024 15:09:00 -0500
Subject: [PATCH 43/50] Update USBImagingToolCreator.ps1
---
FFUDevelopment/USBImagingToolCreator.ps1 | 317 ++++++++++++-----------
1 file changed, 162 insertions(+), 155 deletions(-)
diff --git a/FFUDevelopment/USBImagingToolCreator.ps1 b/FFUDevelopment/USBImagingToolCreator.ps1
index c5c28ab..e5afece 100644
--- a/FFUDevelopment/USBImagingToolCreator.ps1
+++ b/FFUDevelopment/USBImagingToolCreator.ps1
@@ -1,20 +1,35 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $True, Position = 0)]
- [io.fileinfo] $DeployISOPath,
+ $DeployISOPath,
[Switch]$DisableAutoPlay
)
+$Host.UI.RawUI.WindowTitle = 'Imaging Tool USB Creator'
+if($DeployISOPath){
+$DevelopmentPath = $DeployISOPath | Split-Path
+$ImagesPath = "$DevelopmentPath\FFU"
function WriteLog($LogText) {
- $LogFileName = '\Script.log'
- $LogFile = $DevelopmentPath + $LogFilename
+$LogFileName = '\Script.log'
+$LogFile = $DevelopmentPath + $LogFilename
Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText" -Force -ErrorAction SilentlyContinue
Write-Verbose $LogText
}
-function Get-USBDrive {
- $USBDrives = (Get-WmiObject -Class Win32_DiskDrive -Filter "MediaType='Removable Media'")
- If ($USBDrives -and ($null -eq $USBDrives.count)) {
+function Write-ProgressLog {
+ param(
+ [string]$Activity,
+ [string]$Status
+ )
+ Write-Progress -Activity $Activity -Status $Status -PercentComplete (($currentStep / $totalSteps) * 100)
+ WriteLog $Status
+ $script:currentStep++
+
+ }
+Function Get-RemovableDrive {
+writelog "Get information for all removable drives"
+$USBDrives = Get-WmiObject Win32_DiskDrive | Where-Object {$_.MediaType -eq "Removable media"}
+If($USBDrives -and ($null -eq $USBDrives.count)) {
$USBDrivesCount = 1
} else {
$USBDrivesCount = $USBDrives.Count
@@ -24,31 +39,33 @@ function Get-USBDrive {
if ($null -eq $USBDrives) {
WriteLog "No removable USB drive found. Exiting"
Write-Error "No removable USB drive found. Exiting"
+ Pause
exit 1
}
- return $USBDrives, $USBDrivesCount
-}
-
-function Build-DeploymentUSB {
- param(
- [Array]$Drives
- )
- writelog "Checking if ffu files are present in the ffu folder"
- $Images = Get-ChildItem -Path $FFUPath -Filter "*.ffu" -File -Recurse
- writelog "Checking if drivers are present in the drivers folder"
- $Drivers = Get-ChildItem -Path $DriversPath -Recurse
- $DrivesCount = $Drives.Count
- Writelog "Creating partitions..."
- foreach ($USBDrive in $Drives) {
- $DriveNumber = $USBDrive.DeviceID.Replace("\\.\PHYSICALDRIVE", "")
- $Model = $USBDrive.model
- $ScriptBlock = {
+ return $USBDrives, $USBDrivesCount
+ }
+
+Function Build-DeploymentUSB{
+ param(
+ [Array]$Drives
+ )
+ writelog "Creating list of FFU image files"
+ $Images = Get-ChildItem -Path $ImagesPath -Filter "*.ffu" -File -Recurse
+ writelog "Checking if drivers are present in the drivers folder"
+ $Drivers = Get-ChildItem -Path $DriversPath -Recurse
+ $DrivesCount = $Drives.Count
+ Write-ProgressLog "Create Imaging Tool" "Creating partitions..."
+ writelog "Create job to partition each usb drive"
+ foreach ($USBDrive in $Drives) {
+ $DriveNumber = $USBDrive.DeviceID.Replace("\\.\PHYSICALDRIVE", "")
+ $Model = $USBDrive.model
+ $ScriptBlock = {
param($DriveNumber)
Clear-Disk -Number $DriveNumber -RemoveData -RemoveOEM -Confirm:$false
$Disk = Get-Disk -Number $DriveNumber
$PartitionStyle = $Disk.PartitionStyle
- if ($PartitionStyle -ne 'MBR') {
- $Disk | Set-Disk -PartitionStyle MBR
+ if($PartitionStyle -ne 'MBR'){
+ $Disk | Set-Disk -PartitionStyle MBR
}
$BootPartition = New-Partition -DiskNumber $DriveNumber -Size 2GB -IsActive -AssignDriveLetter
$DeployPartition = New-Partition -DiskNumber $DriveNumber -UseMaximumSize -AssignDriveLetter
@@ -60,178 +77,168 @@ function Build-DeploymentUSB {
}
writelog "Wait for partitioning jobs to complete"
Get-Job | Wait-Job | Out-Null
- if ($DrivesCount -gt 1) {
- writelog "Get file system information for all drives"
- $Partitions = Get-Partition | Get-Volume
+ if($DrivesCount -gt 1){
+ writelog "Get file system information for all drives"
+ $Partitions = Get-Partition | Get-Volume
} else {
- writelog "Get file system information for drive number $DiskNumber"
- $Partitions = Get-Partition -DiskNumber $DriveNumber | Get-Volume
+ writelog "Get file system information for drive number $DiskNumber"
+ $Partitions = Get-Partition -DiskNumber $DriveNumber | Get-Volume
}
- writelog "Get drive letter for all volumes labeled:BOOT"
- $BootDrives = ($Partitions | Where-Object { $_.FileSystemLabel -eq "BOOT" }).DriveLetter
- writelog "Get drive letter for all volumes labeled:Deploy"
- $DeployDrives = ($Partitions | Where-Object { $_.FileSystemLabel -eq "Deploy" }).DriveLetter
- writelog "Mount Deployment .iso image"
- $ISOMountPoint = (Mount-DiskImage -ImagePath "$DeployISOPath" -PassThru | Get-Volume).DriveLetter + ":\"
- writelog "Copying boot files to all drives labeled BOOT concurrently"
- foreach ($Drive in $BootDrives) {
- $Destination = $Drive + ":\"
- $jobScriptBlock = {
- param (
- [string]$SFolder,
- [string]$DFolder
- )
- Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
- }
- WriteLog "Start job to copy all boot files to $Destination"
- Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $ISOMountPoint, $Destination | Out-Null
+writelog "Get drive letter for all volumes labeled:BOOT"
+$BootDrives = ($Partitions | Where-Object { $_.FileSystemLabel -eq "BOOT"}).DriveLetter
+writelog "Get drive letter for all volumes labeled:Deploy"
+$DeployDrives = ($Partitions | Where-Object { $_.FileSystemLabel -eq "Deploy"}).DriveLetter
+writelog "Mount Deployment .iso image"
+$ISOMountPoint = (Mount-DiskImage -ImagePath "$DeployISOPath" -PassThru | Get-Volume).DriveLetter + ":\"
+writelog "Copying boot files to all drives labeled BOOT concurrently"
+foreach ($Drive in $BootDrives) {
+$Destination = $Drive + ":\"
+ $jobScriptBlock = {
+ param (
+ [string]$SFolder,
+ [string]$DFolder
+ )
+ Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
+ }
+ WriteLog "Start job to copy all boot files to $Destination"
+ Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $ISOMountPoint, $Destination | Out-Null
+}
+if($Images){
+writelog "Copying FFU image files to all drives labeled deploy concurrently"
+foreach ($Drive in $DeployDrives) {
+$Destination = $Drive + ":\"
+ $jobScriptBlock = {
+ param (
+ [string]$SFolder,
+ [string]$DFolder
+ )
+ New-Item -Path $DFolder -ItemType Directory -Force -Confirm: $false | Out-Null
+ Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
}
- if ($Images) {
- writelog "Copying FFU image files to all drives labeled deploy concurrently"
- foreach ($Drive in $DeployDrives) {
- $Destination = $Drive + ":\"
- $jobScriptBlock = {
- param (
- [string]$SFolder,
- [string]$DFolder
- )
- Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
- }
- WriteLog "Start job to copy all FFU files to $Destination"
- Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $FFUPath, $Destination | Out-Null
+ WriteLog "Start job to copy all FFU files to $Destination"
+ Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $ImagesPath, $Destination | Out-Null
+ }
+}
+if(!($Images)){
+ foreach ($Drive in $DeployDrives) {
+ WriteLog "Create images directory"
+ $drivepath = $Drive + ":\"
+ New-Item -Path "$drivepath" -Name Images -ItemType Directory -Force -Confirm: $false | Out-Null
}
+}
+if($Drivers){
+writelog "Copying driver files to all drives labeled deploy concurrently"
+foreach ($Drive in $DeployDrives) {
+$Destination = $Drive + ":\Drivers"
+ $jobScriptBlock = {
+ param (
+ [string]$SFolder,
+ [string]$DFolder
+ )
+ New-Item -Path $DFolder -ItemType Directory -Force -Confirm: $false | Out-Null
+ Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
}
- if ($Drivers) {
- writelog "Copying driver files to all drives labeled deploy concurrently"
- foreach ($Drive in $DeployDrives) {
- $Destination = $Drive + ":\Drivers"
- $jobScriptBlock = {
- param (
- [string]$SFolder,
- [string]$DFolder
- )
- New-Item -Path $DFolder -ItemType Directory -Force -Confirm: $false | Out-Null
- Robocopy $SFolder $DFolder /E /COPYALL /R:5 /W:5 /J
- }
- WriteLog "Start job to copy all drivers to $Destination"
- Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $DriversPath, $Destination | Out-Null
+ WriteLog "Start job to copy all drivers to $Destination"
+ Start-Job -ScriptBlock $jobScriptBlock -ArgumentList $DriversPath, $Destination | Out-Null
+}
+}
+if(!($Drivers)){
+ foreach ($Drive in $DeployDrives) {
+ WriteLog "Create drivers directory"
+ $drivepath = $Drive + ":\"
+ New-Item -Path "$drivepath" -Name Drivers -ItemType Directory -Force -Confirm: $false | Out-Null
}
- }
- if (!($Drivers)) {
- foreach ($Drive in $DeployDrives) {
- WriteLog "Create drivers directory"
- $drivepath = $Drive + ":\"
- New-Item -Path "$drivepath" -Name Drivers -ItemType Directory -Force -Confirm: $false | Out-Null
- }
- }
- if ($DrivesCount -gt 1) {
- Writelog "Building $DrivesCount drives concurrently...Please be patient..."
- } else {
- Writelog "Building the imaging tool on $model...Please be patient..."
- }
- Get-Job | Wait-Job | Out-Null
+}
+if($DrivesCount -gt 1){
+Write-ProgressLog "Create Imaging Tool" "Building $DrivesCount drives concurrently...Please be patient..."
+} else {
+Write-ProgressLog "Create Imaging Tool" "Building the imaging tool on $model...Please be patient..."
+}
+Get-Job | Wait-Job | Out-Null
- Dismount-DiskImage -ImagePath $DeployISOPath | Out-Null
- Writelog "Drive creation jobs completed..."
+Dismount-DiskImage -ImagePath $DeployISOPath | Out-Null
+Write-ProgressLog "Create Imaging Tool" "Drive creation jobs completed..."
}
-function New-DeploymentUSB {
+Function New-DeploymentUSB {
param(
[Array]$Drives,
[int]$Count,
[String]$FFUPath = "$DevelopmentPath\FFU",
[String]$DriversPath = "$DevelopmentPath\Drivers"
-
+
)
-
+
$Drivelist = @()
writelog "Creating a USB drive selection list"
- for ($i = 0; $i -le $Count - 1; $i++) {
+ for($i=0;$i -le $Count -1;$i++){
$DriveModel = $Drives[$i].Model
- $DriveSize = [math]::round($Drives[$i].size / 1GB, 2)
+ $DriveSize = [math]::round($Drives[$i].size/1GB, 2)
$DiskNumber = $Drives[$i].DeviceID.Replace("\\.\PHYSICALDRIVE", "")
- $Properties = [ordered]@{Number = $i + 1 ; DriveNumber = $DiskNumber ; DriveModel = $driveModel ; 'Size (GB)' = $DriveSize }
-
+ $Properties = [ordered]@{Number = $i + 1 ; DriveNumber = $DiskNumber ; DriveModel = $driveModel ; 'Size (GB)' = $DriveSize}
+
$Drivelist += New-Object PSObject -Property $Properties
- }
- if ($Count -gt 1) {
- $Last = $Count + 1
+ }
+ if($Count -gt 1){
+ $Last = $Count+1
$Drivelist += New-Object -TypeName PSObject -Property @{ Number = "$last"; DriveModel = "Select this option to use all ($count) inserted USB Drives" }
- }
- $Drivelist | Format-Table -AutoSize -Property Number, DriveModel , 'Size (GB)'
- do {
+ }
+ $Drivelist | Format-Table -AutoSize -Property Number, DriveModel , 'Size (GB)'
+ do {
try {
$var = $true
$DriveSelected = Read-Host 'Enter the drive number to apply the .iso to'
- $DriveSelected = ($DriveSelected -as [int]) - 1
- if ($Last) {
- writelog "All drives selected"
+ $DriveSelected = ($DriveSelected -as [int]) -1
+ writelog "Drive $DriveSelected selected"
}
- else {
- writelog "Drive $DriveSelected selected"
- }
- }
+
catch {
Write-Host 'Input was not in correct format. Please enter a valid FFU number'
$var = $false
}
- } until (($DriveSelected -le $Count - 1 -or $last) -and $var)
-
- $DisableAutoPlayCurrentSetting = (Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name DisableAutoplay).DisableAutoplay
- if ($DisableAutoPlay -and $DisableAutoPlayCurrentSetting -ne 1) {
- writelog "Disable autoPlay current setting is $DisableAutoPlayCurrentSetting"
- WriteLog "Setting the registry key to disable autoplay for all drives"
- Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name "DisableAutoplay" -Value 1 -Type DWORD
+ } until (($DriveSelected -le $Count -1 -or $last) -and $var)
+ if($DisableAutoPlay){
+ WriteLog "Setting the registry key to disable autoplay for all drives"
+ Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name "DisableAutoplay" -Value 1 -Type DWORD
}
WriteLog "Closing all MMC windows to prevent drive lock errors"
Stop-Process -Name mmc -ErrorAction SilentlyContinue
WriteLog "Closing all Diskpart windows to prevent drive lock errors"
Stop-Process -Name diskpart -ErrorAction SilentlyContinue
$Selection = $Drivelist[$DriveSelected].Number
-
- if ($Selection -eq $last) {
- Read-Host -Prompt "ALL DRIVES SELECTED! WILL ERASE ALL CURRENTLY CONNECTED USB DRIVES!! Press ENTER to continue"
- Build-DeploymentUSB -Drives $Drives
+ $totalSteps = 5
+ if($Selection -eq $last){
+ Read-Host -Prompt "ALL DRIVES SELECTED! WILL ERASE ALL CURRENTLY CONNECTED USB DRIVES!! Press ENTER to continue"
+ Build-DeploymentUSB -Drives $Drives
} else {
- Read-Host -Prompt "Drive number $Selection was selected. Press ENTER to continue"
- Build-DeploymentUSB -Drives $Drives[$DriveSelected]
+ Read-Host -Prompt "Drive number $Selection was selected. Press ENTER to continue"
+ Build-DeploymentUSB -Drives $Drives[$DriveSelected]
}
WriteLog "Setting the registry key to re-enable autoplay for all drives"
- if ($DisableAutoPlay) {
- Writelog "Setting disable autoplay setting back to $DisableAutoPlayCurrentSetting"
- Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name "DisableAutoplay" -Value $DisableAutoPlayCurrentSetting -Type DWORD
+ if($DisableAutoPlay){
+ Write-ProgressLog "Create Imaging Tool" "Enabling Autoplay"
+ Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers" -Name "DisableAutoplay" -Value 0 -Type DWORD
}
- Writelog "Completed!"
+ Write-ProgressLog "Create Imaging Tool" "Completed!"
+}
+#Get USB Drive and create log file
+if(Test-Path "$DevelopmentPath\Script.log"){
+Remove-Item -Path "$DevelopmentPath\Script.log" -Force -Confirm:$false
+New-item -Path $DevelopmentPath -Name 'Script.log' -ItemType "file" -Force | Out-Null
}
+WriteLog 'Begin Logging'
+WriteLog 'Getting USB drive information and usb drive count'
+$USBDrives,$USBDrivesCount = Get-RemovableDrive
+WriteLog 'Setting first step for percentage progress bar'
+$currentStep = 1
+New-DeploymentUSB -Drives $USBDrives -Count $USBDrivesCount
-$Host.UI.RawUI.WindowTitle = 'USB Imaging Tool Creator'
+read-host -Prompt "USB drive creation complete. Press ENTER to exit"
-# Check if path is relative
-if (-not [System.IO.Path]::IsPathRooted($DeployISOPath)) {
- # Path is relative, Build full path
- [io.fileinfo] $DeployISOPath = (Resolve-Path $DeployISOPath).Path
- Write-Verbose "Path to ISO was relative. Attempting to resolve at: $($DeployISOPath.FullName)"
-}
-
-if ($DeployISOPath.Exists) {
- $DevelopmentPath = $DeployISOPath.Directory.FullName
-
- #Get USB Drive and create log file
- if (Test-Path "$DevelopmentPath\Script.log") {
- Remove-Item -Path "$DevelopmentPath\Script.log" -Force -Confirm:$false
- New-item -Path $DevelopmentPath -Name 'Script.log' -ItemType "file" -Force | Out-Null
- }
-
- WriteLog 'Begin Logging'
- WriteLog 'Getting USB drive information and usb drive count'
-
- $USBDrives, $USBDrivesCount = Get-USBDrive
-
- New-DeploymentUSB -Drives $USBDrives -Count $USBDrivesCount
-
- Read-Host -Prompt "USB drive creation complete. Press ENTER to exit"
- Exit
+Exit
} else {
- Write-Error "Unable to locate ISO file at: $($DeployISOPath.FullName)"
+Write-Host "No .ISO file selected..."
+read-host "Press ENTER to Exit..."
+Exit
}
From 97954f59c355840be931e71d2a8d66a4f5c585b6 Mon Sep 17 00:00:00 2001
From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com>
Date: Tue, 3 Dec 2024 16:55:40 -0800
Subject: [PATCH 44/50] - Added Windows 2025/24H2 mapping
---
FFUDevelopment/BuildFFUVM.ps1 | 1 +
1 file changed, 1 insertion(+)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 58c5cc4..960dc98 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -420,6 +420,7 @@ if ($installationType -eq 'Server'){
2016 { $WindowsVersion = '1607' }
2019 { $WindowsVersion = '1809' }
2022 { $WindowsVersion = '21H2' }
+ 2025 { $WindowsVersion = '24H2' }
}
}
From d7a697d68d52d2ff05c7202dd687ae6b9a08e672 Mon Sep 17 00:00:00 2001
From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com>
Date: Mon, 9 Dec 2024 14:59:02 -0800
Subject: [PATCH 45/50] Misc Fixes
- Added Get-Childprocesses function to return child processes of parent process
- Added a new -Wait boolean parameter to Invoke-Process function. This is to control whether Invoke-Process should wait in order to track stdout and stderr output. This is needed for processes that may hang, waiting for user input and there isn't a way to bypass (some Intel drivers provided by Dell leave dialog windows even when running silently)
-Invoke-Process now returns process information (returns $cmd). This allows for process tracking when calling the function.
- Since Invoke-Process now returns the process information, also needed to add Out-Null to the majority of the Invoke-Process references to prevent Invoke-Process from writing to the terminal
- Refactored a lot of the Get-DellDrivers function due to inconsistencies with how driver extraction behaves between client and server devices. For client, /s /e seemed to work fine, but for server it would only extract the driver installer content and other dell related files, rather than the driver files themselves. We have since switched to using /s /drivers= which will extract the driver content. Not all drivers support /drivers= and may output some information to the terminal that looks like help documentation. If a driver doesn't support /drivers, the script falls back to using /s /e to do the extraction. If this doesn't work for you, you can always provide your own drivers that you manually download from Dell's website.
- Updated Malicious Software Removal Tool (MSRT) code to handle Windows Server
---
FFUDevelopment/BuildFFUVM.ps1 | 186 ++++++++++++++++++++++++----------
1 file changed, 132 insertions(+), 54 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 960dc98..1e42dd5 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -475,6 +475,16 @@ function LogVariableValues {
WriteLog 'End logging variables'
}
+function Get-ChildProcesses($parentId) {
+ $result = @()
+ $children = Get-CimInstance Win32_Process -Filter "ParentProcessId = $parentId"
+ foreach ($child in $children) {
+ $result += $child
+ $result += Get-ChildProcesses $child.ProcessId
+ }
+ return $result
+}
+
function Invoke-Process {
[CmdletBinding(SupportsShouldProcess)]
param
@@ -485,7 +495,11 @@ function Invoke-Process {
[Parameter()]
[ValidateNotNullOrEmpty()]
- [string]$ArgumentList
+ [string]$ArgumentList,
+
+ [Parameter()]
+ [ValidateNotNullOrEmpty()]
+ [bool]$Wait = $true
)
$ErrorActionPreference = 'Stop'
@@ -499,7 +513,7 @@ function Invoke-Process {
ArgumentList = $ArgumentList
RedirectStandardError = $stdErrTempFile
RedirectStandardOutput = $stdOutTempFile
- Wait = $true;
+ Wait = $($Wait);
PassThru = $true;
NoNewWindow = $true;
}
@@ -507,7 +521,7 @@ function Invoke-Process {
$cmd = Start-Process @startProcessParams
$cmdOutput = Get-Content -Path $stdOutTempFile -Raw
$cmdError = Get-Content -Path $stdErrTempFile -Raw
- if ($cmd.ExitCode -ne 0) {
+ if ($cmd.ExitCode -ne 0 -and $wait -eq $true) {
if ($cmdError) {
throw $cmdError.Trim()
}
@@ -525,15 +539,14 @@ function Invoke-Process {
catch {
#$PSCmdlet.ThrowTerminatingError($_)
WriteLog $_
- Write-Host "Script failed - $Logfile for more info"
+ # Write-Host "Script failed - $Logfile for more info"
throw $_
}
finally {
Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore
-
}
-
+ return $cmd
}
function Test-Url {
@@ -734,7 +747,7 @@ function Get-MicrosoftDrivers {
# Extract the MSI file using an administrative install
WriteLog "Extracting MSI file to $modelPath"
$arguments = "/a `"$($filePath)`" /qn TARGETDIR=`"$($modelPath)`""
- Invoke-Process -FilePath "msiexec.exe" -ArgumentList $arguments
+ Invoke-Process -FilePath "msiexec.exe" -ArgumentList $arguments | Out-Null
WriteLog "Extraction complete"
} elseif ($fileExtension -eq ".zip") {
# Extract the ZIP file
@@ -790,7 +803,7 @@ function Get-HPDrivers {
Start-BitsTransferWithRetry -Source $PlatformListUrl -Destination $PlatformListCab
WriteLog "Download complete"
WriteLog "Expanding $PlatformListCab to $PlatformListXml"
- Invoke-Process -FilePath expand.exe -ArgumentList "$PlatformListCab $PlatformListXml"
+ Invoke-Process -FilePath expand.exe -ArgumentList "$PlatformListCab $PlatformListXml" | Out-Null
WriteLog "Expansion complete"
# Parse the PlatformList.xml to find the SystemID based on the ProductName
@@ -920,7 +933,7 @@ function Get-HPDrivers {
Writelog "Downloading HP Driver cab from $DriverCabUrl to $DriverCabFile"
Start-BitsTransferWithRetry -Source $DriverCabUrl -Destination $DriverCabFile
WriteLog "Expanding HP Driver cab to $DriverXmlFile"
- Invoke-Process -FilePath expand.exe -ArgumentList "$DriverCabFile $DriverXmlFile"
+ Invoke-Process -FilePath expand.exe -ArgumentList "$DriverCabFile $DriverXmlFile" | Out-Null
# Parse the extracted XML file to download individual drivers
[xml]$DriverXmlContent = Get-Content -Path $DriverXmlFile
@@ -971,7 +984,7 @@ function Get-HPDrivers {
# Extract the driver
$arguments = "/s /e /f `"$extractFolder`""
WriteLog "Extracting driver"
- Invoke-Process -FilePath $DriverFilePath -ArgumentList $arguments
+ Invoke-Process -FilePath $DriverFilePath -ArgumentList $arguments | Out-Null
WriteLog "Driver extracted to: $extractFolder"
# Delete the .exe driver file after extraction
@@ -1181,7 +1194,7 @@ function Get-LenovoDrivers {
# Extract the driver
# Start-Process -FilePath $driverFilePath -ArgumentList $modifiedExtractCommand -Wait -NoNewWindow
WriteLog "Extracting driver: $driverFilePath to $extractFolder"
- Invoke-Process -FilePath $driverFilePath -ArgumentList $modifiedExtractCommand
+ Invoke-Process -FilePath $driverFilePath -ArgumentList $modifiedExtractCommand | Out-Null
WriteLog "Driver extracted"
# Delete the .exe driver file after extraction
@@ -1212,6 +1225,17 @@ function Get-DellDrivers {
[int]$WindowsRelease
)
+ if (-not (Test-Path -Path $DriversFolder)) {
+ WriteLog "Creating Drivers folder: $DriversFolder"
+ New-Item -Path $DriversFolder -ItemType Directory -Force | Out-Null
+ WriteLog "Drivers folder created"
+ }
+
+ $DriversFolder = "$DriversFolder\$Make"
+ WriteLog "Creating Dell Drivers folder: $DriversFolder"
+ New-Item -Path $DriversFolder -ItemType Directory -Force | Out-Null
+ WriteLog "Dell Drivers folder created"
+
#CatalogPC.cab is the catalog for Windows client PCs, Catalog.cab is the catalog for Windows Server
if ($WindowsRelease -le 11) {
$catalogUrl = "http://downloads.dell.com/catalog/CatalogPC.cab"
@@ -1231,23 +1255,12 @@ function Get-DellDrivers {
exit
}
- if (-not (Test-Path -Path $DriversFolder)) {
- WriteLog "Creating Drivers folder: $DriversFolder"
- New-Item -Path $DriversFolder -ItemType Directory -Force | Out-Null
- WriteLog "Drivers folder created"
- }
-
- $DriversFolder = "$DriversFolder\$Make"
- WriteLog "Creating Dell Drivers folder: $DriversFolder"
- New-Item -Path $DriversFolder -ItemType Directory -Force | Out-Null
- WriteLog "Dell Drivers folder created"
-
WriteLog "Downloading Dell Catalog cab file: $catalogUrl to $DellCabFile"
Start-BitsTransferWithRetry -Source $catalogUrl -Destination $DellCabFile
WriteLog "Dell Catalog cab file downloaded"
WriteLog "Extracting Dell Catalog cab file to $DellCatalogXML"
- Invoke-Process -FilePath Expand.exe -ArgumentList "$DellCabFile $DellCatalogXML"
+ Invoke-Process -FilePath Expand.exe -ArgumentList "$DellCabFile $DellCatalogXML" | Out-Null
WriteLog "Dell Catalog cab file extracted"
$xmlContent = [xml](Get-Content -Path $DellCatalogXML)
@@ -1268,6 +1281,8 @@ function Get-DellDrivers {
$validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { ($_.osArch -eq $WindowsArch) -and ($_.osCode -match "W19") }
} elseif ($WindowsRelease -eq 2022) {
$validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { ($_.osArch -eq $WindowsArch) -and ($_.osCode -match "W22") }
+ } elseif ($WindowsRelease -eq 2025) {
+ $validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { ($_.osArch -eq $WindowsArch) -and ($_.osCode -match "W25") }
} else {
$validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { ($_.osArch -eq $WindowsArch) -and ($_.osCode -match "W22") }
}
@@ -1277,9 +1292,10 @@ function Get-DellDrivers {
$downloadUrl = $baseLocation + $driverPath
$driverFileName = [System.IO.Path]::GetFileName($driverPath)
$name = $component.Name.Display.'#cdata-section'
- $name = $name -replace '[\\\/\:\*\?\"\<\>\|]', '_'
+ $name = $name -replace '[\\\/\:\*\?\"\<\>\| ]', '_'
$name = $name -replace '[\,]', '-'
$category = $component.Category.Display.'#cdata-section'
+ $category = $category -replace '[\\\/\:\*\?\"\<\>\| ]', '_'
$version = [version]$component.vendorVersion
$namePrefix = ($name -split '-')[0]
@@ -1327,7 +1343,7 @@ function Get-DellDrivers {
$driverFilePath = Join-Path -Path $downloadFolder -ChildPath $driver.DriverFileName
if (Test-Path -Path $driverFilePath) {
- Write-Output "Driver already downloaded: $driverFilePath skipping"
+ WriteLog "Driver already downloaded: $driverFilePath skipping"
continue
}
@@ -1349,13 +1365,66 @@ function Get-DellDrivers {
$extractFolder = $downloadFolder + "\" + $driver.DriverFileName.TrimEnd($driver.DriverFileName[-4..-1])
- WriteLog "Creating extraction folder: $extractFolder"
- New-Item -Path $extractFolder -ItemType Directory -Force | Out-Null
- WriteLog "Extraction folder created"
+ # WriteLog "Creating extraction folder: $extractFolder"
+ # New-Item -Path $extractFolder -ItemType Directory -Force | Out-Null
+ # WriteLog "Extraction folder created"
- $arguments = "/s /e=`"$extractFolder`""
- WriteLog "Extracting driver: $driverFilePath to $extractFolder"
- Invoke-Process -FilePath $driverFilePath -ArgumentList $arguments
+ # $arguments = "/s /e=`"$extractFolder`""
+ $arguments = "/s /drivers=`"$extractFolder`""
+ WriteLog "Extracting driver: $driverFilePath $arguments"
+ try {
+ #If Category is Chipset, must add -wait $false to the Invoke-Process command line to prevent the script from hanging on the Intel chipset driver which leaves a Window open
+ if ($driver.Category -eq "Chipset") {
+ $process = Invoke-Process -FilePath $driverFilePath -ArgumentList $arguments -Wait $false
+
+ #Wait 5 seconds to allow for the extraction process to finish
+ Start-Sleep -Seconds 5
+
+ $childProcesses = Get-ChildProcesses $process.Id
+
+ # Find and stop the last created child process
+ if ($childProcesses) {
+ $latestProcess = $childProcesses | Sort-Object CreationDate -Descending | Select-Object -First 1
+ Stop-Process -Id $latestProcess.ProcessId -Force
+ # Sleep 1 second to let process finish exiting so its installer can be removed
+ Start-Sleep -Seconds 1
+ }
+ #If Category is Network and $isServer is $false, must add -wait $false to the Invoke-Process command line to prevent the script from hanging on the Intel network driver which leaves a Window open
+ } elseif ($driver.Category -eq "Network" -and $isServer -eq $false) {
+
+ $process = Invoke-Process -FilePath $driverFilePath -ArgumentList $arguments -Wait $false
+
+ #Sometimes the network drivers will extract on client OS, wait 5 seconds and check if the process is still running
+ Start-Sleep -Seconds 5
+ if ($process.HasExited -eq $false) {
+ $childProcesses = Get-ChildProcesses $process.Id
+
+ # Find and stop the last created child process
+ if ($childProcesses) {
+ $latestProcess = $childProcesses | Sort-Object CreationDate -Descending | Select-Object -First 1
+ Stop-Process -Id $latestProcess.ProcessId -Force
+ #Move on to the next driver and skip this one - it won't extract on a client OS even with /s /e switches
+ continue
+ }
+ }
+ } else {
+ Invoke-Process -FilePath $driverFilePath -ArgumentList $arguments | Out-Null
+ }
+ # If $extractFolder is empty, try alternative extraction method
+ if (!(Get-ChildItem -Path $extractFolder -Recurse | Where-Object { -not $_.PSIsContainer })) {
+ WriteLog 'Extraction with /drivers= switch failed. Removing folder and retrying with /s /e switches'
+ Remove-Item -Path $extractFolder -Force -Recurse -ErrorAction SilentlyContinue
+ $arguments = "/s /e=`"$extractFolder`""
+ WriteLog "Extracting driver: $driverFilePath $arguments"
+ Invoke-Process -FilePath $driverFilePath -ArgumentList $arguments | Out-Null
+ }
+ }
+ catch {
+ WriteLog 'Extraction with /drivers= switch failed. Retrying with /s /e switches'
+ $arguments = "/s /e=`"$extractFolder`""
+ WriteLog "Extracting driver: $driverFilePath $arguments"
+ Invoke-Process -FilePath $driverFilePath -ArgumentList $arguments | Out-Null
+ }
WriteLog "Driver extracted"
WriteLog "Deleting driver file: $driverFilePath"
@@ -1455,7 +1524,7 @@ function Install-ADK {
WriteLog "$ADKOption downloaded to $installerLocation"
WriteLog "Installing $ADKOption with $feature enabled"
- Invoke-Process $installerLocation "/quiet /installpath ""%ProgramFiles(x86)%\Windows Kits\10"" /features $feature"
+ Invoke-Process $installerLocation "/quiet /installpath ""%ProgramFiles(x86)%\Windows Kits\10"" /features $feature" | Out-Null
WriteLog "$ADKOption installation completed."
WriteLog "Removing $installer from $installerLocation"
@@ -1512,7 +1581,7 @@ function Uninstall-ADK {
$adkBundleCachePath = $adkRegKey.GetValue("BundleCachePath")
WriteLog "Uninstalling $ADKOption..."
- Invoke-Process $adkBundleCachePath "/uninstall /quiet"
+ Invoke-Process $adkBundleCachePath "/uninstall /quiet" | Out-Null
WriteLog "$ADKOption uninstalled successfully."
}
catch {
@@ -1615,7 +1684,7 @@ function Get-ADK {
if ($adkBundleCachePath) {
WriteLog "Installing Windows Deployment Tools..."
$adkInstallPath = $adkPath.TrimEnd('\')
- Invoke-Process $adkBundleCachePath "/quiet /installpath ""$adkInstallPath"" /features OptionId.DeploymentTools"
+ Invoke-Process $adkBundleCachePath "/quiet /installpath ""$adkInstallPath"" /features OptionId.DeploymentTools" | Out-Null
WriteLog "Windows Deployment Tools installed successfully."
}
else {
@@ -1668,7 +1737,7 @@ function Get-WindowsESD {
# Extract XML from cab file
WriteLog "Extracting Products XML from cab"
$xmlFilePath = Join-Path $PSScriptRoot "products.xml"
- Invoke-Process Expand "-F:*.xml $cabFilePath $xmlFilePath"
+ Invoke-Process Expand "-F:*.xml $cabFilePath $xmlFilePath" | Out-Null
WriteLog "Products XML extracted"
# Load XML content
@@ -1729,7 +1798,7 @@ function Get-Office {
# Extract ODT
WriteLog "Extracting ODT to $OfficePath"
- Invoke-Process $ODTInstallFile "/extract:$OfficePath /quiet"
+ Invoke-Process $ODTInstallFile "/extract:$OfficePath /quiet" | Out-Null
# Run setup.exe with config.xml and modify xml file to download to $OfficePath
$ConfigXml = "$OfficePath\DownloadFFU.xml"
@@ -1737,7 +1806,7 @@ function Get-Office {
$xmlContent.Configuration.Add.SourcePath = $OfficePath
$xmlContent.Save($ConfigXml)
WriteLog "Downloading M365 Apps/Office to $OfficePath"
- Invoke-Process $OfficePath\setup.exe "/download $ConfigXml"
+ Invoke-Process $OfficePath\setup.exe "/download $ConfigXml" | Out-Null
WriteLog "Cleaning up ODT default config files and checking InstallAppsandSysprep.cmd file for proper command line"
#Clean up default configuration files
@@ -1796,7 +1865,7 @@ function Install-WinGet {
# }
# $wingetVersion = & winget.exe --version
# WriteLog "Installed version of WinGet: $wingetVersion"
-# if ($wingetVersion -match 'v?(\d+\.\d+\.\d+)' -and [version]$matches[1] -lt $minVersion) {
+# if ($wingetVersion -match 'v?(\d+\.\d+.\d+)' -and [version]$matches[1] -lt $minVersion) {
# WriteLog "The installed version of WinGet $($matches[1]) does not support downloading MSStore apps. Downloading the latest version of WinGet..."
# Install-WinGet -Architecture $WindowsArch
# }
@@ -2415,7 +2484,7 @@ function New-AppsISO {
#Create Apps ISO file
$OSCDIMG = "$adkpath`Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe"
#Start-Process -FilePath $OSCDIMG -ArgumentList "-n -m -d $Appspath $AppsISO" -wait
- Invoke-Process $OSCDIMG "-n -m -d $Appspath $AppsISO"
+ Invoke-Process $OSCDIMG "-n -m -d $Appspath $AppsISO" | Out-Null
#Remove the Office Download and ODT
if ($InstallOffice) {
@@ -2707,7 +2776,7 @@ function Add-BootFiles {
)
WriteLog "Adding boot files for `"$($OsPartitionDriveLetter):\Windows`" to System partition `"$($SystemPartitionDriveLetter):`"..."
- Invoke-Process bcdboot "$($OsPartitionDriveLetter):\Windows /S $($SystemPartitionDriveLetter): /F $FirmwareType"
+ Invoke-Process bcdboot "$($OsPartitionDriveLetter):\Windows /S $($SystemPartitionDriveLetter): /F $FirmwareType" | Out-Null
WriteLog "Done."
}
@@ -2848,7 +2917,7 @@ function New-PEMedia {
elseif($WindowsArch -eq 'arm64') {
& cmd /c """$DandIEnv"" && copype arm64 $WinPEFFUPath" | Out-Null
}
- #Invoke-Process cmd "/c ""$DandIEnv"" && copype amd64 $WinPEFFUPath"
+ #Invoke-Process cmd "/c ""$DandIEnv"" && copype amd64 $WinPEFFUPath" | Out-Null
WriteLog 'Files copied successfully'
WriteLog 'Mounting WinPE media to add WinPE optional components'
@@ -2944,7 +3013,7 @@ function New-PEMedia {
}
}
- Invoke-Process $OSCDIMG $OSCDIMGArgs
+ Invoke-Process $OSCDIMG $OSCDIMGArgs | Out-Null
WriteLog "ISO created successfully"
WriteLog "Cleaning up $WinPEFFUPath"
Remove-Item -Path "$WinPEFFUPath" -Recurse -Force
@@ -3025,8 +3094,8 @@ function New-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"
- # Invoke-Process cmd "/c dism /Capture-FFU /ImageFile:$FFUFile /CaptureDrive:\\.\PhysicalDrive$($vhdxDisk.DiskNumber) /Name:$($winverinfo.Name)$($winverinfo.DisplayVersion)$($winverinfo.SKU) /Compress:Default"
+ 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 'FFU Capture complete'
Dismount-ScratchVhdx -VhdxPath $VHDXPath
}
@@ -3063,8 +3132,8 @@ function New-FFU {
if ($Optimize -eq $true) {
WriteLog 'Optimizing FFU - This will take a few minutes, please be patient'
#Need to use ADK version of DISM to address bug in DISM - perhaps Windows 11 24H2 will fix this
- Invoke-Process cmd "/c ""$DandIEnv"" && dism /optimize-ffu /imagefile:$FFUFile"
- #Invoke-Process cmd "/c dism /optimize-ffu /imagefile:$FFUFile"
+ Invoke-Process cmd "/c ""$DandIEnv"" && dism /optimize-ffu /imagefile:$FFUFile" | Out-Null
+ #Invoke-Process cmd "/c dism /optimize-ffu /imagefile:$FFUFile" | Out-Null
WriteLog 'Optimizing FFU complete'
}
@@ -3125,7 +3194,7 @@ function Remove-FFUVM {
}
#Remove unused mountpoints
WriteLog 'Remove unused mountpoints'
- Invoke-Process cmd "/c mountvol /r"
+ Invoke-Process cmd "/c mountvol /r" | Out-Null
WriteLog 'Removal complete'
}
Function Remove-FFUUserShare {
@@ -3145,7 +3214,7 @@ Function Get-WindowsVersionInfo {
#Load Registry Hive
$Software = "$osPartitionDriveLetter`:\Windows\System32\config\software"
WriteLog "Loading Software registry hive"
- Invoke-Process reg "load HKLM\FFU $Software"
+ 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'
@@ -3191,7 +3260,7 @@ Function Get-WindowsVersionInfo {
}
WriteLog "Unloading registry"
- Invoke-Process reg "unload HKLM\FFU"
+ Invoke-Process reg "unload HKLM\FFU" | Out-Null
#This prevents Critical Process Died errors you can have during deployment of the FFU. Capturing from very fast disks (NVME) can cause the capture to happen faster than Windows is ready for.
WriteLog 'Sleep 60 seconds to allow registry to completely unload'
Start-sleep 60
@@ -3579,7 +3648,7 @@ function Get-FFUEnvironment {
# Remove unused mountpoints
WriteLog 'Remove unused mountpoints'
- Invoke-Process cmd "/c mountvol /r"
+ Invoke-Process cmd "/c mountvol /r" | Out-Null
WriteLog 'Removal complete'
# Check for content in the VM folder and delete any folders that start with _FFU-
@@ -3621,8 +3690,8 @@ function Get-FFUEnvironment {
#Clean up registry
if (Test-Path -Path 'HKLM:\FFU') {
- Writelog 'Found HKLM:\FFU, removing it'
- Invoke-Process reg "unload HKLM\FFU"
+ Writelog 'Found HKLM:\FFU, removing it'
+ Invoke-Process reg "unload HKLM\FFU" | Out-Null
}
#Remove FFU User and Share
@@ -4071,7 +4140,16 @@ if ($InstallApps) {
if ($UpdateLatestMSRT) {
WriteLog "`$UpdateLatestMSRT is set to true."
if ($WindowsArch -eq 'x64') {
- $Name = """Windows Malicious Software Removal Tool x64""" + " " + """Windows $WindowsRelease"""
+ if ($installationType -eq 'client') {
+ $Name = """Windows Malicious Software Removal Tool x64""" + " " + """Windows $WindowsRelease"""
+ }
+ #Windows Server 2025 isn't listed as a product in the Microsoft Update Catalog, so we'll use the 2019 version
+ elseif ($installationType -eq 'server' -and $WindowsRelease -eq '24H2') {
+ $Name = """Windows Malicious Software Removal Tool x64""" + " " + """Windows Server 2019"""
+ }
+ else {
+ $Name = """Windows Malicious Software Removal Tool x64""" + " " + """Windows Server $WindowsRelease"""
+ }
}
if ($WindowsArch -eq 'x86') {
$Name = """Windows Malicious Software Removal Tool""" + " " + """Windows $WindowsRelease"""
@@ -4143,7 +4221,7 @@ if ($InstallApps) {
$EdgeMSIFileName = "MicrosoftEdgeEnterprise$WindowsArch.msi"
$EdgeFullFilePath = "$EdgePath\$EdgeMSIFileName"
WriteLog "Expanding $EdgeCABFilePath"
- Invoke-Process Expand "$EdgeCABFilePath -F:*.msi $EdgeFullFilePath"
+ Invoke-Process Expand "$EdgeCABFilePath -F:*.msi $EdgeFullFilePath" | Out-Null
WriteLog "Expansion complete"
#Remove Edge CAB file
From 10624787fe6b98416659691c47a67aaf17fc4437 Mon Sep 17 00:00:00 2001
From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com>
Date: Tue, 10 Dec 2024 15:39:49 -0800
Subject: [PATCH 46/50] Fix ODT
- Refactored the Get-ODTURL function to fix recent download issues. Also added some better error handling
- Moved the odtsetup.exe download to the FFUDevelopment folder and will clean it up after office has downloaded
---
FFUDevelopment/BuildFFUVM.ps1 | 34 +++++++++++++++++++++++++++-------
1 file changed, 27 insertions(+), 7 deletions(-)
diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1
index 1e42dd5..961e4ba 100644
--- a/FFUDevelopment/BuildFFUVM.ps1
+++ b/FFUDevelopment/BuildFFUVM.ps1
@@ -1775,21 +1775,36 @@ function Get-WindowsESD {
function Get-ODTURL {
+ try {
+ [String]$ODTPage = Invoke-WebRequest 'https://www.microsoft.com/en-us/download/details.aspx?id=49117' -Headers $Headers -UserAgent $UserAgent -ErrorAction Stop
- # [String]$MSWebPage = Invoke-RestMethod 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=49117'
- [String]$MSWebPage = Invoke-RestMethod 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=49117' -Headers $Headers -UserAgent $UserAgent
-
- $MSWebPage | ForEach-Object {
- if ($_ -match 'url=(https://.*officedeploymenttool.*\.exe)') {
- $matches[1]
+ # Extract JSON data from the webpage
+ if ($ODTPage -match ''
+ $scriptPattern = ' |