mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Captures FFU directly from host-mounted VHDX
By optimizing and mounting the VHDX directly on the host for image capture, the build process no longer needs to boot the VM into WinPE, create SMB network shares, generate temporary local accounts, or rely on complex Hyper-V switch IP configurations. This streamlines the workflow and eliminates multiple networking and permission-related points of failure. This change also removes the need to generate and attach WinPE capture media. All related parameters (`ShareName`, `Username`, `VMHostIPAddress`, `CreateCaptureMedia`, `CleanupCaptureISO`), UI controls, capture scripts, and documentation references have been removed or updated to reflect the simplified architecture.
This commit is contained in:
+132
-344
@@ -36,9 +36,6 @@ Switch to run cleanup-only mode. When specified, the script performs cleanup and
|
|||||||
.PARAMETER CleanupAppsISO
|
.PARAMETER CleanupAppsISO
|
||||||
When set to $true, will remove the Apps ISO after the FFU has been captured. Default is $true.
|
When set to $true, will remove the Apps ISO after the FFU has been captured. Default is $true.
|
||||||
|
|
||||||
.PARAMETER CleanupCaptureISO
|
|
||||||
When set to $true, will remove the WinPE capture ISO after the FFU has been captured. Default is $true.
|
|
||||||
|
|
||||||
.PARAMETER CleanupCurrentRunDownloads
|
.PARAMETER CleanupCurrentRunDownloads
|
||||||
When set to $true, cleanup mode will remove downloads created during the current run and restore backed up run JSON files. Default is $false.
|
When set to $true, cleanup mode will remove downloads created during the current run and restore backed up run JSON files. Default is $false.
|
||||||
|
|
||||||
@@ -75,9 +72,6 @@ When set to $true, will copy the provisioning package from the $FFUDevelopmentPa
|
|||||||
.PARAMETER CopyUnattend
|
.PARAMETER CopyUnattend
|
||||||
When set to $true, will copy the $FFUDevelopmentPath\Unattend folder to the Deployment partition of the USB drive. Default is $false.
|
When set to $true, will copy the $FFUDevelopmentPath\Unattend folder to the Deployment partition of the USB drive. Default is $false.
|
||||||
|
|
||||||
.PARAMETER CreateCaptureMedia
|
|
||||||
When set to $true, this will create WinPE capture media for use when $InstallApps is set to $true. This capture media will be automatically attached to the VM, and the boot order will be changed to automate the capture of the FFU.
|
|
||||||
|
|
||||||
.PARAMETER CreateDeploymentMedia
|
.PARAMETER CreateDeploymentMedia
|
||||||
When set to $true, this will create WinPE deployment media for use when deploying to a physical device.
|
When set to $true, this will create WinPE deployment media for use when deploying to a physical device.
|
||||||
|
|
||||||
@@ -177,9 +171,6 @@ When set to $true, will remove the downloaded CU, MSRT, Defender, Edge, OneDrive
|
|||||||
.PARAMETER RemoveDownloadedESD
|
.PARAMETER RemoveDownloadedESD
|
||||||
When set to $true, will remove downloaded Windows ESD files after they have been applied. Default is $true.
|
When set to $true, will remove downloaded Windows ESD files after they have been applied. Default is $true.
|
||||||
|
|
||||||
.PARAMETER ShareName
|
|
||||||
Name of the shared folder for FFU capture. The default is FFUCaptureShare. This share will be created with rights for the user account. When finished, the share will be removed.
|
|
||||||
|
|
||||||
.PARAMETER Threads
|
.PARAMETER Threads
|
||||||
Controls the throttle applied to parallel tasks inside the script. Default is 5, matching the UI Threads field, and applies to driver downloads invoked through Invoke-ParallelProcessing.
|
Controls the throttle applied to parallel tasks inside the script. Default is 5, matching the UI Threads field, and applies to driver downloads invoked through Invoke-ParallelProcessing.
|
||||||
|
|
||||||
@@ -228,17 +219,11 @@ User agent string to use when downloading files.
|
|||||||
.PARAMETER UserAppListPath
|
.PARAMETER UserAppListPath
|
||||||
Path to a JSON file containing a list of user-defined applications to install. Default is $FFUDevelopmentPath\Apps\UserAppList.json.
|
Path to a JSON file containing a list of user-defined applications to install. Default is $FFUDevelopmentPath\Apps\UserAppList.json.
|
||||||
|
|
||||||
.PARAMETER Username
|
|
||||||
Username for accessing the shared folder. The default is ffu_user. The script will auto-create the account and password. When finished, it will remove the account.
|
|
||||||
|
|
||||||
.PARAMETER VMHostIPAddress
|
|
||||||
IP address of the Hyper-V host for FFU capture. If $InstallApps is set to $true, this parameter must be configured. You must manually configure this, or use the UI to auto-detect.
|
|
||||||
|
|
||||||
.PARAMETER VMLocation
|
.PARAMETER VMLocation
|
||||||
Default is $FFUDevelopmentPath\VM. This is the location of the VHDX that gets created where Windows will be installed to.
|
Default is $FFUDevelopmentPath\VM. This is the location of the VHDX that gets created where Windows will be installed to.
|
||||||
|
|
||||||
.PARAMETER VMSwitchName
|
.PARAMETER VMSwitchName
|
||||||
Name of the Hyper-V virtual switch. If $InstallApps is set to $true, this must be set to capture the FFU from the VM.
|
Name of the Hyper-V virtual switch. Optional when building with InstallApps. Provide it only if the VM needs network connectivity during provisioning.
|
||||||
|
|
||||||
.PARAMETER WindowsArch
|
.PARAMETER WindowsArch
|
||||||
String value of 'x86', 'x64', or 'arm64'. This is used to identify which architecture of Windows to download. Default is 'x64'.
|
String value of 'x86', 'x64', or 'arm64'. This is used to identify which architecture of Windows to download. Default is 'x64'.
|
||||||
@@ -257,25 +242,25 @@ String value of the Windows version to download. This is used to identify which
|
|||||||
|
|
||||||
.EXAMPLE
|
.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.
|
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
|
.\BuildFFUVM.ps1 -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -CreateDeploymentMedia $true -BuildUSBDrive $true -UpdateLatestCU $true -UpdateLatestNet $true -UpdateLatestDefender $true -UpdateEdge $true -UpdateOneDrive $true -verbose
|
||||||
|
|
||||||
Command line for most people who want to create an FFU with Office and drivers and have downloaded their own ISO. This assumes you have copied this script and associated files to the C:\FFUDevelopment folder. If you need to use another drive or folder, change the -FFUDevelopment parameter (e.g. -FFUDevelopment 'D:\FFUDevelopment')
|
Command line for most people who want to create an FFU with Office and drivers and have downloaded their own ISO. This assumes you have copied this script and associated files to the C:\FFUDevelopment folder. If you need to use another drive or folder, change the -FFUDevelopment parameter (e.g. -FFUDevelopment 'D:\FFUDevelopment')
|
||||||
.\BuildFFUVM.ps1 -ISOPath 'C:\path_to_iso\Windows.iso' -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 -verbose
|
.\BuildFFUVM.ps1 -ISOPath 'C:\path_to_iso\Windows.iso' -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -CreateDeploymentMedia $true -BuildUSBDrive $true -verbose
|
||||||
|
|
||||||
Command line for those who just want a FFU with no drivers, apps, or Office and have downloaded their own ISO.
|
Command line for those who just want a FFU with no drivers, apps, or Office and have downloaded their own ISO.
|
||||||
.\BuildFFUVM.ps1 -ISOPath 'C:\path_to_iso\Windows.iso' -WindowsSKU 'Pro' -Installapps $false -InstallOffice $false -InstallDrivers $false -CreateCaptureMedia $false -CreateDeploymentMedia $true -BuildUSBDrive $true -verbose
|
.\BuildFFUVM.ps1 -ISOPath 'C:\path_to_iso\Windows.iso' -WindowsSKU 'Pro' -Installapps $false -InstallOffice $false -InstallDrivers $false -CreateDeploymentMedia $true -BuildUSBDrive $true -verbose
|
||||||
|
|
||||||
Command line for those who just want a FFU with Apps and drivers, no Office and have downloaded their own ISO.
|
Command line for those who just want a FFU with Apps and drivers, no Office and have downloaded their own ISO.
|
||||||
.\BuildFFUVM.ps1 -ISOPath 'C:\path_to_iso\Windows.iso' -WindowsSKU 'Pro' -Installapps $true -InstallOffice $false -InstallDrivers $true -VMSwitchName 'Name of your VM Switch in Hyper-V' -VMHostIPAddress 'Your IP Address' -CreateCaptureMedia $true -CreateDeploymentMedia $true -BuildUSBDrive $true -verbose
|
.\BuildFFUVM.ps1 -ISOPath 'C:\path_to_iso\Windows.iso' -WindowsSKU 'Pro' -Installapps $true -InstallOffice $false -InstallDrivers $true -CreateDeploymentMedia $true -BuildUSBDrive $true -verbose
|
||||||
|
|
||||||
Command line for those who want to download the latest Windows 11 Pro x64 media in English (US) and install the latest version of Office and drivers.
|
Command line for those who want to download the latest Windows 11 Pro x64 media in English (US) and install the latest version of Office and drivers.
|
||||||
.\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 -verbose
|
.\BuildFFUVM.ps1 -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -CreateDeploymentMedia $true -BuildUSBDrive $true -verbose
|
||||||
|
|
||||||
Command line for those who want to download the latest Windows 11 Pro x64 media in French (CA) and install the latest version of Office and drivers.
|
Command line for those who want to download the latest Windows 11 Pro x64 media in French (CA) and install the latest version of Office and drivers.
|
||||||
.\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 -WindowsRelease 11 -WindowsArch 'x64' -WindowsLang 'fr-ca' -MediaType 'consumer' -verbose
|
.\BuildFFUVM.ps1 -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -CreateDeploymentMedia $true -BuildUSBDrive $true -WindowsRelease 11 -WindowsArch 'x64' -WindowsLang 'fr-ca' -MediaType 'consumer' -verbose
|
||||||
|
|
||||||
Command line for those who want to download the latest Windows 11 Pro x64 media in English (US) and install the latest version of Office and drivers.
|
Command line for those who want to download the latest Windows 11 Pro x64 media in English (US) and install the latest version of Office and drivers.
|
||||||
.\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 -verbose
|
.\BuildFFUVM.ps1 -WindowsSKU 'Pro' -Installapps $true -InstallOffice $true -InstallDrivers $true -CreateDeploymentMedia $true -BuildUSBDrive $true -verbose
|
||||||
|
|
||||||
.NOTES
|
.NOTES
|
||||||
Additional notes about your script.
|
Additional notes about your script.
|
||||||
@@ -336,12 +321,7 @@ param(
|
|||||||
[string]$VMLocation,
|
[string]$VMLocation,
|
||||||
[string]$FFUPrefix = '_FFU',
|
[string]$FFUPrefix = '_FFU',
|
||||||
[string]$FFUCaptureLocation,
|
[string]$FFUCaptureLocation,
|
||||||
[string]$ShareName = "FFUCaptureShare",
|
|
||||||
[string]$Username = "ffu_user",
|
|
||||||
[string]$CustomFFUNameTemplate,
|
[string]$CustomFFUNameTemplate,
|
||||||
[Parameter(Mandatory = $false)]
|
|
||||||
[string]$VMHostIPAddress,
|
|
||||||
[bool]$CreateCaptureMedia = $true,
|
|
||||||
[bool]$CreateDeploymentMedia,
|
[bool]$CreateDeploymentMedia,
|
||||||
[ValidateScript({
|
[ValidateScript({
|
||||||
$allowedFeatures = @("Windows-Defender-Default-Definitions", "Printing-PrintToPDFServices-Features", "Printing-XPSServices-Features", "TelnetClient", "TFTP",
|
$allowedFeatures = @("Windows-Defender-Default-Definitions", "Printing-PrintToPDFServices-Features", "Printing-XPSServices-Features", "TelnetClient", "TFTP",
|
||||||
@@ -425,7 +405,6 @@ param(
|
|||||||
[bool]$CopyUnattend,
|
[bool]$CopyUnattend,
|
||||||
[bool]$CopyAutopilot,
|
[bool]$CopyAutopilot,
|
||||||
[bool]$CompactOS = $true,
|
[bool]$CompactOS = $true,
|
||||||
[bool]$CleanupCaptureISO = $true,
|
|
||||||
[bool]$CleanupDeployISO = $true,
|
[bool]$CleanupDeployISO = $true,
|
||||||
[bool]$CleanupAppsISO = $true,
|
[bool]$CleanupAppsISO = $true,
|
||||||
[bool]$RemoveUpdates = $true,
|
[bool]$RemoveUpdates = $true,
|
||||||
@@ -647,7 +626,6 @@ if (-not $LtscCUStagePath) { $LtscCUStagePath = "$AppsPath\LTSCUpdate" }
|
|||||||
|
|
||||||
if (-not $AppsScriptVarsJsonPath) { $AppsScriptVarsJsonPath = "$OrchestrationPath\AppsScriptVariables.json" }
|
if (-not $AppsScriptVarsJsonPath) { $AppsScriptVarsJsonPath = "$OrchestrationPath\AppsScriptVariables.json" }
|
||||||
if (-not $DeployISO) { $DeployISO = "$FFUDevelopmentPath\WinPE_FFU_Deploy_$WindowsArch.iso" }
|
if (-not $DeployISO) { $DeployISO = "$FFUDevelopmentPath\WinPE_FFU_Deploy_$WindowsArch.iso" }
|
||||||
if (-not $CaptureISO) { $CaptureISO = "$FFUDevelopmentPath\WinPE_FFU_Capture_$WindowsArch.iso" }
|
|
||||||
if (-not $OfficePath) { $OfficePath = "$AppsPath\Office" }
|
if (-not $OfficePath) { $OfficePath = "$AppsPath\Office" }
|
||||||
if (-not $OfficeDownloadXML) { $OfficeDownloadXML = "$OfficePath\DownloadFFU.xml" }
|
if (-not $OfficeDownloadXML) { $OfficeDownloadXML = "$OfficePath\DownloadFFU.xml" }
|
||||||
if (-not $OfficeInstallXML) { $OfficeInstallXML = "DeployFFU.xml" }
|
if (-not $OfficeInstallXML) { $OfficeInstallXML = "DeployFFU.xml" }
|
||||||
@@ -2848,69 +2826,6 @@ function New-FFUVM {
|
|||||||
return $VM
|
return $VM
|
||||||
}
|
}
|
||||||
|
|
||||||
Function Set-CaptureFFU {
|
|
||||||
$CaptureFFUScriptPath = "$FFUDevelopmentPath\WinPECaptureFFUFiles\CaptureFFU.ps1"
|
|
||||||
|
|
||||||
# Workaround for PowerShell 7 issue on Windows 11 23H2 and earlier
|
|
||||||
# https://github.com/PowerShell/PowerShell/issues/21645
|
|
||||||
$osBuild = (Get-CimInstance -ClassName Win32_OperatingSystem).BuildNumber
|
|
||||||
if ($osBuild -le 22631) {
|
|
||||||
WriteLog "Applying workaround for PowerShell 7 LocalAccounts module issue on Windows 11 build $osBuild"
|
|
||||||
Import-Module Microsoft.PowerShell.LocalAccounts -UseWindowsPowerShell
|
|
||||||
}
|
|
||||||
|
|
||||||
If (-not (Test-Path -Path $FFUCaptureLocation)) {
|
|
||||||
WriteLog "Creating FFU capture location at $FFUCaptureLocation"
|
|
||||||
New-Item -Path $FFUCaptureLocation -ItemType Directory -Force
|
|
||||||
WriteLog "Successfully created FFU capture location at $FFUCaptureLocation"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create a standard user
|
|
||||||
$UserExists = Get-LocalUser -Name $UserName -ErrorAction SilentlyContinue
|
|
||||||
if (-not $UserExists) {
|
|
||||||
WriteLog "Creating FFU_User account as standard user"
|
|
||||||
New-LocalUser -Name $UserName -AccountNeverExpires -NoPassword | Out-null
|
|
||||||
WriteLog "Successfully created FFU_User account"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create a random password for the standard user
|
|
||||||
$Password = New-Guid | Select-Object -ExpandProperty Guid
|
|
||||||
$SecurePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force
|
|
||||||
Set-LocalUser -Name $UserName -Password $SecurePassword -PasswordNeverExpires:$true
|
|
||||||
|
|
||||||
# Create a share of the $FFUCaptureLocation variable
|
|
||||||
$ShareExists = Get-SmbShare -Name $ShareName -ErrorAction SilentlyContinue
|
|
||||||
if (-not $ShareExists) {
|
|
||||||
WriteLog "Creating $ShareName and giving access to $UserName"
|
|
||||||
New-SmbShare -Name $ShareName -Path $FFUCaptureLocation -FullAccess $UserName | Out-Null
|
|
||||||
WriteLog "Share created"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Return the share path in the format of \\<IPAddress>\<ShareName> /user:<UserName> <password>
|
|
||||||
$SharePath = "\\$VMHostIPAddress\$ShareName /user:$UserName $Password"
|
|
||||||
$SharePath = "net use W: " + $SharePath + ' 2>&1'
|
|
||||||
|
|
||||||
# Update CaptureFFU.ps1 script
|
|
||||||
if (Test-Path -Path $CaptureFFUScriptPath) {
|
|
||||||
$ScriptContent = Get-Content -Path $CaptureFFUScriptPath
|
|
||||||
#Update variables in CaptureFFU.ps1 script ($VMHostIPAddress, $ShareName, $UserName, $Password)
|
|
||||||
WriteLog 'Updating CaptureFFU.ps1 script with new share information'
|
|
||||||
$ScriptContent = $ScriptContent -replace '(\$VMHostIPAddress = ).*', "`$1'$VMHostIPAddress'"
|
|
||||||
$ScriptContent = $ScriptContent -replace '(\$ShareName = ).*', "`$1'$ShareName'"
|
|
||||||
$ScriptContent = $ScriptContent -replace '(\$UserName = ).*', "`$1'$UserName'"
|
|
||||||
$ScriptContent = $ScriptContent -replace '(\$Password = ).*', "`$1'$Password'"
|
|
||||||
if (![string]::IsNullOrEmpty($CustomFFUNameTemplate)) {
|
|
||||||
$ScriptContent = $ScriptContent -replace '(\$CustomFFUNameTemplate = ).*', "`$1'$CustomFFUNameTemplate'"
|
|
||||||
WriteLog 'Updating CaptureFFU.ps1 script with new ffu name template information'
|
|
||||||
}
|
|
||||||
Set-Content -Path $CaptureFFUScriptPath -Value $ScriptContent
|
|
||||||
WriteLog 'Update complete'
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw "CaptureFFU.ps1 script not found at $CaptureFFUScriptPath"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-PrivateProfileString {
|
function Get-PrivateProfileString {
|
||||||
param (
|
param (
|
||||||
[Parameter()]
|
[Parameter()]
|
||||||
@@ -3371,12 +3286,7 @@ function Copy-Drivers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function New-PEMedia {
|
function New-PEMedia {
|
||||||
param (
|
param ()
|
||||||
[Parameter()]
|
|
||||||
[bool]$Capture,
|
|
||||||
[Parameter()]
|
|
||||||
[bool]$Deploy
|
|
||||||
)
|
|
||||||
#Need to use the Demployment and Imaging tools environment to create winPE media
|
#Need to use the Demployment and Imaging tools environment to create winPE media
|
||||||
$DandIEnv = "$adkPath`Assessment and Deployment Kit\Deployment Tools\DandISetEnv.bat"
|
$DandIEnv = "$adkPath`Assessment and Deployment Kit\Deployment Tools\DandISetEnv.bat"
|
||||||
$WinPEFFUPath = "$FFUDevelopmentPath\WinPE"
|
$WinPEFFUPath = "$FFUDevelopmentPath\WinPE"
|
||||||
@@ -3431,64 +3341,49 @@ function New-PEMedia {
|
|||||||
Add-WindowsPackage -Path "$WinPEFFUPath\mount" -PackagePath $PackagePath | Out-Null
|
Add-WindowsPackage -Path "$WinPEFFUPath\mount" -PackagePath $PackagePath | Out-Null
|
||||||
WriteLog "Adding package complete"
|
WriteLog "Adding package complete"
|
||||||
}
|
}
|
||||||
If ($Capture) {
|
WriteLog "Copying $FFUDevelopmentPath\WinPEDeployFFUFiles\* to WinPE deploy media"
|
||||||
WriteLog "Copying $FFUDevelopmentPath\WinPECaptureFFUFiles\* to WinPE capture media"
|
Copy-Item -Path "$FFUDevelopmentPath\WinPEDeployFFUFiles\*" -Destination "$WinPEFFUPath\mount" -Recurse -Force | Out-Null
|
||||||
Copy-Item -Path "$FFUDevelopmentPath\WinPECaptureFFUFiles\*" -Destination "$WinPEFFUPath\mount" -Recurse -Force | out-null
|
WriteLog 'Copy complete'
|
||||||
WriteLog "Copy complete"
|
#If $CopyPEDrivers = $true, add drivers to WinPE media using dism
|
||||||
#Remove Bootfix.bin - for BIOS systems, shouldn't be needed, but doesn't hurt to remove for our purposes
|
if ($CopyPEDrivers) {
|
||||||
#Remove-Item -Path "$WinPEFFUPath\media\boot\bootfix.bin" -Force | Out-null
|
if ($UseDriversAsPEDrivers) {
|
||||||
# $WinPEISOName = 'WinPE_FFU_Capture.iso'
|
WriteLog "UseDriversAsPEDrivers is set. Building WinPE driver set from Drivers folder (bypassing PEDrivers folder contents)."
|
||||||
$WinPEISOFile = $CaptureISO
|
if (Test-Path -Path $PEDriversFolder) {
|
||||||
# $Capture = $false
|
try {
|
||||||
}
|
Remove-Item -Path (Join-Path $PEDriversFolder '*') -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
|
||||||
If ($Deploy) {
|
|
||||||
WriteLog "Copying $FFUDevelopmentPath\WinPEDeployFFUFiles\* to WinPE deploy media"
|
|
||||||
Copy-Item -Path "$FFUDevelopmentPath\WinPEDeployFFUFiles\*" -Destination "$WinPEFFUPath\mount" -Recurse -Force | Out-Null
|
|
||||||
WriteLog 'Copy complete'
|
|
||||||
#If $CopyPEDrivers = $true, add drivers to WinPE media using dism
|
|
||||||
if ($CopyPEDrivers) {
|
|
||||||
if ($UseDriversAsPEDrivers) {
|
|
||||||
WriteLog "UseDriversAsPEDrivers is set. Building WinPE driver set from Drivers folder (bypassing PEDrivers folder contents)."
|
|
||||||
if (Test-Path -Path $PEDriversFolder) {
|
|
||||||
try {
|
|
||||||
Remove-Item -Path (Join-Path $PEDriversFolder '*') -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
WriteLog "Warning: Failed clearing existing PEDriversFolder contents: $($_.Exception.Message)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
catch {
|
||||||
try {
|
WriteLog "Warning: Failed clearing existing PEDriversFolder contents: $($_.Exception.Message)"
|
||||||
New-Item -Path $PEDriversFolder -ItemType Directory -Force | Out-Null
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
WriteLog "Error: Failed to create PEDriversFolder at $PEDriversFolder - continuing may fail when adding drivers."
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
WriteLog "Copying required WinPE drivers from Drivers folder"
|
|
||||||
Copy-Drivers -Path $DriversFolder -Output $PEDriversFolder
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
WriteLog "Copying PE drivers from PEDrivers folder"
|
try {
|
||||||
|
New-Item -Path $PEDriversFolder -ItemType Directory -Force | Out-Null
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
WriteLog "Error: Failed to create PEDriversFolder at $PEDriversFolder - continuing may fail when adding drivers."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
WriteLog "Copying required WinPE drivers from Drivers folder"
|
||||||
WriteLog "Adding drivers to WinPE media"
|
Copy-Drivers -Path $DriversFolder -Output $PEDriversFolder
|
||||||
try {
|
}
|
||||||
$WinPEMount = "$WinPEFFUPath\Mount"
|
else {
|
||||||
|
WriteLog "Copying PE drivers from PEDrivers folder"
|
||||||
# Inject drivers using deep SUBST mapping (reuse one drive letter and loop each INF folder)
|
|
||||||
Invoke-DismDriverInjectionWithSubstLoop -ImagePath $WinPEMount -DriverRoot $PEDriversFolder
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
WriteLog 'Some drivers failed to be added. This can be expected. Continuing.'
|
|
||||||
}
|
|
||||||
WriteLog "Adding drivers complete"
|
|
||||||
}
|
}
|
||||||
# $WinPEISOName = 'WinPE_FFU_Deploy.iso'
|
|
||||||
$WinPEISOFile = $DeployISO
|
|
||||||
|
|
||||||
# $Deploy = $false
|
WriteLog "Adding drivers to WinPE media"
|
||||||
|
try {
|
||||||
|
$WinPEMount = "$WinPEFFUPath\Mount"
|
||||||
|
|
||||||
|
# Inject drivers using deep SUBST mapping (reuse one drive letter and loop each INF folder)
|
||||||
|
Invoke-DismDriverInjectionWithSubstLoop -ImagePath $WinPEMount -DriverRoot $PEDriversFolder
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
WriteLog 'Some drivers failed to be added. This can be expected. Continuing.'
|
||||||
|
}
|
||||||
|
WriteLog "Adding drivers complete"
|
||||||
}
|
}
|
||||||
|
$WinPEISOFile = $DeployISO
|
||||||
WriteLog 'Dismounting WinPE media'
|
WriteLog 'Dismounting WinPE media'
|
||||||
Dismount-WindowsImage -Path "$WinPEFFUPath\mount" -Save | Out-Null
|
Dismount-WindowsImage -Path "$WinPEFFUPath\mount" -Save | Out-Null
|
||||||
WriteLog 'Dismount complete'
|
WriteLog 'Dismount complete'
|
||||||
@@ -3503,21 +3398,10 @@ function New-PEMedia {
|
|||||||
WriteLog "Creating WinPE ISO at $WinPEISOFile"
|
WriteLog "Creating WinPE ISO at $WinPEISOFile"
|
||||||
# & "$OSCDIMG" -m -o -u2 -udfver102 -bootdata:2`#p0,e,b$OSCDIMGPath\etfsboot.com`#pEF,e,b$OSCDIMGPath\Efisys_noprompt.bin $WinPEFFUPath\media $FFUDevelopmentPath\$WinPEISOName | Out-null
|
# & "$OSCDIMG" -m -o -u2 -udfver102 -bootdata:2`#p0,e,b$OSCDIMGPath\etfsboot.com`#pEF,e,b$OSCDIMGPath\Efisys_noprompt.bin $WinPEFFUPath\media $FFUDevelopmentPath\$WinPEISOName | Out-null
|
||||||
if ($WindowsArch -eq 'x64') {
|
if ($WindowsArch -eq 'x64') {
|
||||||
if ($Capture) {
|
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:2`#p0,e,b`"$OSCDIMGPath\etfsboot.com`"`#pEF,e,b`"$OSCDIMGPath\Efisys.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
||||||
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:2`#p0,e,b`"$OSCDIMGPath\etfsboot.com`"`#pEF,e,b`"$OSCDIMGPath\Efisys_noprompt.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
|
||||||
}
|
|
||||||
if ($Deploy) {
|
|
||||||
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:2`#p0,e,b`"$OSCDIMGPath\etfsboot.com`"`#pEF,e,b`"$OSCDIMGPath\Efisys.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
elseif ($WindowsArch -eq 'arm64') {
|
elseif ($WindowsArch -eq 'arm64') {
|
||||||
if ($Capture) {
|
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:1`#pEF,e,b`"$OSCDIMGPath\Efisys.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
||||||
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:1`#pEF,e,b`"$OSCDIMGPath\Efisys_noprompt.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
|
||||||
}
|
|
||||||
if ($Deploy) {
|
|
||||||
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:1`#pEF,e,b`"$OSCDIMGPath\Efisys.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Invoke-Process $OSCDIMG $OSCDIMGArgs | Out-Null
|
Invoke-Process $OSCDIMG $OSCDIMGArgs | Out-Null
|
||||||
WriteLog "ISO created successfully"
|
WriteLog "ISO created successfully"
|
||||||
@@ -3525,7 +3409,7 @@ function New-PEMedia {
|
|||||||
Remove-Item -Path "$WinPEFFUPath" -Recurse -Force
|
Remove-Item -Path "$WinPEFFUPath" -Recurse -Force
|
||||||
WriteLog 'Cleanup complete'
|
WriteLog 'Cleanup complete'
|
||||||
# Deferred cleanup of preserved driver model folders (only after WinPE Deploy media is created)
|
# Deferred cleanup of preserved driver model folders (only after WinPE Deploy media is created)
|
||||||
if ($UseDriversAsPEDrivers -and $CompressDownloadedDriversToWim -and $Deploy -and $CopyPEDrivers) {
|
if ($UseDriversAsPEDrivers -and $CompressDownloadedDriversToWim -and $CopyPEDrivers) {
|
||||||
WriteLog "Beginning deferred cleanup of preserved driver model folders (UseDriversAsPEDrivers + compression scenario)."
|
WriteLog "Beginning deferred cleanup of preserved driver model folders (UseDriversAsPEDrivers + compression scenario)."
|
||||||
$removedCount = 0
|
$removedCount = 0
|
||||||
$skippedCount = 0
|
$skippedCount = 0
|
||||||
@@ -3616,6 +3500,40 @@ function Optimize-FFUCaptureDrive {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Get-CaptureVhdContext {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$VhdxPath
|
||||||
|
)
|
||||||
|
|
||||||
|
WriteLog 'Resolving VHDX context for host-side FFU capture'
|
||||||
|
|
||||||
|
$vhdInfo = Get-VHD -Path $VhdxPath
|
||||||
|
if ($vhdInfo.Attached) {
|
||||||
|
WriteLog 'VHDX is already mounted for capture'
|
||||||
|
$captureDisk = Get-Disk -Number $vhdInfo.DiskNumber
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
WriteLog 'Mounting VHDX for capture'
|
||||||
|
$captureDisk = Mount-VHD -Path $VhdxPath -Passthru | Get-Disk
|
||||||
|
}
|
||||||
|
|
||||||
|
$captureOsPartition = $captureDisk | Get-Partition | Where-Object { $_.GptType -eq '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}' } | Select-Object -First 1
|
||||||
|
if ($null -eq $captureOsPartition) {
|
||||||
|
throw 'Unable to resolve Windows partition for FFU capture.'
|
||||||
|
}
|
||||||
|
if ([string]::IsNullOrWhiteSpace($captureOsPartition.DriveLetter)) {
|
||||||
|
throw 'Unable to resolve Windows partition drive letter for FFU capture.'
|
||||||
|
}
|
||||||
|
|
||||||
|
return [pscustomobject]@{
|
||||||
|
Disk = $captureDisk
|
||||||
|
OsPartition = $captureOsPartition
|
||||||
|
OsPartitionDriveLetter = $captureOsPartition.DriveLetter
|
||||||
|
WindowsPartition = "$($captureOsPartition.DriveLetter):\"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Get-ShortenedWindowsSKU {
|
function Get-ShortenedWindowsSKU {
|
||||||
param (
|
param (
|
||||||
[string]$WindowsSKU
|
[string]$WindowsSKU
|
||||||
@@ -3703,89 +3621,53 @@ function New-FFUFileName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function New-FFU {
|
function New-FFU {
|
||||||
param (
|
$captureContext = Get-CaptureVhdContext -VhdxPath $VHDXPath
|
||||||
[Parameter(Mandatory = $false)]
|
$captureDisk = $captureContext.Disk
|
||||||
[string]$VMName
|
$osPartitionDriveLetter = $captureContext.OsPartitionDriveLetter
|
||||||
)
|
$WindowsPartition = $captureContext.WindowsPartition
|
||||||
#If $InstallApps = $true, configure the VM
|
|
||||||
If ($InstallApps) {
|
|
||||||
WriteLog 'Creating FFU from VM'
|
|
||||||
WriteLog "Setting $CaptureISO as first boot device"
|
|
||||||
$VMDVDDrive = Get-VMDvdDrive -VMName $VMName
|
|
||||||
Set-VMFirmware -VMName $VMName -FirstBootDevice $VMDVDDrive
|
|
||||||
Set-VMDvdDrive -VMName $VMName -Path $CaptureISO
|
|
||||||
$VMSwitch = Get-VMSwitch -name $VMSwitchName
|
|
||||||
WriteLog "Setting $($VMSwitch.Name) as VMSwitch"
|
|
||||||
get-vm $VMName | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName $VMSwitch.Name
|
|
||||||
WriteLog "Configuring VM complete"
|
|
||||||
|
|
||||||
#Start VM
|
try {
|
||||||
Set-Progress -Percentage 68 -Message "Capturing FFU from VM..."
|
|
||||||
WriteLog "Starting VM"
|
|
||||||
Start-VM -Name $VMName
|
|
||||||
|
|
||||||
# Wait for the VM to turn off
|
|
||||||
do {
|
|
||||||
$FFUVM = Get-VM -Name $VMName
|
|
||||||
Start-Sleep -Seconds 5
|
|
||||||
} while ($FFUVM.State -ne 'Off')
|
|
||||||
WriteLog "VM Shutdown"
|
|
||||||
# Check for .ffu files in the FFUDevelopment folder
|
|
||||||
WriteLog "Checking for FFU Files"
|
|
||||||
$FFUFiles = Get-ChildItem -Path $FFUCaptureLocation -Filter "*.ffu" -File
|
|
||||||
|
|
||||||
# If there's more than one .ffu file, get the most recent and store its path in $FFUFile
|
|
||||||
if ($FFUFiles.Count -gt 0) {
|
|
||||||
WriteLog 'Getting the most recent FFU file'
|
|
||||||
$FFUFile = ($FFUFiles | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1).FullName
|
|
||||||
WriteLog "Most recent .ffu file: $FFUFile"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
WriteLog "No .ffu files found in $FFUCaptureLocation"
|
|
||||||
throw $_
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif (-not $InstallApps -and (-not $AllowVHDXCaching)) {
|
|
||||||
#Get Windows Version Information from the VHDX
|
|
||||||
$winverinfo = Get-WindowsVersionInfo
|
|
||||||
WriteLog 'Creating FFU File Name'
|
|
||||||
if ($CustomFFUNameTemplate) {
|
|
||||||
$FFUFileName = New-FFUFileName
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$FFUFileName = "$($winverinfo.Name)`_$($winverinfo.DisplayVersion)`_$($shortenedWindowsSKU)`_$($winverinfo.BuildDate).ffu"
|
|
||||||
}
|
|
||||||
WriteLog "FFU file name: $FFUFileName"
|
|
||||||
$FFUFile = "$FFUCaptureLocation\$FFUFileName"
|
|
||||||
#Capture the FFU
|
|
||||||
Set-Progress -Percentage 68 -Message "Capturing FFU from VHDX..."
|
Set-Progress -Percentage 68 -Message "Capturing FFU from VHDX..."
|
||||||
WriteLog 'Capturing FFU'
|
|
||||||
Invoke-Process cmd "/c ""$DandIEnv"" && dism /Capture-FFU /ImageFile:$FFUFile /CaptureDrive:\\.\PhysicalDrive$($vhdxDisk.DiskNumber) /Name:$($winverinfo.Name)$($winverinfo.DisplayVersion)$($shortenedWindowsSKU) /Compress:Default" | Out-Null
|
if ($InstallApps -or (-not $AllowVHDXCaching)) {
|
||||||
WriteLog 'FFU Capture complete'
|
# Resolve live Windows metadata from the mounted VHDX when the image was customized in a VM.
|
||||||
Dismount-ScratchVhdx -VhdxPath $VHDXPath
|
$winverinfo = Get-WindowsVersionInfo
|
||||||
}
|
WriteLog 'Creating FFU File Name'
|
||||||
elseif (-not $InstallApps -and $AllowVHDXCaching) {
|
if ($CustomFFUNameTemplate) {
|
||||||
# Make $FFUFileName based on values in the config.json file
|
$FFUFileName = New-FFUFileName
|
||||||
WriteLog 'Creating FFU File Name'
|
|
||||||
if ($CustomFFUNameTemplate) {
|
|
||||||
$FFUFileName = New-FFUFileName
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$BuildDate = Get-Date -UFormat %b%Y
|
|
||||||
# Get Windows Information to make the FFU file name from the cachedVHDXInfo file
|
|
||||||
if ($installationType -eq 'Client') {
|
|
||||||
$FFUFileName = "Win$($cachedVHDXInfo.WindowsRelease)`_$($cachedVHDXInfo.WindowsVersion)`_$($shortenedWindowsSKU)`_$BuildDate.ffu"
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$FFUFileName = "Server$($cachedVHDXInfo.WindowsRelease)`_$($cachedVHDXInfo.WindowsVersion)`_$($shortenedWindowsSKU)`_$BuildDate.ffu"
|
$FFUFileName = "$($winverinfo.Name)`_$($winverinfo.DisplayVersion)`_$($shortenedWindowsSKU)`_$($winverinfo.BuildDate).ffu"
|
||||||
}
|
}
|
||||||
|
WriteLog "FFU file name: $FFUFileName"
|
||||||
|
$FFUFile = "$FFUCaptureLocation\$FFUFileName"
|
||||||
|
WriteLog 'Capturing FFU from mounted VHDX on host'
|
||||||
|
Invoke-Process cmd "/c ""$DandIEnv"" && dism /Capture-FFU /ImageFile:$FFUFile /CaptureDrive:\\.\PhysicalDrive$($captureDisk.DiskNumber) /Name:$($winverinfo.Name)$($winverinfo.DisplayVersion)$($shortenedWindowsSKU) /Compress:Default" | Out-Null
|
||||||
}
|
}
|
||||||
WriteLog "FFU file name: $FFUFileName"
|
else {
|
||||||
$FFUFile = "$FFUCaptureLocation\$FFUFileName"
|
# Use cached Windows metadata only when the VHDX contents were reused without VM customization.
|
||||||
#Capture the FFU
|
WriteLog 'Creating FFU File Name'
|
||||||
WriteLog 'Capturing FFU'
|
if ($CustomFFUNameTemplate) {
|
||||||
Invoke-Process cmd "/c ""$DandIEnv"" && dism /Capture-FFU /ImageFile:$FFUFile /CaptureDrive:\\.\PhysicalDrive$($vhdxDisk.DiskNumber) /Name:$($cachedVHDXInfo.WindowsRelease)$($cachedVHDXInfo.WindowsVersion)$($shortenedWindowsSKU) /Compress:Default" | Out-Null
|
$FFUFileName = New-FFUFileName
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$BuildDate = Get-Date -UFormat %b%Y
|
||||||
|
if ($installationType -eq 'Client') {
|
||||||
|
$FFUFileName = "Win$($cachedVHDXInfo.WindowsRelease)`_$($cachedVHDXInfo.WindowsVersion)`_$($shortenedWindowsSKU)`_$BuildDate.ffu"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$FFUFileName = "Server$($cachedVHDXInfo.WindowsRelease)`_$($cachedVHDXInfo.WindowsVersion)`_$($shortenedWindowsSKU)`_$BuildDate.ffu"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WriteLog "FFU file name: $FFUFileName"
|
||||||
|
$FFUFile = "$FFUCaptureLocation\$FFUFileName"
|
||||||
|
WriteLog 'Capturing FFU from mounted VHDX on host'
|
||||||
|
Invoke-Process cmd "/c ""$DandIEnv"" && dism /Capture-FFU /ImageFile:$FFUFile /CaptureDrive:\\.\PhysicalDrive$($captureDisk.DiskNumber) /Name:$($cachedVHDXInfo.WindowsRelease)$($cachedVHDXInfo.WindowsVersion)$($shortenedWindowsSKU) /Compress:Default" | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
WriteLog 'FFU Capture complete'
|
WriteLog 'FFU Capture complete'
|
||||||
|
}
|
||||||
|
finally {
|
||||||
Dismount-ScratchVhdx -VhdxPath $VHDXPath
|
Dismount-ScratchVhdx -VhdxPath $VHDXPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3893,15 +3775,6 @@ function Remove-FFUVM {
|
|||||||
Invoke-Process cmd "/c mountvol /r" | Out-Null
|
Invoke-Process cmd "/c mountvol /r" | Out-Null
|
||||||
WriteLog 'Removal complete'
|
WriteLog 'Removal complete'
|
||||||
}
|
}
|
||||||
Function Remove-FFUUserShare {
|
|
||||||
WriteLog "Removing $ShareName"
|
|
||||||
Remove-SmbShare -Name $ShareName -Force | Out-null
|
|
||||||
WriteLog 'Removal complete'
|
|
||||||
WriteLog "Removing $Username"
|
|
||||||
Remove-LocalUser -Name $Username | Out-Null
|
|
||||||
WriteLog 'Removal complete'
|
|
||||||
}
|
|
||||||
|
|
||||||
Function Get-WindowsVersionInfo {
|
Function Get-WindowsVersionInfo {
|
||||||
#This sleep prevents CBS/CSI corruption which causes issues with Windows update after deployment. Capturing from very fast disks (NVME) can cause the capture to happen faster than Windows is ready for. This seems to affect VHDX-only captures, not VM captures.
|
#This sleep prevents CBS/CSI corruption which causes issues with Windows update after deployment. Capturing from very fast disks (NVME) can cause the capture to happen faster than Windows is ready for. This seems to affect VHDX-only captures, not VM captures.
|
||||||
WriteLog 'Sleep 60 seconds before opening registry to grab Windows version info '
|
WriteLog 'Sleep 60 seconds before opening registry to grab Windows version info '
|
||||||
@@ -4441,16 +4314,8 @@ function Get-FFUEnvironment {
|
|||||||
Invoke-Process reg "unload HKLM\FFU" | Out-Null
|
Invoke-Process reg "unload HKLM\FFU" | Out-Null
|
||||||
}
|
}
|
||||||
|
|
||||||
#Remove FFU User and Share
|
|
||||||
$UserExists = Get-LocalUser -Name $UserName -ErrorAction SilentlyContinue
|
|
||||||
if ($UserExists) {
|
|
||||||
WriteLog "Removing FFU User and Share"
|
|
||||||
Remove-FFUUserShare
|
|
||||||
WriteLog 'Removal complete'
|
|
||||||
}
|
|
||||||
|
|
||||||
#Run shared cleanup to avoid duplicated logic
|
#Run shared cleanup to avoid duplicated logic
|
||||||
Invoke-FFUPostBuildCleanup -RootPath $FFUDevelopmentPath -AppsPath $AppsPath -DriversPath $DriversFolder -FFUCapturePath $FFUCaptureLocation -CaptureISOPath $CaptureISO -DeployISOPath $DeployISO -AppsISOPath $AppsISO -RemoveCaptureISO:$CleanupCaptureISO -RemoveDeployISO:$CleanupDeployISO -RemoveAppsISO:$CleanupAppsISO -RemoveDrivers:$CleanupDrivers -RemoveFFU:$RemoveFFU -RemoveApps:$RemoveApps -RemoveUpdates:$RemoveUpdates -RemoveDownloadedESD:$RemoveDownloadedESD -KBPath:$KBPath
|
Invoke-FFUPostBuildCleanup -RootPath $FFUDevelopmentPath -AppsPath $AppsPath -DriversPath $DriversFolder -FFUCapturePath $FFUCaptureLocation -DeployISOPath $DeployISO -AppsISOPath $AppsISO -RemoveDeployISO:$CleanupDeployISO -RemoveAppsISO:$CleanupAppsISO -RemoveDrivers:$CleanupDrivers -RemoveFFU:$RemoveFFU -RemoveApps:$RemoveApps -RemoveUpdates:$RemoveUpdates -RemoveDownloadedESD:$RemoveDownloadedESD -KBPath:$KBPath
|
||||||
|
|
||||||
# Remove existing Apps.iso
|
# Remove existing Apps.iso
|
||||||
if (Test-Path -Path $AppsISO) {
|
if (Test-Path -Path $AppsISO) {
|
||||||
@@ -5576,14 +5441,6 @@ if ($CopyUnattend) {
|
|||||||
WriteLog 'Unattend validation complete'
|
WriteLog 'Unattend validation complete'
|
||||||
}
|
}
|
||||||
|
|
||||||
# If InstallApps is true, we need capture media.
|
|
||||||
if ($InstallApps) {
|
|
||||||
if (-not $CreateCaptureMedia) {
|
|
||||||
WriteLog "InstallApps is true, but CreateCaptureMedia is false. Forcing to true to allow for VM capture to FFU."
|
|
||||||
$CreateCaptureMedia = $true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#Override $InstallApps value if using ESD to build FFU. This is due to a strange issue where building the FFU
|
#Override $InstallApps value if using ESD to build FFU. This is due to a strange issue where building the FFU
|
||||||
#from vhdx doesn't work (you get an older style OOBE screen and get stuck in an OOBE reboot loop when hitting next).
|
#from vhdx doesn't work (you get an older style OOBE screen and get stuck in an OOBE reboot loop when hitting next).
|
||||||
#This behavior doesn't happen with WIM files.
|
#This behavior doesn't happen with WIM files.
|
||||||
@@ -5595,45 +5452,14 @@ if ($InstallApps) {
|
|||||||
if (($InstallOffice -eq $true) -and ($InstallApps -eq $false)) {
|
if (($InstallOffice -eq $true) -and ($InstallApps -eq $false)) {
|
||||||
throw "If variable InstallOffice is set to `$true, InstallApps must also be set to `$true."
|
throw "If variable InstallOffice is set to `$true, InstallApps must also be set to `$true."
|
||||||
}
|
}
|
||||||
if (($InstallApps -and ($VMSwitchName -eq ''))) {
|
if ($VMSwitchName) {
|
||||||
throw "If variable InstallApps is set to `$true, VMSwitchName must also be set to capture the FFU. Please set -VMSwitchName and try again."
|
WriteLog "Validating -VMSwitchName $VMSwitchName"
|
||||||
}
|
|
||||||
|
|
||||||
if (($InstallApps -and ($VMHostIPAddress -eq ''))) {
|
|
||||||
throw "If variable InstallApps is set to `$true, VMHostIPAddress must also be set to capture the FFU. Please set -VMHostIPAddress and try again."
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($VMHostIPAddress) -and ($VMSwitchName)) {
|
|
||||||
WriteLog "Validating -VMSwitchName $VMSwitchName and -VMHostIPAddress $VMHostIPAddress"
|
|
||||||
#Check $VMSwitchName by using Get-VMSwitch
|
#Check $VMSwitchName by using Get-VMSwitch
|
||||||
$VMSwitch = Get-VMSwitch -Name $VMSwitchName -ErrorAction SilentlyContinue
|
$VMSwitch = Get-VMSwitch -Name $VMSwitchName -ErrorAction SilentlyContinue
|
||||||
if (-not $VMSwitch) {
|
if (-not $VMSwitch) {
|
||||||
throw "-VMSwitchName $VMSwitchName not found. Please check the -VMSwitchName parameter and try again."
|
throw "-VMSwitchName $VMSwitchName not found. Please check the -VMSwitchName parameter and try again."
|
||||||
}
|
}
|
||||||
#Find the IP address of $VMSwitch and check if it matches $VMHostIPAddress
|
WriteLog '-VMSwitchName validation complete'
|
||||||
$interfaceAlias = "vEthernet ($VMSwitchName)"
|
|
||||||
$VMSwitchIPAddress = (Get-NetIPAddress -InterfaceAlias $interfaceAlias -AddressFamily 'IPv4' -ErrorAction SilentlyContinue).IPAddress
|
|
||||||
if (-not $VMSwitchIPAddress) {
|
|
||||||
throw "IP address for -VMSwitchName $VMSwitchName not found. Please check the -VMSwitchName parameter and try again."
|
|
||||||
}
|
|
||||||
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."
|
|
||||||
}
|
|
||||||
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'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-not ($ISOPath) -and ($OptionalFeatures -like '*netfx3*')) {
|
if (-not ($ISOPath) -and ($OptionalFeatures -like '*netfx3*')) {
|
||||||
@@ -7455,32 +7281,6 @@ if ($InstallApps) {
|
|||||||
throw $_
|
throw $_
|
||||||
|
|
||||||
}
|
}
|
||||||
#Create ffu user and share to capture FFU to
|
|
||||||
try {
|
|
||||||
Set-CaptureFFU
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Host 'Set-CaptureFFU function failed'
|
|
||||||
WriteLog "Set-CaptureFFU function failed with error $_"
|
|
||||||
Remove-FFUVM -VMName $VMName
|
|
||||||
throw $_
|
|
||||||
|
|
||||||
}
|
|
||||||
If ($CreateCaptureMedia) {
|
|
||||||
#Create Capture Media
|
|
||||||
try {
|
|
||||||
Set-Progress -Percentage 45 -Message "Creating WinPE capture media..."
|
|
||||||
#This should happen while the FFUVM is building
|
|
||||||
New-PEMedia -Capture $true
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Host 'Creating capture media failed'
|
|
||||||
WriteLog "Creating capture media failed with error $_"
|
|
||||||
Remove-FFUVM -VMName $VMName
|
|
||||||
throw $_
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#Capture FFU file
|
#Capture FFU file
|
||||||
try {
|
try {
|
||||||
@@ -7490,6 +7290,10 @@ try {
|
|||||||
New-Item -Path $FFUCaptureLocation -ItemType Directory -Force
|
New-Item -Path $FFUCaptureLocation -ItemType Directory -Force
|
||||||
WriteLog "Successfully created FFU capture location at $FFUCaptureLocation"
|
WriteLog "Successfully created FFU capture location at $FFUCaptureLocation"
|
||||||
}
|
}
|
||||||
|
#Shorten Windows SKU for use in FFU file name to remove spaces and long names
|
||||||
|
WriteLog "Shortening Windows SKU: $WindowsSKU for FFU file name"
|
||||||
|
$shortenedWindowsSKU = Get-ShortenedWindowsSKU -WindowsSKU $WindowsSKU
|
||||||
|
WriteLog "Shortened Windows SKU: $shortenedWindowsSKU"
|
||||||
#Check if VM is done provisioning
|
#Check if VM is done provisioning
|
||||||
If ($InstallApps) {
|
If ($InstallApps) {
|
||||||
Set-Progress -Percentage 50 -Message "Installing applications in VM; please wait for VM to shut down..."
|
Set-Progress -Percentage 50 -Message "Installing applications in VM; please wait for VM to shut down..."
|
||||||
@@ -7502,13 +7306,9 @@ try {
|
|||||||
Set-Progress -Percentage 65 -Message "Optimizing VHDX before capture..."
|
Set-Progress -Percentage 65 -Message "Optimizing VHDX before capture..."
|
||||||
Optimize-FFUCaptureDrive -VhdxPath $VHDXPath
|
Optimize-FFUCaptureDrive -VhdxPath $VHDXPath
|
||||||
#Capture FFU file
|
#Capture FFU file
|
||||||
New-FFU $FFUVM.Name
|
New-FFU
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
#Shorten Windows SKU for use in FFU file name to remove spaces and long names
|
|
||||||
WriteLog "Shortening Windows SKU: $WindowsSKU for FFU file name"
|
|
||||||
$shortenedWindowsSKU = Get-ShortenedWindowsSKU -WindowsSKU $WindowsSKU
|
|
||||||
WriteLog "Shortened Windows SKU: $shortenedWindowsSKU"
|
|
||||||
#Create FFU file
|
#Create FFU file
|
||||||
New-FFU
|
New-FFU
|
||||||
}
|
}
|
||||||
@@ -7526,18 +7326,6 @@ Catch {
|
|||||||
throw $_
|
throw $_
|
||||||
|
|
||||||
}
|
}
|
||||||
#Clean up ffu_user and Share and clean up apps
|
|
||||||
If ($InstallApps) {
|
|
||||||
try {
|
|
||||||
Remove-FFUUserShare
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Host 'Cleaning up FFU User and/or share failed'
|
|
||||||
WriteLog "Cleaning up FFU User and/or share failed with error $_"
|
|
||||||
Remove-FFUVM -VMName $VMName
|
|
||||||
throw $_
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#Clean up VM or VHDX
|
#Clean up VM or VHDX
|
||||||
try {
|
try {
|
||||||
Remove-FFUVM
|
Remove-FFUVM
|
||||||
@@ -7554,7 +7342,7 @@ catch {
|
|||||||
If ($CreateDeploymentMedia) {
|
If ($CreateDeploymentMedia) {
|
||||||
Set-Progress -Percentage 91 -Message "Creating deployment media..."
|
Set-Progress -Percentage 91 -Message "Creating deployment media..."
|
||||||
try {
|
try {
|
||||||
New-PEMedia -Deploy $true
|
New-PEMedia
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Host 'Creating deployment media failed'
|
Write-Host 'Creating deployment media failed'
|
||||||
@@ -7611,7 +7399,7 @@ If ($BuildUSBDrive) {
|
|||||||
|
|
||||||
Set-Progress -Percentage 99 -Message "Finalizing and cleaning up..."
|
Set-Progress -Percentage 99 -Message "Finalizing and cleaning up..."
|
||||||
# Delegated post-build cleanup to common module
|
# Delegated post-build cleanup to common module
|
||||||
Invoke-FFUPostBuildCleanup -RootPath $FFUDevelopmentPath -AppsPath $AppsPath -DriversPath $DriversFolder -FFUCapturePath $FFUCaptureLocation -CaptureISOPath $CaptureISO -DeployISOPath $DeployISO -AppsISOPath $AppsISO -RemoveCaptureISO:$CleanupCaptureISO -RemoveDeployISO:$CleanupDeployISO -RemoveAppsISO:$CleanupAppsISO -RemoveDrivers:$CleanupDrivers -RemoveFFU:$RemoveFFU -RemoveApps:$RemoveApps -RemoveUpdates:$RemoveUpdates -RemoveDownloadedESD:$RemoveDownloadedESD -KBPath:$KBPath
|
Invoke-FFUPostBuildCleanup -RootPath $FFUDevelopmentPath -AppsPath $AppsPath -DriversPath $DriversFolder -FFUCapturePath $FFUCaptureLocation -DeployISOPath $DeployISO -AppsISOPath $AppsISO -RemoveDeployISO:$CleanupDeployISO -RemoveAppsISO:$CleanupAppsISO -RemoveDrivers:$CleanupDrivers -RemoveFFU:$RemoveFFU -RemoveApps:$RemoveApps -RemoveUpdates:$RemoveUpdates -RemoveDownloadedESD:$RemoveDownloadedESD -KBPath:$KBPath
|
||||||
|
|
||||||
|
|
||||||
# Remove WinGetWin32Apps.json so it is always rebuilt next run
|
# Remove WinGetWin32Apps.json so it is always rebuilt next run
|
||||||
|
|||||||
@@ -320,9 +320,6 @@
|
|||||||
<ComboBox x:Name="cmbVMSwitchName" HorizontalAlignment="Stretch" Margin="0,0,0,20" ToolTip="Name of the Hyper-V virtual switch. If $InstallApps is set to $true, this must be set. This is required to capture the FFU from the VM. The default is '*external*', but you will likely need to change this."/>
|
<ComboBox x:Name="cmbVMSwitchName" HorizontalAlignment="Stretch" Margin="0,0,0,20" ToolTip="Name of the Hyper-V virtual switch. If $InstallApps is set to $true, this must be set. This is required to capture the FFU from the VM. The default is '*external*', but you will likely need to change this."/>
|
||||||
<!-- Custom VM Switch Name -->
|
<!-- Custom VM Switch Name -->
|
||||||
<TextBox x:Name="txtCustomVMSwitchName" HorizontalAlignment="Stretch" Visibility="Collapsed" Margin="0,0,0,20" ToolTip="Enter your custom VM Switch Name if 'Other' is selected."/>
|
<TextBox x:Name="txtCustomVMSwitchName" HorizontalAlignment="Stretch" Visibility="Collapsed" Margin="0,0,0,20" ToolTip="Enter your custom VM Switch Name if 'Other' is selected."/>
|
||||||
<!-- VM Host IP Address -->
|
|
||||||
<TextBlock Text="VM Host IP Address" Margin="0,0,0,8" ToolTip="IP address of the Hyper-V host for FFU capture. If $InstallApps is set to $true, this parameter must be configured. You must manually configure this. The script will not auto-detect your IP (depending on your network adapters, it may not find the correct IP)."/>
|
|
||||||
<TextBox x:Name="txtVMHostIPAddress" HorizontalAlignment="Stretch" Margin="0,0,0,20" ToolTip="IP address of the Hyper-V host for FFU capture."/>
|
|
||||||
<!-- Disk Size (GB) -->
|
<!-- Disk Size (GB) -->
|
||||||
<TextBlock Text="Disk Size (GB)" Margin="0,0,0,8" ToolTip="Size of the virtual hard disk for the virtual machine. Default is a 50GB dynamic disk."/>
|
<TextBlock Text="Disk Size (GB)" Margin="0,0,0,8" ToolTip="Size of the virtual hard disk for the virtual machine. Default is a 50GB dynamic disk."/>
|
||||||
<TextBox x:Name="txtDiskSize" HorizontalAlignment="Stretch" Text="50" Margin="0,0,0,20" ToolTip="Size of the virtual hard disk for the virtual machine. Default is a 50GB dynamic disk."/>
|
<TextBox x:Name="txtDiskSize" HorizontalAlignment="Stretch" Text="50" Margin="0,0,0,20" ToolTip="Size of the virtual hard disk for the virtual machine. Default is a 50GB dynamic disk."/>
|
||||||
@@ -823,13 +820,13 @@
|
|||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<!-- Row 3: FFU Capture Location -->
|
<!-- Row 3: FFU Capture Location -->
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<!-- Row 4: Share Name -->
|
<!-- Row 4: Threads -->
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<!-- Row 5: Username -->
|
<!-- Row 5: BITS Priority -->
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<!-- Row 6: Threads -->
|
<!-- Row 6: Max USB Drives -->
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<!-- Row 7: BITS Priority -->
|
<!-- Row 7: Build USB Drive -->
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<!-- Row 8: General Build Options Header -->
|
<!-- Row 8: General Build Options Header -->
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
@@ -874,16 +871,6 @@
|
|||||||
<Button x:Name="btnBrowseFFUCaptureLocation" Grid.Column="1" Content="Browse..." Padding="12,4" Margin="8,0,0,0" VerticalAlignment="Center"/>
|
<Button x:Name="btnBrowseFFUCaptureLocation" Grid.Column="1" Content="Browse..." Padding="12,4" Margin="8,0,0,0" VerticalAlignment="Center"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<!-- Row 4: Share Name -->
|
|
||||||
<StackPanel Grid.Row="4" Margin="0,0,0,20">
|
|
||||||
<TextBlock Text="Share Name" Margin="0,0,0,8" ToolTip="Name of the shared folder for FFU capture. The default is FFUCaptureShare. This share will be created with rights for the user account. When finished, the share will be removed."/>
|
|
||||||
<TextBox x:Name="txtShareName" VerticalAlignment="Center" ToolTip="Name of the shared folder for FFU capture. The default is FFUCaptureShare. This share will be created with rights for the user account. When finished, the share will be removed."/>
|
|
||||||
</StackPanel>
|
|
||||||
<!-- Row 5: Username -->
|
|
||||||
<StackPanel Grid.Row="5" Margin="0,0,0,20">
|
|
||||||
<TextBlock Text="Username" Margin="0,0,0,8" ToolTip="Username for accessing the shared folder. The default is ffu_user. The script will auto-create the account and password. When finished, it will remove the account."/>
|
|
||||||
<TextBox x:Name="txtUsername" VerticalAlignment="Center" ToolTip="Username for accessing the shared folder. The default is ffu_user. The script will auto-create the account and password. When finished, it will remove the account."/>
|
|
||||||
</StackPanel>
|
|
||||||
<!-- Row 6: Threads -->
|
<!-- Row 6: Threads -->
|
||||||
<StackPanel Grid.Row="6" Margin="0,0,0,20">
|
<StackPanel Grid.Row="6" Margin="0,0,0,20">
|
||||||
<TextBlock Text="Threads" Margin="0,0,0,8" ToolTip="Controls the number of parallel threads used by ForEach-Object -Parallel and sets the value of the -ThrottleLimit parameter. Default is 5. Used in Winget, Application Copy, and driver downloads"/>
|
<TextBlock Text="Threads" Margin="0,0,0,8" ToolTip="Controls the number of parallel threads used by ForEach-Object -Parallel and sets the value of the -ThrottleLimit parameter. Default is 5. Used in Winget, Application Copy, and driver downloads"/>
|
||||||
@@ -906,7 +893,6 @@
|
|||||||
<CheckBox x:Name="chkUpdateADK" Content="Update ADK" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, the script will check for and install/update to the latest Windows ADK and WinPE add-on."/>
|
<CheckBox x:Name="chkUpdateADK" Content="Update ADK" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, the script will check for and install/update to the latest Windows ADK and WinPE add-on."/>
|
||||||
<CheckBox x:Name="chkOptimize" Content="Optimize" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, will optimize the OS when building the FFU."/>
|
<CheckBox x:Name="chkOptimize" Content="Optimize" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, will optimize the OS when building the FFU."/>
|
||||||
<CheckBox x:Name="chkAllowVHDXCaching" Content="Allow VHDX Caching" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, will cache the VHDX file to cache folder and create a config json file to track Windows build information."/>
|
<CheckBox x:Name="chkAllowVHDXCaching" Content="Allow VHDX Caching" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, will cache the VHDX file to cache folder and create a config json file to track Windows build information."/>
|
||||||
<CheckBox x:Name="chkCreateCaptureMedia" Content="Create Capture Media" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, this will create WinPE capture media for use when InstallApps is set to $true."/>
|
|
||||||
<CheckBox x:Name="chkCreateDeploymentMedia" Content="Create Deployment Media" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, this will create WinPE deployment media for use when deploying to a physical device."/>
|
<CheckBox x:Name="chkCreateDeploymentMedia" Content="Create Deployment Media" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true, this will create WinPE deployment media for use when deploying to a physical device."/>
|
||||||
<CheckBox x:Name="chkInjectUnattend" Content="Inject Unattend.xml" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true and Install Apps is enabled, copies unattend_[arch].xml from $FFUDevelopmentPath\unattend into Apps\Unattend\Unattend.xml to be used by sysprep."/>
|
<CheckBox x:Name="chkInjectUnattend" Content="Inject Unattend.xml" Margin="0,0,0,8" VerticalAlignment="Center" Tag="When set to $true and Install Apps is enabled, copies unattend_[arch].xml from $FFUDevelopmentPath\unattend into Apps\Unattend\Unattend.xml to be used by sysprep."/>
|
||||||
<CheckBox x:Name="chkVerbose" Content="Verbose" Margin="0" VerticalAlignment="Center" Tag="When set to $true, will enable write-verbose output to the console for the build script."/>
|
<CheckBox x:Name="chkVerbose" Content="Verbose" Margin="0" VerticalAlignment="Center" Tag="When set to $true, will enable write-verbose output to the console for the build script."/>
|
||||||
@@ -983,7 +969,6 @@
|
|||||||
<Expander Grid.Row="11" Header="Post-Build Cleanup" IsExpanded="False" Margin="0" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
|
<Expander Grid.Row="11" Header="Post-Build Cleanup" IsExpanded="False" Margin="0" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
|
||||||
<StackPanel Margin="0,8,0,0">
|
<StackPanel Margin="0,8,0,0">
|
||||||
<CheckBox x:Name="chkCleanupAppsISO" Content="Cleanup Apps ISO" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Remove Apps ISO after FFU capture."/>
|
<CheckBox x:Name="chkCleanupAppsISO" Content="Cleanup Apps ISO" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Remove Apps ISO after FFU capture."/>
|
||||||
<CheckBox x:Name="chkCleanupCaptureISO" Content="Cleanup Capture ISO" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Remove WinPE capture ISO after FFU capture."/>
|
|
||||||
<CheckBox x:Name="chkCleanupDeployISO" Content="Cleanup Deploy ISO" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Remove WinPE deployment ISO after FFU capture."/>
|
<CheckBox x:Name="chkCleanupDeployISO" Content="Cleanup Deploy ISO" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Remove WinPE deployment ISO after FFU capture."/>
|
||||||
<CheckBox x:Name="chkCleanupDrivers" Content="Cleanup Drivers" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Remove drivers folder after FFU capture."/>
|
<CheckBox x:Name="chkCleanupDrivers" Content="Cleanup Drivers" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Remove drivers folder after FFU capture."/>
|
||||||
<CheckBox x:Name="chkRemoveFFU" Content="Remove FFU" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Remove FFU after copying to USB drive."/>
|
<CheckBox x:Name="chkRemoveFFU" Content="Remove FFU" Margin="0,0,0,8" VerticalAlignment="Center" Tag="Remove FFU after copying to USB drive."/>
|
||||||
|
|||||||
@@ -3,11 +3,8 @@ param (
|
|||||||
[string]$adkPath = 'C:\Program Files (x86)\Windows Kits\10\',
|
[string]$adkPath = 'C:\Program Files (x86)\Windows Kits\10\',
|
||||||
[string]$WindowsArch = 'x64',
|
[string]$WindowsArch = 'x64',
|
||||||
[bool]$CopyPEDrivers = $false,
|
[bool]$CopyPEDrivers = $false,
|
||||||
[string]$CaptureISO = "$PSScriptRoot\WinPE_FFU_Capture_x64.iso",
|
|
||||||
[string]$DeployISO = "$PSScriptRoot\WinPE_FFU_Deploy_x64.iso",
|
[string]$DeployISO = "$PSScriptRoot\WinPE_FFU_Deploy_x64.iso",
|
||||||
[string]$LogFile = "$PSScriptRoot\Create-PEMedia.log",
|
[string]$LogFile = "$PSScriptRoot\Create-PEMedia.log"
|
||||||
[bool]$Capture,
|
|
||||||
[bool]$Deploy = $true
|
|
||||||
)
|
)
|
||||||
|
|
||||||
function WriteLog($LogText) {
|
function WriteLog($LogText) {
|
||||||
@@ -77,12 +74,7 @@ function Invoke-Process {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function New-PEMedia {
|
function New-PEMedia {
|
||||||
param (
|
param ()
|
||||||
[Parameter()]
|
|
||||||
[bool]$Capture,
|
|
||||||
[Parameter()]
|
|
||||||
[bool]$Deploy
|
|
||||||
)
|
|
||||||
#Need to use the Demployment and Imaging tools environment to create winPE media
|
#Need to use the Demployment and Imaging tools environment to create winPE media
|
||||||
$DandIEnv = "$adkPath`Assessment and Deployment Kit\Deployment Tools\DandISetEnv.bat"
|
$DandIEnv = "$adkPath`Assessment and Deployment Kit\Deployment Tools\DandISetEnv.bat"
|
||||||
$WinPEFFUPath = "$FFUDevelopmentPath\WinPE"
|
$WinPEFFUPath = "$FFUDevelopmentPath\WinPE"
|
||||||
@@ -135,36 +127,21 @@ function New-PEMedia {
|
|||||||
Add-WindowsPackage -Path "$WinPEFFUPath\mount" -PackagePath $PackagePath | Out-Null
|
Add-WindowsPackage -Path "$WinPEFFUPath\mount" -PackagePath $PackagePath | Out-Null
|
||||||
WriteLog "Adding package complete"
|
WriteLog "Adding package complete"
|
||||||
}
|
}
|
||||||
If ($Capture) {
|
WriteLog "Copying $FFUDevelopmentPath\WinPEDeployFFUFiles\* to WinPE deploy media"
|
||||||
WriteLog "Copying $FFUDevelopmentPath\WinPECaptureFFUFiles\* to WinPE capture media"
|
Copy-Item -Path "$FFUDevelopmentPath\WinPEDeployFFUFiles\*" -Destination "$WinPEFFUPath\mount" -Recurse -Force | Out-Null
|
||||||
Copy-Item -Path "$FFUDevelopmentPath\WinPECaptureFFUFiles\*" -Destination "$WinPEFFUPath\mount" -Recurse -Force | out-null
|
WriteLog 'Copy complete'
|
||||||
WriteLog "Copy complete"
|
#If $CopyPEDrivers = $true, add drivers to WinPE media using dism
|
||||||
#Remove Bootfix.bin - for BIOS systems, shouldn't be needed, but doesn't hurt to remove for our purposes
|
if ($CopyPEDrivers) {
|
||||||
#Remove-Item -Path "$WinPEFFUPath\media\boot\bootfix.bin" -Force | Out-null
|
WriteLog "Adding drivers to WinPE media"
|
||||||
# $WinPEISOName = 'WinPE_FFU_Capture.iso'
|
try {
|
||||||
$WinPEISOFile = $CaptureISO
|
Add-WindowsDriver -Path "$WinPEFFUPath\Mount" -Driver "$FFUDevelopmentPath\PEDrivers" -Recurse -ErrorAction SilentlyContinue | Out-null
|
||||||
# $Capture = $false
|
|
||||||
}
|
|
||||||
If ($Deploy) {
|
|
||||||
WriteLog "Copying $FFUDevelopmentPath\WinPEDeployFFUFiles\* to WinPE deploy media"
|
|
||||||
Copy-Item -Path "$FFUDevelopmentPath\WinPEDeployFFUFiles\*" -Destination "$WinPEFFUPath\mount" -Recurse -Force | Out-Null
|
|
||||||
WriteLog 'Copy complete'
|
|
||||||
#If $CopyPEDrivers = $true, add drivers to WinPE media using dism
|
|
||||||
if ($CopyPEDrivers) {
|
|
||||||
WriteLog "Adding drivers to WinPE media"
|
|
||||||
try {
|
|
||||||
Add-WindowsDriver -Path "$WinPEFFUPath\Mount" -Driver "$FFUDevelopmentPath\PEDrivers" -Recurse -ErrorAction SilentlyContinue | Out-null
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
WriteLog 'Some drivers failed to be added to the FFU. This can be expected. Continuing.'
|
|
||||||
}
|
|
||||||
WriteLog "Adding drivers complete"
|
|
||||||
}
|
}
|
||||||
# $WinPEISOName = 'WinPE_FFU_Deploy.iso'
|
catch {
|
||||||
$WinPEISOFile = $DeployISO
|
WriteLog 'Some drivers failed to be added to the FFU. This can be expected. Continuing.'
|
||||||
|
}
|
||||||
# $Deploy = $false
|
WriteLog "Adding drivers complete"
|
||||||
}
|
}
|
||||||
|
$WinPEISOFile = $DeployISO
|
||||||
WriteLog 'Dismounting WinPE media'
|
WriteLog 'Dismounting WinPE media'
|
||||||
Dismount-WindowsImage -Path "$WinPEFFUPath\mount" -Save | Out-Null
|
Dismount-WindowsImage -Path "$WinPEFFUPath\mount" -Save | Out-Null
|
||||||
WriteLog 'Dismount complete'
|
WriteLog 'Dismount complete'
|
||||||
@@ -179,21 +156,10 @@ function New-PEMedia {
|
|||||||
WriteLog "Creating WinPE ISO at $WinPEISOFile"
|
WriteLog "Creating WinPE ISO at $WinPEISOFile"
|
||||||
# & "$OSCDIMG" -m -o -u2 -udfver102 -bootdata:2`#p0,e,b$OSCDIMGPath\etfsboot.com`#pEF,e,b$OSCDIMGPath\Efisys_noprompt.bin $WinPEFFUPath\media $FFUDevelopmentPath\$WinPEISOName | Out-null
|
# & "$OSCDIMG" -m -o -u2 -udfver102 -bootdata:2`#p0,e,b$OSCDIMGPath\etfsboot.com`#pEF,e,b$OSCDIMGPath\Efisys_noprompt.bin $WinPEFFUPath\media $FFUDevelopmentPath\$WinPEISOName | Out-null
|
||||||
if($WindowsArch -eq 'x64'){
|
if($WindowsArch -eq 'x64'){
|
||||||
if($Capture){
|
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:2`#p0,e,b`"$OSCDIMGPath\etfsboot.com`"`#pEF,e,b`"$OSCDIMGPath\Efisys.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
||||||
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:2`#p0,e,b`"$OSCDIMGPath\etfsboot.com`"`#pEF,e,b`"$OSCDIMGPath\Efisys_noprompt.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
|
||||||
}
|
|
||||||
if($Deploy){
|
|
||||||
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:2`#p0,e,b`"$OSCDIMGPath\etfsboot.com`"`#pEF,e,b`"$OSCDIMGPath\Efisys.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
elseif($WindowsArch -eq 'arm64'){
|
elseif($WindowsArch -eq 'arm64'){
|
||||||
if($Capture){
|
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:1`#pEF,e,b`"$OSCDIMGPath\Efisys.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
||||||
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:1`#pEF,e,b`"$OSCDIMGPath\Efisys_noprompt.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
|
||||||
}
|
|
||||||
if($Deploy){
|
|
||||||
$OSCDIMGArgs = "-m -o -u2 -udfver102 -bootdata:1`#pEF,e,b`"$OSCDIMGPath\Efisys.bin`" `"$WinPEFFUPath\media`" `"$WinPEISOFile`""
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Invoke-Process $OSCDIMG $OSCDIMGArgs
|
Invoke-Process $OSCDIMG $OSCDIMGArgs
|
||||||
WriteLog "ISO created successfully"
|
WriteLog "ISO created successfully"
|
||||||
@@ -201,9 +167,4 @@ function New-PEMedia {
|
|||||||
Remove-Item -Path "$WinPEFFUPath" -Recurse -Force
|
Remove-Item -Path "$WinPEFFUPath" -Recurse -Force
|
||||||
WriteLog 'Cleanup complete'
|
WriteLog 'Cleanup complete'
|
||||||
}
|
}
|
||||||
if($Capture){
|
New-PEMedia
|
||||||
New-PEMedia -Capture $Capture
|
|
||||||
}
|
|
||||||
if($Deploy){
|
|
||||||
New-PEMedia -Deploy $Deploy
|
|
||||||
}
|
|
||||||
@@ -85,12 +85,10 @@ graph TD
|
|||||||
subgraph "VM-Based Capture (-InstallApps)"
|
subgraph "VM-Based Capture (-InstallApps)"
|
||||||
direction LR
|
direction LR
|
||||||
BB[Create Hyper-V VM from VHDX];
|
BB[Create Hyper-V VM from VHDX];
|
||||||
BB --> BC["Create WinPE Capture Media iso"];
|
BB --> BE["Start VM: Boots to Audit Mode"];
|
||||||
BC --> BD[Configure network share for capture];
|
|
||||||
BD --> BE["Start VM: Boots to Audit Mode"];
|
|
||||||
BE --> BF[Orchestrator runs: Installs apps, syspreps, shuts down];
|
BE --> BF[Orchestrator runs: Installs apps, syspreps, shuts down];
|
||||||
BF --> BG[VM reboots from Capture Media];
|
BF --> BG[Host optimizes and remounts VHDX];
|
||||||
BG --> BH["CaptureFFU.ps1 runs, saves FFU to share, shuts down"];
|
BG --> BH["DISM captures FFU directly from host-mounted VHDX"];
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph "Direct VHDX Capture"
|
subgraph "Direct VHDX Capture"
|
||||||
|
|||||||
@@ -6,11 +6,9 @@ function Invoke-FFUPostBuildCleanup {
|
|||||||
[string]$AppsPath,
|
[string]$AppsPath,
|
||||||
[string]$DriversPath,
|
[string]$DriversPath,
|
||||||
[string]$FFUCapturePath,
|
[string]$FFUCapturePath,
|
||||||
[string]$CaptureISOPath,
|
|
||||||
[string]$DeployISOPath,
|
[string]$DeployISOPath,
|
||||||
[string]$AppsISOPath,
|
[string]$AppsISOPath,
|
||||||
[string]$KBPath,
|
[string]$KBPath,
|
||||||
[bool]$RemoveCaptureISO = $false,
|
|
||||||
[bool]$RemoveDeployISO = $false,
|
[bool]$RemoveDeployISO = $false,
|
||||||
[bool]$RemoveAppsISO = $false,
|
[bool]$RemoveAppsISO = $false,
|
||||||
[bool]$RemoveDrivers = $false,
|
[bool]$RemoveDrivers = $false,
|
||||||
@@ -22,13 +20,9 @@ function Invoke-FFUPostBuildCleanup {
|
|||||||
$originalProgressPreference = $ProgressPreference
|
$originalProgressPreference = $ProgressPreference
|
||||||
$ProgressPreference = 'SilentlyContinue'
|
$ProgressPreference = 'SilentlyContinue'
|
||||||
try {
|
try {
|
||||||
WriteLog "CommonCleanup: Starting cleanup (CaptureISO=$RemoveCaptureISO DeployISO=$RemoveDeployISO AppsISO=$RemoveAppsISO Drivers=$RemoveDrivers FFU=$RemoveFFU Apps=$RemoveApps Updates=$RemoveUpdates RemoveDownloadedESD=$RemoveDownloadedESD KBPath=$KBPath)."
|
WriteLog "CommonCleanup: Starting cleanup (DeployISO=$RemoveDeployISO AppsISO=$RemoveAppsISO Drivers=$RemoveDrivers FFU=$RemoveFFU Apps=$RemoveApps Updates=$RemoveUpdates RemoveDownloadedESD=$RemoveDownloadedESD KBPath=$KBPath)."
|
||||||
|
|
||||||
# Primary ISO paths (new naming/location)
|
# Primary ISO paths (new naming/location)
|
||||||
if ($RemoveCaptureISO -and -not [string]::IsNullOrWhiteSpace($CaptureISOPath) -and (Test-Path -LiteralPath $CaptureISOPath)) {
|
|
||||||
WriteLog "CommonCleanup: Removing $CaptureISOPath"
|
|
||||||
try { Remove-Item -LiteralPath $CaptureISOPath -Force -ErrorAction Stop } catch { WriteLog "CommonCleanup: Failed removing $CaptureISOPath : $($_.Exception.Message)" }
|
|
||||||
}
|
|
||||||
if ($RemoveDeployISO -and -not [string]::IsNullOrWhiteSpace($DeployISOPath) -and (Test-Path -LiteralPath $DeployISOPath)) {
|
if ($RemoveDeployISO -and -not [string]::IsNullOrWhiteSpace($DeployISOPath) -and (Test-Path -LiteralPath $DeployISOPath)) {
|
||||||
WriteLog "CommonCleanup: Removing $DeployISOPath"
|
WriteLog "CommonCleanup: Removing $DeployISOPath"
|
||||||
try { Remove-Item -LiteralPath $DeployISOPath -Force -ErrorAction Stop } catch { WriteLog "CommonCleanup: Failed removing $DeployISOPath : $($_.Exception.Message)" }
|
try { Remove-Item -LiteralPath $DeployISOPath -Force -ErrorAction Stop } catch { WriteLog "CommonCleanup: Failed removing $DeployISOPath : $($_.Exception.Message)" }
|
||||||
@@ -39,11 +33,6 @@ function Invoke-FFUPostBuildCleanup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Legacy / root-level WinPE ISOs (pattern-based)
|
# Legacy / root-level WinPE ISOs (pattern-based)
|
||||||
if ($RemoveCaptureISO) {
|
|
||||||
Get-ChildItem -LiteralPath $RootPath -Filter 'WinPE_FFU_Capture*.iso' -ErrorAction SilentlyContinue | ForEach-Object {
|
|
||||||
try { WriteLog "CommonCleanup: Removing legacy capture ISO $($_.FullName)"; Remove-Item -LiteralPath $_.FullName -Force -ErrorAction Stop } catch { WriteLog "CommonCleanup: Failed removing legacy capture ISO $($_.FullName) : $($_.Exception.Message)" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($RemoveDeployISO) {
|
if ($RemoveDeployISO) {
|
||||||
Get-ChildItem -LiteralPath $RootPath -Filter 'WinPE_FFU_Deploy*.iso' -ErrorAction SilentlyContinue | ForEach-Object {
|
Get-ChildItem -LiteralPath $RootPath -Filter 'WinPE_FFU_Deploy*.iso' -ErrorAction SilentlyContinue | ForEach-Object {
|
||||||
try { WriteLog "CommonCleanup: Removing legacy deploy ISO $($_.FullName)"; Remove-Item -LiteralPath $_.FullName -Force -ErrorAction Stop } catch { WriteLog "CommonCleanup: Failed removing legacy deploy ISO $($_.FullName) : $($_.Exception.Message)" }
|
try { WriteLog "CommonCleanup: Removing legacy deploy ISO $($_.FullName)"; Remove-Item -LiteralPath $_.FullName -Force -ErrorAction Stop } catch { WriteLog "CommonCleanup: Failed removing legacy deploy ISO $($_.FullName) : $($_.Exception.Message)" }
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ function Get-UIConfig {
|
|||||||
else { $null }
|
else { $null }
|
||||||
BuildUSBDrive = $State.Controls.chkBuildUSBDriveEnable.IsChecked
|
BuildUSBDrive = $State.Controls.chkBuildUSBDriveEnable.IsChecked
|
||||||
CleanupAppsISO = $State.Controls.chkCleanupAppsISO.IsChecked
|
CleanupAppsISO = $State.Controls.chkCleanupAppsISO.IsChecked
|
||||||
CleanupCaptureISO = $State.Controls.chkCleanupCaptureISO.IsChecked
|
|
||||||
CleanupDeployISO = $State.Controls.chkCleanupDeployISO.IsChecked
|
CleanupDeployISO = $State.Controls.chkCleanupDeployISO.IsChecked
|
||||||
CleanupDrivers = $State.Controls.chkCleanupDrivers.IsChecked
|
CleanupDrivers = $State.Controls.chkCleanupDrivers.IsChecked
|
||||||
CompactOS = $State.Controls.chkCompactOS.IsChecked
|
CompactOS = $State.Controls.chkCompactOS.IsChecked
|
||||||
@@ -38,7 +37,6 @@ function Get-UIConfig {
|
|||||||
CopyPPKG = $State.Controls.chkCopyPPKG.IsChecked
|
CopyPPKG = $State.Controls.chkCopyPPKG.IsChecked
|
||||||
CopyUnattend = $State.Controls.chkCopyUnattend.IsChecked
|
CopyUnattend = $State.Controls.chkCopyUnattend.IsChecked
|
||||||
CopyAdditionalFFUFiles = $State.Controls.chkCopyAdditionalFFUFiles.IsChecked
|
CopyAdditionalFFUFiles = $State.Controls.chkCopyAdditionalFFUFiles.IsChecked
|
||||||
CreateCaptureMedia = $State.Controls.chkCreateCaptureMedia.IsChecked
|
|
||||||
CreateDeploymentMedia = $State.Controls.chkCreateDeploymentMedia.IsChecked
|
CreateDeploymentMedia = $State.Controls.chkCreateDeploymentMedia.IsChecked
|
||||||
InjectUnattend = $State.Controls.chkInjectUnattend.IsChecked
|
InjectUnattend = $State.Controls.chkInjectUnattend.IsChecked
|
||||||
CustomFFUNameTemplate = $State.Controls.txtCustomFFUNameTemplate.Text
|
CustomFFUNameTemplate = $State.Controls.txtCustomFFUNameTemplate.Text
|
||||||
@@ -84,7 +82,6 @@ function Get-UIConfig {
|
|||||||
RemoveFFU = $State.Controls.chkRemoveFFU.IsChecked
|
RemoveFFU = $State.Controls.chkRemoveFFU.IsChecked
|
||||||
RemoveUpdates = $State.Controls.chkRemoveUpdates.IsChecked
|
RemoveUpdates = $State.Controls.chkRemoveUpdates.IsChecked
|
||||||
RemoveDownloadedESD = $State.Controls.chkRemoveDownloadedESD.IsChecked
|
RemoveDownloadedESD = $State.Controls.chkRemoveDownloadedESD.IsChecked
|
||||||
ShareName = $State.Controls.txtShareName.Text
|
|
||||||
UpdateADK = $State.Controls.chkUpdateADK.IsChecked
|
UpdateADK = $State.Controls.chkUpdateADK.IsChecked
|
||||||
UpdateEdge = $State.Controls.chkUpdateEdge.IsChecked
|
UpdateEdge = $State.Controls.chkUpdateEdge.IsChecked
|
||||||
UpdateLatestCU = $State.Controls.chkUpdateLatestCU.IsChecked
|
UpdateLatestCU = $State.Controls.chkUpdateLatestCU.IsChecked
|
||||||
@@ -96,13 +93,11 @@ function Get-UIConfig {
|
|||||||
UpdatePreviewCU = $State.Controls.chkUpdatePreviewCU.IsChecked
|
UpdatePreviewCU = $State.Controls.chkUpdatePreviewCU.IsChecked
|
||||||
UserAppListPath = $State.Controls.txtUserAppListPath.Text
|
UserAppListPath = $State.Controls.txtUserAppListPath.Text
|
||||||
USBDriveList = @{}
|
USBDriveList = @{}
|
||||||
Username = $State.Controls.txtUsername.Text
|
|
||||||
Threads = [int]$State.Controls.txtThreads.Text
|
Threads = [int]$State.Controls.txtThreads.Text
|
||||||
BitsPriority = $State.Controls.cmbBitsPriority.SelectedItem
|
BitsPriority = $State.Controls.cmbBitsPriority.SelectedItem
|
||||||
MaxUSBDrives = [int]$State.Controls.txtMaxUSBDrives.Text
|
MaxUSBDrives = [int]$State.Controls.txtMaxUSBDrives.Text
|
||||||
ThemeMode = if ($null -ne $State.Controls.cmbThemeMode -and $null -ne $State.Controls.cmbThemeMode.SelectedItem) { $State.Controls.cmbThemeMode.SelectedItem } else { "System" }
|
ThemeMode = if ($null -ne $State.Controls.cmbThemeMode -and $null -ne $State.Controls.cmbThemeMode.SelectedItem) { $State.Controls.cmbThemeMode.SelectedItem } else { "System" }
|
||||||
Verbose = $State.Controls.chkVerbose.IsChecked
|
Verbose = $State.Controls.chkVerbose.IsChecked
|
||||||
VMHostIPAddress = $State.Controls.txtVMHostIPAddress.Text
|
|
||||||
VMLocation = $State.Controls.txtVMLocation.Text
|
VMLocation = $State.Controls.txtVMLocation.Text
|
||||||
VMSwitchName = if ($State.Controls.cmbVMSwitchName.SelectedItem -eq 'Other') {
|
VMSwitchName = if ($State.Controls.cmbVMSwitchName.SelectedItem -eq 'Other') {
|
||||||
$State.Controls.txtCustomVMSwitchName.Text
|
$State.Controls.txtCustomVMSwitchName.Text
|
||||||
@@ -414,7 +409,6 @@ function Select-VMSwitchFromConfig {
|
|||||||
$State.Controls.txtCustomVMSwitchName.Visibility = 'Visible'
|
$State.Controls.txtCustomVMSwitchName.Visibility = 'Visible'
|
||||||
$State.Controls.txtCustomVMSwitchName.Text = $configSwitch
|
$State.Controls.txtCustomVMSwitchName.Text = $configSwitch
|
||||||
$State.Data.customVMSwitchName = $configSwitch
|
$State.Data.customVMSwitchName = $configSwitch
|
||||||
$State.Data.customVMHostIP = $ConfigContent.VMHostIPAddress
|
|
||||||
WriteLog "LoadConfig: VMSwitchName '$configSwitch' not found. Selected 'Other' and populated custom VM Switch Name textbox."
|
WriteLog "LoadConfig: VMSwitchName '$configSwitch' not found. Selected 'Other' and populated custom VM Switch Name textbox."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,8 +436,6 @@ function Update-UIFromConfig {
|
|||||||
Set-UIValue -ControlName 'txtFFUDevPath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'FFUDevelopmentPath' -State $State
|
Set-UIValue -ControlName 'txtFFUDevPath' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'FFUDevelopmentPath' -State $State
|
||||||
Set-UIValue -ControlName 'txtCustomFFUNameTemplate' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'CustomFFUNameTemplate' -State $State
|
Set-UIValue -ControlName 'txtCustomFFUNameTemplate' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'CustomFFUNameTemplate' -State $State
|
||||||
Set-UIValue -ControlName 'txtFFUCaptureLocation' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'FFUCaptureLocation' -State $State
|
Set-UIValue -ControlName 'txtFFUCaptureLocation' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'FFUCaptureLocation' -State $State
|
||||||
Set-UIValue -ControlName 'txtShareName' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'ShareName' -State $State
|
|
||||||
Set-UIValue -ControlName 'txtUsername' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Username' -State $State
|
|
||||||
Set-UIValue -ControlName 'txtThreads' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Threads' -State $State
|
Set-UIValue -ControlName 'txtThreads' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Threads' -State $State
|
||||||
Set-UIValue -ControlName 'cmbBitsPriority' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'BitsPriority' -State $State
|
Set-UIValue -ControlName 'cmbBitsPriority' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'BitsPriority' -State $State
|
||||||
Set-UIValue -ControlName 'txtMaxUSBDrives' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'MaxUSBDrives' -State $State
|
Set-UIValue -ControlName 'txtMaxUSBDrives' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'MaxUSBDrives' -State $State
|
||||||
@@ -455,7 +447,6 @@ function Update-UIFromConfig {
|
|||||||
Set-UIValue -ControlName 'chkAllowExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'AllowExternalHardDiskMedia' -State $State
|
Set-UIValue -ControlName 'chkAllowExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'AllowExternalHardDiskMedia' -State $State
|
||||||
Set-UIValue -ControlName 'chkPromptExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'PromptExternalHardDiskMedia' -State $State
|
Set-UIValue -ControlName 'chkPromptExternalHardDiskMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'PromptExternalHardDiskMedia' -State $State
|
||||||
Set-UIValue -ControlName 'chkCopyAdditionalFFUFiles' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyAdditionalFFUFiles' -State $State
|
Set-UIValue -ControlName 'chkCopyAdditionalFFUFiles' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CopyAdditionalFFUFiles' -State $State
|
||||||
Set-UIValue -ControlName 'chkCreateCaptureMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateCaptureMedia' -State $State
|
|
||||||
Set-UIValue -ControlName 'chkCreateDeploymentMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateDeploymentMedia' -State $State
|
Set-UIValue -ControlName 'chkCreateDeploymentMedia' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CreateDeploymentMedia' -State $State
|
||||||
Set-UIValue -ControlName 'chkInjectUnattend' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'InjectUnattend' -State $State
|
Set-UIValue -ControlName 'chkInjectUnattend' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'InjectUnattend' -State $State
|
||||||
Set-UIValue -ControlName 'chkVerbose' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'Verbose' -State $State
|
Set-UIValue -ControlName 'chkVerbose' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'Verbose' -State $State
|
||||||
@@ -467,7 +458,6 @@ function Update-UIFromConfig {
|
|||||||
|
|
||||||
# Post Build Cleanup group (Build Tab)
|
# Post Build Cleanup group (Build Tab)
|
||||||
Set-UIValue -ControlName 'chkCleanupAppsISO' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupAppsISO' -State $State
|
Set-UIValue -ControlName 'chkCleanupAppsISO' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupAppsISO' -State $State
|
||||||
Set-UIValue -ControlName 'chkCleanupCaptureISO' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupCaptureISO' -State $State
|
|
||||||
Set-UIValue -ControlName 'chkCleanupDeployISO' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupDeployISO' -State $State
|
Set-UIValue -ControlName 'chkCleanupDeployISO' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupDeployISO' -State $State
|
||||||
Set-UIValue -ControlName 'chkCleanupDrivers' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupDrivers' -State $State
|
Set-UIValue -ControlName 'chkCleanupDrivers' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CleanupDrivers' -State $State
|
||||||
Set-UIValue -ControlName 'chkRemoveFFU' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveFFU' -State $State
|
Set-UIValue -ControlName 'chkRemoveFFU' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'RemoveFFU' -State $State
|
||||||
@@ -477,7 +467,6 @@ function Update-UIFromConfig {
|
|||||||
|
|
||||||
# Hyper-V Settings
|
# Hyper-V Settings
|
||||||
Select-VMSwitchFromConfig -State $State -ConfigContent $ConfigContent
|
Select-VMSwitchFromConfig -State $State -ConfigContent $ConfigContent
|
||||||
Set-UIValue -ControlName 'txtVMHostIPAddress' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'VMHostIPAddress' -State $State
|
|
||||||
Set-UIValue -ControlName 'txtDiskSize' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Disksize' -TransformValue { param($val) $val / 1GB } -State $State
|
Set-UIValue -ControlName 'txtDiskSize' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Disksize' -TransformValue { param($val) $val / 1GB } -State $State
|
||||||
Set-UIValue -ControlName 'txtMemory' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Memory' -TransformValue { param($val) $val / 1GB } -State $State
|
Set-UIValue -ControlName 'txtMemory' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Memory' -TransformValue { param($val) $val / 1GB } -State $State
|
||||||
Set-UIValue -ControlName 'txtProcessors' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Processors' -State $State
|
Set-UIValue -ControlName 'txtProcessors' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Processors' -State $State
|
||||||
@@ -884,11 +873,10 @@ function Invoke-RestoreDefaults {
|
|||||||
$ffuCaptureRaw = Get-PathScalar -value $State.Controls.txtFFUCaptureLocation.Text
|
$ffuCaptureRaw = Get-PathScalar -value $State.Controls.txtFFUCaptureLocation.Text
|
||||||
$ffuCapturePath = if ([string]::IsNullOrWhiteSpace($ffuCaptureRaw)) { Join-Path $rootPath 'FFU' } else { $ffuCaptureRaw }
|
$ffuCapturePath = if ([string]::IsNullOrWhiteSpace($ffuCaptureRaw)) { Join-Path $rootPath 'FFU' } else { $ffuCaptureRaw }
|
||||||
|
|
||||||
$captureISOPath = Join-Path $rootPath 'WinPECaptureFFUFiles\WinPE-Capture.iso'
|
|
||||||
$deployISOPath = Join-Path $rootPath 'WinPEDeployFFUFiles\WinPE-Deploy.iso'
|
$deployISOPath = Join-Path $rootPath 'WinPEDeployFFUFiles\WinPE-Deploy.iso'
|
||||||
$appsISOPath = Join-Path $rootPath 'Apps.iso'
|
$appsISOPath = Join-Path $rootPath 'Apps.iso'
|
||||||
|
|
||||||
$msg = "Restore Defaults will:`n`n- Delete generated config and app/driver list JSON files`n- Remove ISO files (Capture, Deploy, Apps) if present`n- Remove Apps/Update/downloaded artifacts`n- Remove driver folder contents (not the folder)`n- Remove FFU files in the capture folder`n`nSample/template files and VM/VHDX cache are NOT removed.`n`nProceed?"
|
$msg = "Restore Defaults will:`n`n- Delete generated config and app/driver list JSON files`n- Remove ISO files (Deploy, Apps) if present`n- Remove Apps/Update/downloaded artifacts`n- Remove driver folder contents (not the folder)`n- Remove FFU files in the capture folder`n`nSample/template files and VM/VHDX cache are NOT removed.`n`nProceed?"
|
||||||
$result = [System.Windows.MessageBox]::Show($msg, "Confirm Restore Defaults", "YesNo", "Warning")
|
$result = [System.Windows.MessageBox]::Show($msg, "Confirm Restore Defaults", "YesNo", "Warning")
|
||||||
if ($result -ne [System.Windows.MessageBoxResult]::Yes) {
|
if ($result -ne [System.Windows.MessageBoxResult]::Yes) {
|
||||||
WriteLog "RestoreDefaults: User cancelled."
|
WriteLog "RestoreDefaults: User cancelled."
|
||||||
@@ -924,11 +912,9 @@ function Invoke-RestoreDefaults {
|
|||||||
-AppsPath $appsPath `
|
-AppsPath $appsPath `
|
||||||
-DriversPath $driversPath `
|
-DriversPath $driversPath `
|
||||||
-FFUCapturePath $ffuCapturePath `
|
-FFUCapturePath $ffuCapturePath `
|
||||||
-CaptureISOPath $captureISOPath `
|
|
||||||
-DeployISOPath $deployISOPath `
|
-DeployISOPath $deployISOPath `
|
||||||
-AppsISOPath $appsISOPath `
|
-AppsISOPath $appsISOPath `
|
||||||
-KBPath (Join-Path $rootPath 'KB') `
|
-KBPath (Join-Path $rootPath 'KB') `
|
||||||
-RemoveCaptureISO:$true `
|
|
||||||
-RemoveDeployISO:$true `
|
-RemoveDeployISO:$true `
|
||||||
-RemoveAppsISO:$true `
|
-RemoveAppsISO:$true `
|
||||||
-RemoveDrivers:$true `
|
-RemoveDrivers:$true `
|
||||||
|
|||||||
@@ -391,30 +391,13 @@ function Register-EventHandlers {
|
|||||||
if ([string]::IsNullOrWhiteSpace($localState.Controls.txtCustomVMSwitchName.Text) -and $null -ne $localState.Data.customVMSwitchName) {
|
if ([string]::IsNullOrWhiteSpace($localState.Controls.txtCustomVMSwitchName.Text) -and $null -ne $localState.Data.customVMSwitchName) {
|
||||||
$localState.Controls.txtCustomVMSwitchName.Text = $localState.Data.customVMSwitchName
|
$localState.Controls.txtCustomVMSwitchName.Text = $localState.Data.customVMSwitchName
|
||||||
}
|
}
|
||||||
if ($null -ne $localState.Data.customVMHostIP -and -not [string]::IsNullOrWhiteSpace($localState.Data.customVMHostIP)) {
|
|
||||||
$localState.Controls.txtVMHostIPAddress.Text = $localState.Data.customVMHostIP
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$localState.Controls.txtCustomVMSwitchName.Visibility = 'Collapsed'
|
$localState.Controls.txtCustomVMSwitchName.Visibility = 'Collapsed'
|
||||||
if ($null -ne $selectedItem -and $localState.Data.vmSwitchMap.ContainsKey($selectedItem)) {
|
|
||||||
$localState.Controls.txtVMHostIPAddress.Text = $localState.Data.vmSwitchMap[$selectedItem]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$localState.Controls.txtVMHostIPAddress.Text = '' # Clear IP if not found or key null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
# Persist custom VM switch name/IP when user edits them while 'Other' is selected
|
# Persist custom VM switch name when user edits it while 'Other' is selected
|
||||||
$State.Controls.txtVMHostIPAddress.Add_LostFocus({
|
|
||||||
param($eventSource, $routedEventArgs)
|
|
||||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
|
||||||
$localState = $window.Tag
|
|
||||||
if ($localState.Controls.cmbVMSwitchName.SelectedItem -eq 'Other') {
|
|
||||||
$localState.Data.customVMHostIP = $localState.Controls.txtVMHostIPAddress.Text
|
|
||||||
}
|
|
||||||
})
|
|
||||||
$State.Controls.txtCustomVMSwitchName.Add_LostFocus({
|
$State.Controls.txtCustomVMSwitchName.Add_LostFocus({
|
||||||
param($eventSource, $routedEventArgs)
|
param($eventSource, $routedEventArgs)
|
||||||
$window = [System.Windows.Window]::GetWindow($eventSource)
|
$window = [System.Windows.Window]::GetWindow($eventSource)
|
||||||
|
|||||||
@@ -206,20 +206,16 @@ function Initialize-UIControls {
|
|||||||
$State.Controls.pbOverallProgress = $window.FindName('progressBar')
|
$State.Controls.pbOverallProgress = $window.FindName('progressBar')
|
||||||
$State.Controls.txtOverallStatus = $window.FindName('txtStatus')
|
$State.Controls.txtOverallStatus = $window.FindName('txtStatus')
|
||||||
$State.Controls.cmbVMSwitchName = $window.FindName('cmbVMSwitchName')
|
$State.Controls.cmbVMSwitchName = $window.FindName('cmbVMSwitchName')
|
||||||
$State.Controls.txtVMHostIPAddress = $window.FindName('txtVMHostIPAddress')
|
|
||||||
$State.Controls.txtCustomVMSwitchName = $window.FindName('txtCustomVMSwitchName')
|
$State.Controls.txtCustomVMSwitchName = $window.FindName('txtCustomVMSwitchName')
|
||||||
$State.Controls.txtFFUDevPath = $window.FindName('txtFFUDevPath')
|
$State.Controls.txtFFUDevPath = $window.FindName('txtFFUDevPath')
|
||||||
$State.Controls.txtCustomFFUNameTemplate = $window.FindName('txtCustomFFUNameTemplate')
|
$State.Controls.txtCustomFFUNameTemplate = $window.FindName('txtCustomFFUNameTemplate')
|
||||||
$State.Controls.txtFFUCaptureLocation = $window.FindName('txtFFUCaptureLocation')
|
$State.Controls.txtFFUCaptureLocation = $window.FindName('txtFFUCaptureLocation')
|
||||||
$State.Controls.txtShareName = $window.FindName('txtShareName')
|
|
||||||
$State.Controls.txtUsername = $window.FindName('txtUsername')
|
|
||||||
$State.Controls.txtThreads = $window.FindName('txtThreads')
|
$State.Controls.txtThreads = $window.FindName('txtThreads')
|
||||||
$State.Controls.cmbBitsPriority = $window.FindName('cmbBitsPriority')
|
$State.Controls.cmbBitsPriority = $window.FindName('cmbBitsPriority')
|
||||||
$State.Controls.txtMaxUSBDrives = $window.FindName('txtMaxUSBDrives')
|
$State.Controls.txtMaxUSBDrives = $window.FindName('txtMaxUSBDrives')
|
||||||
$State.Controls.chkCompactOS = $window.FindName('chkCompactOS')
|
$State.Controls.chkCompactOS = $window.FindName('chkCompactOS')
|
||||||
$State.Controls.chkOptimize = $window.FindName('chkOptimize')
|
$State.Controls.chkOptimize = $window.FindName('chkOptimize')
|
||||||
$State.Controls.chkAllowVHDXCaching = $window.FindName('chkAllowVHDXCaching')
|
$State.Controls.chkAllowVHDXCaching = $window.FindName('chkAllowVHDXCaching')
|
||||||
$State.Controls.chkCreateCaptureMedia = $window.FindName('chkCreateCaptureMedia')
|
|
||||||
$State.Controls.chkCreateDeploymentMedia = $window.FindName('chkCreateDeploymentMedia')
|
$State.Controls.chkCreateDeploymentMedia = $window.FindName('chkCreateDeploymentMedia')
|
||||||
$State.Controls.chkInjectUnattend = $window.FindName('chkInjectUnattend')
|
$State.Controls.chkInjectUnattend = $window.FindName('chkInjectUnattend')
|
||||||
$State.Controls.chkVerbose = $window.FindName('chkVerbose')
|
$State.Controls.chkVerbose = $window.FindName('chkVerbose')
|
||||||
@@ -227,7 +223,6 @@ function Initialize-UIControls {
|
|||||||
$State.Controls.chkCopyUnattend = $window.FindName('chkCopyUnattend')
|
$State.Controls.chkCopyUnattend = $window.FindName('chkCopyUnattend')
|
||||||
$State.Controls.chkCopyPPKG = $window.FindName('chkCopyPPKG')
|
$State.Controls.chkCopyPPKG = $window.FindName('chkCopyPPKG')
|
||||||
$State.Controls.chkCleanupAppsISO = $window.FindName('chkCleanupAppsISO')
|
$State.Controls.chkCleanupAppsISO = $window.FindName('chkCleanupAppsISO')
|
||||||
$State.Controls.chkCleanupCaptureISO = $window.FindName('chkCleanupCaptureISO')
|
|
||||||
$State.Controls.chkCleanupDeployISO = $window.FindName('chkCleanupDeployISO')
|
$State.Controls.chkCleanupDeployISO = $window.FindName('chkCleanupDeployISO')
|
||||||
$State.Controls.chkCleanupDrivers = $window.FindName('chkCleanupDrivers')
|
$State.Controls.chkCleanupDrivers = $window.FindName('chkCleanupDrivers')
|
||||||
$State.Controls.chkRemoveFFU = $window.FindName('chkRemoveFFU')
|
$State.Controls.chkRemoveFFU = $window.FindName('chkRemoveFFU')
|
||||||
@@ -351,18 +346,11 @@ function Initialize-VMSwitchData {
|
|||||||
if ($State.Controls.cmbVMSwitchName.Items.Count -gt 1) {
|
if ($State.Controls.cmbVMSwitchName.Items.Count -gt 1) {
|
||||||
$State.Controls.cmbVMSwitchName.SelectedIndex = 0
|
$State.Controls.cmbVMSwitchName.SelectedIndex = 0
|
||||||
$firstSwitch = $State.Controls.cmbVMSwitchName.SelectedItem
|
$firstSwitch = $State.Controls.cmbVMSwitchName.SelectedItem
|
||||||
if ($null -ne $firstSwitch -and $State.Data.vmSwitchMap.ContainsKey($firstSwitch)) {
|
|
||||||
$State.Controls.txtVMHostIPAddress.Text = $State.Data.vmSwitchMap[$firstSwitch]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$State.Controls.txtVMHostIPAddress.Text = $State.Defaults.generalDefaults.VMHostIPAddress # Use default if IP not found or key null
|
|
||||||
}
|
|
||||||
$State.Controls.txtCustomVMSwitchName.Visibility = 'Collapsed'
|
$State.Controls.txtCustomVMSwitchName.Visibility = 'Collapsed'
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$State.Controls.cmbVMSwitchName.SelectedItem = 'Other'
|
$State.Controls.cmbVMSwitchName.SelectedItem = 'Other'
|
||||||
$State.Controls.txtCustomVMSwitchName.Visibility = 'Visible'
|
$State.Controls.txtCustomVMSwitchName.Visibility = 'Visible'
|
||||||
$State.Controls.txtVMHostIPAddress.Text = $State.Defaults.generalDefaults.VMHostIPAddress # Use default
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,8 +366,6 @@ function Initialize-UIDefaults {
|
|||||||
$State.Controls.txtFFUDevPath.Text = $State.FFUDevelopmentPath
|
$State.Controls.txtFFUDevPath.Text = $State.FFUDevelopmentPath
|
||||||
$State.Controls.txtCustomFFUNameTemplate.Text = $State.Defaults.generalDefaults.CustomFFUNameTemplate
|
$State.Controls.txtCustomFFUNameTemplate.Text = $State.Defaults.generalDefaults.CustomFFUNameTemplate
|
||||||
$State.Controls.txtFFUCaptureLocation.Text = $State.Defaults.generalDefaults.FFUCaptureLocation
|
$State.Controls.txtFFUCaptureLocation.Text = $State.Defaults.generalDefaults.FFUCaptureLocation
|
||||||
$State.Controls.txtShareName.Text = $State.Defaults.generalDefaults.ShareName
|
|
||||||
$State.Controls.txtUsername.Text = $State.Defaults.generalDefaults.Username
|
|
||||||
$State.Controls.txtThreads.Text = $State.Defaults.generalDefaults.Threads
|
$State.Controls.txtThreads.Text = $State.Defaults.generalDefaults.Threads
|
||||||
$State.Controls.cmbBitsPriority.SelectedItem = $State.Defaults.generalDefaults.BitsPriority
|
$State.Controls.cmbBitsPriority.SelectedItem = $State.Defaults.generalDefaults.BitsPriority
|
||||||
$State.Controls.txtMaxUSBDrives.Text = $State.Defaults.generalDefaults.MaxUSBDrives
|
$State.Controls.txtMaxUSBDrives.Text = $State.Defaults.generalDefaults.MaxUSBDrives
|
||||||
@@ -389,7 +375,6 @@ function Initialize-UIDefaults {
|
|||||||
$State.Controls.chkOptimize.IsChecked = $State.Defaults.generalDefaults.Optimize
|
$State.Controls.chkOptimize.IsChecked = $State.Defaults.generalDefaults.Optimize
|
||||||
$State.Controls.chkAllowVHDXCaching.IsChecked = $State.Defaults.generalDefaults.AllowVHDXCaching
|
$State.Controls.chkAllowVHDXCaching.IsChecked = $State.Defaults.generalDefaults.AllowVHDXCaching
|
||||||
$State.Controls.chkInjectUnattend.IsChecked = $State.Defaults.generalDefaults.InjectUnattend
|
$State.Controls.chkInjectUnattend.IsChecked = $State.Defaults.generalDefaults.InjectUnattend
|
||||||
$State.Controls.chkCreateCaptureMedia.IsChecked = $State.Defaults.generalDefaults.CreateCaptureMedia
|
|
||||||
$State.Controls.chkCreateDeploymentMedia.IsChecked = $State.Defaults.generalDefaults.CreateDeploymentMedia
|
$State.Controls.chkCreateDeploymentMedia.IsChecked = $State.Defaults.generalDefaults.CreateDeploymentMedia
|
||||||
$State.Controls.chkAllowExternalHardDiskMedia.IsChecked = $State.Defaults.generalDefaults.AllowExternalHardDiskMedia
|
$State.Controls.chkAllowExternalHardDiskMedia.IsChecked = $State.Defaults.generalDefaults.AllowExternalHardDiskMedia
|
||||||
$State.Controls.chkPromptExternalHardDiskMedia.IsChecked = $State.Defaults.generalDefaults.PromptExternalHardDiskMedia
|
$State.Controls.chkPromptExternalHardDiskMedia.IsChecked = $State.Defaults.generalDefaults.PromptExternalHardDiskMedia
|
||||||
@@ -398,7 +383,6 @@ function Initialize-UIDefaults {
|
|||||||
$State.Controls.chkCopyUnattend.IsChecked = $State.Defaults.generalDefaults.CopyUnattend
|
$State.Controls.chkCopyUnattend.IsChecked = $State.Defaults.generalDefaults.CopyUnattend
|
||||||
$State.Controls.chkCopyPPKG.IsChecked = $State.Defaults.generalDefaults.CopyPPKG
|
$State.Controls.chkCopyPPKG.IsChecked = $State.Defaults.generalDefaults.CopyPPKG
|
||||||
$State.Controls.chkCleanupAppsISO.IsChecked = $State.Defaults.generalDefaults.CleanupAppsISO
|
$State.Controls.chkCleanupAppsISO.IsChecked = $State.Defaults.generalDefaults.CleanupAppsISO
|
||||||
$State.Controls.chkCleanupCaptureISO.IsChecked = $State.Defaults.generalDefaults.CleanupCaptureISO
|
|
||||||
$State.Controls.chkCleanupDeployISO.IsChecked = $State.Defaults.generalDefaults.CleanupDeployISO
|
$State.Controls.chkCleanupDeployISO.IsChecked = $State.Defaults.generalDefaults.CleanupDeployISO
|
||||||
$State.Controls.chkCleanupDrivers.IsChecked = $State.Defaults.generalDefaults.CleanupDrivers
|
$State.Controls.chkCleanupDrivers.IsChecked = $State.Defaults.generalDefaults.CleanupDrivers
|
||||||
$State.Controls.chkRemoveFFU.IsChecked = $State.Defaults.generalDefaults.RemoveFFU
|
$State.Controls.chkRemoveFFU.IsChecked = $State.Defaults.generalDefaults.RemoveFFU
|
||||||
|
|||||||
@@ -115,8 +115,6 @@ function Get-GeneralDefaults {
|
|||||||
# Build Tab Defaults
|
# Build Tab Defaults
|
||||||
CustomFFUNameTemplate = "{WindowsRelease}_{WindowsVersion}_{SKU}_{yyyy}-{MM}-{dd}_{HH}{mm}"
|
CustomFFUNameTemplate = "{WindowsRelease}_{WindowsVersion}_{SKU}_{yyyy}-{MM}-{dd}_{HH}{mm}"
|
||||||
FFUCaptureLocation = $ffuCapturePath
|
FFUCaptureLocation = $ffuCapturePath
|
||||||
ShareName = "FFUCaptureShare"
|
|
||||||
Username = "ffu_user"
|
|
||||||
Threads = 5
|
Threads = 5
|
||||||
BitsPriority = 'Normal'
|
BitsPriority = 'Normal'
|
||||||
MaxUSBDrives = 5
|
MaxUSBDrives = 5
|
||||||
@@ -124,7 +122,6 @@ function Get-GeneralDefaults {
|
|||||||
CompactOS = $true
|
CompactOS = $true
|
||||||
Optimize = $true
|
Optimize = $true
|
||||||
AllowVHDXCaching = $false
|
AllowVHDXCaching = $false
|
||||||
CreateCaptureMedia = $true
|
|
||||||
CreateDeploymentMedia = $true
|
CreateDeploymentMedia = $true
|
||||||
Verbose = $false
|
Verbose = $false
|
||||||
AllowExternalHardDiskMedia = $false
|
AllowExternalHardDiskMedia = $false
|
||||||
@@ -136,7 +133,6 @@ function Get-GeneralDefaults {
|
|||||||
CopyPPKG = $false
|
CopyPPKG = $false
|
||||||
InjectUnattend = $false
|
InjectUnattend = $false
|
||||||
CleanupAppsISO = $true
|
CleanupAppsISO = $true
|
||||||
CleanupCaptureISO = $true
|
|
||||||
CleanupDeployISO = $true
|
CleanupDeployISO = $true
|
||||||
CleanupDrivers = $false
|
CleanupDrivers = $false
|
||||||
RemoveFFU = $false
|
RemoveFFU = $false
|
||||||
@@ -144,7 +140,6 @@ function Get-GeneralDefaults {
|
|||||||
RemoveUpdates = $false
|
RemoveUpdates = $false
|
||||||
RemoveDownloadedESD = $true
|
RemoveDownloadedESD = $true
|
||||||
# Hyper-V Settings Defaults
|
# Hyper-V Settings Defaults
|
||||||
VMHostIPAddress = ""
|
|
||||||
DiskSizeGB = 50
|
DiskSizeGB = 50
|
||||||
MemoryGB = 4
|
MemoryGB = 4
|
||||||
Processors = 4
|
Processors = 4
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
select disk 0
|
|
||||||
select partition 3
|
|
||||||
Assign letter="M"
|
|
||||||
exit
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
$VMHostIPAddress = '192.168.1.158'
|
|
||||||
$ShareName = 'FFUCaptureShare'
|
|
||||||
$UserName = 'ffu_user'
|
|
||||||
$Password = '23202eb4-10c3-47e9-b389-f0c462663a23'
|
|
||||||
$CustomFFUNameTemplate = '{WindowsRelease}_{WindowsVersion}_{SKU}_{yyyy}-{MM}-{dd}_{HH}{mm}'
|
|
||||||
|
|
||||||
$netuseCommand = "net use W: \\$VMHostIPAddress\$ShareName /user:$UserName $Password 2>&1"
|
|
||||||
|
|
||||||
# Connect to network share
|
|
||||||
try {
|
|
||||||
Write-Host "Connecting to network share via $netuseCommand"
|
|
||||||
$netUseResult = net use W: "\\$VMHostIPAddress\$ShareName" "/user:$UserName" "$Password" 2>&1
|
|
||||||
|
|
||||||
# Check if the result contains an error
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
# Extract the error code from the Exception Message
|
|
||||||
# Example message format: "System error 53 has occurred."
|
|
||||||
$message = $netUseResult.Exception.Message
|
|
||||||
$regex = [regex]'System error (\d+)'
|
|
||||||
$match = $regex.Match($message)
|
|
||||||
if ($match.Success) {
|
|
||||||
$errorCode = [int]$match.Groups[1].Value
|
|
||||||
|
|
||||||
$errorMessage = switch ($errorCode) {
|
|
||||||
53 { "Network path not found. Verify the IP address is correct and the server is accessible." }
|
|
||||||
67 { "Network name cannot be found. Verify the share name exists on the server." }
|
|
||||||
86 { "Password is incorrect for the specified username." }
|
|
||||||
1219 { "Multiple connections to the share exist."}
|
|
||||||
1326 { "Logon failure: unknown username or bad password." }
|
|
||||||
1385 { "Logon failure: the user has not been granted the requested logon type at this computer.
|
|
||||||
This is likely due to changes to the User Rights Assignment: Access this computer from the network local security policy
|
|
||||||
See: https://github.com/rbalsleyMSFT/FFU/issues/122 for more info" }
|
|
||||||
1792 { "Unable to connect. Verify the server is running and accepting connections." }
|
|
||||||
2250 { "Network connection attempt timed out." }
|
|
||||||
default { "Network connection failed with error code: $errorCode. Details: $message" }
|
|
||||||
}
|
|
||||||
# Write-Error $errorMessage
|
|
||||||
throw $errorMessage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Write-Error "Failed to connect to network share: Error code: $errorcode $_"
|
|
||||||
Write-Host "Some things to try:"
|
|
||||||
Write-Host '1. If not using an external switch, change to using an external switch'
|
|
||||||
Write-Host '2. Make sure the VMHostIPAddress is correct for the VMSwitch that is being used'
|
|
||||||
Write-Host '3. Try disabling the Windows Firewall on the host machine as a test only. If that helps, there is a Windows firewall rule that is blocking SMB 445 into the VM host'
|
|
||||||
Write-Host '4. If this is a machine that is managed by your organization, try using another machine that is not managed. There could be security policies in place that are blocking the connection to the share.'
|
|
||||||
Write-Host '5. You can also try disabling Hyper-V and re-enabling it. This has helped some users in the past.'
|
|
||||||
Write-Host '6. If all else fails, open an issue on the github repo and attach screenshots of this message, your FFUDevelopment.log, your command line that you used to build the FFU, and/or the config file you used (if you used one).'
|
|
||||||
pause
|
|
||||||
throw
|
|
||||||
}
|
|
||||||
|
|
||||||
$AssignDriveLetter = 'x:\AssignDriveLetter.txt'
|
|
||||||
try {
|
|
||||||
Write-Host 'Assigning M: as Windows drive letter'
|
|
||||||
Start-Process -FilePath diskpart.exe -ArgumentList "/S $AssignDriveLetter" -Wait -ErrorAction Stop | Out-Null
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Error "Failed to assign drive letter using diskpart: $_"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#Load Registry Hive
|
|
||||||
$Software = 'M:\Windows\System32\config\software'
|
|
||||||
try {
|
|
||||||
Write-Host "Loading software registry hive to $Software"
|
|
||||||
if (-not (Test-Path -Path $Software)) {
|
|
||||||
throw "Software registry hive not found at $Software"
|
|
||||||
}
|
|
||||||
$regResult = reg load "HKLM\FFU" $Software 2>&1
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw "Registry load failed with exit code $($LASTEXITCODE): $regResult"
|
|
||||||
}
|
|
||||||
Write-Host "Successfully loaded software registry hive."
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Error "Failed to load registry hive: $_"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
#Find Windows version values
|
|
||||||
Write-Host "Retrieving Windows information from the registry..."
|
|
||||||
$SKU = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'EditionID'
|
|
||||||
Write-Host "SKU: $SKU"
|
|
||||||
[int]$CurrentBuild = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'CurrentBuild'
|
|
||||||
Write-Host "CurrentBuild: $CurrentBuild"
|
|
||||||
if ($CurrentBuild -notin 14393, 17763) {
|
|
||||||
Write-Host "CurrentBuild is not 14393 or 17763, retrieving WindowsVersion..."
|
|
||||||
$WindowsVersion = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'DisplayVersion'
|
|
||||||
Write-Host "WindowsVersion: $WindowsVersion"
|
|
||||||
}
|
|
||||||
$InstallationType = Get-ItemPropertyValue -Path 'HKLM:\FFU\Microsoft\Windows NT\CurrentVersion\' -Name 'InstallationType'
|
|
||||||
Write-Host "InstallationType: $InstallationType"
|
|
||||||
$BuildDate = Get-Date -uformat %b%Y
|
|
||||||
Write-Host "BuildDate: $BuildDate"
|
|
||||||
|
|
||||||
$SKU = switch ($SKU) {
|
|
||||||
Core { 'Home' }
|
|
||||||
CoreN { 'Home_N' }
|
|
||||||
CoreSingleLanguage { 'Home_SL' }
|
|
||||||
Professional { 'Pro' }
|
|
||||||
ProfessionalN { 'Pro_N' }
|
|
||||||
ProfessionalEducation { 'Pro_Edu' }
|
|
||||||
ProfessionalEducationN { 'Pro_Edu_N' }
|
|
||||||
Enterprise { 'Ent' }
|
|
||||||
EnterpriseN { 'Ent_N' }
|
|
||||||
EnterpriseS { 'Ent_LTSC' }
|
|
||||||
EnterpriseSN { 'Ent_N_LTSC' }
|
|
||||||
IoTEnterpriseS { 'IoT_Ent_LTSC' }
|
|
||||||
Education { 'Edu' }
|
|
||||||
EducationN { 'Edu_N' }
|
|
||||||
ProfessionalWorkstation { 'Pro_Wks' }
|
|
||||||
ProfessionalWorkstationN { 'Pro_Wks_N' }
|
|
||||||
ServerStandard { 'Srv_Std' }
|
|
||||||
ServerDatacenter { 'Srv_Dtc' }
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($InstallationType -eq "Client") {
|
|
||||||
if ($CurrentBuild -ge 22000) {
|
|
||||||
$WindowsRelease = 'Win11'
|
|
||||||
Write-Host "WindowsRelease: $WindowsRelease"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$WindowsRelease = 'Win10'
|
|
||||||
Write-Host "WindowsRelease: $WindowsRelease"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$WindowsRelease = switch ($CurrentBuild) {
|
|
||||||
26100 { '2025' }
|
|
||||||
20348 { '2022' }
|
|
||||||
17763 { '2019' }
|
|
||||||
14393 { '2016' }
|
|
||||||
Default { $WindowsVersion }
|
|
||||||
}
|
|
||||||
Write-Host "WindowsRelease: $WindowsRelease"
|
|
||||||
if ($InstallationType -eq "Server Core") {
|
|
||||||
$SKU += "_Core"
|
|
||||||
Write-Host "InstallType is Server Core, changing SKU to: $SKU"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($CustomFFUNameTemplate) {
|
|
||||||
Write-Host 'Using custom FFU name template...'
|
|
||||||
$FFUFileName = $CustomFFUNameTemplate
|
|
||||||
$FFUFileName = $FFUFileName -replace '{WindowsRelease}', $WindowsRelease
|
|
||||||
$FFUFileName = $FFUFileName -replace '{WindowsVersion}', $WindowsVersion
|
|
||||||
$FFUFileName = $FFUFileName -replace '{SKU}', $SKU
|
|
||||||
$FFUFileName = $FFUFileName -replace '{BuildDate}', $BuildDate
|
|
||||||
$FFUFileName = $FFUFileName -replace '{yyyy}', (Get-Date -UFormat '%Y')
|
|
||||||
$FFUFileName = $FFUFileName -creplace '{MM}', (Get-Date -UFormat '%m')
|
|
||||||
$FFUFileName = $FFUFileName -replace '{dd}', (Get-Date -UFormat '%d')
|
|
||||||
$FFUFileName = $FFUFileName -creplace '{HH}', (Get-Date -UFormat '%H')
|
|
||||||
$FFUFileName = $FFUFileName -creplace '{hh}', (Get-Date -UFormat '%I')
|
|
||||||
$FFUFileName = $FFUFileName -creplace '{mm}', (Get-Date -UFormat '%M')
|
|
||||||
$FFUFileName = $FFUFileName -replace '{tt}', (Get-Date -UFormat '%p')
|
|
||||||
Write-Host "FFU File Name: $FFUFileName"
|
|
||||||
#If the custom FFU name template does not end with .ffu, append it
|
|
||||||
if ($FFUFileName -notlike '*.ffu') {
|
|
||||||
$FFUFileName += '.ffu'
|
|
||||||
Write-Host "Appended .ffu to FFU file name: $FFUFileName"
|
|
||||||
}
|
|
||||||
$dismArgs = "/capture-ffu /imagefile=W:\$FFUFileName /capturedrive=\\.\PhysicalDrive0 /name:$WindowsRelease$WindowsVersion$SKU /Compress:Default"
|
|
||||||
Write-Host "DISM arguments for capture: $dismArgs"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
#If Office is installed, modify the file name of the FFU
|
|
||||||
$Office = Get-ChildItem -Path 'M:\Program Files\Microsoft Office' -ErrorAction SilentlyContinue
|
|
||||||
if ($Office) {
|
|
||||||
$ffuFilePath = "W:\$WindowsRelease`_$WindowsVersion`_$SKU`_Office`_$BuildDate.ffu"
|
|
||||||
Write-Host "Office is installed, using modified FFU file name: $ffuFilePath"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$ffuFilePath = "W:\$WindowsRelease`_$WindowsVersion`_$SKU`_Apps`_$BuildDate.ffu"
|
|
||||||
Write-Host "Office is not installed, using modified FFU file name: $ffuFilePath"
|
|
||||||
}
|
|
||||||
$dismArgs = "/capture-ffu /imagefile=$ffuFilePath /capturedrive=\\.\PhysicalDrive0 /name:$WindowsRelease$WindowsVersion$SKU /Compress:Default"
|
|
||||||
Write-Host "DISM arguments for capture: $dismArgs"
|
|
||||||
}
|
|
||||||
|
|
||||||
#Unload Registry
|
|
||||||
Set-Location X:\
|
|
||||||
Remove-Variable SKU
|
|
||||||
Remove-Variable CurrentBuild
|
|
||||||
if ($CurrentBuild -notin 14393, 17763) {
|
|
||||||
Remove-Variable WindowsVersion
|
|
||||||
}
|
|
||||||
if ($Office) {
|
|
||||||
Remove-Variable Office
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Write-Host "Unloading registry hive HKLM\FFU..."
|
|
||||||
$regUnloadResult = reg unload "HKLM\FFU" 2>&1
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw "Registry unload failed with exit code $($LASTEXITCODE): $regUnloadResult"
|
|
||||||
}
|
|
||||||
Write-Host "Successfully unloaded registry hive."
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Error "Failed to unload registry hive: $_"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Sleeping for 60 seconds to allow registry to unload prior to capture"
|
|
||||||
Start-sleep 60
|
|
||||||
|
|
||||||
try {
|
|
||||||
Write-Host "Starting DISM FFU capture..."
|
|
||||||
$dismProcess = Start-Process -FilePath dism.exe -ArgumentList $dismArgs -Wait -PassThru -ErrorAction Stop
|
|
||||||
if ($dismProcess.ExitCode -ne 0) {
|
|
||||||
throw "DISM capture failed with exit code $($dismProcess.ExitCode)"
|
|
||||||
}
|
|
||||||
Write-Host "DISM FFU capture completed successfully."
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Error "FFU capture failed: $_"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Write-Host "Copying DISM log to network share..."
|
|
||||||
xcopy X:\Windows\logs\dism\dism.log W:\ /Y | Out-Null
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Warning "Failed to copy DISM log: $_"
|
|
||||||
}
|
|
||||||
Write-Host "DISM log copied to network share, shutting down..."
|
|
||||||
wpeutil Shutdown
|
|
||||||
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Error "An unexpected error occurred: $_"
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
wpeinit
|
|
||||||
powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
|
|
||||||
powershell -Noprofile -ExecutionPolicy Bypass -File x:\CaptureFFU.ps1
|
|
||||||
exit
|
|
||||||
|
|
||||||
Binary file not shown.
@@ -57,7 +57,6 @@ This allows for you to create a dynamic task sequence via a PowerShell script wi
|
|||||||
},
|
},
|
||||||
"BuildUSBDrive": false,
|
"BuildUSBDrive": false,
|
||||||
"CleanupAppsISO": true,
|
"CleanupAppsISO": true,
|
||||||
"CleanupCaptureISO": true,
|
|
||||||
"CleanupDeployISO": true,
|
"CleanupDeployISO": true,
|
||||||
"CleanupDrivers": false,
|
"CleanupDrivers": false,
|
||||||
"CompactOS": true,
|
"CompactOS": true,
|
||||||
@@ -69,7 +68,6 @@ This allows for you to create a dynamic task sequence via a PowerShell script wi
|
|||||||
"CopyPEDrivers": false,
|
"CopyPEDrivers": false,
|
||||||
"CopyPPKG": false,
|
"CopyPPKG": false,
|
||||||
"CopyUnattend": false,
|
"CopyUnattend": false,
|
||||||
"CreateCaptureMedia": true,
|
|
||||||
"CreateDeploymentMedia": true,
|
"CreateDeploymentMedia": true,
|
||||||
"CustomFFUNameTemplate": "{WindowsRelease}_{WindowsVersion}_{SKU}_{yyyy}-{MM}-{dd}_{HH}{mm}",
|
"CustomFFUNameTemplate": "{WindowsRelease}_{WindowsVersion}_{SKU}_{yyyy}-{MM}-{dd}_{HH}{mm}",
|
||||||
"Disksize": 53687091200,
|
"Disksize": 53687091200,
|
||||||
@@ -101,7 +99,6 @@ This allows for you to create a dynamic task sequence via a PowerShell script wi
|
|||||||
"RemoveApps": false,
|
"RemoveApps": false,
|
||||||
"RemoveFFU": false,
|
"RemoveFFU": false,
|
||||||
"RemoveUpdates": false,
|
"RemoveUpdates": false,
|
||||||
"ShareName": "FFUCaptureShare",
|
|
||||||
"Threads": 5,
|
"Threads": 5,
|
||||||
"UpdateADK": true,
|
"UpdateADK": true,
|
||||||
"UpdateEdge": true,
|
"UpdateEdge": true,
|
||||||
@@ -115,9 +112,7 @@ This allows for you to create a dynamic task sequence via a PowerShell script wi
|
|||||||
"USBDriveList": {},
|
"USBDriveList": {},
|
||||||
"UseDriversAsPEDrivers": false,
|
"UseDriversAsPEDrivers": false,
|
||||||
"UserAppListPath": "C:\\FFUDevelopment\\Apps\\UserAppList.json",
|
"UserAppListPath": "C:\\FFUDevelopment\\Apps\\UserAppList.json",
|
||||||
"Username": "ffu_user",
|
|
||||||
"Verbose": false,
|
"Verbose": false,
|
||||||
"VMHostIPAddress": "192.168.1.169",
|
|
||||||
"VMLocation": "C:\\FFUDevelopment\\VM",
|
"VMLocation": "C:\\FFUDevelopment\\VM",
|
||||||
"VMSwitchName": "External",
|
"VMSwitchName": "External",
|
||||||
"WindowsArch": "x64",
|
"WindowsArch": "x64",
|
||||||
|
|||||||
+2
-46
@@ -79,26 +79,10 @@ Result: `Win11_24h2_Pro_Nov2025.ffu`
|
|||||||
|
|
||||||
The FFU Capture Location sets the `-FFUCaptureLocation` parameter that determines where completed `.ffu` images are written. By default it points to `$FFUDevelopmentPath\FFU`, and the build script creates the folder automatically if it does not already exist.
|
The FFU Capture Location sets the `-FFUCaptureLocation` parameter that determines where completed `.ffu` images are written. By default it points to `$FFUDevelopmentPath\FFU`, and the build script creates the folder automatically if it does not already exist.
|
||||||
|
|
||||||
When apps are installed in a VM, the host converts this folder into a temporary SMB share using the **Share Name** and **Username** fields. The capture WinPE environment maps that share as drive `W:` and streams the captured image directly into this folder. When the build finishes, the share and local account are removed, but the FFU files remain unless a cleanup option deletes them.
|
When apps are installed in a VM, the build still uses the VM for application installs and sysprep, but the actual FFU capture now happens on the host after the VHDX is optimized and remounted. That means completed images are written directly to this folder without creating a temporary SMB share, temporary local account, or capture ISO.
|
||||||
|
|
||||||
Choose a path on fast storage with plenty of free space—the directory must be local to the host running `BuildFFUVM.ps1`, and large captures can easily exceed 25–30 GB. This location also feeds other options such as **Copy Additional FFU Files**, **Build USB Drive**, and **Remove FFU**, so keeping all finished images here keeps those workflows simple.
|
Choose a path on fast storage with plenty of free space—the directory must be local to the host running `BuildFFUVM.ps1`, and large captures can easily exceed 25–30 GB. This location also feeds other options such as **Copy Additional FFU Files**, **Build USB Drive**, and **Remove FFU**, so keeping all finished images here keeps those workflows simple.
|
||||||
|
|
||||||
## Share Name
|
|
||||||
|
|
||||||
The Share Name sets the `-ShareName` parameter that defines the name of the temporary SMB share created during the FFU capture process. The default is `FFUCaptureShare`.
|
|
||||||
|
|
||||||
During the build, the host creates an SMB share that points to the **FFU Capture Location** and grants access to the temporary local user account defined in **Username**. The capture WinPE environment maps this share as drive `W:` using `net use` and streams the captured FFU image directly to it.
|
|
||||||
|
|
||||||
When the build completes, the share is automatically removed along with the temporary user account, leaving only the captured FFU files behind in the FFU Capture Location.
|
|
||||||
|
|
||||||
## Username
|
|
||||||
|
|
||||||
The Username field sets the `-Username` parameter that `BuildFFUVM.ps1` uses when creating the temporary SMB share user. The value becomes a local standard user account that is granted Full Control on the **FFU Capture Location** share (default C:\FFUDevelopment\FFU) so the capture WinPE session can copy the FFU over `net use `. The default `ffu_user` account name works for most scenarios, but you can supply any other local account name that meets your organization's policies.
|
|
||||||
|
|
||||||
When the build starts, the script ensures the account exists, rotates its password to a randomly generated GUID, and grants it access to the share. The Capture WinPE environment maps drive `W:` with those credentials, then writes the captured image directly into the FFU Capture Location.
|
|
||||||
|
|
||||||
After the build finishes, the share is removed and the temporary account is deleted, leaving only the FFU files stored in the capture folder.
|
|
||||||
|
|
||||||
## Threads
|
## Threads
|
||||||
|
|
||||||
Controls the `-Threads` parameter, which sets the number of parallel threads used for concurrent operations throughout FFU Builder. The default value is **5**.
|
Controls the `-Threads` parameter, which sets the number of parallel threads used for concurrent operations throughout FFU Builder. The default value is **5**.
|
||||||
@@ -471,28 +455,6 @@ VHDX caching trades disk space for speed. The `VHDXCache` folder can grow over t
|
|||||||
>
|
>
|
||||||
> To force a full rebuild, delete the contents of `$FFUDevelopmentPath\VHDXCache` (or disable **Allow VHDX Caching**) and run the build again.
|
> To force a full rebuild, delete the contents of `$FFUDevelopmentPath\VHDXCache` (or disable **Allow VHDX Caching**) and run the build again.
|
||||||
|
|
||||||
## Create Capture Media
|
|
||||||
|
|
||||||
Controls the `-CreateCaptureMedia` parameter.
|
|
||||||
|
|
||||||
When enabled, FFU Builder creates WinPE capture media that is used during VM-based builds (when apps are installed in the VM). FFU Builder attaches this media to the VM and adjusts boot order so the VM can reboot into WinPE and automatically capture the FFU to your **FFU Capture Location**.
|
|
||||||
|
|
||||||
The capture media uses the parameter values from `VMHostIPAddress`, `ShareName`, `UserName`, and `CustomFFUNameTemplate` and inserts them into `CaptureFFU.ps1` which is what is responsible for capturing the FFU from the guest VM to the Host.
|
|
||||||
|
|
||||||
**Default:** Enabled (`-CreateCaptureMedia $true`)
|
|
||||||
|
|
||||||
{: .note-title}
|
|
||||||
|
|
||||||
> Note
|
|
||||||
>
|
|
||||||
> This option is only relevant when **Install Apps** is enabled. If **Install Apps** is enabled, the build forces `-CreateCaptureMedia` to `$true` because capture media is required to capture an FFU from the VM.
|
|
||||||
|
|
||||||
{: .tip-title}
|
|
||||||
|
|
||||||
> Tip
|
|
||||||
>
|
|
||||||
> If you just need to re-create media, you can use the `Create-PEMedia.ps1` script to regenerate the capture or deploy ISO using `Create-PEMedia.ps1 -Capture $true` or `CreatePEMedia.ps1 -Deploy $true`.
|
|
||||||
|
|
||||||
## Create Deployment Media
|
## Create Deployment Media
|
||||||
|
|
||||||
Controls the `-CreateDeploymentMedia` parameter.
|
Controls the `-CreateDeploymentMedia` parameter.
|
||||||
@@ -513,7 +475,7 @@ The deployment media is saved as an ISO file at `$FFUDevelopmentPath\WinPE_FFU_D
|
|||||||
|
|
||||||
> Tip
|
> Tip
|
||||||
>
|
>
|
||||||
> If you just need to re-create media, you can use the `Create-PEMedia.ps1` script to regenerate the capture or deploy ISO using `Create-PEMedia.ps1 -Capture $true` or `CreatePEMedia.ps1 -Deploy $true`.
|
> If you just need to re-create deployment media, you can use the `Create-PEMedia.ps1` script to regenerate the deploy ISO without running a full build.
|
||||||
|
|
||||||
## Inject Unattend.xml
|
## Inject Unattend.xml
|
||||||
|
|
||||||
@@ -604,12 +566,6 @@ You may want to disable Cleanup Apps ISO in the following scenarios:
|
|||||||
>
|
>
|
||||||
> The Apps ISO is only created when applications are configured for installation. If no apps are being installed in the FFU, this option has no effect. Keeping this option enabled helps conserve disk space by removing temporary build artifacts.
|
> The Apps ISO is only created when applications are configured for installation. If no apps are being installed in the FFU, this option has no effect. Keeping this option enabled helps conserve disk space by removing temporary build artifacts.
|
||||||
|
|
||||||
## Cleanup Capture ISO
|
|
||||||
|
|
||||||
Controls the `-CleanupCaptureISO` parameter. When checked, the WinPE capture ISO file is automatically deleted after the FFU has been successfully captured. The default is **checked**.
|
|
||||||
|
|
||||||
It's recommended to keep this checked as each new build re-creates the local username account (e.g. `ffu_user`) and its password. If you were to retain the capture ISO from a previous build, it'd be using an old password and the capture would fail.
|
|
||||||
|
|
||||||
## Cleanup Deploy ISO
|
## Cleanup Deploy ISO
|
||||||
|
|
||||||
Controls the `-CleanupDeployISO` parameter. When checked, the WinPE deployment ISO file is automatically deleted after the FFU has been successfully captured. The default is **checked**.
|
Controls the `-CleanupDeployISO` parameter. When checked, the WinPE deployment ISO file is automatically deleted after the FFU has been successfully captured. The default is **checked**.
|
||||||
|
|||||||
+4
-10
@@ -9,7 +9,7 @@ parent: Helper Scripts
|
|||||||
---
|
---
|
||||||
# Create PE Media
|
# Create PE Media
|
||||||
|
|
||||||
`Create-PEMedia.ps1` is a standalone helper script that creates WinPE capture or deployment ISO files outside the main build flow.
|
`Create-PEMedia.ps1` is a standalone helper script that creates WinPE deployment ISO files outside the main build flow.
|
||||||
|
|
||||||
This is useful when admins need to quickly generate a deploy ISO for a share (or local staging folder) that technicians will use with `USBImagingToolCreator.ps1`.
|
This is useful when admins need to quickly generate a deploy ISO for a share (or local staging folder) that technicians will use with `USBImagingToolCreator.ps1`.
|
||||||
|
|
||||||
@@ -40,25 +40,19 @@ Default output file:
|
|||||||
Create deploy ISO for x64:
|
Create deploy ISO for x64:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
.\Create-PEMedia.ps1 -Deploy $true -WindowsArch 'x64'
|
.\Create-PEMedia.ps1 -WindowsArch 'x64'
|
||||||
```
|
```
|
||||||
|
|
||||||
Create deploy ISO for ARM64:
|
Create deploy ISO for ARM64:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
.\Create-PEMedia.ps1 -Deploy $true -WindowsArch 'arm64' -DeployISO "$PSScriptRoot\WinPE_FFU_Deploy_arm64.iso"
|
.\Create-PEMedia.ps1 -WindowsArch 'arm64' -DeployISO "$PSScriptRoot\WinPE_FFU_Deploy_arm64.iso"
|
||||||
```
|
|
||||||
|
|
||||||
Create capture ISO only:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
.\Create-PEMedia.ps1 -Capture $true -Deploy $false
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Create deploy ISO and include PE drivers from `.\PEDrivers`:
|
Create deploy ISO and include PE drivers from `.\PEDrivers`:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
.\Create-PEMedia.ps1 -Deploy $true -CopyPEDrivers $true
|
.\Create-PEMedia.ps1 -CopyPEDrivers $true
|
||||||
```
|
```
|
||||||
|
|
||||||
## Stage output for USB imaging
|
## Stage output for USB imaging
|
||||||
|
|||||||
@@ -15,11 +15,7 @@ parent: UI Overview
|
|||||||
|
|
||||||
Drop down of detected VM Switches. There's also an **Other** option which allows you to specify a VM Switch Name. The other option is useful in scenarios where the machine you're running the UI from isn't going to be the machine where you plan to build the FFU from.
|
Drop down of detected VM Switches. There's also an **Other** option which allows you to specify a VM Switch Name. The other option is useful in scenarios where the machine you're running the UI from isn't going to be the machine where you plan to build the FFU from.
|
||||||
|
|
||||||
## VM Host IP Address
|
This setting is now optional for FFU capture itself. VM-based builds still capture from the host-side VHDX after the VM shuts down, so you only need a switch when the VM requires network connectivity during provisioning.
|
||||||
|
|
||||||
IP address of the selected Hyper-V switch that will be used for FFU capture. The UI will auto-detected this based on the VM Switch that was selected.
|
|
||||||
|
|
||||||
If `$InstallApps` is set to `$true`, this parameter must be configured.
|
|
||||||
|
|
||||||
## Disk Size (GB)
|
## Disk Size (GB)
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ This table lists all top-level parameters in BuildFFUVM.ps1.
|
|||||||
| -BuildUSBDrive | bool | Build USB Drive | When set to $true, will partition and format a USB drive and copy the captured FFU to the drive. |
|
| -BuildUSBDrive | bool | Build USB Drive | When set to $true, will partition and format a USB drive and copy the captured FFU to the drive. |
|
||||||
| -Cleanup | switch | Monitor cancel build action (no direct control) | Switch to run cleanup-only mode. When specified, the script performs cleanup and exits without starting a new build. |
|
| -Cleanup | switch | Monitor cancel build action (no direct control) | Switch to run cleanup-only mode. When specified, the script performs cleanup and exits without starting a new build. |
|
||||||
| -CleanupAppsISO | bool | Cleanup Apps ISO | When set to $true, will remove the Apps ISO after the FFU has been captured. Default is $true. |
|
| -CleanupAppsISO | bool | Cleanup Apps ISO | When set to $true, will remove the Apps ISO after the FFU has been captured. Default is $true. |
|
||||||
| -CleanupCaptureISO | bool | Cleanup Capture ISO | When set to $true, will remove the WinPE capture ISO after the FFU has been captured. Default is $true. |
|
|
||||||
| -CleanupCurrentRunDownloads | bool | Monitor cancel prompt option (no direct control) | When set to $true, cleanup mode will remove downloads created during the current run and restore backed up run JSON files. Default is $false. |
|
| -CleanupCurrentRunDownloads | bool | Monitor cancel prompt option (no direct control) | When set to $true, cleanup mode will remove downloads created during the current run and restore backed up run JSON files. Default is $false. |
|
||||||
| -CleanupDeployISO | bool | Cleanup Deploy ISO | When set to $true, will remove the WinPE deployment ISO after the FFU has been captured. Default is $true. |
|
| -CleanupDeployISO | bool | Cleanup Deploy ISO | When set to $true, will remove the WinPE deployment ISO after the FFU has been captured. Default is $true. |
|
||||||
| -CleanupDrivers | bool | Cleanup Drivers | When set to $true, will remove the drivers folder after the FFU has been captured. Default is $true. |
|
| -CleanupDrivers | bool | Cleanup Drivers | When set to $true, will remove the drivers folder after the FFU has been captured. Default is $true. |
|
||||||
@@ -40,7 +39,6 @@ This table lists all top-level parameters in BuildFFUVM.ps1.
|
|||||||
| -CopyPEDrivers | bool | Copy PE Drivers | When set to $true, enables adding WinPE drivers. By default copies drivers from $FFUDevelopmentPath\PEDrivers to the WinPE deployment media unless -UseDriversAsPEDrivers is also $true. |
|
| -CopyPEDrivers | bool | Copy PE Drivers | When set to $true, enables adding WinPE drivers. By default copies drivers from $FFUDevelopmentPath\PEDrivers to the WinPE deployment media unless -UseDriversAsPEDrivers is also $true. |
|
||||||
| -CopyPPKG | bool | Copy Provisioning Package | When set to $true, will copy the provisioning package from the $FFUDevelopmentPath\PPKG folder to the Deployment partition of the USB drive. Default is $false. |
|
| -CopyPPKG | bool | Copy Provisioning Package | When set to $true, will copy the provisioning package from the $FFUDevelopmentPath\PPKG folder to the Deployment partition of the USB drive. Default is $false. |
|
||||||
| -CopyUnattend | bool | Copy Unattend.xml | When set to $true, will copy the $FFUDevelopmentPath\Unattend folder to the Deployment partition of the USB drive. Default is $false. |
|
| -CopyUnattend | bool | Copy Unattend.xml | When set to $true, will copy the $FFUDevelopmentPath\Unattend folder to the Deployment partition of the USB drive. Default is $false. |
|
||||||
| -CreateCaptureMedia | bool | Create Capture Media | When set to $true, this will create WinPE capture media for use when $InstallApps is set to $true. This capture media will be automatically attached to the VM, and the boot order will be changed to automate the capture of the FFU. |
|
|
||||||
| -CreateDeploymentMedia | bool | Create Deployment Media | When set to $true, this will create WinPE deployment media for use when deploying to a physical device. |
|
| -CreateDeploymentMedia | bool | Create Deployment Media | When set to $true, this will create WinPE deployment media for use when deploying to a physical device. |
|
||||||
| -CustomFFUNameTemplate | string | Custom FFU Name Template | Sets a custom FFU output name with placeholders. Allowed placeholders are: {WindowsRelease}, {WindowsVersion}, {SKU}, {BuildDate}, {yyyy}, {MM}, {dd}, {H}, {hh}, {mm}, {tt}. |
|
| -CustomFFUNameTemplate | string | Custom FFU Name Template | Sets a custom FFU output name with placeholders. Allowed placeholders are: {WindowsRelease}, {WindowsVersion}, {SKU}, {BuildDate}, {yyyy}, {MM}, {dd}, {H}, {hh}, {mm}, {tt}. |
|
||||||
| -Disksize | uint64 | Disk Size (GB) | Size of the virtual hard disk for the virtual machine. Default is a 50GB dynamic disk. |
|
| -Disksize | uint64 | Disk Size (GB) | Size of the virtual hard disk for the virtual machine. Default is a 50GB dynamic disk. |
|
||||||
@@ -73,7 +71,6 @@ This table lists all top-level parameters in BuildFFUVM.ps1.
|
|||||||
| -RemoveApps | bool | Remove Apps Folder Content | When set to $true, will remove the application content in the Apps folder after the FFU has been captured. Default is $true. |
|
| -RemoveApps | bool | Remove Apps Folder Content | When set to $true, will remove the application content in the Apps folder after the FFU has been captured. Default is $true. |
|
||||||
| -RemoveFFU | bool | Remove FFU | When set to $true, will remove the FFU file from the $FFUDevelopmentPath\FFU folder after it has been copied to the USB drive. Default is $false. |
|
| -RemoveFFU | bool | Remove FFU | When set to $true, will remove the FFU file from the $FFUDevelopmentPath\FFU folder after it has been copied to the USB drive. Default is $false. |
|
||||||
| -RemoveUpdates | bool | Remove Downloaded Update Files | When set to $true, will remove the downloaded CU, MSRT, Defender, Edge, OneDrive, and .NET files downloaded. Default is $true. |
|
| -RemoveUpdates | bool | Remove Downloaded Update Files | When set to $true, will remove the downloaded CU, MSRT, Defender, Edge, OneDrive, and .NET files downloaded. Default is $true. |
|
||||||
| -ShareName | string | Share Name | Name of the shared folder for FFU capture. The default is FFUCaptureShare. This share will be created with rights for the user account. When finished, the share will be removed. |
|
|
||||||
| -Threads | int | Threads | Controls the throttle applied to parallel tasks inside the script. Default is 5, matching the UI Threads field, and applies to driver downloads invoked through Invoke-ParallelProcessing. |
|
| -Threads | int | Threads | Controls the throttle applied to parallel tasks inside the script. Default is 5, matching the UI Threads field, and applies to driver downloads invoked through Invoke-ParallelProcessing. |
|
||||||
| -UpdateADK | bool | Update ADK | When set to $true, the script will check for and install the latest Windows ADK and WinPE add-on if they are not already installed or up-to-date. Default is $true. |
|
| -UpdateADK | bool | Update ADK | When set to $true, the script will check for and install the latest Windows ADK and WinPE add-on if they are not already installed or up-to-date. Default is $true. |
|
||||||
| -UpdateEdge | bool | Update Edge | When set to $true, will download and install the latest Microsoft Edge. Default is $false. |
|
| -UpdateEdge | bool | Update Edge | When set to $true, will download and install the latest Microsoft Edge. Default is $false. |
|
||||||
@@ -88,10 +85,8 @@ This table lists all top-level parameters in BuildFFUVM.ps1.
|
|||||||
| -UseDriversAsPEDrivers | bool | Use Drivers Folder as PE Drivers Source | When set to $true (and -CopyPEDrivers is also $true), bypasses the contents of $FFUDevelopmentPath\PEDrivers and instead builds the WinPE driver set dynamically from the $DriversFolder path, copying only the required WinPE drivers. Has no effect if -CopyPEDrivers is not specified. Default is $false. |
|
| -UseDriversAsPEDrivers | bool | Use Drivers Folder as PE Drivers Source | When set to $true (and -CopyPEDrivers is also $true), bypasses the contents of $FFUDevelopmentPath\PEDrivers and instead builds the WinPE driver set dynamically from the $DriversFolder path, copying only the required WinPE drivers. Has no effect if -CopyPEDrivers is not specified. Default is $false. |
|
||||||
| -UserAgent | string | CLI only (no UI control) | User agent string to use when downloading files. |
|
| -UserAgent | string | CLI only (no UI control) | User agent string to use when downloading files. |
|
||||||
| -UserAppListPath | string | Application Path (derived UserAppList.json) | Path to a JSON file containing a list of user-defined applications to install. Default is $FFUDevelopmentPath\Apps\UserAppList.json. |
|
| -UserAppListPath | string | Application Path (derived UserAppList.json) | Path to a JSON file containing a list of user-defined applications to install. Default is $FFUDevelopmentPath\Apps\UserAppList.json. |
|
||||||
| -Username | string | Username | Username for accessing the shared folder. The default is ffu_user. The script will auto-create the account and password. When finished, it will remove the account. |
|
|
||||||
| -VMHostIPAddress | string | VM Host IP Address | IP address of the Hyper-V host for FFU capture. If $InstallApps is set to $true, this parameter must be configured. You must manually configure this, or use the UI to auto-detect. |
|
|
||||||
| -VMLocation | string | VM Location | Default is $FFUDevelopmentPath\VM. This is the location of the VHDX that gets created where Windows will be installed to. |
|
| -VMLocation | string | VM Location | Default is $FFUDevelopmentPath\VM. This is the location of the VHDX that gets created where Windows will be installed to. |
|
||||||
| -VMSwitchName | string | VM Switch Name + Custom VM Switch Name (when Other selected) | Name of the Hyper-V virtual switch. If $InstallApps is set to $true, this must be set to capture the FFU from the VM. |
|
| -VMSwitchName | string | VM Switch Name + Custom VM Switch Name (when Other selected) | Name of the Hyper-V virtual switch. Provide it when the VM needs network connectivity during provisioning. |
|
||||||
| -WindowsArch | string | Windows Architecture | String value of 'x86', 'x64', or 'arm64'. This is used to identify which architecture of Windows to download. Default is 'x64'. |
|
| -WindowsArch | string | Windows Architecture | String value of 'x86', 'x64', or 'arm64'. This is used to identify which architecture of Windows to download. Default is 'x64'. |
|
||||||
| -WindowsLang | string | Windows Language | String value in language-region format (e.g., 'en-us'). This is used to identify which language of media to download. Default is 'en-us'. |
|
| -WindowsLang | string | Windows Language | String value in language-region format (e.g., 'en-us'). This is used to identify which language of media to download. Default is 'en-us'. |
|
||||||
| -WindowsRelease | int | Windows Release | Integer value of 10, 11, 2016, 2019, 2021, 2022, 2024, or 2025. This is used to identify which Windows client/LTSC/server release to use. Default is 11. |
|
| -WindowsRelease | int | Windows Release | Integer value of 10, 11, 2016, 2019, 2021, 2022, 2024, or 2025. This is used to identify which Windows client/LTSC/server release to use. Default is 11. |
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ Follow the [prerequisites](/FFU/prerequisites.html) documentation before getting
|
|||||||
|
|
||||||
Click the Hyper-V Settings tab
|
Click the Hyper-V Settings tab
|
||||||
|
|
||||||
You should be able to keep these settings at the defaults. For VM Switch Name and VM Host IP Address, you'll want to make sure the switch you created in the prerequisites section is listed. FFU Builder should automatically figure out the IP address of the swtich for you.
|
You should be able to keep these settings at the defaults. If the VM needs network connectivity during provisioning, make sure the switch you created in the prerequisites section is listed under VM Switch Name. If the build does not need VM networking, you can leave the switch unset.
|
||||||
|
|
||||||
One setting you might need to set is the Logical Sector Size. 512 is the default and that's what most physical disks use today. However 4kn drives and UFS drives use 4k sector sizes, which would require you to select 4096. For now, select 512. If you have issues during deployment, there is error logging that will tell you if you need to set to 4096 on your device.
|
One setting you might need to set is the Logical Sector Size. 512 is the default and that's what most physical disks use today. However 4kn drives and UFS drives use 4k sector sizes, which would require you to select 4096. For now, select 512. If you have issues during deployment, there is error logging that will tell you if you need to set to 4096 on your device.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user