diff --git a/FFUDevelopment/Apps/InstallAppsandSysprep.cmd b/FFUDevelopment/Apps/InstallAppsandSysprep.cmd index aba7da8..66012bf 100644 --- a/FFUDevelopment/Apps/InstallAppsandSysprep.cmd +++ b/FFUDevelopment/Apps/InstallAppsandSysprep.cmd @@ -5,9 +5,12 @@ REM d:\Office\setup.exe /configure d:\office\DeployFFU.xml REM Install Defender Platform Update REM Install Defender Definitions REM Install Windows Security Platform Update +REM Install Windows Malicious Software Removal Tool REM Install OneDrive Per Machine REM Install Edge Stable REM Winget Win32 Apps +REM START Batch variables placeholder +REM END Batch variables placeholder REM Add additional apps below here REM Contoso App (Example) REM msiexec /i d:\Contoso\setup.msi /qn /norestart diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1 index 7829a3f..efff889 100644 --- a/FFUDevelopment/BuildFFUVM.ps1 +++ b/FFUDevelopment/BuildFFUVM.ps1 @@ -1,6 +1,5 @@ #Requires -Modules Hyper-V, Storage -#Requires -PSEdition Desktop #Requires -RunAsAdministrator <# @@ -10,89 +9,38 @@ A PowerShell script to create a Windows 10/11 FFU file. .DESCRIPTION This script creates a Windows 10/11 FFU and USB drive to help quickly get a Windows device reimaged. FFU can be customized with drivers, apps, and additional settings. -.PARAMETER ISOPath -Path to the Windows 10/11 ISO file. +.PARAMETER AllowExternalHardDiskMedia +When set to $true, will allow the use of media identified as External Hard Disk media via WMI class Win32_DiskDrive. Default is not defined. -.PARAMETER WindowsSKU -Edition of Windows 10/11 to be installed, e.g., accepted values are: 'Home', 'Home N', 'Home Single Language', 'Education', 'Education N', 'Pro', 'Pro N', 'Pro Education', 'Pro Education N', 'Pro for Workstations', 'Pro N for Workstations', 'Enterprise', 'Enterprise N' +.PARAMETER AllowVHDXCaching +When set to $true, will cache the VHDX file to the $FFUDevelopmentPath\VHDXCache folder and create a config json file that will keep track of the Windows build information, the updates installed, and the logical sector byte size information. Default is $false. -.PARAMETER FFUDevelopmentPath -Path to the FFU development folder (default is C:\FFUDevelopment). - -.PARAMETER InstallApps -When set to $true, the script will create an Apps.iso file from the $FFUDevelopmentPath\Apps folder. It will also create a VM, mount the Apps.ISO, install the Apps, sysprep, and capture the VM. When set to $False, the FFU is created from a VHDX file. No VM is created. - -.PARAMETER InstallOffice -Install Microsoft Office if set to $true. The script will download the latest ODT and Office files in the $FFUDevelopmentPath\Apps\Office folder and install Office in the FFU via VM - -.PARAMETER InstallDrivers -Install device drivers from the specified $FFUDevelopmentPath\Drivers folder if set to $true. Download the drivers and put them in the Drivers folder. The script will recurse the drivers folder and add the drivers to the FFU. - -.PARAMETER Memory -Amount of memory to allocate for the virtual machine. Recommended to use 8GB if possible, especially for Windows 11. Use 4GB if necesary. - -.PARAMETER Disksize -Size of the virtual hard disk for the virtual machine. Default is a 30GB dynamic disk. - -.PARAMETER Processors -Number of virtual processors for the virtual machine. Recommended to use at least 4. - -.PARAMETER VMSwitchName -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. - -.PARAMETER VMLocation -Default is $FFUDevelopmentPath\VM. This is the location of the VHDX that gets created where Windows will be installed to. - -.PARAMETER FFUPrefix -Prefix for the generated FFU file. Default is _FFU - -.PARAMETER FFUCaptureLocation -Path to the folder where the captured FFU will be stored. Default is $FFUDevelopmentPath\FFU - -.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 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. The script will not auto detect your IP (depending on your network adapters, it may not find the correct IP). - -.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 -When set to $true, this will create WinPE deployment media for use when deploying to a physical device. - -.PARAMETER OptionalFeatures -Provide a semi-colon separated list of Windows optional features you want to include in the FFU (e.g. netfx3;TFTP) - -.PARAMETER ProductKey -Product key for the Windows 10/11 edition specified in WindowsSKU. This will overwrite whatever SKU is entered for WindowsSKU. Recommended to use if you want to use a MAK or KMS key to activate Enterprise or Education. If using VL media instead of consumer media, you'll want to enter a MAK or KMS key here. +.PARAMETER AppsScriptVariables +When passed a hashtable, the script will alter the $FFUDevelopmentPath\Apps\InstallAppsandSysprep.cmd file to set variables with the hashtable keys as variable names and the hashtable values their content. .PARAMETER BuildUSBDrive -When set to $true, will partition and format a USB drive and copy the captured FFU to the drive. If you'd like to customize the drive to add drivers, provisioning packages, name prefix, etc. You'll need to do that afterward. +When set to $true, will partition and format a USB drive and copy the captured FFU to the drive. -.PARAMETER WindowsRelease -Integer value of 10 or 11. This is used to identify which release of Windows to download. Default is 11. +.PARAMETER CleanupAppsISO +When set to $true, will remove the Apps ISO after the FFU has been captured. Default is $true. -.PARAMETER WindowsVersion -String value of the Windows version to download. This is used to identify which version of Windows to download. Default is 23h2. +.PARAMETER CleanupCaptureISO +When set to $true, will remove the WinPE capture ISO after the FFU has been captured. Default is $true. -.PARAMETER WindowsArch -String value of x86 or x64. This is used to identify which architecture of Windows to download. Default is x64. +.PARAMETER CleanupDeployISO +When set to $true, will remove the WinPE deployment ISO after the FFU has been captured. Default is $true. -.PARAMETER WindowsLang -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. +.PARAMETER CleanupDrivers +When set to $true, will remove the drivers folder after the FFU has been captured. Default is $true. -.PARAMETER MediaType -String value of either business or consumer. This is used to identify which media type to download. Default is consumer. +.PARAMETER CompactOS +When set to $true, will compact the OS when building the FFU. Default is $true. -.PARAMETER LogicalSectorBytes -unit32 value of 512 or 4096. Not recommended to change from 512. Might be useful for 4kn drives, but needs more testing. Default is 512. +.PARAMETER ConfigFile +Path to a JSON file containing parameters to use for the script. Default is $null. -.PARAMETER Optimize -When set to $true, will optimize the FFU file. Default is $true. +.PARAMETER CopyAutopilot +When set to $true, will copy the $FFUDevelopmentPath\Autopilot folder to the Deployment partition of the USB drive. Default is $false. .PARAMETER CopyDrivers When set to $true, will copy the drivers from the $FFUDevelopmentPath\Drivers folder to the Drivers folder on the deploy partition of the USB drive. Default is $false. @@ -100,9 +48,93 @@ When set to $true, will copy the drivers from the $FFUDevelopmentPath\Drivers fo .PARAMETER CopyPEDrivers When set to $true, will copy the drivers from the $FFUDevelopmentPath\PEDrivers folder to the WinPE deployment media. Default is $false. +.PARAMETER CopyPPKG +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. + +.PARAMETER CopyUnattend +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 +When set to $true, this will create WinPE deployment media for use when deploying to a physical device. + +.PARAMETER CustomFFUNameTemplate +Sets a custom FFU output name with placeholders. Allowed placeholders are: {WindowsRelease}, {WindowsVersion}, {SKU}, {BuildDate}, {yyyy}, {MM}, {dd}, {H}, {hh}, {mm}, {tt}. + +.PARAMETER Disksize +Size of the virtual hard disk for the virtual machine. Default is a 30GB dynamic disk. + +.PARAMETER DriversFolder +Path to the drivers folder. Default is $FFUDevelopmentPath\Drivers. + +.PARAMETER ExportConfigFile +Path to a JSON file to export the parameters used for the script. + +.PARAMETER FFUCaptureLocation +Path to the folder where the captured FFU will be stored. Default is $FFUDevelopmentPath\FFU. + +.PARAMETER FFUDevelopmentPath +Path to the FFU development folder. Default is C:\FFUDevelopment. + +.PARAMETER FFUPrefix +Prefix for the generated FFU file. Default is _FFU. + +.PARAMETER Headers +Headers to use when downloading files. Not recommended to modify. + +.PARAMETER InstallApps +When set to $true, the script will create an Apps.iso file from the $FFUDevelopmentPath\Apps folder. It will also create a VM, mount the Apps.iso, install the apps, sysprep, and capture the VM. When set to $false, the FFU is created from a VHDX file, and no VM is created. + +.PARAMETER InstallDrivers +Install device drivers from the specified $FFUDevelopmentPath\Drivers folder if set to $true. Download the drivers and put them in the Drivers folder. The script will recurse the drivers folder and add the drivers to the FFU. + +.PARAMETER InstallOffice +Install Microsoft Office if set to $true. The script will download the latest ODT and Office files in the $FFUDevelopmentPath\Apps\Office folder and install Office in the FFU via VM. + +.PARAMETER ISOPath +Path to the Windows 10/11 ISO file. + +.PARAMETER LogicalSectorSizeBytes +Unit32 value of 512 or 4096. Useful for 4Kn drives or devices shipping with UFS drives. Default is 512. + +.PARAMETER Make +Make of the device to download drivers. Accepted values are: 'Microsoft', 'Dell', 'HP', 'Lenovo'. + +.PARAMETER MediaType +String value of either 'business' or 'consumer'. This is used to identify which media type to download. Default is 'consumer'. + +.PARAMETER Memory +Amount of memory to allocate for the virtual machine. Recommended to use 8GB if possible, especially for Windows 11. Default is 4GB. + +.PARAMETER Model +Model of the device to download drivers. This is required if Make is set. + +.PARAMETER Optimize +When set to $true, will optimize the FFU file. Default is $true. + +.PARAMETER OptionalFeatures +Provide a semicolon-separated list of Windows optional features you want to include in the FFU (e.g., netfx3;TFTP). + +.PARAMETER Processors +Number of virtual processors for the virtual machine. Recommended to use at least 4. + +.PARAMETER ProductKey +Product key for the Windows edition specified in WindowsSKU. This will overwrite whatever SKU is entered for WindowsSKU. Recommended to use if you want to use a MAK or KMS key to activate Enterprise or Education. If using VL media instead of consumer media, you'll want to enter a MAK or KMS key here. + +.PARAMETER PromptExternalHardDiskMedia +When set to $true, will prompt the user to confirm the use of media identified as External Hard Disk media via WMI class Win32_DiskDrive. Default is $true. + .PARAMETER RemoveFFU 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. +.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 UpdateEdge +When set to $true, will download and install the latest Microsoft Edge for Windows 10/11. Default is $false. + .PARAMETER UpdateLatestCU When set to $true, will download and install the latest cumulative update for Windows 10/11. Default is $false. @@ -115,56 +147,47 @@ When set to $true, will download and install the latest .NET Framework for Windo .PARAMETER UpdateLatestDefender When set to $true, will download and install the latest Windows Defender definitions and Defender platform update. Default is $false. -.PARAMETER UpdateEdge -When set to $true, will download and install the latest Microsoft Edge for Windows 10/11. Default is $false. +.PARAMETER UpdateLatestMSRT +When set to $true, will download and install the latest Windows Malicious Software Removal Tool. Default is $false. + +.PARAMETER UpdateLatestNet +When set to $true, will download and install the latest .NET Framework for Windows 10/11. Default is $false. .PARAMETER UpdateOneDrive -When set to $true, will download and install the latest OneDrive for Windows 10/11 and install it as a per machine installation instead of per user. Default is $false. +When set to $true, will download and install the latest OneDrive for Windows 10/11 and install it as a per-machine installation instead of per-user. Default is $false. -.PARAMETER CopyPPKG -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. - -.PARAMETER CopyUnattend -When set to $true, will copy the $FFUDevelopmentPath\Unattend folder to the Deployment partition of the USB drive. Default is $false. - -.PARAMETER CopyAutopilot -When set to $true, will copy the $FFUDevelopmentPath\Autopilot folder to the Deployment partition of the USB drive. Default is $false. - -.PARAMETER CompactOS -When set to $true, will compact the OS when building the FFU. 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 CleanupDeployISO -When set to $true, will remove the WinPE deployment ISO after the FFU has been captured. Default is $true. - -.PARAMETER CleanupAppsISO -When set to $true, will remove the Apps ISO after the FFU has been captured. Default is $true. - -.PARAMETER DriversFolder -Path to the drivers folder. Default is $FFUDevelopmentPath\Drivers. - -.PARAMETER CleanupDrivers -When set to $true, will remove the drivers folder after the FFU has been captured. Default is $true. +.PARAMETER UpdatePreviewCU +When set to $true, will download and install the latest Preview cumulative update for Windows 10/11. Default is $false. .PARAMETER UserAgent User agent string to use when downloading files. -.PARAMETER Headers -Headers to use when downloading files. +.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 AllowExternalHardDiskMedia -When set to $true, will allow the use of media identified as External Hard Disk media via WMI class Win32_DiskDrive. Default is not defined. +.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. The script will not auto-detect your IP (depending on your network adapters, it may not find the correct IP). -.PARAMETER PromptExternalHardDiskMedia -When set to $true, will prompt the user to confirm the use of media identified as External Hard Disk media via WMI class Win32_DiskDrive. Default is $true. +.PARAMETER VMLocation +Default is $FFUDevelopmentPath\VM. This is the location of the VHDX that gets created where Windows will be installed to. -.PARAMETER Make -Make of the device to download drivers. Accepted values are: 'Microsoft', 'Dell', 'HP', 'Lenovo' +.PARAMETER VMSwitchName +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. -.PARAMETER Model -Model of the device to download drivers. This is required if Make is set. +.PARAMETER WindowsArch +String value of 'x86' or 'x64'. This is used to identify which architecture of Windows to download. Default is 'x64'. + +.PARAMETER WindowsLang +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'. + +.PARAMETER WindowsRelease +Integer value of 10 or 11. This is used to identify which release of Windows to download. Default is 11. + +.PARAMETER WindowsSKU +Edition of Windows 10/11 to be installed. Accepted values are: 'Home', 'Home N', 'Home Single Language', 'Education', 'Education N', 'Pro', 'Pro N', 'Pro Education', 'Pro Education N', 'Pro for Workstations', 'Pro N for Workstations', 'Enterprise', 'Enterprise N'. + +.PARAMETER WindowsVersion +String value of the Windows version to download. This is used to identify which version of Windows to download. Default is '24h2'. .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. @@ -201,25 +224,18 @@ param( [Parameter(Mandatory = $false, Position = 0)] [ValidateScript({ Test-Path $_ })] [string]$ISOPath, - [ValidateSet('Home', 'Home N', 'Home Single Language', 'Education', 'Education N', 'Pro', 'Pro N', 'Pro Education', 'Pro Education N', 'Pro for Workstations', 'Pro N for Workstations', 'Enterprise', 'Enterprise N')] + [ValidateSet('Home', 'Home N', 'Home Single Language', 'Education', 'Education N', 'Pro', 'Pro N', 'Pro Education', 'Pro Education N', 'Pro for Workstations', 'Pro N for Workstations', 'Enterprise', 'Enterprise N', 'Standard', 'Standard (Desktop Experience)', 'Datacenter', 'Datacenter (Desktop Experience)')] [string]$WindowsSKU = 'Pro', [ValidateScript({ Test-Path $_ })] [string]$FFUDevelopmentPath = $PSScriptRoot, + [bool]$InstallApps, + + [hashtable]$AppsScriptVariables, [bool]$InstallOffice, [ValidateSet('Microsoft', 'Dell', 'HP', 'Lenovo')] [string]$Make, [string]$Model, - # [Parameter(Mandatory = $false)] - # [ValidateScript({ - # if ($Make) { - # return $true - # } - # if ($_ -and (!(Test-Path -Path '.\Drivers') -or ((Get-ChildItem -Path '.\Drivers' -Recurse | Measure-Object -Property Length -Sum).Sum -lt 1MB))) { - # throw 'InstallDrivers is set to $true, but either the Drivers folder is missing or empty' - # } - # return $true - # })] [bool]$InstallDrivers, [uint64]$Memory = 4GB, [uint64]$Disksize = 30GB, @@ -228,8 +244,9 @@ param( [string]$VMLocation, [string]$FFUPrefix = '_FFU', [string]$FFUCaptureLocation, - [String]$ShareName = "FFUCaptureShare", + [string]$ShareName = "FFUCaptureShare", [string]$Username = "ffu_user", + [string]$CustomFFUNameTemplate, [Parameter(Mandatory = $false)] [string]$VMHostIPAddress, [bool]$CreateCaptureMedia = $true, @@ -267,10 +284,10 @@ param( [string]$ProductKey, [bool]$BuildUSBDrive, [Parameter(Mandatory = $false)] - [ValidateSet(10, 11)] + [ValidateSet(10, 11, 2016, 2019, 2022, 2025)] [int]$WindowsRelease = 11, [Parameter(Mandatory = $false)] - [string]$WindowsVersion = '23h2', + [string]$WindowsVersion = '24h2', [Parameter(Mandatory = $false)] [ValidateSet('x86', 'x64', 'arm64')] [string]$WindowsArch = 'x64', @@ -306,8 +323,10 @@ param( [bool]$UpdatePreviewCU, [bool]$UpdateLatestNet, [bool]$UpdateLatestDefender, + [bool]$UpdateLatestMSRT, [bool]$UpdateEdge, [bool]$UpdateOneDrive, + [bool]$AllowVHDXCaching, [bool]$CopyPPKG, [bool]$CopyUnattend, [bool]$CopyAutopilot, @@ -316,6 +335,7 @@ param( [bool]$CleanupDeployISO = $true, [bool]$CleanupAppsISO = $true, [string]$DriversFolder, + [string]$PEDriversFolder, [bool]$CleanupDrivers = $true, [string]$UserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0', #Microsoft sites will intermittently fail on downloads. These headers are to help with that. @@ -334,9 +354,69 @@ param( "Upgrade-Insecure-Requests" = "1" }, [bool]$AllowExternalHardDiskMedia, - [bool]$PromptExternalHardDiskMedia = $true + [bool]$PromptExternalHardDiskMedia = $true, + [Parameter(Mandatory = $false)] + [ValidateScript({ $_ -eq $null -or (Test-Path $_) })] + [string]$ConfigFile, + [Parameter(Mandatory = $false)] + [string]$ExportConfigFile ) -$version = '2409.2' +$version = '2412.1' + +# If a config file is specified and it exists, load it +if ($ConfigFile -and (Test-Path -Path $ConfigFile)) { + $configData = Get-Content $ConfigFile -Raw | ConvertFrom-Json + $keys = $configData.psobject.Properties.Name + + # Iterate through the keys in the config data + foreach ($key in $keys) { + $value = $configdata.$key + + # If $value is empty, skip + if ($null -eq $value -or + ([string]::IsNullOrEmpty([string]$value)) -or + ($value -is [System.Collections.Hashtable] -and $value.Count -eq 0) -or + ($value -is [System.UInt32] -and $value -eq 0) -or + ($value -is [System.UInt64] -and $value -eq 0) -or + ($value -is [System.Int32] -and $value -eq 0)) { + continue + } + + # If this is the Headers parameter, convert PSCustomObject to hashtable + if ($key -eq 'Headers' -and $value -is [System.Management.Automation.PSCustomObject]) { + $headers = [hashtable]::new() + foreach ($prop in $value.psobject.Properties) { + $headers[$prop.Name] = $prop.Value + } + $value = $headers + } + + # Check if this key matches a parameter in the script + # and if the user did NOT explicitly supply it on the command line + if ($MyInvocation.MyCommand.Parameters.ContainsKey($key) -and -not $PSBoundParameters.ContainsKey($key)) { + # Set the parameter's value to what's in the config file + Set-Variable -Name $key -Value $value -Scope 0 + } + } +} + +#Class definition for vhdx cache +class VhdxCacheUpdateItem { + [string]$Name + VhdxCacheUpdateItem([string]$Name) { + $this.Name = $Name + } +} + +class VhdxCacheItem { + [string]$VhdxFileName = "" + [uint32]$LogicalSectorSizeBytes = "" + [string]$WindowsSKU = "" + [string]$WindowsRelease = "" + [string]$WindowsVersion = "" + [string]$OptionalFeatures = "" + [VhdxCacheUpdateItem[]]$IncludedUpdates = @() +} #Check if Hyper-V feature is installed (requires only checks the module) $osInfo = Get-WmiObject -Class Win32_OperatingSystem @@ -372,6 +452,7 @@ if (-not $FFUCaptureLocation) { $FFUCaptureLocation = "$FFUDevelopmentPath\FFU" if (-not $LogFile) { $LogFile = "$FFUDevelopmentPath\FFUDevelopment.log" } if (-not $KBPath) { $KBPath = "$FFUDevelopmentPath\KB" } if (-not $DefenderPath) { $DefenderPath = "$AppsPath\Defender" } +if (-not $MSRTPath) { $MSRTPath = "$AppsPath\MSRT" } if (-not $OneDrivePath) { $OneDrivePath = "$AppsPath\OneDrive" } if (-not $EdgePath) { $EdgePath = "$AppsPath\Edge" } if (-not $DriversFolder) { $DriversFolder = "$FFUDevelopmentPath\Drivers" } @@ -379,7 +460,17 @@ if (-not $PPKGFolder) { $PPKGFolder = "$FFUDevelopmentPath\PPKG" } if (-not $UnattendFolder) { $UnattendFolder = "$FFUDevelopmentPath\Unattend" } if (-not $AutopilotFolder) { $AutopilotFolder = "$FFUDevelopmentPath\Autopilot" } if (-not $PEDriversFolder) { $PEDriversFolder = "$FFUDevelopmentPath\PEDrivers" } - +if (-not $VHDXCacheFolder) { $VHDXCacheFolder = "$FFUDevelopmentPath\VHDXCache" } +if (-not $installationType) { $installationType = if ($WindowsRelease.ToString().Length -eq 2) { 'Client' } else { 'Server' } } +if ($installationType -eq 'Server'){ + #Map $WindowsRelease to $WindowsVersion for Windows Server + switch ($WindowsRelease) { + 2016 { $WindowsVersion = '1607' } + 2019 { $WindowsVersion = '1809' } + 2022 { $WindowsVersion = '21H2' } + 2025 { $WindowsVersion = '24H2' } + } +} #FUNCTIONS function WriteLog($LogText) { @@ -387,6 +478,20 @@ function WriteLog($LogText) { Write-Verbose $LogText } +function Get-Parameters{ + [CmdletBinding()] + param ( + [Parameter()] + $ParamNames + ) +# Define unwanted parameters +$excludedParams = 'Debug','ErrorAction','ErrorVariable','InformationAction','InformationVariable','OutBuffer','OutVariable','PipelineVariable','Verbose','WarningAction','WarningVariable' + +# Filter out the unwanted parameters +$filteredParamNames = $paramNames | Where-Object { $excludedParams -notcontains $_ } +return $filteredParamNames +} + function LogVariableValues { $excludedVariables = @( 'PSBoundParameters', @@ -432,6 +537,16 @@ function LogVariableValues { WriteLog 'End logging variables' } +function Get-ChildProcesses($parentId) { + $result = @() + $children = Get-CimInstance Win32_Process -Filter "ParentProcessId = $parentId" + foreach ($child in $children) { + $result += $child + $result += Get-ChildProcesses $child.ProcessId + } + return $result +} + function Invoke-Process { [CmdletBinding(SupportsShouldProcess)] param @@ -442,7 +557,11 @@ function Invoke-Process { [Parameter()] [ValidateNotNullOrEmpty()] - [string]$ArgumentList + [string]$ArgumentList, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [bool]$Wait = $true ) $ErrorActionPreference = 'Stop' @@ -456,7 +575,7 @@ function Invoke-Process { ArgumentList = $ArgumentList RedirectStandardError = $stdErrTempFile RedirectStandardOutput = $stdOutTempFile - Wait = $true; + Wait = $($Wait); PassThru = $true; NoNewWindow = $true; } @@ -464,7 +583,7 @@ function Invoke-Process { $cmd = Start-Process @startProcessParams $cmdOutput = Get-Content -Path $stdOutTempFile -Raw $cmdError = Get-Content -Path $stdErrTempFile -Raw - if ($cmd.ExitCode -ne 0) { + if ($cmd.ExitCode -ne 0 -and $wait -eq $true) { if ($cmdError) { throw $cmdError.Trim() } @@ -482,15 +601,14 @@ function Invoke-Process { catch { #$PSCmdlet.ThrowTerminatingError($_) WriteLog $_ - Write-Host "Script failed - $Logfile for more info" + # Write-Host "Script failed - $Logfile for more info" throw $_ } finally { Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore - } - + return $cmd } function Test-Url { @@ -541,163 +659,6 @@ function Start-BitsTransferWithRetry { return $false } -# function Get-MicrosoftDrivers { -# param ( -# [string]$Make, -# [string]$Model, -# [int]$WindowsRelease -# ) - -# $url = "https://support.microsoft.com/en-us/surface/download-drivers-and-firmware-for-surface-09bb2e09-2a4b-cb69-0951-078a7739e120" - -# # Download the webpage content -# WriteLog "Getting Surface driver information from $url" -# $OriginalVerbosePreference = $VerbosePreference -# $VerbosePreference = 'SilentlyContinue' -# $webContent = Invoke-WebRequest -Uri $url -UseBasicParsing -Headers $Headers -UserAgent $UserAgent -# $VerbosePreference = $OriginalVerbosePreference -# WriteLog "Complete" - -# # Parse the content of the relevant nested divs -# WriteLog "Parsing web content for models and download links" -# $html = $webContent.Content -# $nestedDivPattern = '
(.*?)
' -# $nestedDivMatches = [regex]::Matches($html, $nestedDivPattern, [System.Text.RegularExpressions.RegexOptions]::Singleline) - -# $models = @() -# $modelPattern = '

(.*?)

\s*\s*\s*

\s* and

tags if present + # $modelName = $modelName -replace ']*>', '' -replace '

', '' + # $modelName = $modelName.Trim() + + + # The second TD might contain a link or just text + $secondTdContent = $cellMatches[1].Groups[1].Value.Trim() + + # Look for a link in the second TD + $linkPattern = ']+href="([^"]+)"[^>]*>' + $linkMatch = [regex]::Match($secondTdContent, $linkPattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) + + if ($linkMatch.Success) { + $modelLink = $linkMatch.Groups[1].Value } else { - # Handle cases with instructions instead of links - $modelLink = $cells[1].innerText.Trim() - $models += [PSCustomObject]@{ Model = $modelName; Link = $modelLink } + # No link, just text instructions + $modelLink = $secondTdContent } + + $models += [PSCustomObject]@{ Model = $modelName; Link = $modelLink } } } } } + WriteLog "Parsing complete" # Validate the model @@ -762,7 +748,7 @@ function Get-MicrosoftDrivers { } WriteLog "The model '$Model' was not found in the list of available models." WriteLog "Please select a model from the list below by number:" - + for ($i = 0; $i -lt $models.Count; $i++) { if ($VerbosePreference -ne 'Continue') { Write-Host "$($i + 1). $($models[$i].Model)" @@ -773,7 +759,7 @@ function Get-MicrosoftDrivers { do { $selection = Read-Host "Enter the number of the model you want to select" WriteLog "User selected model number: $selection" - + if ($selection -match '^\d+$' -and [int]$selection -ge 1 -and [int]$selection -le $models.Count) { $selectedModel = $models[$selection - 1] } else { @@ -797,7 +783,7 @@ function Get-MicrosoftDrivers { $VerbosePreference = $OriginalVerbosePreference WriteLog "Complete" WriteLog "Parsing download page for file" - $scriptPattern = '' + $scriptPattern = '