Compare commits

..

23 Commits

Author SHA1 Message Date
rbalsleyMSFT 60d147c71d Update README.md 2024-09-10 11:45:44 -07:00
rbalsleyMSFT cd36150ddc Merge pull request #71 from rbalsleyMSFT/2409.1
2409.1
2024-09-10 11:44:49 -07:00
rbalsleyMSFT 198a544dbb update change log 2024-09-10 11:44:12 -07:00
rbalsleyMSFT 40616776eb - Added VMHostIPAddress and VMSwitchName validation to validate the IP address matches the VMSwitchName 2024-09-07 09:53:14 -07:00
rbalsleyMSFT 20c9cf8ab3 Merge pull request #65 from HedgeComp/Time-to-Complete-Hours
Time To Complete Shows Hours if Needed
2024-09-06 11:26:54 -07:00
rbalsleyMSFT 17558f86aa Merge pull request #64 from HedgeComp/OneDrive-Silent
Silently Install Onedrive
2024-09-06 11:26:15 -07:00
rbalsleyMSFT 7408dbb435 Merge branch '2409.1' of https://github.com/rbalsleyMSFT/FFU into 2409.1 2024-09-06 11:24:21 -07:00
rbalsleyMSFT 9c1fc59af9 - Added new variables for the PPKG, Unattend, Autopilot, and PEDrivers validation 2024-09-06 11:24:16 -07:00
HedgeComp 31c785b5da Update BuildFFUVM.ps1
Add Hours to Total Time to complete only if greater than 1 hour
2024-09-06 13:00:09 -04:00
HedgeComp 5b93135ebb Update BuildFFUVM.ps1
Add Silent switch install to Onedrivesetup.exe
2024-09-06 12:57:36 -04:00
rbalsleyMSFT 5f4cf0c66e Merge pull request #60 from zehadialam/2409.1
Fix for Missing Windows Boot Manager Entry
2024-09-06 09:34:11 -07:00
Zehadi Alam ddbf2b0339 Use bcdedit to set Windows Boot Manager and default Windows boot loader to be first in the display order of UEFI firmware 2024-09-05 17:57:35 -04:00
rbalsleyMSFT e62d481405 - Remove ValidateScript on InstallDrivers and break it out in a validation block so -Make and -Model can be specified anywhere in the command line
- Check for Prefixes.txt file and copy to the USB drive if it exists
- Perform better validation for PPKG, Unattend, Autopilot json, and drivers
- Comment out the Windows Security Platform update as the file has been removed from the MU Catalog.
2024-09-04 17:05:06 -07:00
rbalsleyMSFT 6c07ac8595 Merge branch '2409.1' of https://github.com/rbalsleyMSFT/FFU into 2409.1 2024-09-04 13:20:38 -07:00
rbalsleyMSFT 7d74feec0c - Fix an issue with removal of Defender/OneDrive/Edge after FFU is complete
- Migrate Winget downloads to use Export-WingetPackage cmdlet as per issue Known Issue: Winget downloads fail on Non-English OS #50
- Add better logging when unable to find HDD when applying FFU
2024-09-04 13:20:35 -07:00
rbalsleyMSFT 6b2a4bcb27 Merge pull request #51 from HedgeComp/2408.2
Allow Preview CU update
2024-09-04 13:08:58 -07:00
rbalsleyMSFT 6da9ece0d8 Merge pull request #54 from w0/main
use ValidateSet for WindowsSKU
2024-08-15 18:59:01 -07:00
w0 dc4438dcf9 use ValidateSet for WindowsSKU 2024-08-15 20:11:41 -05:00
rbalsleyMSFT e250e2a130 Changed some logging when winget apps can't be found 2024-08-13 16:11:36 -07:00
Doctair dad51fdf80 updated Docs to relect new $UpdatePreviewCU Parameter 2024-08-12 13:15:16 -04:00
Doctair d60b0301c5 Remove a temp copy of BUildScript 2024-08-12 10:15:45 -04:00
Doctair db3e09650a Add new Parameter for Installing Preview CU from
Mircosoft Update Catalog. Recent Windows Pro
not Auto Activating to Enterprise License Bug speared
this idea as its resoleve in lastes Prieview CU.

Parameter is default $False but if set to $true will install
Preivew CU and take precendence over $UpdateLastestCU.
2024-08-12 10:07:41 -04:00
rbalsleyMSFT bcb9911cd0 Small update to fix a logging issue with script run time duration 2024-08-07 13:01:26 -07:00
6 changed files with 517 additions and 221 deletions
+16
View File
@@ -1,5 +1,21 @@
# Change Log
## **2409.1**
### Fixes
- Fix an issue with removal of Defender/OneDrive/Edge after FFU is complete
- Migrate Winget downloads to use [Export-WingetPackage cmdlet](https://github.com/microsoft/winget-cli/blob/master/doc/specs/%23658%20-%20WinGet%20Download.md#winget-powershell-cmdlet) as per issue #50
- Add support for preview updates https://github.com/rbalsleyMSFT/FFU/pull/51 - thanks to @HedgeComp
- Refactor validation of Unattend/prefixes, PPKG, Autopilot to check for these files early in the process, similar to how we check for drivers
- Add better logging when unable to find HDD when applying FFU. Will inform to add WinPE drivers to Deployment Media if HDD not found.
- Remove ValidateScript on InstallDrivers and break it out in a validation block so -Make and -Model can be specified anywhere in the command line
- Add validation for VMHostIPAddress and VMSwtichName and inform the user if these don't match. Should prevent issues where the FFU isn't getting created.
- Removed installation of the Windows Security Platform Update as it has been removed from the MU Catalog. See issue #58
- Thanks to w0 for PR #54 to change the validation set for WindowsSKU
- Thanks to @zehadialam for PR #60 to fix an issue with Windows boot loader for certain devices where Windows Boot Manager is not the first boot entry after the FFU is applied.
- Thanks to @HedgeComp for PR #64 and PR #65
## **2408.1**
### External Drive Support
+481 -212
View File
@@ -106,6 +106,9 @@ When set to $true, will remove the FFU file from the $FFUDevelopmentPath\FFU fol
.PARAMETER UpdateLatestCU
When set to $true, will download and install the latest cumulative update for Windows 10/11. Default is $false.
.PARAMETER UpdatePreviewCU
When set to $true, will download and install the latest Preview cumulative update for Windows 10/11. Default is $false.
.PARAMETER UpdateLatestNet
When set to $true, will download and install the latest .NET Framework for Windows 10/11. Default is $false.
@@ -198,11 +201,7 @@ param(
[Parameter(Mandatory = $false, Position = 0)]
[ValidateScript({ Test-Path $_ })]
[string]$ISOPath,
[ValidateScript({
$allowedSKUs = @('Home', 'Home N', 'Home Single Language', 'Education', 'Education N', 'Pro', 'Pro N', 'Pro Education', 'Pro Education N', 'Pro for Workstations', 'Pro N for Workstations', 'Enterprise', 'Enterprise N')
if ($allowedSKUs -contains $_) { $true } else { throw "Invalid WindowsSKU value. Allowed values: $($allowedSKUs -join ', ')" }
return $true
})]
[ValidateSet('Home', 'Home N', 'Home Single Language', 'Education', 'Education N', 'Pro', 'Pro N', 'Pro Education', 'Pro Education N', 'Pro for Workstations', 'Pro N for Workstations', 'Enterprise', 'Enterprise N')]
[string]$WindowsSKU = 'Pro',
[ValidateScript({ Test-Path $_ })]
[string]$FFUDevelopmentPath = $PSScriptRoot,
@@ -211,16 +210,16 @@ param(
[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
})]
# [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,
@@ -304,6 +303,7 @@ param(
[bool]$CopyPEDrivers,
[bool]$RemoveFFU,
[bool]$UpdateLatestCU,
[bool]$UpdatePreviewCU,
[bool]$UpdateLatestNet,
[bool]$UpdateLatestDefender,
[bool]$UpdateEdge,
@@ -336,7 +336,7 @@ param(
[bool]$AllowExternalHardDiskMedia,
[bool]$PromptExternalHardDiskMedia = $true
)
$version = '2408.1'
$version = '2409.1'
#Check if Hyper-V feature is installed (requires only checks the module)
$osInfo = Get-WmiObject -Class Win32_OperatingSystem
@@ -375,6 +375,11 @@ if (-not $DefenderPath) { $DefenderPath = "$AppsPath\Defender" }
if (-not $OneDrivePath) { $OneDrivePath = "$AppsPath\OneDrive" }
if (-not $EdgePath) { $EdgePath = "$AppsPath\Edge" }
if (-not $DriversFolder) { $DriversFolder = "$FFUDevelopmentPath\Drivers" }
if (-not $PPKGFolder) { $PPKGFolder = "$FFUDevelopmentPath\PPKG" }
if (-not $UnattendFolder) { $UnattendFolder = "$FFUDevelopmentPath\Unattend" }
if (-not $AutopilotFolder) { $AutopilotFolder = "$FFUDevelopmentPath\Autopilot" }
if (-not $PEDriversFolder) { $PEDriversFolder = "$FFUDevelopmentPath\PEDrivers" }
#FUNCTIONS
function WriteLog($LogText) {
@@ -1683,33 +1688,82 @@ function Install-WinGet {
WriteLog "Downloading $($package.Name) from $($package.Url) to $destination"
Start-BitsTransferWithRetry -Source $package.Url -Destination $destination
WriteLog "Installing $($package.Name)..."
# Don't show progress bar for Add-AppxPackage - there's a weird issue where the progress stays on the screen after the apps are installed
$ProgressPreference = 'SilentlyContinue'
Add-AppxPackage -Path $destination -ErrorAction SilentlyContinue
# Set progress preference back to default
$ProgressPreference = 'Continue'
WriteLog "Removing $($package.Name)..."
Remove-Item -Path $destination -Force -ErrorAction SilentlyContinue
}
WriteLog "WinGet installation complete."
}
# function Confirm-WinGetInstallation {
# WriteLog 'Checking if WinGet is installed...'
# $wingetPath = "$env:LOCALAPPDATA\Microsoft\WindowsApps\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\winget.exe"
# $minVersion = [version]"1.8.1911"
# if (-not (Test-Path -Path $wingetPath -PathType Leaf)) {
# WriteLog "WinGet is not installed. Downloading WinGet..."
# Install-WinGet -Architecture $WindowsArch
# }
# if (-not (Get-Command -Name winget -ErrorAction SilentlyContinue)) {
# WriteLog "WinGet not found. Downloading WinGet..."
# Install-WinGet -Architecture $WindowsArch
# }
# $wingetVersion = & winget.exe --version
# WriteLog "Installed version of WinGet: $wingetVersion"
# if ($wingetVersion -match 'v?(\d+\.\d+\.\d+)' -and [version]$matches[1] -lt $minVersion) {
# WriteLog "The installed version of WinGet $($matches[1]) does not support downloading MSStore apps. Downloading the latest version of WinGet..."
# Install-WinGet -Architecture $WindowsArch
# }
# # Check if Winget PowerShell module version 1.8.1911 or later is installed
# $wingetModule = Get-InstalledModule -Name Microsoft.Winget.Client -ErrorAction SilentlyContinue
# if ($wingetModule.Version -lt $minVersion -or -not $wingetModule) {
# WriteLog 'Microsoft.Winget.Client module is not installed or is an older version. Installing the latest version...'
# #Check if PSGallery is a trusted repository
# $PSGalleryTrust = (Get-PSRepository -Name 'PSGallery').InstallationPolicy
# if($PSGalleryTrust -eq 'Untrusted'){
# WriteLog 'Temporarily setting PSGallery as a trusted repository...'
# Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
# }
# Install-Module -Name Microsoft.Winget.Client -Force -Repository 'PSGallery'
# if($PSGalleryTrust -eq 'Untrusted'){
# WriteLog 'Setting PSGallery back to untrusted repository...'
# Set-PSRepository -Name 'PSGallery' -InstallationPolicy Untrusted
# WriteLog 'Done'
# }
# }
# }
function Confirm-WinGetInstallation {
WriteLog 'Checking if WinGet is installed...'
$wingetPath = "$env:LOCALAPPDATA\Microsoft\WindowsApps\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\winget.exe"
$minVersion = [version]"1.8.1911"
if (-not (Test-Path -Path $wingetPath -PathType Leaf)) {
WriteLog "WinGet is not installed. Downloading WinGet..."
Install-WinGet -Architecture $WindowsArch
return
# Check if Winget PowerShell module version 1.8.1911 or later is installed
$wingetModule = Get-InstalledModule -Name Microsoft.Winget.Client -ErrorAction SilentlyContinue
if ($wingetModule.Version -lt $minVersion -or -not $wingetModule) {
WriteLog 'Microsoft.Winget.Client module is not installed or is an older version. Installing the latest version...'
#Check if PSGallery is a trusted repository
$PSGalleryTrust = (Get-PSRepository -Name 'PSGallery').InstallationPolicy
if($PSGalleryTrust -eq 'Untrusted'){
WriteLog 'Temporarily setting PSGallery as a trusted repository...'
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
}
if (-not (Get-Command -Name winget -ErrorAction SilentlyContinue)) {
WriteLog "WinGet not found. Downloading WinGet..."
Install-WinGet -Architecture $WindowsArch
return
Install-Module -Name Microsoft.Winget.Client -Force -Repository 'PSGallery'
if($PSGalleryTrust -eq 'Untrusted'){
WriteLog 'Setting PSGallery back to untrusted repository...'
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Untrusted
WriteLog 'Done'
}
$wingetVersion = & winget.exe --version
WriteLog "Installed version of WinGet: $wingetVersion"
if ($wingetVersion -match 'v?(\d+\.\d+\.\d+)' -and [version]$matches[1] -lt $minVersion) {
WriteLog "The installed version of WinGet $($matches[1]) does not support downloading MSStore apps. Downloading the latest version of WinGet..."
}
$wingetVersion = Get-WinGetVersion
if (-not $wingetVersion) {
WriteLog "WinGet is not installed. Installing WinGet..."
Install-WinGet -Architecture $WindowsArch
}
if (($wingetVersion -match 'v?(\d+\.\d+\.\d+)' -and [version]$matches[1] -lt $minVersion)) {
WriteLog "The installed version of WinGet $($matches[1]) does not support downloading MSStore apps. Installing the latest version of WinGet..."
Install-WinGet -Architecture $WindowsArch
return
}
}
@@ -1757,43 +1811,106 @@ function Set-InstallStoreAppsFlag {
}
}
# function Get-WinGetApp {
# param (
# [string]$WinGetAppName,
# [string]$WinGetAppId
# )
# $wingetSearchResult = & winget.exe search --id "$WinGetAppId" --exact --accept-source-agreements --source winget
# if ($wingetSearchResult -contains "No package found matching input criteria.") {
# if ($VerbosePreference -ne 'Continue'){
# Write-Error "$WinGetAppName not found in WinGet repository. Skipping download."
# Write-Error "Check the AppList.json file and make sure the AppID is correct."
# Write-Error "If OS language is not English, winget download may fail. We hope to have this addressed in a future release."
# }
# WriteLog "$WinGetAppName not found in WinGet repository. Exiting."
# WriteLog "Check the AppList.json file and make sure the AppID is correct."
# WriteLog "If OS language is not English, winget download may fail. We hope to have this addressed in a future release."
# Exit 1
# }
# $appFolderPath = Join-Path -Path "$AppsPath\Win32" -ChildPath $WinGetAppName
# WriteLog "Creating $appFolderPath"
# New-Item -Path $appFolderPath -ItemType Directory -Force | Out-Null
# WriteLog "Downloading $WinGetAppName to $appFolderPath"
# $downloadParams = @(
# "download",
# "--id", "$WinGetAppId",
# "--exact",
# "--download-directory", "$appFolderPath",
# "--accept-package-agreements",
# "--accept-source-agreements",
# "--source", "winget",
# "--scope", "machine",
# "--architecture", "$WindowsArch"
# )
# WriteLog "winget command: winget.exe $downloadParams"
# $wingetDownloadResult = & winget.exe @downloadParams | Out-String
# if ($wingetDownloadResult -match "No applicable installer found") {
# WriteLog "No installer found for $WindowsArch architecture. Attempting to download without specifying architecture..."
# $downloadParams = $downloadParams | Where-Object { $_ -notmatch "--architecture" -and $_ -notmatch "$WindowsArch" }
# $wingetDownloadResult = & winget.exe @downloadParams | Out-String
# if ($wingetDownloadResult -match "Installer downloaded") {
# WriteLog "Downloaded $WinGetAppName without specifying architecture."
# }
# }
# if ($wingetDownloadResult -notmatch "Installer downloaded") {
# WriteLog "No installer found for $WinGetAppName. Skipping download."
# Remove-Item -Path $appFolderPath -Recurse -Force
# }
# WriteLog "$WinGetAppName downloaded to $appFolderPath"
# $installerPath = Get-ChildItem -Path "$appFolderPath\*" -Exclude "*.yaml", "*.xml" -File -ErrorAction Stop
# $uwpExtensions = @(".appx", ".appxbundle", ".msix", ".msixbundle")
# if ($uwpExtensions -contains $installerPath.Extension) {
# $NewAppPath = "$AppsPath\MSStore\$WinGetAppName"
# Writelog "$WinGetAppName is a UWP app. Moving to $NewAppPath"
# WriteLog "Creating $NewAppPath"
# New-Item -Path "$AppsPath\MSStore\$WinGetAppName" -ItemType Directory -Force | Out-Null
# WriteLog "Moving $WinGetAppName to $NewAppPath"
# Move-Item -Path "$appFolderPath\*" -Destination "$AppsPath\MSStore\$WinGetAppName" -Force
# WriteLog "Removing $appFolderPath"
# Remove-Item -Path $appFolderPath -Force
# WriteLog "$WinGetAppName moved to $NewAppPath"
# Set-InstallStoreAppsFlag
# }
# else {
# Add-Win32SilentInstallCommand -AppFolder $WinGetAppName -AppFolderPath $appFolderPath
# }
# }
function Get-WinGetApp {
param (
[string]$WinGetAppName,
[string]$WinGetAppId
)
$wingetSearchResult = & winget.exe search --id "$WinGetAppId" --exact --accept-source-agreements --source winget
if ($wingetSearchResult -contains "No package found matching input criteria.") {
WriteLog "$WinGetAppName not found in WinGet repository. Skipping download."
$Source = 'winget'
$wingetSearchResult = Find-WinGetPackage -id $WinGetAppId -MatchOption Equals -Source $Source
if (-not $wingetSearchResult) {
if ($VerbosePreference -ne 'Continue'){
Write-Error "$WinGetAppName not found in WinGet repository. Exiting."
Write-Error "Check the AppList.json file and make sure the AppID is correct."
}
WriteLog "$WinGetAppName not found in WinGet repository. Exiting."
WriteLog "Check the AppList.json file and make sure the AppID is correct."
Exit 1
}
$appFolderPath = Join-Path -Path "$AppsPath\Win32" -ChildPath $WinGetAppName
WriteLog "Creating $appFolderPath"
New-Item -Path $appFolderPath -ItemType Directory -Force | Out-Null
WriteLog "Downloading $WinGetAppName to $appFolderPath"
$downloadParams = @(
"download",
"--id", "$WinGetAppId",
"--exact",
"--download-directory", "$appFolderPath",
"--accept-package-agreements",
"--accept-source-agreements",
"--source", "winget",
"--scope", "machine",
"--architecture", "$WindowsArch"
)
WriteLog "winget command: winget.exe $downloadParams"
$wingetDownloadResult = & winget.exe @downloadParams | Out-String
if ($wingetDownloadResult -match "No applicable installer found") {
WriteLog "WinGet command: Export-WinGetPackage -id $WinGetAppId -DownloadDirectory $appFolderPath -Architecture $WindowsArch -Source $Source"
$wingetDownloadResult = Export-WinGetPackage -id $WinGetAppId -DownloadDirectory $appFolderPath -Architecture $WindowsArch -Source $Source
if ($wingetDownloadResult.status -eq 'NoApplicableInstallers') {
# If no applicable installer is found, try downloading without specifying architecture
WriteLog "No installer found for $WindowsArch architecture. Attempting to download without specifying architecture..."
$downloadParams = $downloadParams | Where-Object { $_ -notmatch "--architecture" -and $_ -notmatch "$WindowsArch" }
$wingetDownloadResult = & winget.exe @downloadParams | Out-String
if ($wingetDownloadResult -match "Installer downloaded") {
$wingetDownloadResult = Export-WinGetPackage -id $WinGetAppId -DownloadDirectory $appFolderPath -Source $Source
if ($wingetDownloadResult.status -eq 'Ok') {
WriteLog "Downloaded $WinGetAppName without specifying architecture."
}
}
if ($wingetDownloadResult -notmatch "Installer downloaded") {
WriteLog "No installer found for $WinGetAppName. Skipping download."
else{
WriteLog "No installer found for $WinGetAppName. Exiting."
Remove-Item -Path $appFolderPath -Recurse -Force
Exit 1
}
}
WriteLog "$WinGetAppName downloaded to $appFolderPath"
$installerPath = Get-ChildItem -Path "$appFolderPath\*" -Exclude "*.yaml", "*.xml" -File -ErrorAction Stop
@@ -1815,15 +1932,105 @@ function Get-WinGetApp {
}
}
# function Get-StoreApp {
# param (
# [string]$StoreAppName,
# [string]$StoreAppId
# )
# $wingetSearchResult = & winget.exe search "$StoreAppId" --accept-source-agreements --source msstore
# if ($wingetSearchResult -contains "No package found matching input criteria.") {
# WriteLog "$StoreAppName not found in WinGet repository. Skipping download."
# return
# }
# WriteLog "Checking if $StoreAppName is a win32 app..."
# $appIsWin32 = $StoreAppId.StartsWith("XP")
# if ($appIsWin32) {
# WriteLog "$StoreAppName is a win32 app. Adding to $AppsPath\win32 folder"
# $appFolderPath = Join-Path -Path "$AppsPath\win32" -ChildPath $StoreAppName
# }
# else {
# WriteLog "$StoreAppName is not a win32 app."
# $appFolderPath = Join-Path -Path "$AppsPath\MSStore" -ChildPath $StoreAppName
# }
# New-Item -Path $appFolderPath -ItemType Directory -Force | Out-Null
# WriteLog "Downloading $StoreAppName for $WindowsArch architecture..."
# $downloadParams = @(
# "download", "$StoreAppId",
# "--download-directory", "$appFolderPath",
# "--accept-package-agreements",
# "--accept-source-agreements",
# "--source", "msstore",
# "--scope", "machine",
# "--architecture", "$WindowsArch"
# )
# WriteLog 'MSStore app downloads require authentication with an Entra ID account. You may be prompted twice for credentials, once for the app and another for the license file.'
# WriteLog "Attempting to download $StoreAppName and dependencies for $WindowsArch architecture..."
# $wingetDownloadResult = & winget.exe @downloadParams | Out-String
# # For some apps, specifying the architecture leads to no results found for the app. In those cases, the command will be run without the architecture parameter.
# if ($wingetDownloadResult -match "No applicable installer found") {
# WriteLog "No installer found for $WindowsArch architecture. Attempting to download without specifying architecture..."
# $downloadParams = $downloadParams | Where-Object { $_ -notmatch "--architecture" -and $_ -notmatch "$WindowsArch" }
# $wingetDownloadResult = & winget.exe @downloadParams | Out-String
# if ($wingetDownloadResult -match "Microsoft Store package download completed") {
# WriteLog "Downloaded $StoreAppName without specifying architecture."
# }
# }
# if ($wingetDownloadResult -notmatch "Installer downloaded|Microsoft Store package download completed") {
# WriteLog "Download not supported for $StoreAppName. Skipping download."
# Remove-Item -Path $appFolderPath -Recurse -Force
# return
# }
# if ($appIsWin32) {
# Add-Win32SilentInstallCommand -AppFolder $StoreAppName -AppFolderPath $appFolderPath
# }
# Set-InstallStoreAppsFlag
# # If $WindowsArch -eq 'ARM64', remove all dependency files that are not ARM64
# if ($WindowsArch -eq 'ARM64') {
# WriteLog 'Windows architecture is ARM64. Removing dependencies that are not ARM64.'
# $dependencies = Get-ChildItem -Path "$appFolderPath\Dependencies" -ErrorAction SilentlyContinue
# if ($dependencies) {
# foreach ($dependency in $dependencies) {
# if ($dependency.Name -notmatch 'ARM64') {
# WriteLog "Removing dependency file $($dependency.FullName)"
# Remove-Item -Path $dependency.FullName -Recurse -Force
# }
# }
# }
# }
# WriteLog "$StoreAppName has completed downloading. Identifying the latest version of $StoreAppName."
# $packages = Get-ChildItem -Path "$appFolderPath\*" -Exclude "Dependencies\*", "*.xml", "*.yaml" -File -ErrorAction Stop
# # WinGet downloads multiple versions of certain store apps. The latest version of the package will be determined based on the date of the file signature.
# $latestPackage = $packages | Sort-Object { (Get-AuthenticodeSignature $_.FullName).SignerCertificate.NotBefore } -Descending | Select-Object -First 1
# # Removing all packages that are not the latest version
# WriteLog "Latest version of $StoreAppName has been identified as $latestPackage. Removing old versions of $StoreAppName that may have downloaded."
# foreach ($package in $packages) {
# if ($package.FullName -ne $latestPackage) {
# try {
# WriteLog "Removing $($package.FullName)"
# Remove-Item -Path $package.FullName -Force
# }
# catch {
# WriteLog "Failed to delete: $($package.FullName) - $_"
# throw $_
# }
# }
# }
# }
function Get-StoreApp {
param (
[string]$StoreAppName,
[string]$StoreAppId
)
$wingetSearchResult = & winget.exe search "$StoreAppId" --accept-source-agreements --source msstore
if ($wingetSearchResult -contains "No package found matching input criteria.") {
WriteLog "$StoreAppName not found in WinGet repository. Skipping download."
return
$Source = 'msstore'
$wingetSearchResult = Find-WinGetPackage -id $StoreAppId -MatchOption Equals -Source $Source
if (-not $wingetSearchResult) {
if ($VerbosePreference -ne 'Continue'){
Write-Error "$WinGetAppName not found in WinGet repository. Exiting."
Write-Error "Check the AppList.json file and make sure the AppID is correct."
}
WriteLog "$WinGetAppName not found in WinGet repository. Exiting."
WriteLog "Check the AppList.json file and make sure the AppID is correct."
Exit 1
}
WriteLog "Checking if $StoreAppName is a win32 app..."
$appIsWin32 = $StoreAppId.StartsWith("XP")
@@ -1837,31 +2044,22 @@ function Get-StoreApp {
}
New-Item -Path $appFolderPath -ItemType Directory -Force | Out-Null
WriteLog "Downloading $StoreAppName for $WindowsArch architecture..."
$downloadParams = @(
"download", "$StoreAppId",
"--download-directory", "$appFolderPath",
"--accept-package-agreements",
"--accept-source-agreements",
"--source", "msstore",
"--scope", "machine",
"--architecture", "$WindowsArch"
)
WriteLog 'MSStore app downloads require authentication with an Entra ID account. You may be prompted twice for credentials, once for the app and another for the license file.'
WriteLog "Attempting to download $StoreAppName and dependencies for $WindowsArch architecture..."
$wingetDownloadResult = & winget.exe @downloadParams | Out-String
# For some apps, specifying the architecture leads to no results found for the app. In those cases, the command will be run without the architecture parameter.
if ($wingetDownloadResult -match "No applicable installer found") {
WriteLog "WinGet command: Export-WinGetPackage -id $StoreAppId -DownloadDirectory $appFolderPath -Architecture $WindowsArch -Source $Source"
$wingetDownloadResult = Export-WinGetPackage -id $StoreAppId -DownloadDirectory $appFolderPath -Architecture $WindowsArch -Source $Source
if ($wingetDownloadResult.status -eq 'NoApplicableInstallerFound') {
# If no applicable installer is found, try downloading without specifying architecture
WriteLog "No installer found for $WindowsArch architecture. Attempting to download without specifying architecture..."
$downloadParams = $downloadParams | Where-Object { $_ -notmatch "--architecture" -and $_ -notmatch "$WindowsArch" }
$wingetDownloadResult = & winget.exe @downloadParams | Out-String
if ($wingetDownloadResult -match "Microsoft Store package download completed") {
WriteLog "Downloaded $StoreAppName without specifying architecture."
$wingetDownloadResult = Export-WinGetPackage -id $StoreAppId -DownloadDirectory $appFolderPath -Source $Source
if ($wingetDownloadResult.status -eq 'Ok') {
WriteLog "Downloaded $WinGetAppName without specifying architecture."
}
}
if ($wingetDownloadResult -notmatch "Installer downloaded|Microsoft Store package download completed") {
WriteLog "Download not supported for $StoreAppName. Skipping download."
else{
WriteLog "No installer found for $WinGetAppName. Exiting"
Remove-Item -Path $appFolderPath -Recurse -Force
return
Exit 1
}
}
if ($appIsWin32) {
Add-Win32SilentInstallCommand -AppFolder $StoreAppName -AppFolderPath $appFolderPath
@@ -2050,68 +2248,6 @@ function Save-KB {
if ($WindowsArch -eq 'x64') {
[array]$WindowsArch = @("x64", "amd64")
}
#Keep for now, will remove in future
# foreach ($kb in $name) {
# $links = Get-KBLink -Name $kb
# foreach ($link in $links) {
# #Check if $WindowsArch is an array
# if ($WindowsArch -is [array]) {
# #Some file names include either x64 or amd64
# if ($link -match $WindowsArch[0] -or $link -match $WindowsArch[1]) {
# Start-BitsTransferWithRetry -Source $link -Destination $Path
# $fileName = ($link -split '/')[-1]
# break
# }
# # elseif (!($link -match 'x64' -or $link -match 'amd64' -or $link -match 'x86' -or $link -match 'arm64')) {
# # Write-Host "No architecture found in $link, assume it's for all architectures"
# # Start-BitsTransfer -Source $link -Destination $Path
# # $fileName = ($link -split '/')[-1]
# # break
# # }
# elseif (!($link -match 'x64' -or $link -match 'amd64' -or $link -match 'x86' -or $link -match 'arm64')) {
# WriteLog "No architecture found in $link, assume this is for all architectures"
# #FIX: 3/22/2024 - the SecurityHealthSetup fix was updated and now includes two files (one is x64 and the other is arm64)
# #Unfortunately there is no easy way to determine the architecture from the file name
# #There is a support doc that include links to download, but it's out of date (n-1)
# #https://support.microsoft.com/en-us/topic/windows-security-update-a6ac7d2e-b1bf-44c0-a028-41720a242da3
# #These files don't change that often, so will check the link above to see when it updates and may use that
# #For now this is hard-coded for these specific file names
# if ($link -match 'security'){
# #Make sure we're getting the correct architecture for the Security Health Setup update
# WriteLog "Link: $link matches security"
# if ($WindowsArch -eq 'x64'){
# if ($link -match 'securityhealthsetup_e1'){
# Writelog "Downloading $Link for $WindowsArch to $Path"
# Start-BitsTransferWithRetry -Source $link -Destination $Path
# $fileName = ($link -split '/')[-1]
# Writelog "Returning $fileName"
# break
# }
# }
# elseif ($WindowsArch -eq 'arm64'){
# if ($link -match 'securityhealthsetup_25'){
# Writelog "Downloading $Link for $WindowsArch to $Path"
# Start-BitsTransferWithRetry -Source $link -Destination $Path
# $fileName = ($link -split '/')[-1]
# Writelog "Returning $fileName"
# break
# }
# }
# continue
# }
# Start-BitsTransferWithRetry -Source $link -Destination $Path
# $fileName = ($link -split '/')[-1]
# }
# }
# else {
# if ($link -match $WindowsArch) {
# Start-BitsTransferWithRetry -Source $link -Destination $Path
# $fileName = ($link -split '/')[-1]
# break
# }
# }
# }
# }
foreach ($kb in $name) {
$links = Get-KBLink -Name $kb
foreach ($link in $links) {
@@ -3228,9 +3364,14 @@ Function New-DeploymentUSB {
if ($WindowsArch -eq 'x64') {
Copy-Item -Path "$FFUDevelopmentPath\unattend\unattend_x64.xml" -Destination "$DeployUnattendPath\Unattend.xml" -Force | Out-Null
}
else {
if ($WindowsArch -eq 'arm64') {
Copy-Item -Path "$FFUDevelopmentPath\unattend\unattend_arm64.xml" -Destination "$DeployUnattendPath\Unattend.xml" -Force | Out-Null
}
#Check for prefixes.txt file and copy it to the USB drive
if (Test-Path "$FFUDevelopmentPath\unattend\prefixes.txt") {
WriteLog "Copying prefixes.txt file to $DeployUnattendPath"
Copy-Item -Path "$FFUDevelopmentPath\unattend\prefixes.txt" -Destination "$DeployUnattendPath\prefixes.txt" -Force | Out-Null
}
WriteLog 'Copy completed'
}
#Copy PPKG folder in the FFU folder to the USB drive. Can use copy-item as it's a small folder
@@ -3322,9 +3463,14 @@ function Get-FFUEnvironment {
foreach ($image in $mountedImages) {
$mountPath = $image.Path
WriteLog "Dismounting image at $mountPath"
try {
Dismount-WindowsImage -Path $mountPath -discard | Out-null
WriteLog "Successfully dismounted image at $mountPath"
}
catch {
WriteLog "Failed to dismount image at $mountPath with error: $_"
}
}
}
# Remove Mount folder if it exists
@@ -3352,6 +3498,7 @@ function Get-FFUEnvironment {
Remove-FFUUserShare
WriteLog 'Removal complete'
}
Clear-InstallAppsandSysprep
#Clean up $KBPath
If (Test-Path -Path $KBPath) {
WriteLog "Removing $KBPath"
@@ -3384,7 +3531,6 @@ function Get-FFUEnvironment {
WriteLog "Cleaning up MSStore folder"
Remove-Item -Path "$AppsPath\MSStore" -Recurse -Force -ErrorAction SilentlyContinue
}
Clear-InstallAppsandSysprep
Writelog 'Removing dirty.txt file'
Remove-Item -Path "$FFUDevelopmentPath\dirty.txt" -Force
WriteLog "Cleanup complete"
@@ -3408,29 +3554,34 @@ function Clear-InstallAppsandSysprep {
WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to remove Defender Platform Update"
$CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
$CmdContent -notmatch 'd:\\Defender*' | Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
# #Remove $DefenderPath
# WriteLog "Removing $DefenderPath"
# Remove-Item -Path $DefenderPath -Recurse -Force
# WriteLog "Removal complete"
#Clean up $DefenderPath
If (Test-Path -Path $DefenderPath) {
WriteLog "Removing $DefenderPath"
Remove-Item -Path $DefenderPath -Recurse -Force -ErrorAction SilentlyContinue
WriteLog 'Removal complete'
}
}
if ($UpdateOneDrive) {
WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to remove OneDrive install"
$CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
$CmdContent -notmatch 'd:\\OneDrive*' | Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
# #Remove $OneDrivePath
# WriteLog "Removing $OneDrivePath"
# Remove-Item -Path $OneDrivePath -Recurse -Force
# WriteLog "Removal complete"
#Clean up $OneDrivePath
If (Test-Path -Path $OneDrivePath) {
WriteLog "Removing $OneDrivePath"
Remove-Item -Path $OneDrivePath -Recurse -Force -ErrorAction SilentlyContinue
WriteLog 'Removal complete'
}
}
if ($UpdateEdge) {
WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to remove Edge install"
$CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
$CmdContent -notmatch 'd:\\Edge*' | Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
# #Remove $EdgePath
# WriteLog "Removing $EdgePath"
# Remove-Item -Path $EdgePath -Recurse -Force
# WriteLog "Removal complete"
#Clean up $EdgePath
If (Test-Path -Path $EdgePath) {
WriteLog "Removing $EdgePath"
Remove-Item -Path $EdgePath -Recurse -Force -ErrorAction SilentlyContinue
WriteLog 'Removal complete'
}
}
}
@@ -3447,6 +3598,84 @@ Write-Host "To track progress, please open the log file $Logfile or use the -Ver
WriteLog 'Begin Logging'
###PARAMETER VALIDATION
#Validate drivers folder
if ($InstallDrivers -or $CopyDrivers) {
WriteLog 'Doing driver validation'
if ($Make -and $Model){
WriteLog "Make and Model are set to $Make and $Model, will attempt to download drivers"
} else {
if (!(Test-Path -Path $DriversFolder)) {
WriteLog "-InstallDrivers or -CopyDrivers is set to `$true, but the $DriversFolder folder is missing"
throw "-InstallDrivers or -CopyDrivers is set to `$true, but the $DriversFolder folder is missing"
}
if ((Get-ChildItem -Path $DriversFolder -Recurse | Measure-Object -Property Length -Sum).Sum -lt 1MB) {
WriteLog "-InstallDrivers or -CopyDrivers is set to `$true, but the $DriversFolder folder is empty"
throw "-InstallDrivers or -CopyDrivers is set to `$true, but the $DriversFolder folder is empty"
}
WriteLog 'Driver validation complete'
}
}
#Validate PEDrivers folder
if ($CopyPEDrivers) {
WriteLog 'Doing PEDriver validation'
if (!(Test-Path -Path $PEDriversFolder)) {
WriteLog "-CopyPEDrivers is set to `$true, but the $PEDriversFolder folder is missing"
throw "-CopyPEDrivers is set to `$true, but the $PEDriversFolder folder is missing"
}
if ((Get-ChildItem -Path $PEDriversFolder -Recurse | Measure-Object -Property Length -Sum).Sum -lt 1MB) {
WriteLog "-CopyPEDrivers is set to `$true, but the $PEDriversFolder folder is empty"
throw "-CopyPEDrivers is set to `$true, but the $PEDriversFolder folder is empty"
}
WriteLog 'PEDriver validation complete'
}
#Validate PPKG folder
if ($CopyPPKG) {
WriteLog 'Doing PPKG validation'
if (!(Test-Path -Path $PPKGFolder)) {
WriteLog "-CopyPPKG is set to `$true, but the $PPKGFolder folder is missing"
throw "-CopyPPKG is set to `$true, but the $PPKGFolder folder is missing"
}
#Check for at least one .PPKG file
if (!(Get-ChildItem -Path $PPKGFolder -Filter *.ppkg)) {
WriteLog "-CopyPPKG is set to `$true, but the $PPKGFolder folder is missing a .PPKG file"
throw "-CopyPPKG is set to `$true, but the $PPKGFolder folder is missing a .PPKG file"
}
WriteLog 'PPKG validation complete'
}
#Validate Autopilot folder
if ($CopyAutopilot) {
WriteLog 'Doing Autopilot validation'
if (!(Test-Path -Path $AutopilotFolder)) {
WriteLog "-CopyAutopilot is set to `$true, but the $AutopilotFolder folder is missing"
throw "-CopyAutopilot is set to `$true, but the $AutopilotFolder folder is missing"
}
#Check for .JSON file
if (!(Get-ChildItem -Path $AutopilotFolder -Filter *.json)) {
WriteLog "-CopyAutopilot is set to `$true, but the $AutopilotFolder folder is missing a .JSON file"
throw "-CopyAutopilot is set to `$true, but the $AutopilotFolder folder is missing a .JSON file"
}
WriteLog 'Autopilot validation complete'
}
#Validate Unattend folder
if ($CopyUnattend) {
WriteLog 'Doing Unattend validation'
if (!(Test-Path -Path $UnattendFolder)) {
WriteLog "-CopyUnattend is set to `$true, but the $UnattendFolder folder is missing"
throw "-CopyUnattend is set to `$true, but the $UnattendFolder folder is missing"
}
#Check for .XML file
if (!(Get-ChildItem -Path $UnattendFolder -Filter unattend_*.xml)) {
WriteLog "-CopyUnattend is set to `$true, but the $UnattendFolder folder is missing a .XML file"
throw "-CopyUnattend is set to `$true, but the $UnattendFolder folder is missing a .XML file"
}
WriteLog 'Unattend validation complete'
}
#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).
#This behavior doesn't happen with WIM files.
@@ -3466,6 +3695,25 @@ 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
$VMSwitch = Get-VMSwitch -Name $VMSwitchName -ErrorAction SilentlyContinue
if (-not $VMSwitch) {
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
$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) {
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*')) {
throw "netfx3 specified as an optional feature, however Windows ISO isn't defined. Unable to get netfx3 source files from downloaded ESD media. Please specify a Windows ISO in the ISOPath parameter."
}
@@ -3492,10 +3740,8 @@ if (($WindowsArch -eq 'ARM64') -and ($UpdateOneDrive -eq $true)) {
$UpdateOneDrive = $false
WriteLog 'OneDrive currently fails to install on ARM64 VMs (even with the OneDrive ARM setup files). Setting UpdateOneDrive to false'
}
# if(($WindowsArch -eq 'ARM64') -and ($UpdateLatestDefender -eq $true)){
# $UpdateLatestDefender = $false
# WriteLog 'Defender ARM and x64 updates currently fail to install on ARM64 VMs. Setting UpdateLatestDefender to false'
# }
###END PARAMETER VALIDATION
#Get script variable values
LogVariableValues
@@ -3599,17 +3845,20 @@ if ($InstallApps) {
Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $UpdatedcmdContent
WriteLog "Update complete"
#Get Windows Security platform update
$Name = "Windows Security platform definition updates"
WriteLog "Searching for $Name from Microsoft Update Catalog and saving to $DefenderPath"
$KBFilePath = Save-KB -Name $Name -Path $DefenderPath
WriteLog "Latest Security Platform Update saved to $DefenderPath\$KBFilePath"
#Modify InstallAppsandSysprep.cmd to add in $KBFilePath on the line after REM Install Windows Security Platform Update
WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to include Windows Security Platform Update"
$CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
$UpdatedcmdContent = $CmdContent -replace '^(REM Install Windows Security Platform Update)', ("REM Install Windows Security Platform Update`r`nd:\Defender\$KBFilePath")
Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $UpdatedcmdContent
WriteLog "Update complete"
###### 9/4/2024 - Windows Security Platform update is no longer available from Update Catalog. Will change to using
###### https://support.microsoft.com/en-us/topic/windows-security-update-a6ac7d2e-b1bf-44c0-a028-41720a242da3
# #Get Windows Security platform update
# $Name = "Windows Security platform definition updates"
# WriteLog "Searching for $Name from Microsoft Update Catalog and saving to $DefenderPath"
# $KBFilePath = Save-KB -Name $Name -Path $DefenderPath
# WriteLog "Latest Security Platform Update saved to $DefenderPath\$KBFilePath"
# #Modify InstallAppsandSysprep.cmd to add in $KBFilePath on the line after REM Install Windows Security Platform Update
# WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to include Windows Security Platform Update"
# $CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
# $UpdatedcmdContent = $CmdContent -replace '^(REM Install Windows Security Platform Update)', ("REM Install Windows Security Platform Update`r`nd:\Defender\$KBFilePath")
# Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $UpdatedcmdContent
# WriteLog "Update complete"
#Download latest Defender Definitions
WriteLog "Downloading latest Defender Definitions"
@@ -3668,7 +3917,7 @@ if ($InstallApps) {
#Modify InstallAppsandSysprep.cmd to add in $OneDrivePath on the line after REM Install Defender Definitions
WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to include OneDrive client"
$CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
$UpdatedcmdContent = $CmdContent -replace '^(REM Install OneDrive Per Machine)', ("REM Install OneDrive Per Machine`r`nd:\OneDrive\OneDriveSetup.exe /allusers")
$UpdatedcmdContent = $CmdContent -replace '^(REM Install OneDrive Per Machine)', ("REM Install OneDrive Per Machine`r`nd:\OneDrive\OneDriveSetup.exe /allusers /silent")
Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" -Value $UpdatedcmdContent
WriteLog "Update complete"
}
@@ -3749,10 +3998,10 @@ try {
Add-BootFiles -OsPartitionDriveLetter $osPartitionDriveLetter -SystemPartitionDriveLetter $systemPartitionDriveLetter[1]
#Update latest Cumulative Update
#Update latest Cumulative Update if both $UpdateLatestCU is $true and $UpdatePreviewCU is $false
#Changed to use MU Catalog instead of using Get-LatestWindowsKB
#The Windows release info page is updated later than the MU Catalog
if ($UpdateLatestCU) {
if ($UpdateLatestCU -and -not $UpdatePreviewCU) {
Writelog "`$UpdateLatestCU is set to true, checking for latest CU"
$Name = """Cumulative update for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
#Check if $KBPath exists, if not, create it
@@ -3765,6 +4014,20 @@ try {
WriteLog "Latest CU saved to $KBPath\$KBFilePath"
}
#Update Latest Preview Cumlative Update
#will take Precendence over $UpdateLastestCU if both were set to $true
if ($UpdatePreviewCU) {
Writelog "`$UpdatePreviewCU is set to true, checking for latest Preview CU"
$Name = """Cumulative update Preview for Windows $WindowsRelease Version $WindowsVersion for $WindowsArch"""
#Check if $KBPath exists, if not, create it
If (-not (Test-Path -Path $KBPath)) {
WriteLog "Creating $KBPath"
New-Item -Path $KBPath -ItemType Directory -Force | Out-Null
}
WriteLog "Searching for $name from Microsoft Update Catalog and saving to $KBPath"
$KBFilePath = Save-KB -Name $Name -Path $KBPath
WriteLog "Latest Preview CU saved to $KBPath\$KBFilePath"
}
#Update Latest .NET Framework
if ($UpdateLatestNet) {
@@ -3779,23 +4042,23 @@ try {
$KBFilePath = Save-KB -Name $Name -Path $KBPath
WriteLog "Latest .NET saved to $KBPath\$KBFilePath"
}
#Update Latest Security Platform Update
if ($UpdateSecurityPlatform) {
WriteLog "`$UpdateSecurityPlatform is set to true, checking for latest Security Platform Update"
$Name = "Windows Security platform definition updates"
#Check if $KBPath exists, if not, create it
If (-not (Test-Path -Path $KBPath)) {
WriteLog "Creating $KBPath"
New-Item -Path $KBPath -ItemType Directory -Force | Out-Null
}
WriteLog "Searching for $Name from Microsoft Update Catalog and saving to $KBPath"
$KBFilePath = Save-KB -Name $Name -Path $KBPath
WriteLog "Latest Security Platform Update saved to $KBPath\$KBFilePath"
}
# #Update Latest Security Platform Update
# if ($UpdateSecurityPlatform) {
# WriteLog "`$UpdateSecurityPlatform is set to true, checking for latest Security Platform Update"
# $Name = "Windows Security platform definition updates"
# #Check if $KBPath exists, if not, create it
# If (-not (Test-Path -Path $KBPath)) {
# WriteLog "Creating $KBPath"
# New-Item -Path $KBPath -ItemType Directory -Force | Out-Null
# }
# WriteLog "Searching for $Name from Microsoft Update Catalog and saving to $KBPath"
# $KBFilePath = Save-KB -Name $Name -Path $KBPath
# WriteLog "Latest Security Platform Update saved to $KBPath\$KBFilePath"
# }
#Add Windows packages
if ($UpdateLatestCU -or $UpdateLatestNet) {
if ($UpdateLatestCU -or $UpdateLatestNet -or $UpdatePreviewCU ) {
try {
WriteLog "Adding KBs to $WindowsPartition"
WriteLog 'This can take 10+ minutes depending on how old the media is and the size of the KB. Please be patient'
@@ -3999,30 +4262,30 @@ catch {
throw $_
}
#Clean up InstallAppsandSysprep.cmd
try {
WriteLog "Cleaning up $AppsPath\InstallAppsandSysprep.cmd"
Clear-InstallAppsandSysprep
}
catch {
Write-Host 'Cleaning up InstallAppsandSysprep.cmd failed'
Writelog "Cleaning up InstallAppsandSysprep.cmd failed with error $_"
throw $_
}
try {
if (Test-Path -Path "$AppsPath\Win32" -PathType Container) {
WriteLog "Cleaning up Win32 folder"
Remove-Item -Path "$AppsPath\Win32" -Recurse -Force
}
if (Test-Path -Path "$AppsPath\MSStore" -PathType Container) {
WriteLog "Cleaning up MSStore folder"
Remove-Item -Path "$AppsPath\MSStore" -Recurse -Force
}
}
catch {
WriteLog "$_"
throw $_
}
# #Clean up InstallAppsandSysprep.cmd
# try {
# WriteLog "Cleaning up $AppsPath\InstallAppsandSysprep.cmd"
# Clear-InstallAppsandSysprep
# }
# catch {
# Write-Host 'Cleaning up InstallAppsandSysprep.cmd failed'
# Writelog "Cleaning up InstallAppsandSysprep.cmd failed with error $_"
# throw $_
# }
# try {
# if (Test-Path -Path "$AppsPath\Win32" -PathType Container) {
# WriteLog "Cleaning up Win32 folder"
# Remove-Item -Path "$AppsPath\Win32" -Recurse -Force
# }
# if (Test-Path -Path "$AppsPath\MSStore" -PathType Container) {
# WriteLog "Cleaning up MSStore folder"
# Remove-Item -Path "$AppsPath\MSStore" -Recurse -Force
# }
# }
# catch {
# WriteLog "$_"
# throw $_
# }
#Create Deployment Media
If ($CreateDeploymentMedia) {
try {
@@ -4127,10 +4390,16 @@ Write-Host "FFU build process completed at" $endTime
# Calculate the total run time
$runTime = $endTime - $startTime
# Format the runtime as minutes and seconds
# Format the runtime with hours, minutes, and seconds
if ($runTime.TotalHours -ge 1) {
$runTimeFormatted = 'Duration: {0:hh} hr {0:mm} min {0:ss} sec' -f $runTime
}
else {
$runTimeFormatted = 'Duration: {0:mm} min {0:ss} sec' -f $runTime
}
if ($VerbosePreference -ne 'Continue'){
Write-Host $runTimeFormatted
}
WriteLog 'Script complete: ' + $runTimeFormatted
WriteLog 'Script complete'
WriteLog $runTimeFormatted
Binary file not shown.
@@ -128,13 +128,18 @@ $LogFileName = 'ScriptLog.txt'
$USBDrive = Get-USBDrive
New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null
$LogFile = $USBDrive + $LogFilename
$version = '2408.1'
$version = '2409.1'
WriteLog 'Begin Logging'
WriteLog "Script version: $version"
#Find PhysicalDrive
# $PhysicalDeviceID = Get-HardDrive
$hardDrive = Get-HardDrive
if($hardDrive -eq $null){
WriteLog 'No hard drive found. Exiting'
WriteLog 'Try adding storage drivers to the PE boot image (you can re-create your FFU and USB drive and add the PE drivers to the PEDrivers folder and add -CopyPEDrivers $true to the command line, or manually add them via DISM)'
Exit
}
$PhysicalDeviceID = $hardDrive.DeviceID
$BytesPerSector = $hardDrive.BytesPerSector
WriteLog "Physical BytesPerSector is $BytesPerSector"
@@ -549,6 +554,12 @@ If (Test-Path -Path $Drivers)
WriteLog 'Copying drivers succeeded'
}
WriteLog "Setting Windows Boot Manager to be first in the display order."
Invoke-Process bcdedit.exe "/set {fwbootmgr} displayorder {bootmgr} /addfirst"
WriteLog "Windows Boot Manager has been set to be first in the display order."
WriteLog "Setting default Windows boot loader to be first in the display order."
Invoke-Process bcdedit.exe "/set {bootmgr} displayorder {default} /addfirst"
WriteLog "The default Windows boot loader has been set to be first in the display order."
#Copy DISM log to USBDrive
WriteLog "Copying dism log to $USBDrive"
invoke-process xcopy "X:\Windows\logs\dism\dism.log $USBDrive /Y"
+1 -1
View File
@@ -20,7 +20,7 @@ The Full-Flash update (FFU) process can automatically download the latest releas
# Updates
2408.1 has been released! Check out the changes in the [Change Log](ChangeLog.md)
2409.1 has been released! Check out the changes in the [Change Log](ChangeLog.md)
# Getting Started