- 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
This commit is contained in:
rbalsleyMSFT
2024-09-04 13:20:35 -07:00
parent 6da9ece0d8
commit 7d74feec0c
2 changed files with 308 additions and 170 deletions
+297 -164
View File
@@ -332,7 +332,7 @@ param(
[bool]$AllowExternalHardDiskMedia, [bool]$AllowExternalHardDiskMedia,
[bool]$PromptExternalHardDiskMedia = $true [bool]$PromptExternalHardDiskMedia = $true
) )
$version = '2408.2' $version = '2409.1'
#Check if Hyper-V feature is installed (requires only checks the module) #Check if Hyper-V feature is installed (requires only checks the module)
$osInfo = Get-WmiObject -Class Win32_OperatingSystem $osInfo = Get-WmiObject -Class Win32_OperatingSystem
@@ -1679,33 +1679,82 @@ function Install-WinGet {
WriteLog "Downloading $($package.Name) from $($package.Url) to $destination" WriteLog "Downloading $($package.Name) from $($package.Url) to $destination"
Start-BitsTransferWithRetry -Source $package.Url -Destination $destination Start-BitsTransferWithRetry -Source $package.Url -Destination $destination
WriteLog "Installing $($package.Name)..." 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 Add-AppxPackage -Path $destination -ErrorAction SilentlyContinue
# Set progress preference back to default
$ProgressPreference = 'Continue'
WriteLog "Removing $($package.Name)..." WriteLog "Removing $($package.Name)..."
Remove-Item -Path $destination -Force -ErrorAction SilentlyContinue Remove-Item -Path $destination -Force -ErrorAction SilentlyContinue
} }
WriteLog "WinGet installation complete." 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 { function Confirm-WinGetInstallation {
WriteLog 'Checking if WinGet is installed...' WriteLog 'Checking if WinGet is installed...'
$wingetPath = "$env:LOCALAPPDATA\Microsoft\WindowsApps\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\winget.exe"
$minVersion = [version]"1.8.1911" $minVersion = [version]"1.8.1911"
if (-not (Test-Path -Path $wingetPath -PathType Leaf)) { # Check if Winget PowerShell module version 1.8.1911 or later is installed
WriteLog "WinGet is not installed. Downloading WinGet..." $wingetModule = Get-InstalledModule -Name Microsoft.Winget.Client -ErrorAction SilentlyContinue
Install-WinGet -Architecture $WindowsArch if ($wingetModule.Version -lt $minVersion -or -not $wingetModule) {
return 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)) { Install-Module -Name Microsoft.Winget.Client -Force -Repository 'PSGallery'
WriteLog "WinGet not found. Downloading WinGet..." if($PSGalleryTrust -eq 'Untrusted'){
Install-WinGet -Architecture $WindowsArch WriteLog 'Setting PSGallery back to untrusted repository...'
return Set-PSRepository -Name 'PSGallery' -InstallationPolicy Untrusted
WriteLog 'Done'
} }
$wingetVersion = & winget.exe --version }
WriteLog "Installed version of WinGet: $wingetVersion" $wingetVersion = Get-WinGetVersion
if ($wingetVersion -match 'v?(\d+\.\d+\.\d+)' -and [version]$matches[1] -lt $minVersion) { if (-not $wingetVersion) {
WriteLog "The installed version of WinGet $($matches[1]) does not support downloading MSStore apps. Downloading the latest version of WinGet..." 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 Install-WinGet -Architecture $WindowsArch
return
} }
} }
@@ -1753,51 +1802,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 { function Get-WinGetApp {
param ( param (
[string]$WinGetAppName, [string]$WinGetAppName,
[string]$WinGetAppId [string]$WinGetAppId
) )
$wingetSearchResult = & winget.exe search --id "$WinGetAppId" --exact --accept-source-agreements --source winget $Source = 'winget'
if ($wingetSearchResult -contains "No package found matching input criteria.") { $wingetSearchResult = Find-WinGetPackage -id $WinGetAppId -MatchOption Equals -Source $Source
if (-not $wingetSearchResult) {
if ($VerbosePreference -ne 'Continue'){ if ($VerbosePreference -ne 'Continue'){
Write-Error "$WinGetAppName not found in WinGet repository. Skipping download." Write-Error "$WinGetAppName not found in WinGet repository. Exiting."
Write-Error "Check the AppList.json file and make sure the AppID is correct." 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 "$WinGetAppName not found in WinGet repository. Exiting."
WriteLog "Check the AppList.json file and make sure the AppID is correct." 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 Exit 1
} }
$appFolderPath = Join-Path -Path "$AppsPath\Win32" -ChildPath $WinGetAppName $appFolderPath = Join-Path -Path "$AppsPath\Win32" -ChildPath $WinGetAppName
WriteLog "Creating $appFolderPath" WriteLog "Creating $appFolderPath"
New-Item -Path $appFolderPath -ItemType Directory -Force | Out-Null New-Item -Path $appFolderPath -ItemType Directory -Force | Out-Null
WriteLog "Downloading $WinGetAppName to $appFolderPath" WriteLog "Downloading $WinGetAppName to $appFolderPath"
$downloadParams = @(
"download", WriteLog "WinGet command: Export-WinGetPackage -id $WinGetAppId -DownloadDirectory $appFolderPath -Architecture $WindowsArch -Source $Source"
"--id", "$WinGetAppId", $wingetDownloadResult = Export-WinGetPackage -id $WinGetAppId -DownloadDirectory $appFolderPath -Architecture $WindowsArch -Source $Source
"--exact", if ($wingetDownloadResult.status -eq 'NoApplicableInstallers') {
"--download-directory", "$appFolderPath", # If no applicable installer is found, try downloading without specifying architecture
"--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..." WriteLog "No installer found for $WindowsArch architecture. Attempting to download without specifying architecture..."
$downloadParams = $downloadParams | Where-Object { $_ -notmatch "--architecture" -and $_ -notmatch "$WindowsArch" } $wingetDownloadResult = Export-WinGetPackage -id $WinGetAppId -DownloadDirectory $appFolderPath -Source $Source
$wingetDownloadResult = & winget.exe @downloadParams | Out-String if ($wingetDownloadResult.status -eq 'Ok') {
if ($wingetDownloadResult -match "Installer downloaded") {
WriteLog "Downloaded $WinGetAppName without specifying architecture." WriteLog "Downloaded $WinGetAppName without specifying architecture."
} }
} else{
if ($wingetDownloadResult -notmatch "Installer downloaded") { WriteLog "No installer found for $WinGetAppName. Exiting."
WriteLog "No installer found for $WinGetAppName. Skipping download."
Remove-Item -Path $appFolderPath -Recurse -Force Remove-Item -Path $appFolderPath -Recurse -Force
Exit 1
}
} }
WriteLog "$WinGetAppName downloaded to $appFolderPath" WriteLog "$WinGetAppName downloaded to $appFolderPath"
$installerPath = Get-ChildItem -Path "$appFolderPath\*" -Exclude "*.yaml", "*.xml" -File -ErrorAction Stop $installerPath = Get-ChildItem -Path "$appFolderPath\*" -Exclude "*.yaml", "*.xml" -File -ErrorAction Stop
@@ -1819,15 +1923,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 { function Get-StoreApp {
param ( param (
[string]$StoreAppName, [string]$StoreAppName,
[string]$StoreAppId [string]$StoreAppId
) )
$wingetSearchResult = & winget.exe search "$StoreAppId" --accept-source-agreements --source msstore $Source = 'msstore'
if ($wingetSearchResult -contains "No package found matching input criteria.") { $wingetSearchResult = Find-WinGetPackage -id $StoreAppId -MatchOption Equals -Source $Source
WriteLog "$StoreAppName not found in WinGet repository. Skipping download." if (-not $wingetSearchResult) {
return 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..." WriteLog "Checking if $StoreAppName is a win32 app..."
$appIsWin32 = $StoreAppId.StartsWith("XP") $appIsWin32 = $StoreAppId.StartsWith("XP")
@@ -1841,31 +2035,22 @@ function Get-StoreApp {
} }
New-Item -Path $appFolderPath -ItemType Directory -Force | Out-Null New-Item -Path $appFolderPath -ItemType Directory -Force | Out-Null
WriteLog "Downloading $StoreAppName for $WindowsArch architecture..." 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 '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..." WriteLog "Attempting to download $StoreAppName and dependencies for $WindowsArch architecture..."
$wingetDownloadResult = & winget.exe @downloadParams | Out-String WriteLog "WinGet command: Export-WinGetPackage -id $StoreAppId -DownloadDirectory $appFolderPath -Architecture $WindowsArch -Source $Source"
# 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. $wingetDownloadResult = Export-WinGetPackage -id $StoreAppId -DownloadDirectory $appFolderPath -Architecture $WindowsArch -Source $Source
if ($wingetDownloadResult -match "No applicable installer found") { 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..." WriteLog "No installer found for $WindowsArch architecture. Attempting to download without specifying architecture..."
$downloadParams = $downloadParams | Where-Object { $_ -notmatch "--architecture" -and $_ -notmatch "$WindowsArch" } $wingetDownloadResult = Export-WinGetPackage -id $StoreAppId -DownloadDirectory $appFolderPath -Source $Source
$wingetDownloadResult = & winget.exe @downloadParams | Out-String if ($wingetDownloadResult.status -eq 'Ok') {
if ($wingetDownloadResult -match "Microsoft Store package download completed") { WriteLog "Downloaded $WinGetAppName without specifying architecture."
WriteLog "Downloaded $StoreAppName without specifying architecture."
} }
} else{
if ($wingetDownloadResult -notmatch "Installer downloaded|Microsoft Store package download completed") { WriteLog "No installer found for $WinGetAppName. Exiting"
WriteLog "Download not supported for $StoreAppName. Skipping download."
Remove-Item -Path $appFolderPath -Recurse -Force Remove-Item -Path $appFolderPath -Recurse -Force
return Exit 1
}
} }
if ($appIsWin32) { if ($appIsWin32) {
Add-Win32SilentInstallCommand -AppFolder $StoreAppName -AppFolderPath $appFolderPath Add-Win32SilentInstallCommand -AppFolder $StoreAppName -AppFolderPath $appFolderPath
@@ -2054,68 +2239,6 @@ function Save-KB {
if ($WindowsArch -eq 'x64') { if ($WindowsArch -eq 'x64') {
[array]$WindowsArch = @("x64", "amd64") [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) { foreach ($kb in $name) {
$links = Get-KBLink -Name $kb $links = Get-KBLink -Name $kb
foreach ($link in $links) { foreach ($link in $links) {
@@ -3326,9 +3449,14 @@ function Get-FFUEnvironment {
foreach ($image in $mountedImages) { foreach ($image in $mountedImages) {
$mountPath = $image.Path $mountPath = $image.Path
WriteLog "Dismounting image at $mountPath" WriteLog "Dismounting image at $mountPath"
try {
Dismount-WindowsImage -Path $mountPath -discard | Out-null Dismount-WindowsImage -Path $mountPath -discard | Out-null
WriteLog "Successfully dismounted image at $mountPath" WriteLog "Successfully dismounted image at $mountPath"
} }
catch {
WriteLog "Failed to dismount image at $mountPath with error: $_"
}
}
} }
# Remove Mount folder if it exists # Remove Mount folder if it exists
@@ -3356,6 +3484,7 @@ function Get-FFUEnvironment {
Remove-FFUUserShare Remove-FFUUserShare
WriteLog 'Removal complete' WriteLog 'Removal complete'
} }
Clear-InstallAppsandSysprep
#Clean up $KBPath #Clean up $KBPath
If (Test-Path -Path $KBPath) { If (Test-Path -Path $KBPath) {
WriteLog "Removing $KBPath" WriteLog "Removing $KBPath"
@@ -3388,7 +3517,6 @@ function Get-FFUEnvironment {
WriteLog "Cleaning up MSStore folder" WriteLog "Cleaning up MSStore folder"
Remove-Item -Path "$AppsPath\MSStore" -Recurse -Force -ErrorAction SilentlyContinue Remove-Item -Path "$AppsPath\MSStore" -Recurse -Force -ErrorAction SilentlyContinue
} }
Clear-InstallAppsandSysprep
Writelog 'Removing dirty.txt file' Writelog 'Removing dirty.txt file'
Remove-Item -Path "$FFUDevelopmentPath\dirty.txt" -Force Remove-Item -Path "$FFUDevelopmentPath\dirty.txt" -Force
WriteLog "Cleanup complete" WriteLog "Cleanup complete"
@@ -3412,29 +3540,34 @@ function Clear-InstallAppsandSysprep {
WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to remove Defender Platform Update" WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to remove Defender Platform Update"
$CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" $CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
$CmdContent -notmatch 'd:\\Defender*' | Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" $CmdContent -notmatch 'd:\\Defender*' | Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
# #Remove $DefenderPath #Clean up $DefenderPath
# WriteLog "Removing $DefenderPath" If (Test-Path -Path $DefenderPath) {
# Remove-Item -Path $DefenderPath -Recurse -Force WriteLog "Removing $DefenderPath"
# WriteLog "Removal complete" Remove-Item -Path $DefenderPath -Recurse -Force -ErrorAction SilentlyContinue
WriteLog 'Removal complete'
}
} }
if ($UpdateOneDrive) { if ($UpdateOneDrive) {
WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to remove OneDrive install" WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to remove OneDrive install"
$CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" $CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
$CmdContent -notmatch 'd:\\OneDrive*' | Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" $CmdContent -notmatch 'd:\\OneDrive*' | Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
# #Remove $OneDrivePath #Clean up $OneDrivePath
# WriteLog "Removing $OneDrivePath" If (Test-Path -Path $OneDrivePath) {
# Remove-Item -Path $OneDrivePath -Recurse -Force WriteLog "Removing $OneDrivePath"
# WriteLog "Removal complete" Remove-Item -Path $OneDrivePath -Recurse -Force -ErrorAction SilentlyContinue
WriteLog 'Removal complete'
}
} }
if ($UpdateEdge) { if ($UpdateEdge) {
WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to remove Edge install" WriteLog "Updating $AppsPath\InstallAppsandSysprep.cmd to remove Edge install"
$CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" $CmdContent = Get-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
$CmdContent -notmatch 'd:\\Edge*' | Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd" $CmdContent -notmatch 'd:\\Edge*' | Set-Content -Path "$AppsPath\InstallAppsandSysprep.cmd"
# #Remove $EdgePath #Clean up $EdgePath
# WriteLog "Removing $EdgePath" If (Test-Path -Path $EdgePath) {
# Remove-Item -Path $EdgePath -Recurse -Force WriteLog "Removing $EdgePath"
# WriteLog "Removal complete" Remove-Item -Path $EdgePath -Recurse -Force -ErrorAction SilentlyContinue
WriteLog 'Removal complete'
}
} }
} }
@@ -4003,30 +4136,30 @@ catch {
throw $_ throw $_
} }
#Clean up InstallAppsandSysprep.cmd # #Clean up InstallAppsandSysprep.cmd
try { # try {
WriteLog "Cleaning up $AppsPath\InstallAppsandSysprep.cmd" # WriteLog "Cleaning up $AppsPath\InstallAppsandSysprep.cmd"
Clear-InstallAppsandSysprep # Clear-InstallAppsandSysprep
} # }
catch { # catch {
Write-Host 'Cleaning up InstallAppsandSysprep.cmd failed' # Write-Host 'Cleaning up InstallAppsandSysprep.cmd failed'
Writelog "Cleaning up InstallAppsandSysprep.cmd failed with error $_" # Writelog "Cleaning up InstallAppsandSysprep.cmd failed with error $_"
throw $_ # throw $_
} # }
try { # try {
if (Test-Path -Path "$AppsPath\Win32" -PathType Container) { # if (Test-Path -Path "$AppsPath\Win32" -PathType Container) {
WriteLog "Cleaning up Win32 folder" # WriteLog "Cleaning up Win32 folder"
Remove-Item -Path "$AppsPath\Win32" -Recurse -Force # Remove-Item -Path "$AppsPath\Win32" -Recurse -Force
} # }
if (Test-Path -Path "$AppsPath\MSStore" -PathType Container) { # if (Test-Path -Path "$AppsPath\MSStore" -PathType Container) {
WriteLog "Cleaning up MSStore folder" # WriteLog "Cleaning up MSStore folder"
Remove-Item -Path "$AppsPath\MSStore" -Recurse -Force # Remove-Item -Path "$AppsPath\MSStore" -Recurse -Force
} # }
} # }
catch { # catch {
WriteLog "$_" # WriteLog "$_"
throw $_ # throw $_
} # }
#Create Deployment Media #Create Deployment Media
If ($CreateDeploymentMedia) { If ($CreateDeploymentMedia) {
try { try {
@@ -128,13 +128,18 @@ $LogFileName = 'ScriptLog.txt'
$USBDrive = Get-USBDrive $USBDrive = Get-USBDrive
New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null New-item -Path $USBDrive -Name $LogFileName -ItemType "file" -Force | Out-Null
$LogFile = $USBDrive + $LogFilename $LogFile = $USBDrive + $LogFilename
$version = '2408.2' $version = '2409.1'
WriteLog 'Begin Logging' WriteLog 'Begin Logging'
WriteLog "Script version: $version" WriteLog "Script version: $version"
#Find PhysicalDrive #Find PhysicalDrive
# $PhysicalDeviceID = Get-HardDrive # $PhysicalDeviceID = Get-HardDrive
$hardDrive = 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 $PhysicalDeviceID = $hardDrive.DeviceID
$BytesPerSector = $hardDrive.BytesPerSector $BytesPerSector = $hardDrive.BytesPerSector
WriteLog "Physical BytesPerSector is $BytesPerSector" WriteLog "Physical BytesPerSector is $BytesPerSector"