SurfaceDeploymentAccelerator/CreateSurfaceWindowsImage.ps1

4154 строки
199 KiB
PowerShell
Исходник Постоянная ссылка Ответственный История

Этот файл содержит неоднозначные символы Юникода!

Этот файл содержит неоднозначные символы Юникода, которые могут быть перепутаны с другими в текущей локали. Если это намеренно, можете спокойно проигнорировать это предупреждение. Используйте кнопку Экранировать, чтобы подсветить эти символы.

<#
.SYNOPSIS
This script creates a Surface Windows image.
.DESCRIPTION
This script creates a Surface Windows image, including Office 365 and requisite Visual C runtime libraries as required.
.EXAMPLE
.\CreateSurfaceWindowsImage.ps1 -ISO <ISO path> -OSSKU Pro -DestinationFolder "C:\Temp" -Device SurfacePro7
.NOTES
Author: Microsoft
Last Update: 18th July 2023
Version: 1.4.0.0
Version 1.4.0.0
- Changed design and added support to configure the correct ADK and WinPE tools based on ISO version.
- Corrected the ADK download & installation logic for Windows 10, Windows 11 21H2 and Windows 11 22H2 with required URLs.
- Fixed the issue installation of SSU and latest Cumulative Update on to WinPE (boot.wim) image.
- Read ADK root value from registry and set to $WindowsKitsInstall, if $WindowsKitsInstall is not valid. It is important to avoid using unsupported dism.exe.
- Added Automated flag to skip any pause commands in script
- Added support for Surface Pro 9 Intel
- Added support for Surface Laptop 5
Version 1.3.2.0
- Inserted Fix for Microsoft Update Catalog downloads by Fvbor
Version 1.3.1.0
- Added support for Windows 11 22H2
- Added support for Windows 10 22H2
Version 1.3.0.0
- Added support for Surface Laptop Studio
- Added support for Surface Pro 8
- Added support for Surface Go 3
- Added support for Windows 11
- Added support for Windows 10 21H1/21H2
Version 1.2.5.7
- Fixed Microsoft Update Catalog downloads
Version 1.2.5.6
- Added support for Surface Laptop 4
Version 1.2.5.5
- Added support for Surface Pro 7+
- Fixed download issues for 20H2 images
- Fixed download issues when specifying "Custom" device type
Version 1.2.5.4
- Added support for Surface Laptop Go
- Added support for Windows 10 20H2.
- Added language support:
- Chinese (Simplified and Traditional)
- French
- Russian
Version 1.2.5.3
- Fixed default PowerShell execution policy to match OOB defaults (Restricted)
Version 1.2.5.2
- Split prereq installation check into two checks for ADK installation to avoid WinPE not installed bug after ADK install check succeeds
- Office 365 does not install if not passed full path parameter, added explicit check and handler
Version 1.2.5.1
- Fixed typos in code causing first-run errors
Version 1.2.5
- Prevent usage of spaces in file paths for DestinationFolder and LocalDriverPath
- Prevent script from executing if prior execution failed at a specific point
Version 1.2.4
- Support for ESD file format added
Version 1.2.3
- LocalDriverPath can now point to a flat (extracted) driver path, or a Surface platform MSI file
- Logging functionality added
- Changed all Get-WmiObject calls with Get-CimInstance calls to be more compatible with PowerShell Core
- Added registry tattoo
- Handles install.esd files properly
- Performance improvements
Version 1.2.2
- Added USB drive picker
Version 1.2.1
- Fixed sysprep audit bugs
Version 1.2.0
- Added support for Surface Laptop 3 AMD SKUs (please note the "Device" name change from 1.0 and 1.1 versions for SurfaceLaptop3* variants)
- Added support for including Office 365 into images
- Bugfixes / performance improvements
Version 1.1.0
- Added support for local driver paths
- Added support for Surface Go 2 and Surface Book 3
Version 1.0.0
- Initial release
#>
# Parse Params:
[CmdletBinding()]
Param(
[Parameter(
Position=1,
Mandatory=$True,
HelpMessage="Location of ISO containing Windows image (ex. D:\18362.1.190318-1202.19h1_release_CLIENT_BUSINESS_VOL_x64FRE_en-us.iso) to use as template"
)]
[string]$ISO,
[Parameter(
Position=2,
Mandatory=$False,
HelpMessage="What SKU should be used inside ISO (valid parameters are 'Pro' or 'Enterprise'), default is Pro - note checking is disabled currently as language support is added"
)]
#[ValidateSet('Pro', 'Enterprise')]
[string]$OSSKU = 'Pro',
[Parameter(
Position=3,
Mandatory=$True,
HelpMessage="Destination folder to where resulting WIM image(s) should be placed"
)]
[string]$DestinationFolder,
[Parameter(
Position=4,
Mandatory=$False,
HelpMessage="Architecture of image being used (valid options are x64 and ARM64), default is x64"
)]
[ValidateSet('x64', 'ARM64')]
[string]$Architecture = 'x64',
[Parameter(
Position=5,
Mandatory=$False,
HelpMessage="Install .NET 3.5 (bool true/false, default is true)"
)]
[bool]$DotNet35 = $True,
[Parameter(
Position=6,
Mandatory=$False,
HelpMessage="Add latest servicing stack update (bool true/false, default is true)"
)]
[bool]$ServicingStack = $True,
[Parameter(
Position=7,
Mandatory=$False,
HelpMessage="Add latest cumulative update (bool true/false, default is true)"
)]
[bool]$CumulativeUpdate = $True,
[Parameter(
Position=8,
Mandatory=$False,
HelpMessage="Add latest cumulative .NET update (bool true/false, default is true)"
)]
[bool]$CumulativeDotNetUpdate = $True,
[Parameter(
Position=9,
Mandatory=$False,
HelpMessage="Add latest Adobe Flash Player Security update (bool true/false, default is true)"
)]
[bool]$AdobeFlashUpdate = $True,
[Parameter(
Position=10,
Mandatory=$False,
HelpMessage="Add latest Out-Of-Band/Non Security update (bool true/false, default is false)"
)]
[bool]$OOBUpdate = $False,
[Parameter(
Position=11,
Mandatory=$False,
HelpMessage="Add Office 365 C2R (bool true/false, default is true)"
)]
[bool]$Office365 = $True,
[Parameter(
Position=12,
Mandatory=$False,
HelpMessage="Surface device type to add drivers to image for, if not specified no drivers injected - Custom can be used if using with a non-Surface device"
)]
[ValidateSet('SurfacePro4', 'SurfacePro5', 'SurfacePro6', 'SurfacePro7', 'SurfacePro7Plus', 'SurfacePro8', 'SurfacePro9Intel', 'SurfaceLaptop', 'SurfaceLaptop2', 'SurfaceLaptop3Intel', 'SurfaceLaptop3AMD', 'SurfaceLaptop4Intel', 'SurfaceLaptop4AMD', 'SurfaceLaptop5', 'SurfaceLaptopGo', 'SurfaceLaptopStudio', 'SurfaceBook', 'SurfaceBook2', 'SurfaceBook3', 'SurfaceStudio', 'SurfaceStudio2', 'SurfaceGo', 'SurfaceGoLTE', 'SurfaceGo2', 'SurfaceGo3', 'SurfaceHub2', 'Custom')]
[string]$Device = "SurfacePro8",
[Parameter(
Position=13,
Mandatory=$False,
HelpMessage="Create USB key when finished (bool true/false, default is true)"
)]
[bool]$CreateUSB = $True,
[Parameter(
Position=14,
Mandatory=$False,
HelpMessage="Create bootable ISO file (useful for testing) when finished (bool true/false, default is true)"
)]
[bool]$CreateISO = $True,
[Parameter(
Position=15,
Mandatory=$False,
HelpMessage="Location of Windows ADK installation"
)]
[string]$WindowsKitsInstall = "${env:ProgramFiles(x86)}\Windows Kits\10\Assessment and Deployment Kit",
[Parameter(
Position=16,
Mandatory=$False,
HelpMessage="Use BITS for downloads"
)]
[bool]$BITSTransfer = $True,
[Parameter(
Position=17,
Mandatory=$False,
HelpMessage="Edit Install.wim"
)]
[bool]$InstallWIM = $True,
[Parameter(
Position=18,
Mandatory=$False,
HelpMessage="Edit boot.wim"
)]
[bool]$BootWIM = $True,
[Parameter(
Position=19,
Mandatory=$False,
HelpMessage="Keep original unsplit WIM even if resulting image size >4GB (bool true false, default is true)"
)]
[bool]$KeepOriginalWIM = $True,
[Parameter(
Position=20,
Mandatory=$False,
HelpMessage="Use a local driver path instead of downloading an MSI (bool true false, default is false)"
)]
[bool]$UseLocalDriverPath = $False,
[Parameter(
Position=21,
Mandatory=$False,
HelpMessage="Path to an MSI or extracted driver folder - required if you set UseLocalDriverPath variable to true or script will not find any drivers to inject"
)]
[string]$LocalDriverPath,
[Parameter(
Position=22,
Mandatory=$False,
HelpMessage="If true, skips any pause commands in this script, default is false"
)]
[bool]$Automated = $False
)
$SDAVersion = "1.4.0.0"
$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding
Add-Type AssemblyName System.Speech
$SpeechSynthesizer = New-Object TypeName System.Speech.Synthesis.SpeechSynthesizer
$Windows10Versions = @("10.0.19041", "10.0.19042", "10.0.19043", "10.0.19044", "10.0.19045")
Function Start-Log
{
Param (
[Parameter(Mandatory = $True)]
[String]$FilePath,
[Parameter(Mandatory = $True)]
[String]$FileName
)
Try
{
If (!(Test-Path $FilePath))
{
## Create the log file
New-Item -Path "$FilePath" -ItemType "directory" | Out-Null
New-Item -Path "$FilePath\$FileName" -ItemType "file"
}
Else
{
New-Item -Path "$FilePath\$FileName" -ItemType "file"
}
## Set the global variable to be used as the FilePath for all subsequent Write-Log calls in this session
$global:ScriptLogFilePath = "$FilePath\$FileName"
}
Catch
{
Write-Error $_.Exception.Message
Exit
}
}
Function Write-Log
{
Param (
[Parameter(Mandatory = $True)]
[String]$Message,
[Parameter(Mandatory = $False)]
# 1 == "Informational"
# 2 == "Warning'
# 3 == "Error"
[ValidateSet(1, 2, 3)]
[Int]$LogLevel = 1,
[Parameter(Mandatory = $False)]
[String]$LogFilePath = $ScriptLogFilePath,
[Parameter(Mandatory = $False)]
[String]$ScriptLineNumber
)
$TimeGenerated = "$(Get-Date -Format HH:mm:ss).$((Get-Date).Millisecond)+000"
$Line = '<![LOG[{0}]LOG]!><time="{1}" date="{2}" component="{3}" context="" type="{4}" thread="" file="">'
$LineFormat = $Message, $TimeGenerated, (Get-Date -Format MM-dd-yyyy), "$ScriptLineNumber", $LogLevel
$Line = $Line -f $LineFormat
#Add-Content -Path $LogFilePath -Value $Line
Out-File -InputObject $Line -Append -NoClobber -Encoding Default -FilePath $ScriptLogFilePath
}
Function Receive-Output
{
Param(
$Color,
$BGColor,
$LogLevel,
$LogFile,
$LineNumber
)
Process
{
If ($BGColor)
{
Write-Host $_ -ForegroundColor $Color -BackgroundColor $BGColor
}
Else
{
Write-Host $_ -ForegroundColor $Color
}
If (($LogLevel) -or ($LogFile))
{
Write-Log -Message $_ -LogLevel $LogLevel -LogFilePath $ScriptLogFilePath -ScriptLineNumber $LineNumber
}
}
}
Function AddHeaderSpace
{
Write-Output "This space intentionally left blank..." | Receive-Output -Color Gray
Write-Output ""
Write-Output ""
Write-Output ""
Write-Output ""
Write-Output ""
Write-Output ""
}
Function CheckIfRunAsAdmin
{
If (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] Administrator))
{
Write-Output You do not have Administrator rights to run this script!`nPlease re-run this script as an Administrator to continue. | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Break
}
}
Function Check-Internet
{
While (([Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]{DCB00C01-570F-4A9B-8D69-199FDBA5723B})).IsConnectedToInternet) -eq $False)
{
Write-Output "No internet connection detected. Retrying in 60 seconds..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Sleep -Seconds 60
}
}
Function Get-RedirectedUrl
{
Param(
$URL
)
$Request = [System.Net.WebRequest]::Create($URL)
$Request.AllowAutoRedirect=$false
$Request.Timeout = 3000
$Response = $Request.GetResponse()
If ($Response.ResponseUri)
{
$Response.GetResponseHeader("Location")
}
$Response.Close()
}
Function DownloadFile
{
Param(
[System.Uri]$URL,
[System.String]$Path,
[bool]$ForceDownload
)
# Get file name
Start-Sleep 1
If ($URL.Host -like "*aka.ms*")
{
$ActualURL = Get-RedirectedUrl -URL "$URL" -ErrorAction Continue -WarningAction Continue
$FileName = $ActualURL.Substring($ActualURL.LastIndexOf("/") + 1)
Write-Output "aka.ms link: $URL" | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Actual URL: $ActualURL" | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "File name: $FileName" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
}
Else
{
$ActualURL = $URL
$FileName = $URL.AbsoluteUri.Substring($URL.AbsoluteUri.LastIndexOf("/") +1)
Write-Output "Actual URL: $URL" | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "File name: $FileName" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
}
$global:Output = "$Path\$Filename"
If (($ForceDownload -eq $true) -and (Test-Path -Path "$global:Output"))
{
Write-Output "Delete the existing file: $global:Output" | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Remove-Item -Path "$global:Output" -Force
}
# If file does not exist, download file
If (!(Test-Path -Path "$global:Output"))
{
Write-Output "Using BITS to download files" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Downloading $FileName to $Path..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
Import-Module BitsTransfer
Start-BitsTransfer -Source $ActualURL -Destination "$global:Output" -Priority Foreground -RetryTimeout 60 -RetryInterval 120
}
Else
{
Write-Output "File $global:Output exists, skipping file download." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
}
Return $global:Output
}
# Using this to avoid reinstalling and breaking installed Win32 MSI apps via WMI calls to Win32_Product!
Function GetInstalledAppStatus
{
Param(
$AppName,
$AppVersion
)
$OSArch = Get-CimInstance -ClassName Win32_OperatingSystem
If ($OSArch.OSArchitecture -eq "64-bit")
{
$InstalledPrograms32 = Get-ChildItem "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" -Recurse
$InstalledPrograms64 = Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall" -Recurse
ForEach ($Item in $InstalledPrograms32)
{
If ($Item.GetValue("DisplayName") -like "*$AppName*" -and ($Item.GetValue("DisplayVersion")) -like "*$AppVersion*")
{
$global:IsInstalled = $true
Break
}
}
ForEach ($Item in $InstalledPrograms64)
{
If ($Item.GetValue("DisplayName") -like "*$AppName*" -and ($Item.GetValue("DisplayVersion")) -like "*$AppVersion*")
{
$global:IsInstalled = $true
Break
}
}
}
Else
{
$InstalledPrograms32 = Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall" -Recurse
ForEach ($Item in $InstalledPrograms32)
{
If ($Item.GetValue("DisplayName") -like "*$AppName*" -and ($Item.GetValue("DisplayVersion")) -like "*$AppVersion*")
{
$global:IsInstalled = $true
Break
}
}
}
}
Function PrereqCheck
{
# Check variables for spaces and not fully-defined paths
If ($DestinationFolder.Contains(" "))
{
Write-Output "`$DestinationFolder cannot contain spaces: $DestinationFolder" | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
$IsThisAFullLocalPath = $DestinationFolder.Substring(1,1)
If ($IsThisAFullLocalPath -ne ":")
{
Write-Output "$DestinationFolder was not passed as a full path to a local folder. Please pass the full path to the DestinationFolder parameter." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
If ($UseLocalDriverPath -and $LocalDriverPath.Contains(" "))
{
Write-Output "`$LocalDriverPath cannot contain spaces: $LocalDriverPath" | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
# Check for admin rights
CheckIfRunAsAdmin
# Windows Version Check
$OSCaption = (Get-CimInstance -ClassName win32_operatingsystem).caption
If ($OSCaption -like "Microsoft Windows 10*" -or $OSCaption -like "Microsoft Windows 11*" -or $OSCaption -like "Microsoft Windows Server 2019*")
{
# All OK
}
Else
{
Write-Output "$Env:Computername You must use Windows 10 1809 or newer, or Windows Server 2019 or newer when servicing Windows 10 offline, with the latest ADK installed." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "$Env:Computername Aborting script..." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
}
## Uninstalls existing ADK and WinPE Addon
## Downloads latest ADK and WinPE based on OSFullVersion
## Installs ADK and WinPE Addon into {$env:SystemDrive}\ADK_Tools
Function ConfigureADKTools
{
Param(
[string]$OSFullVersion
)
Write-Output ""
Write-Output ""
$IsCorrectADKInstalled = $false
$IsCorrectWinPEInstalled = $false
$OSFullVersion = $OSFullVersion.Trim()
$ADKInstallationPath = $env:SystemDrive + "\ADK_Tools"
$ADKPATHARGS = " /installpath $ADKInstallationPath "
$ADKRootRegistryKey = "HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots"
If (!(Test-Path -Path $ADKRootRegistryKey))
{
$ADKRootRegistryKey = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Roots"
}
If (Test-Path -Path $ADKRootRegistryKey)
{
$ADKRootPath = Get-ItemPropertyValue -Path $ADKRootRegistryKey -Name "KitsRoot10"
Write-Output "Found registry key: $ADKRootRegistryKey" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "ADK root from registry: $ADKRootPath" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If (Test-Path -Path $ADKRootPath)
{
$ADKInstallationPath = $ADKRootPath
$ADKPATHARGS = ""
}
}
If ($IsCorrectADKInstalled -eq $false -or $IsCorrectWinPEInstalled -eq $false)
{
Write-Output ""
Write-Output "Either ADK OR WinPE re-installation is required" | Receive-Output -Color Red -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If (!($Automated)) { PAUSE }
# Check whether ADK and WinPE binaries are installed or not.
$InstalledApps_32bits = Get-ChildItem "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" | ForEach { gp $_.PSPath } | ? { $_ -like "*Windows Assessment and Deployment Kit*" }
$InstalledApps_64bits = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" | ForEach { gp $_.PSPath } | ? { $_ -like "*Windows Assessment and Deployment Kit*" }
# Merge both types of apps
$InstalledApps = @()
$InstalledApps += $InstalledApps_32bits
$InstalledApps += $InstalledApps_64bits
# Uninstall old version of ADK and WinPE
ForEach ( $AppInfo in $InstalledApps)
{
If ($IsCorrectADKInstalled -eq $false)
{
# Check Windows 11 ADK
If ($AppInfo.DisplayName -eq "Windows Assessment and Deployment Kit")
{
$u = $AppInfo.UninstallString -Replace "/uninstall", ""
$u = $u.Trim()
Write-Output "Removing old ADK components. Command is $u and args are /uninstall /quiet" | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Process -filepath $u -argumentlist "/uninstall /quiet" -wait
Start-Sleep 5
}
# Check Windows 10 ADK
ElseIf ($AppInfo.DisplayName -eq "Windows Assessment and Deployment Kit - Windows 10")
{
$u = $AppInfo.UninstallString -Replace "/uninstall", ""
$u = $u.Trim()
Write-Output "Removing old ADK components. Command is $u and args are /uninstall /quiet" | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Process -filepath $u -argumentlist "/uninstall /quiet" -wait
Start-Sleep 5
}
}
If ($IsCorrectWinPEInstalled -eq $false)
{
# Check Windows 11 WinPE
If (($AppInfo.DisplayName -match "Windows Assessment and Deployment Kit") -and ($AppInfo.DisplayName -like "*Preinstallation Environment*")) {
$u = $AppInfo.UninstallString -Replace "/uninstall", ""
$u = $u.Trim()
Write-Output "Removing old Windows 11 WinPE components. Command is $u and args are /uninstall /quiet" | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Process -filepath $u -argumentlist "/uninstall /quiet" -wait
Start-Sleep 5
}
# Check Windows 10 WinPE
ElseIf (($AppInfo.DisplayName -match "Windows Assessment and Deployment Kit") -and ($AppInfo.DisplayName -like "*Preinstallation Environment*") -and ($AppInfo.DisplayName -like "*Add-ons - Windows 10*"))
{
$u = $AppInfo.UninstallString -Replace "/uninstall", ""
$u = $u.Trim()
Write-Output "Removing old WinPE components. Command is $u and args are /uninstall /quiet" | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Process -filepath $u -argumentlist "/uninstall /quiet" -wait
Start-Sleep 5
}
}
}
Start-Sleep 5
# Download and install correct version of ADK and WinPE, if required.
# As on 2023-06-11: ADK and WinPE download information is available at https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install#other-adk-downloads
$TrimmedOSVersionFromISO = $OSFullVersion.Substring(0, $OSFullVersion.LastIndexOf('.')).Trim()
Write-Output ""
$ADKURL = "https://aka.ms/sdaadk/w11-22h2"
$WINPEURL = "https://aka.ms/sdaadkpe/w11-22h2"
$ADKArguments = " $ADKPATHARGS /features OptionId.DeploymentTools /quiet"
$WinPEArguments = " $ADKPATHARGS /features OptionId.WindowsPreinstallationEnvironment /quiet"
If ($Windows10Versions -contains $TrimmedOSVersionFromISO)
{
Write-Output "Configure Windows 10 ADK & WinPE" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$ADKURL = "https://aka.ms/sdaadk/2004"
$WINPEURL = "https://aka.ms/sdaadkpe/2004"
}
ElseIf ($TrimmedOSVersionFromISO -eq "10.0.22000")
{
Write-Output "Configure Windows 11 21H2 ADK & WinPE" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$ADKURL = "https://aka.ms/sdaadk/W11-21H2"
$WINPEURL = "https://aka.ms/sdaadkpe/W11-21H2"
}
ElseIf ($TrimmedOSVersionFromISO -eq "10.0.22621")
{
Write-Output "Configure Windows 11 22H2 ADK & WinPE" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$ADKURL = "https://aka.ms/sdaadk/w11-22h2"
$WINPEURL = "https://aka.ms/sdaadkpe/w11-22h2"
}
Write-Output "ADK URL: $ADKURL" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "WinPE URL: $WINPEURL" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If ($IsCorrectADKInstalled -eq $false)
{
Check-Internet
$URL = $ADKURL
$Path = "$env:TEMP"
DownloadFile $URL $Path $true
$SourceFilePath = $global:Output
Try
{
Write-Output "Installing Windows Assessment and Deployment Kit Deployment Tools" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "ADKArguments: $ADKArguments" | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Process -File $SourceFilePath -Arg $ADKArguments -passthru | Wait-Process
$IsCorrectADKInstalled = $true
Write-Output "$AppName - ADK INSTALLATION SUCCESSFULLY COMPLETED" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
}
Catch
{
Write-Output "$AppName - INSTALLATION ERROR - check logs in $env:TEMP\adk for more info." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
Exit
}
}
If ($IsCorrectWinPEInstalled -eq $false)
{
Check-Internet
$URL = $WINPEURL
$Path = "$env:TEMP"
DownloadFile $URL $Path $true
$SourceFilePath = $global:Output
Try
{
Write-Output "Installing Windows Assessment and Deployment Kit Windows Preinstallation Environment Add-Ons" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "WinPEArguments: $WinPEArguments" | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Process -File $SourceFilePath -Arg $WinPEArguments -passthru | Wait-Process
$IsCorrectWinPEInstalled = $true
Write-Output "$AppName - ADK WinPE Add-Ons INSTALLATION SUCCESSFULLY COMPLETED" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
}
Catch
{
Write-Output "$AppName - INSTALLATION ERROR - check logs in $env:TEMP\adkwinpeaddons for more info." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
Exit
}
}
Write-output "IsCorrectADKInstalled : $IsCorrectADKInstalled" | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-output "IsCorrectWinPEInstalled : $IsCorrectWinPEInstalled" | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
if (($IsCorrectADKInstalled -eq $true) -and ($IsCorrectWinPEInstalled -eq $true))
{
$global:InstalledADKRoot = "$ADKInstallationPath\Assessment and Deployment Kit"
}
}
}
Function Get-DownloadDialogText
{
Param(
$Text,
$Pattern
)
return [regex]::Match($Text, $Pattern + "\s?'?(.*?)'?;").Groups[1].Value
}
Function Download-LatestUpdates
{
Param(
$uri,
$Path,
$Date,
$Servicing,
$Cumulative,
$CumulativeDotNet,
$Adobe,
$OOB,
$Windows,
$OSBuild
)
$kbObj = Invoke-WebRequest -Uri $uri -UseBasicParsing
# Parse the Response
$kbObjects = $kbObj.InputFields |
Where-Object { $_.type -eq 'Button' -and $_.Value -eq 'Download' } |
Select-Object -ExpandProperty ID
$kbObjectsLinks = $kbObj.Links |
Where-Object ID -match '_link' |
Where-Object { $_.OuterHTML -match ( "(?=.*" + ( $Filter -join ")(?=.*" ) + ")" ) }
# Initialize array, get title and GUID of update
$guids = $null
$guids = @()
foreach ($kbObjectsLink in $kbObjectsLinks)
{
$itemguid = $kbObjectsLink.id.replace('_link', '')
$itemtitle = ($kbObjectsLink.outerHTML -replace '<[^>]+>', '').Trim()
if ($itemguid -in $kbObjects) {
$guids += [pscustomobject]@{
guid = $itemguid
description = $itemtitle
}
}
}
If ($Windows -eq "Windows 10")
{
If ($Servicing)
{
$global:KBGUID = $guids | Where-Object {($_.description -like "*$Date*") -and ($_.description -like "*Servicing Stack Update for Windows 10*") -and ($_.description -like "*$OSBuild*") -and ($_.description -like "*$Architecture*")}
}
If ($Cumulative)
{
$global:KBGUID = $guids | Where-Object {($_.description -like "*$Date*") -and ($_.description -like "*Cumulative Update for Windows 10*") -and -not ($_.description -like "*Dynamic*") -and ($_.description -like "*$OSBuild*") -and ($_.description -like "*$Architecture*")}
}
If ($CumulativeDotNet)
{
$global:KBGUID = $guids | Where-Object {($_.description -like "*$Date*") -and ($_.description -like "*Cumulative Update for .NET Framework*") -and ($_.description -like "*Windows 10*") -and ($_.description -like "*$OSBuild*") -and ($_.description -like "*$Architecture*")}
}
If ($Adobe)
{
$global:KBGUID = $guids | Where-Object {($_.description -like "*$Date*") -and ($_.description -like "*Security Update for Adobe Flash Player for Windows 10*") -and ($_.description -like "*$OSBuild*") -and ($_.description -like "*$Architecture*")}
}
If ($OOB)
{
$global:KBGUID = $guids | Where-Object {($_.description -like "*$Date*") -and ($_.description -like "*Update for Windows 10*") -and -not ($_.description -like "*Dynamic*") -and -not ($_.description -like "*Cumulative*") -and ($_.description -like "*$Architecture*")}
}
}
ElseIf ($Windows -eq "Windows 11")
{
If ($Servicing)
{
$global:KBGUID = $guids | Where-Object {($_.description -like "*$Date*") -and ($_.description -like "*Servicing Stack Update for Windows*") -and ($_.description -like "*$OSBuild*") -and ($_.description -like "*$Architecture*")}
}
If ($Cumulative)
{
$global:KBGUID = $guids | Where-Object {($_.description -like "*$Date*") -and ($_.description -like "*Cumulative Update for $Windows*") -and -not ($_.description -like "*Dynamic Cumulative Update for*") -and ($_.description -like "*$OSBuild*") -and ($_.description -like "*$Architecture*")}
}
If ($CumulativeDotNet)
{
$global:KBGUID = $guids | Where-Object {($_.description -like "*$Date*") -and ($_.description -like "*Cumulative Update for .NET Framework*") -and ($_.description -like "*$OSBuild*") -and ($_.description -like "*$Architecture*")}
}
If ($Adobe)
{
$global:KBGUID = $guids | Where-Object {($_.description -like "*$Date*") -and ($_.description -like "*Security Update for Adobe Flash Player for Windows*") -and ($_.description -like "*$OSBuild*") -and ($_.description -like "*$Architecture*")}
}
If ($OOB)
{
$global:KBGUID = $guids | Where-Object {($_.description -like "*$Date*") -and ($_.description -like "*Update for*") -and -not ($_.description -like "*Dynamic*") -and -not ($_.description -like "*Cumulative*") -and ($_.description -like "*$Windows*") -and ($_.description -like "*$Architecture*")}
}
}
$scriptblock = {
$guid = $_.Guid
$itemtitle = $_.description
$post = @{ size = 0; updateID = $guid; uidInfo = $guid } | ConvertTo-Json -Compress
$body = @{ updateIDs = "[$post]" }
Invoke-WebRequest -Uri 'https://www.catalog.update.microsoft.com/DownloadDialog.aspx' -Method Post -Body $body | Select-Object -ExpandProperty Content
}
$downloaddialogs = $global:KBGUID | ForEach-Object -Process $scriptblock
$updatesFound = $false
ForEach ($downloaddialog in $downloaddialogs)
{
$title = Get-DownloadDialogText -Text $downloaddialog -Pattern 'enTitle ='
If (!($title))
{
#do nothing
}
Else
{
$downloaddialog = $downloaddialog.Replace('www.download.windowsupdate', 'download.windowsupdate')
$DLWUDOTCOM = ($downloaddialog | Select-String -AllMatches -Pattern "(http[s]?\://download\.windowsupdate\.com\/[^\'\""]*)" | Select-Object -Unique | ForEach-Object { [PSCustomObject] @{ Source = $_.matches.value } } ).source
$DLDELDOTCOM = ($downloaddialog | Select-String -AllMatches -Pattern "(http[s]?\://dl\.delivery\.mp\.microsoft\.com\/[^\'\""]*)" | Select-Object -Unique | ForEach-Object { [PSCustomObject] @{ Source = $_.matches.value } } ).source
$DLCATWUDOTCOM = ($downloaddialog | Select-String -AllMatches -Pattern "(http[s]?\://catalog\.s\.download\.windowsupdate\.com\/[^\'\""]*)" | Select-Object -Unique | ForEach-Object { [PSCustomObject] @{ Source = $_.matches.value } }).source
$DLSFCATWUDOTCOM = ($downloaddialog | Select-String -AllMatches -Pattern "(http[s]?\://catalog\.sf\.dl\.delivery\.mp\.microsoft\.com\/[^\'\""]*)" | Select-Object -Unique | ForEach-Object { [PSCustomObject] @{ Source = $_.matches.value } }).source
If ($DLWUDOTCOM)
{
$links = $DLWUDOTCOM
}
If ($DLDELDOTCOM)
{
$links = $DLDELDOTCOM
}
If ($DLCATWUDOTCOM)
{
$links = $DLCATWUDOTCOM
}
If ($DLSFCATWUDOTCOM)
{
$links = $DLSFCATWUDOTCOM
}
If ($links)
{
$updatesFound = $true
ForEach ($link in $links)
{
Write-Output "Download found:"
Write-Output "Title: $itemtitle"
Write-Output "URL: $link"
Write-Output ""
DownloadFile -URL $link -Path $Path
Write-Output ""
Write-Output ""
Write-Output ""
}
}
}
}
If (!($updatesFound))
{
$global:KBGUID = $null
}
}
Function Get-LatestUpdates
{
Param(
$Servicing = $False,
$Cumulative = $False,
$CumulativeDotNet = $False,
$Adobe = $False,
$OOB = $False,
$Windows,
$Path,
$Date,
$OSBuild,
$Architecture
)
If ($Windows -eq "W10")
{
$Windows = "Windows 10"
}
If ($Windows -eq "W11")
{
$Windows = "Windows 11"
}
If (!($Path))
{
$Path = $WorkingDirPath
}
If (!(Test-Path -Path $Path))
{
New-Item -path "$Path" -ItemType "directory" | Out-Null
}
If (!($Date))
{
$Date = Get-Date -Format "yyyy-MM"
}
<# Windows 10 22H2 LCU would be applicable to install it on all versions of Windows 10 (19041~19045)#>
If ($Windows -eq "Windows 10")
{
$OSBuild = "22H2"
}
$ServicingURI = "http://www.catalog.update.microsoft.com/Search.aspx?q=" + $Date + " Servicing Stack " + $Architecture + " " + $Windows + " " + $OSBuild
$CumulativeURI = "http://www.catalog.update.microsoft.com/Search.aspx?q=" + $Date + " Cumulative update for " + $Windows + " Version " + $OSBuild + " for " + $Architecture + "-based Systems"
$CumulativeDotNetURI = "http://www.catalog.update.microsoft.com/Search.aspx?q=" + $Date + " Cumulative update for .NET Framework " + $Windows + " " + $Architecture + " " + $OSBuild
$AdobeURI = "http://www.catalog.update.microsoft.com/Search.aspx?q=" + $Date + " Security Update for Adobe Flash Player for " + $Windows + " " + $Architecture + " " + $OSBuild
$OOBURI = "http://www.catalog.update.microsoft.com/Search.aspx?q=" + $Date + " Update for " + $Windows + " for " + $Architecture + "-based Systems "
If ($Servicing)
{
Write-Output "Attempting to find and download Servicing Stack updates for $Architecture $Windows version $OSBuild for month $Date..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$uri = $ServicingURI
Download-LatestUpdates -uri $uri -Path $Path -Date $Date -Servicing $True -Cumulative $False -CumulativeDotNet $False -Adobe $False -OOB $False -Windows $Windows -OSBuild $OSBuild
If (!($global:KBGUID))
{
While (!($global:KBGUID))
{
If ($LoopBreak -le 5)
{
$LoopBreak++
Start-Sleep 1
$NewDate = (Get-Date).AddMonths(-$LoopBreak)
$NewDate = $NewDate.ToString("yyyy-MM")
Write-Output "No update found for month ($Date) - attempting previous month ($NewDate)..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$Date = $NewDate
$ServicingURI = "http://www.catalog.update.microsoft.com/Search.aspx?q=" + $Date + " Servicing Stack " + $Architecture + " " + $Windows + " " + $OSBuild
$uri = $ServicingURI
Download-LatestUpdates -uri $uri -Path $Path -Date $Date -Servicing $True -Cumulative $False -CumulativeDotNet $False -Adobe $False -OOB $False -Windows $Windows -OSBuild $OSBuild
}
Else
{
Write-Output "Unable to find update for past $LoopBreak months of searches. Continuing..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Break
}
}
}
$LoopBreak = $null
$Date = Get-Date -Format "yyyy-MM"
}
If ($Cumulative)
{
Write-Output "Attempting to find and download Cumulative Update updates for $Architecture $Windows version $OSBuild for month $Date..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$uri = $CumulativeURI
Download-LatestUpdates -uri $uri -Path $Path -Date $Date -Servicing $False -Cumulative $True -CumulativeDotNet $False -Adobe $False -OOB $False -Windows $Windows -OSBuild $OSBuild
If (!($global:KBGUID))
{
While (!($global:KBGUID))
{
If ($LoopBreak -le 5)
{
$LoopBreak++
Start-Sleep 1
$NewDate = (Get-Date).AddMonths(-$LoopBreak)
$NewDate = $NewDate.ToString("yyyy-MM")
Write-Output "No update found for month ($Date) - attempting previous month ($NewDate)..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$Date = $NewDate
$CumulativeURI = "http://www.catalog.update.microsoft.com/Search.aspx?q=" + $Date + " Cumulative update for " + $Windows + " for " + $Architecture + "-based Systems " + $OSBuild
$uri = $CumulativeURI
Download-LatestUpdates -uri $uri -Path $Path -Date $Date -Servicing $False -Cumulative $True -CumulativeDotNet $False -Adobe $False -OOB $False -Windows $Windows -OSBuild $OSBuild
}
Else
{
Write-Output "Unable to find update for past $LoopBreak months of searches. Continuing..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Break
}
}
}
$Date = Get-Date -Format "yyyy-MM"
$LoopBreak = $null
}
If ($CumulativeDotNet)
{
Write-Output "Attempting to find and download Cumulative .NET Framework Update updates for $Architecture $Windows version $OSBuild for month $Date..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$uri = $CumulativeDotNetURI
Download-LatestUpdates -uri $uri -Path $Path -Date $Date -Servicing $False -Cumulative $False -CumulativeDotNet $True -Adobe $False -OOB $False -Windows $Windows -OSBuild $OSBuild
If (!($global:KBGUID))
{
While (!($global:KBGUID))
{
If ($LoopBreak -le 5)
{
$LoopBreak++
Start-Sleep 1
$NewDate = (Get-Date).AddMonths(-$LoopBreak)
$NewDate = $NewDate.ToString("yyyy-MM")
Write-Output "No update found for month ($Date) - attempting previous month ($NewDate)..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$Date = $NewDate
$CumulativeDotNetURI = "http://www.catalog.update.microsoft.com/Search.aspx?q=" + $Date + " Cumulative update for .NET Framework " + $Windows + " " + $Architecture + " " + $OSBuild
$uri = $CumulativeDotNetURI
Download-LatestUpdates -uri $uri -Path $Path -Date $Date -Servicing $False -Cumulative $False -CumulativeDotNet $True -Adobe $False -OOB $False -Windows $Windows -OSBuild $OSBuild
}
Else
{
Write-Output "Unable to find update for past $LoopBreak months of searches. Continuing..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Break
}
}
}
$Date = Get-Date -Format "yyyy-MM"
$LoopBreak = $null
}
If ($Adobe)
{
Write-Output "Attempting to find and download Adobe Flash Player updates for $Architecture $Windows version $OSBuild for month $Date..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$uri = $AdobeURI
Download-LatestUpdates -uri $uri -Path $Path -Date $Date -Servicing $False -Cumulative $False -CumulativeDotNet $False -Adobe $True -OOB $False -OSBuild $OSBuild
If (!($global:KBGUID))
{
While (!($global:KBGUID))
{
If ($LoopBreak -le 11)
{
$LoopBreak++
Start-Sleep 1
$NewDate = (Get-Date).AddMonths(-$LoopBreak)
$NewDate = $NewDate.ToString("yyyy-MM")
Write-Output "No update found for month ($Date) - attempting previous month ($NewDate)..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$Date = $NewDate
$AdobeURI = "http://www.catalog.update.microsoft.com/Search.aspx?q=" + $Date + " Security Update for Adobe Flash Player for " + $Windows + " " + $Architecture + " " + $OSBuild
$uri = $AdobeURI
Download-LatestUpdates -uri $uri -Path $Path -Date $Date -Servicing $False -Cumulative $False -CumulativeDotNet $False -Adobe $True -OOB $False -Windows $Windows -OSBuild $OSBuild
}
Else
{
Write-Output "Unable to find update for past $LoopBreak month's of searches. Continuing..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Break
}
}
}
$Date = Get-Date -Format "yyyy-MM"
$LoopBreak = $null
}
If ($OutOfBand)
{
Write-Output "Attempting to find and download Out-of-band/Non-security updates for $Architecture $Windows version $OSBuild for month $Date..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$uri = $OOBURI
Download-LatestUpdates -uri $uri -Path $Path -Date $Date -Servicing $False -Cumulative $False -CumulativeDotNet $False -Adobe $False -OOB $True -Windows $Windows -OSBuild $OSBuild
If (!($global:KBGUID))
{
While (!($global:KBGUID))
{
If ($LoopBreak -le 5)
{
$LoopBreak++
Start-Sleep 1
$NewDate = (Get-Date).AddMonths(-$LoopBreak)
$NewDate = $NewDate.ToString("yyyy-MM")
Write-Output "No update found for month ($Date) - attempting previous month ($NewDate)..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$Date = $NewDate
$OOBURI = "http://www.catalog.update.microsoft.com/Search.aspx?q=" + $Date + " Update for " + $Windows + " for " + $Architecture + "-based Systems "
$uri = $OOBURI
Download-LatestUpdates -uri $uri -Path $Path -Date $Date -Servicing $False -Cumulative $False -CumulativeDotNet $False -Adobe $False -OOB $True -Windows $Windows -OSBuild $OSBuild
}
Else
{
Write-Output "Unable to find update for past $LoopBreak month's of searches. Continuing..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Break
}
}
}
$Date = Get-Date -Format "yyyy-MM"
$LoopBreak = $null
}
}
Function ExtractMSIFile
{
Param
(
$MsiFile,
$Path
)
If (Test-Path "$Path\Extract")
{
Write-Output "Deleting $Path\Extract\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$Path\Extract\" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$Path\Extract" -Force
}
If (!(Test-Path "$Path\Extract"))
{
New-Item -Path "$Path\Extract" -ItemType "directory" | Out-Null
}
Write-Output "Extracting file $MsiFile to $Path\Extract..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Process "msiexec" -ArgumentList "/a $MsiFile /qn TARGETDIR=$Path\Extract" -Wait -NoNewWindow
}
Function Get-LatestSurfaceEthernetDrivers
{
Param(
$Device,
$TempFolder
)
Write-Output ""
Write-Output ""
$DeviceDriverPath = "$TempFolder\$Device"
If (!($Device) -or ($Device -eq "Custom"))
{
# Nothing yet
}
ElseIf ($Device -eq "SurfaceHub2")
{
# Nothing yet
}
Else
{
$URI = 'https://www.catalog.update.microsoft.com/Search.aspx?q=Realtek - Net - 10.45.0308.2021'
$kbObj = Invoke-WebRequest -Uri $uri -UseBasicParsing
# Parse the Response
$kbObjects = $kbObj.InputFields |
Where-Object { $_.type -eq 'Button' -and $_.Value -eq 'Download' } |
Select-Object -ExpandProperty ID
$kbObjectsLinks = $kbObj.Links |
Where-Object ID -match '_link' |
Where-Object { $_.OuterHTML -match ( "(?=.*" + ( $Filter -join ")(?=.*" ) + ")" ) }
# Initialize array, get title and GUID of update
$guids = $null
$guids = @()
foreach ($kbObjectsLink in $kbObjectsLinks)
{
$itemguid = $kbObjectsLink.id.replace('_link', '')
$itemtitle = ($kbObjectsLink.outerHTML -replace '<[^>]+>', '').Trim()
if ($itemguid -in $kbObjects) {
$guids += [pscustomobject]@{
guid = $itemguid
description = $itemtitle
}
}
}
# Return a hard-coded array member for now until this settles out - changeover from "Surface - NET" to "Realtek - Net" causes issues with # of returns and version info changes
#$global:KBGUID = $guids | Where-Object {($_.description -like "*Realtek - Net - 10.45.0308.2021*")}
$global:KBGUID = $guids[0]
$scriptblock = {
$guid = $_.Guid
$itemtitle = $_.description
$guid
$post = @{ size = 0; updateID = $guid; uidInfo = $guid } | ConvertTo-Json -Compress
$body = @{ updateIDs = "[$post]" }
Invoke-WebRequest -Uri 'https://www.catalog.update.microsoft.com/DownloadDialog.aspx' -Method Post -Body $body | Select-Object -ExpandProperty Content
}
$downloaddialogs = $global:KBGUID | ForEach-Object -Process $scriptblock
$updatesFound = $false
ForEach ($downloaddialog in $downloaddialogs)
{
$title = Get-DownloadDialogText -Text $downloaddialog -Pattern 'enTitle ='
If (!($title))
{
#do nothing
}
Else
{
$downloaddialog = $downloaddialog.Replace('www.download.windowsupdate', 'download.windowsupdate')
$DLWUDOTCOM = ($downloaddialog | Select-String -AllMatches -Pattern "(http[s]?\://download\.windowsupdate\.com\/[^\'\""]*)" | Select-Object -Unique | ForEach-Object { [PSCustomObject] @{ Source = $_.matches.value } } ).source
$DLDELDOTCOM = ($downloaddialog | Select-String -AllMatches -Pattern "(http[s]?\://dl\.delivery\.mp\.microsoft\.com\/[^\'\""]*)" | Select-Object -Unique | ForEach-Object { [PSCustomObject] @{ Source = $_.matches.value } } ).source
If ($DLWUDOTCOM)
{
"URL: $link"
$links = $DLWUDOTCOM
}
If ($DLDELDOTCOM)
{
$links = $DLDELDOTCOM
}
If ($links)
{
$updatesFound = $true
ForEach ($link in $links)
{
Write-Output "Download found:" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Title: $itemtitle" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "URL: $link" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
$TempCAB = DownloadFile -URL $link -Path "$DeviceDriverPath"
Write-Output ""
$expand = "$env:WINDIR\System32\expand.exe"
$args = "-f:* $TempCAB $DeviceDriverPath"
Start-Process -FilePath $expand -ArgumentList $args -Wait -NoNewWindow
Write-Output ""
Write-Output ""
Write-Output ""
}
}
}
}
}
}
Function Get-LatestWinUSBDrivers
{
Param(
$Device,
$TempFolder
)
Write-Output ""
Write-Output ""
$DeviceDriverPath = "$TempFolder\$Device"
If (!($Device -eq "SurfaceHub2"))
{
# Do nothing
}
Else
{
$URI = "http://www.catalog.update.microsoft.com/Search.aspx?q=SMSC-Microchip WinUSB USB2534 Device"
$kbObj = Invoke-WebRequest -Uri $URI -UseBasicParsing
$global:KBGUID = $null
$kbObjectLinks = ($kbObj.Links | Where-Object {$_.id -match "_link"})
$array = @()
$kbObj = Invoke-WebRequest -Uri $uri -UseBasicParsing
# Parse the Response
$global:KBGUID = $null
$kbObjectLinks = ($kbObj.Links | Where-Object {$_.id -match "_link"})
$array = @()
ForEach ($link in $kbObjectLinks)
{
$xmlNode = [XML]($link.outerHTML)
If ($xmlNode.HasChildNodes)
{
$kbId = $link.id -replace "_link", ""
$description = $xmlNode.FirstChild.InnerText.Trim()
$array += [PSCustomObject]@{
kbId = $kbId
description = $description
}
}
}
If ($array.count -gt 0)
{
$global:KBGUID = $array | Where-Object {($_.description -like "*SMSC-Microchip WinUSB USB2534 Device*")}
If ($global:KBGUID.Count -gt 1)
{
#32 and 64bit driver packages have both, so just grab the first in the array which should be amd64
$global:KBGUID = $global:KBGUID[0]
}
}
ForEach ($Object in $global:KBGUID)
{
$kb = $Object.kbId
$curTxt = $Object.description
##Create Post Request to get the Download URL of the Update
$Post = @{ size = 0; updateID = $kb; uidInfo = $kb } | ConvertTo-Json -Compress
$PostBody = @{ updateIDs = "[$Post]" }
## Fetch and parse the download URL
$PostRes = (Invoke-WebRequest -Uri 'http://www.catalog.update.microsoft.com/DownloadDialog.aspx' -Method Post -Body $postBody).content
$DownloadLinks = ($PostRes | Select-String -AllMatches -Pattern "(http[s]?\://download\.windowsupdate\.com\/[^\'\""]*)" | Select-Object -Unique | ForEach-Object { [PSCustomObject] @{ Source = $_.matches.value } } ).source
If ($DownloadLinks)
{
If ($DownloadLinks.Count -gt 1)
{
ForEach ($URL in $DownloadLinks)
{
Write-Output "Download found:"
Write-Output $curTxt
Write-Output ""
Write-Output ""
$TempCAB = DownloadFile -URL $URL -Path "$DeviceDriverPath"
Write-Output ""
Write-Output ""
Write-Output ""
Write-Output ""
Write-Output ""
$expand = "$env:WINDIR\System32\expand.exe"
$args = "-f:* $TempCAB $DeviceDriverPath"
Start-Process -FilePath $expand -ArgumentList $args -Wait -NoNewWindow
Write-Output ""
Write-Output ""
}
}
Else
{
Write-Output "Download found:"
Write-Output $curTxt
Write-Output ""
Write-Output ""
$TempCAB = DownloadFile -URL $DownloadLinks -Path "$DeviceDriverPath"
Write-Output ""
Write-Output ""
Write-Output ""
Write-Output ""
Write-Output ""
$expand = "$env:WINDIR\System32\expand.exe"
$args = "-f:* $TempCAB $DeviceDriverPath"
Start-Process -FilePath $expand -ArgumentList $args -Wait -NoNewWindow
Write-Output ""
Write-Output ""
}
}
}
}
}
Function Get-LatestDrivers
{
Param(
$Device,
$TempFolder
)
Write-Output ""
Write-Output ""
If (!($Device))
{
Write-Output "Surface device not specified. Skipping driver download." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
return
}
$DeviceDriverPath = "$TempFolder\$Device"
If (Test-Path "$DeviceDriverPath")
{
Write-Output "Deleting $DeviceDriverPath\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$DeviceDriverPath" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$DeviceDriverPath" -Force
}
If (!(Test-Path "$DeviceDriverPath"))
{
New-Item -path "$DeviceDriverPath" -ItemType "directory" | Out-Null
}
If ($UseLocalDriverPath -eq $True)
{
If (!(Test-Path "$LocalDriverPath"))
{
Write-Output "$LocalDriverPath not found, continuing without drivers..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$Device = $null
}
Else
{
$TempLocalDriverPath = (Get-Item $LocalDriverPath) -is [System.IO.DirectoryInfo]
If ($TempLocalDriverPath -eq $False)
{
ExtractMSIFile -MsiFile $LocalDriverPath -Path "$DeviceDriverPath"
}
ElseIf ($TempLocalDriverPath -eq $True)
{
$TempDeviceDriverPath = "$DeviceDriverPath\Extract"
If (Test-Path "$TempDeviceDriverPath")
{
Write-Output "Deleting $TempDeviceDriverPath\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$TempDeviceDriverPath" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$TempDeviceDriverPath" -Force
}
If (!(Test-Path "$TempDeviceDriverPath"))
{
New-Item -path "$TempDeviceDriverPath" -ItemType "directory" | Out-Null
}
# Use local drivers
Write-Output "Copying drivers from $LocalDriverPath to $TempDeviceDriverPath..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
& xcopy.exe /herky "$LocalDriverPath" "$TempDeviceDriverPath"
}
Write-Output ""
}
}
Else
{
If ($Device -eq "Custom")
{
Write-Output "Surface device not specified. Skipping driver download..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
return
}
Write-Output "Downloading latest drivers for $Device ..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$OSBuild = New-Object string (,@($global:OSVersion.ToCharArray() | Select-Object -Last 5))
If ($Device -eq "SurfaceLaptop3Intel")
{
$TempDevice = "SurfaceLaptop3"
$TempDeviceType = "Intel"
$URL = "https://aka.ms/" + $TempDevice + "/" + $TempDeviceType + "/" + $OSBuild
}
ElseIf ($Device -eq "SurfaceLaptop3AMD")
{
$TempDevice = "SurfaceLaptop3"
$TempDeviceType = "AMD"
$URL = "https://aka.ms/" + $TempDevice + "/" + $TempDeviceType + "/" + $OSBuild
}
ElseIf ($Device -eq "SurfaceLaptop4Intel")
{
$TempDevice = "SurfaceLaptop4"
$TempDeviceType = "Intel"
$URL = "https://aka.ms/" + $TempDevice + "/" + $TempDeviceType + "/" + $OSBuild
}
ElseIf ($Device -eq "SurfaceLaptop4AMD")
{
$TempDevice = "SurfaceLaptop4"
$TempDeviceType = "AMD"
$URL = "https://aka.ms/" + $TempDevice + "/" + $TempDeviceType + "/" + $OSBuild
}
ElseIf ($Device -eq "SurfacePro9Intel")
{
$TempDevice = "SurfacePro9"
$TempDeviceType = "Intel"
$URL = "https://aka.ms/" + $TempDevice + "/" + $TempDeviceType + "/" + $OSBuild
}
Else
{
$URL = "https://aka.ms/" + $Device + "/" + $OSBuild
}
$DownloadedFile = DownloadFile -URL $URL -Path "$DeviceDriverPath"
Write-Output "Downloaded File: $DownloadedFile" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$FileToExtract = $DownloadedFile
ExtractMSIFile -MsiFile $FileToExtract -Path $DeviceDriverPath
Write-Output ""
}
If ($Device -eq "Custom")
{
Write-Output "Surface device not specified. Skipping driver download..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
return
}
ElseIf ($Device -eq "SurfaceHub2")
{
Write-Output "Downloading latest WinUSB drivers for $Device..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-LatestWinUSBDrivers -Device $Device -TempFolder $TempFolder
Write-Output ""
}
Else
{
Write-Output "Downloading latest Surface Ethernet drivers for $Device..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-LatestSurfaceEthernetDrivers -Device $Device -TempFolder $TempFolder
Write-Output ""
}
}
Function Get-LatestVCRuntimes
{
Param(
$TempFolder
)
Write-Output ""
Write-Output ""
$VisualCRuntimePath = "$TempFolder\VCRuntimes"
If (Test-Path "$VisualCRuntimePath")
{
Write-Output "Deleting $VisualCRuntimePath\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$VisualCRuntimePath" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$VisualCRuntimePath" -Force
}
If (!(Test-Path "$VisualCRuntimePath"))
{
New-Item -path "$VisualCRuntimePath" -ItemType "directory" | Out-Null
}
If (!(Test-Path "$VisualCRuntimePath\2013"))
{
New-Item -path "$VisualCRuntimePath\2013" -ItemType "directory" | Out-Null
}
If (!(Test-Path "$VisualCRuntimePath\2019"))
{
New-Item -path "$VisualCRuntimePath\2019" -ItemType "directory" | Out-Null
}
Write-Output "Downloading latest VisualC++ Runtimes..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$VC2013x86URL = "https://aka.ms/vcpp2013x86"
$VC2013x64URL = "https://aka.ms/vcpp2013x64"
$VC2019X86URL = "https://aka.ms/vcpp2019x86"
$VC2019X64URL = "https://aka.ms/vcpp2019x64"
# 2013
$VC2013x86 = DownloadFile -URL $VC2013x86URL -Path "$VisualCRuntimePath\2013"
Write-Output "Downloaded File: $VC2013x86" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
$VC2013x64 = DownloadFile -URL $VC2013x64URL -Path "$VisualCRuntimePath\2013"
Write-Output "Downloaded File: $VC2013x64" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
# 2019
$VC2019x86 = DownloadFile -URL $VC2019x86URL -Path "$VisualCRuntimePath\2019"
Write-Output "Downloaded File: $VC2019x86" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
$VC2019x64 = DownloadFile -URL $VC2019x64URL -Path "$VisualCRuntimePath\2019"
Write-Output "Downloaded File: $VC2019x64" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
}
Function Get-Office365
{
Param(
[string]$TempFolder
)
Write-Output ""
Write-Output ""
Write-Output ""
$Office365Path = "$TempFolder\Office365"
If (Test-Path "$Office365Path")
{
Write-Output "Deleting $Office365Path\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$Office365Path" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$Office365Path" -Force
}
If (!(Test-Path "$Office365Path"))
{
New-Item -Path "$Office365Path" -ItemType "directory" | Out-Null
}
Write-Output "Downloading Office 365 $Office365SKU..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$Office365OfflineURL = "https://aka.ms/sdao365"
$Office365TempFile = DownloadFile -URL $Office365OfflineURL -Path "$Office365Path"
Write-Output "Downloaded File: $Office365TempFile" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
Write-Output "Extracting Office 365 offline installer..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Process -FilePath "$Office365TempFile" -ArgumentList "/extract:$Office365Path /quiet" -Wait
Write-Output ""
If (!(Test-Path "$Office365Path\setup.exe"))
{
#File not downloaded, bail
Write-Output "Office offline setup file download appears to have failed. Exiting..." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
Else
{
$O365ProPlusDownloadXMLPath = "$WorkingDirPath\O365_Download.xml"
$O365ProPlusConfigurationXMLPath = "$WorkingDirPath\O365_Configuration.xml"
Copy-Item -Path $O365ProPlusDownloadXMLPath -Destination $Office365Path
Copy-Item -Path $O365ProPlusConfigurationXMLPath -Destination $Office365Path
$TempFile = "$Office365Path\O365_Download.xml"
If (Test-Path $TempFile)
{
$O365ProPlusDownloadXMLPath = "$Office365Path\O365_Download.xml"
}
Write-Output "Downloading Office 365 offline package..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$Argumentlist = "/download $O365ProPlusDownloadXMLPath"
Set-Location -Path "$Office365Path"
Start-Process -FilePath "$Office365Path\setup.exe" -ArgumentList $Argumentlist -Wait
Start-Sleep 2
Set-Location $WorkingDirPath
}
Write-Output ""
Write-Output ""
Write-Output ""
}
Function Get-OOBUpdates
{
Param(
[string]$TempFolder
)
$OOBUpdatePath = "$TempFolder\OOB"
If (Test-Path "$OOBUpdatePath")
{
Write-Output "Deleting $OOBUpdatePath\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$OOBUpdatePath" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$OOBUpdatePath" -Force
}
If (!(Test-Path "$OOBUpdatePath"))
{
New-Item -path "$OOBUpdatePath" -ItemType "directory" | Out-Null
}
Write-Output "Downloading latest Out-Of-Band/Non-security update for $global:OSVersion..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-LatestUpdates -OOB $True -Path $OOBUpdatePath -Windows $global:WindowsVersion -OSBuild $global:ReleaseId -Architecture $Architecture
}
Function Get-AdobeFlashUpdates
{
Param(
[string]$TempFolder
)
$adobeUpdatePath = "$TempFolder\Adobe"
If (Test-Path "$adobeUpdatePath")
{
Write-Output "Deleting $adobeUpdatePath\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$adobeUpdatePath" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$adobeUpdatePath" -Force
}
If (!(Test-Path "$adobeUpdatePath"))
{
New-Item -path "$adobeUpdatePath" -ItemType "directory" | Out-Null
}
Write-Output "Downloading latest Adobe Flash update for $global:OSVersion..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-LatestUpdates -Adobe $True -Path $adobeUpdatePath -Windows $global:WindowsVersion -OSBuild $global:ReleaseId -Architecture $Architecture
}
Function Get-CumulativeUpdates
{
Param(
[string]$TempFolder
)
$CumulativeUpdatePath = "$TempFolder\Cumulative"
If (Test-Path "$CumulativeUpdatePath")
{
Write-Output "Deleting $CumulativeUpdatePath\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$CumulativeUpdatePath" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$CumulativeUpdatePath" -Force
}
If (!(Test-Path "$CumulativeUpdatePath"))
{
New-Item -path "$CumulativeUpdatePath" -ItemType "directory" | Out-Null
}
Write-Output "Downloading latest Cumulative Update for $global:OSVersion..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-LatestUpdates -Cumulative $True -Path $CumulativeUpdatePath -Windows $global:WindowsVersion -OSBuild $global:ReleaseId -Architecture $Architecture
}
Function Get-ServicingStackUpdates
{
Param(
[string]$TempFolder
)
$ServicingStackPath = "$TempFolder\Servicing"
If (Test-Path "$ServicingStackPath")
{
Write-Output "Deleting $ServicingStackPath\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$ServicingStackPath" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$ServicingStackPath" -Force
}
If (!(Test-Path "$ServicingStackPath"))
{
New-Item -Path "$ServicingStackPath" -ItemType "directory" | Out-Null
}
Write-Output "Downloading latest Servicing Stack update for $global:OSVersion..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-LatestUpdates -Servicing $True -Path $ServicingStackPath -Windows $global:WindowsVersion -OSBuild $global:ReleaseId -Architecture $Architecture
}
Function Get-CumulativeDotNetUpdates
{
Param(
[string]$TempFolder
)
$CumulativeDotNetPath = "$TempFolder\DotNet"
If (Test-Path "$CumulativeDotNetPath")
{
Write-Output "Deleting $CumulativeDotNetPath\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$CumulativeDotNetPath" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$CumulativeDotNetPath" -Force
}
If (!(Test-Path "$CumulativeDotNetPath"))
{
New-Item -Path "$CumulativeDotNetPath" -ItemType "directory" | Out-Null
}
Write-Output "Downloading latest Dot Net Cumulative updates for $global:OSVersion..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-LatestUpdates -CumulativeDotNet $True -Path $CumulativeDotNetPath -Windows $global:WindowsVersion -OSBuild $global:ReleaseId -Architecture $Architecture
}
Function Get-WindowsOSVersionFromISO
{
Param(
[String]$ISO
)
$global:FullOSVersionFromSetupEXE = "10.0.22621.1"
Write-Output "Mounting ISO $ISO..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$ISOPath = (Mount-DiskImage -ImagePath $ISO -StorageType ISO -PassThru | Get-Volume).DriveLetter
$Drive = $ISOPath + ":"
If ($ISOPath)
{
Write-Output "ISO successfully mounted at $Drive" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
}
Else
{
Write-Output "Failed to mount the ISO. Please verify the ISO path and try again" | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
Write-Output "Parsing version from $Drive\sources\setup.exe..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$fileVersion = (Get-Item -Path "$Drive\sources\setup.exe").VersionInfo.FileVersion
If ($fileVersion -match '\s')
{
$fileVersion = ($fileVersion -split '\s')[0]
}
if ($fileVersion)
{
$global:FullOSVersionFromSetupEXE = $fileVersion.Trim()
}
Dismount-DiskImage -ImagePath $ISO | Out-Null
Write-Output ""
return $global:FullOSVersionFromSetupEXE
}
Function CloseRegEdit
{
$regeditProcess = Get-Process -Name regedit -ErrorAction SilentlyContinue
if ($regeditProcess) {
Write-Output "Terminating regedit.exe..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$regeditProcess | ForEach-Object { $_.Kill() }
Write-Output "regedit.exe terminated." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
}
Function LoadRegistryHive
{
Param(
$MountPoint,
$RegistryHive
)
# Unmount if hive is already mounted.
UnloadRegistryHive -MountPoint $MountPoint
Write-Output "Loading registry..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
& reg.exe load "$MountPoint" "$RegistryHive"
if ($LASTEXITCODE -eq 0)
{
Write-Output "Done loading registry..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
else
{
Write-Output "Failed to mount $RegistryHive to $MountPoint" | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
}
Function UnloadRegistryHive
{
Param(
$MountPoint
)
If (Test-Path -Path $MountPoint.Replace("HKLM\","HKLM:\"))
{
$retries = 3
Write-Output "Close regedit.exe if it is opened" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
CloseRegEdit
do
{
Write-Output "Unloading registry..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
& reg.exe unload "$MountPoint"
if ($LASTEXITCODE -eq 0)
{
Write-Output "Done unloading registry..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
break
}
$retries = $retries - 1
} while ($retries -gt 0)
}
}
Function Get-OSWIMFromISO
{
Param(
$ISO,
$OSSKU,
$DestinationFolder,
$Architecture,
$global:OSVersion,
$WindowsKitsInstall,
$ScratchMountFolder
)
If (Test-Path "$ScratchMountFolder")
{
Write-Output "Deleting $ScratchMountFolder\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$ScratchMountFolder" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$ScratchMountFolder" -Force
}
If (!(Test-Path -path $ScratchMountFolder))
{
New-Item -path $ScratchMountFolder -ItemType Directory | Out-Null
}
Write-Output "Mounting ISO $ISO..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$ISOPath = (Mount-DiskImage -ImagePath $ISO -StorageType ISO -PassThru | Get-Volume).DriveLetter
$Drive = $ISOPath + ":"
If ($ISOPath)
{
Write-Output "ISO successfully mounted at $Drive" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
}
Else
{
Write-Output "Failed to mount the ISO. Please verify the ISO path and try again" | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
Write-Output "Parsing install.wim/install.esd file(s) in $Drive for images..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$IsESD = $False
$WIMs = Get-ChildItem -Path "$Drive" -Filter install.wim -Recurse
If (!($WIMs))
{
$WIMs = Get-ChildItem -Path "$Drive" -Filter install.esd -Recurse
$IsESD = $True
}
If (!($WIMs))
{
Write-Output "No WIM or ESD files found in $Drive, aborting." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Dismount-DiskImage -ImagePath $ISO | Out-Null
Exit
}
ForEach ($WIM in $WIMs)
{
$TempWIM = $WIM.FullName
# Handle different language support as per issue #1 (https://github.com/microsoft/SurfaceDeploymentAccelerator/issues/1)
$OSImages = Get-WindowsImage -ImagePath $TempWIM
# Read WinPEXML file
[string]$XmlPath = "$WorkingDirPath\Languages.xml"
[Xml]$LanguagesXML = Get-Content $XmlPath
$Editions = $LanguagesXML.Windows.Editions.$OSSKU.Variants.Variant
Write-Output "Checking $TempWIM for valid images..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$OSImageFound = $False
ForEach ($Edition in $Editions)
{
ForEach ($OSImage in $OSImages)
{
If ($OSImage.ImageName -eq $Edition.name)
{
$ImagePath = $OSImage.ImagePath
$ImageIndex = $OSImage.ImageIndex
$OSImage = Get-WindowsImage -ImagePath $ImagePath -Index $ImageIndex
$ImageName = $OSImage.ImageName
$ImageVersion = $OSImage.Version
$ImageArch = $OSImage.Architecture
$OSImageFound = $True
}
Else
{
# Do nothing
}
}
}
If ($OSImageFound -eq $False)
{
# $OSImage not found
Write-Output "No OS Image found in $TempWIM matching $OSSKU, exiting." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Images inside $TempWIM :"
$OSImages
Write-Output ""
Write-Output ""
Start-Sleep 2
Dismount-DiskImage -ImagePath $ISO | Out-Null
Exit
}
Else
{
If ($ImageArch -eq "0")
{
$ImageArch = "x86"
}
If ($ImageArch -eq "9")
{
$ImageArch = "x64"
}
ElseIf ($ImageArch -eq "Unknown")
{
$ImageArch = "ARM64"
}
Write-Output "Found image matching $OSSKU :" | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Path: $ImagePath" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Index: $ImageIndex" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Name: $ImageName" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Version: $ImageVersion" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Architecture: $ImageArch" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
If ($ImageName -like "*Windows 11*")
{
$global:WindowsVersion = "W11"
}
ElseIf ($ImageName -like "*Windows 10*")
{
$global:WindowsVersion = "W10"
}
Start-Sleep 3
If ($IsESD -eq $True)
{
$TmpESDConvertWIM = "$env:TEMP\install.wim"
$TempWIM = $TmpESDConvertWIM
If (Test-Path "$TmpESDConvertWIM")
{
Write-Output "Deleting $TmpESDConvertWIM..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Remove-Item -Path "$TmpESDConvertWIM" -Force | Out-Null
Write-Output ""
}
Write-Output "Exporting $ImagePath to $TmpESDConvertWIM..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$Process = $DISMFile
$ArgumentList = "/Export-Image /SourceImageFile:$ImagePath /SourceIndex:$ImageIndex /DestinationImageFile:$TmpESDConvertWIM /CheckIntegrity /Compress:fast"
Start-Process -FilePath $Process -ArgumentList $Argumentlist -Wait -NoNewWindow
Write-Output ""
$ImagePath = $TmpESDConvertWIM
$ImageIndex = "1"
$global:OriginalOSIndex = "1"
}
Else
{
$global:OriginalOSIndex = $ImageIndex
}
$global:OSVersionFull = (Get-WindowsImage -ImagePath "$ImagePath" -Index "$ImageIndex").Version
If ($global:OSVersionFull)
{
$global:OSVersion = $global:OSVersionFull.Substring(0, $global:OSVersionFull.LastIndexOf('.'))
If (($global:OSVersion -like "10.0.18362*") -or ($global:OSVersion -like "10.0.19041*"))
{
Write-Output "$ImagePath contains image version $global:OSVersion, validating build..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
Write-Output "Mounting $ImagePath in $ScratchMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Mount-WindowsImage -ImagePath $ImagePath -Index $ImageIndex -Path $ScratchMountFolder -ReadOnly | Out-Null
Write-Output "Querying image registry for ReleaseId..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
LoadRegistryHive -MountPoint "HKLM\Mount" -RegistryHive "$ScratchMountFolder\Windows\system32\config\SOFTWARE"
$Key = "HKLM:\Mount\Microsoft\Windows NT\CurrentVersion"
$global:ReleaseId = (Get-ItemProperty -Path $Key -Name ReleaseId).ReleaseId
$global:CurrentBuild = (Get-ItemProperty -Path $Key -Name CurrentBuild).CurrentBuild
Write-Output "Unloading image registry..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
UnloadRegistryHive -MountPoint "HKLM\Mount"
Write-Output "Dismounting $ScratchMountFolder..." |Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Dismount-WindowsImage -Path $ScratchMountFolder -Discard | Out-Null
Write-Output ""
# Specific 1909 check as it will report as 10.0.18362 still when offline
If ($global:ReleaseId -eq "1909")
{
$global:OSVersion = "10.0.18363"
}
# Specific 20H2/21H1/21H2/22H2 check as it will report as 10.0.19041 still when offline
If ($global:ReleaseId -eq "2009")
{
If ($global:CurrentBuild -eq "19042")
{
$global:OSVersion = "10.0.19042"
$global:ReleaseID = "20H2"
}
ElseIf ($global:CurrentBuild -eq "19043")
{
$global:OSVersion = "10.0.19043"
$global:ReleaseID = "21H1"
}
ElseIf ($global:CurrentBuild -eq "19044")
{
$global:OSVersion = "10.0.19044"
$global:ReleaseID = "21H2"
}
ElseIf ($global:CurrentBuild -eq "19045")
{
$global:OSVersion = "10.0.19045"
$global:ReleaseID = "22H2"
}
ElseIf ($global:CurrentBuild -eq "22621")
{
$global:OSVersion = "10.0.22621"
$global:ReleaseID = "22H2"
}
}
}
Else
{
$global:ReleaseId = Switch ($global:OSVersion)
{
10.0.17763 {"1809"} # Windows 10 RS5
10.0.19041 {"2004"} # Windows 10 20H1
10.0.22000 {"21H2"} # Windows 11 21H2
10.0.22621 {"22H2"} # Windows 11 22H2
}
}
If (!($global:ReleaseID))
{
Write-Output "Unknown Windows release found ( $global:OSVersion ), aborting." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
Exit
}
}
Else
{
Write-Output "OS Version not pulled from $ImagePath, aborting." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
}
}
If ($OSImageFound -eq $False)
{
Dismount-DiskImage -ImagePath $ISO | Out-Null
Write-Output "$OSSKU not found in $WIMs on $ISO. Please make sure to use an ISO file that contains $OSSKU, and try again." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
If (!(Test-Path "$Mount"))
{
New-Item -path "$Mount" -ItemType "directory" | Out-Null
}
If (!(Test-Path "$DestinationFolder"))
{
New-Item -path "$DestinationFolder" -ItemType "directory" | Out-Null
}
If (!(Test-Path "$DestinationFolder\$OSSKU"))
{
New-Item -path "$DestinationFolder\$OSSKU" -ItemType "directory" | Out-Null
}
If (!(Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion"))
{
New-Item -path "$DestinationFolder\$OSSKU\$global:WindowsVersion" -ItemType "directory" | Out-Null
}
If (!(Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion"))
{
New-Item -path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion" -ItemType "directory" | Out-Null
}
If (!(Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture"))
{
New-Item -path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture" -ItemType "directory" | Out-Null
}
If (Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp")
{
Write-Output "Deleting $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp" -Recurse -Filter *.wim | Remove-Item -Force -Recurse
Remove-Item -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp" -Force -Recurse
}
If (!(Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp"))
{
New-Item -path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp" -ItemType "directory" | Out-Null
}
If (!(Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs"))
{
New-Item -path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs" -ItemType "directory" | Out-Null
}
If (Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs\install.wim")
{
Write-Output "Deleting $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs\install.wim..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Remove-Item -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs\install.wim" -Force
Start-Sleep 5
}
If (Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs\boot.wim")
{
Write-Output "Deleting $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs\boot.wim..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Remove-Item -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs\boot.wim" -Force
Start-Sleep 5
}
If ($Architecture -eq "x64")
{
$Arch = "amd64"
}
ElseIf ($Architecture -eq "ARM64")
{
$Arch = "arm64"
}
## Check whether boot.wim exists in the path or not.
If (Test-Path "$WindowsKitsInstall\Windows Preinstallation Environment\$Arch\en-us\winpe.wim")
{
Write-Output "Copying $WindowsKitsInstall\Windows Preinstallation Environment\$Arch\en-us\winpe.wim to $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs\boot.wim..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path "$WindowsKitsInstall\Windows Preinstallation Environment\$Arch\en-us\winpe.wim" -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs\boot.wim"
}
Start-Sleep 3
$SourceBootWIMs = Get-ChildItem -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs" -filter boot.wim -Recurse
ForEach ($SourceBootWIM in $SourceBootWIMs)
{
$TempBootWIM = $SourceBootWIM.FullName
$PEWIM = Get-WindowsImage -ImagePath $TempBootWIM | Where-Object {$_.ImageName -like "*Windows PE*"}
$ImagePath = $PEWIM.ImagePath
$ImageIndex = $PEWIM.ImageIndex
$ImageName = $PEWIM.ImageName
$global:WinPEVersion = (& $DISMFile /Get-WimInfo /WimFile:$ImagePath /index:$ImageIndex | Select-String "Version ").ToString().Split(":")[1].Trim()
}
Write-Output "Got WinPEVersion :$global:WinPEVersion" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If ($DotNet35 -eq $true)
{
If (Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\sxs")
{
Write-Output "Deleting $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\sxs..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\sxs" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\sxs" -Force
}
If (!(Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\sxs"))
{
New-Item -path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\sxs" -ItemType "directory" | Out-Null
}
Write-Output "Copying $Drive\Sources\sxs\* to $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\sxs\..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path "$Drive\Sources\sxs\*" -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\sxs" -PassThru | Set-ItemProperty -Name IsReadOnly -Value $false
}
If ($IsESD -eq $True)
{
Write-Output "Copying $TempWIM to $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs\install.wim..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path $TempWIM -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs" -PassThru | Set-ItemProperty -Name IsReadOnly -Value $false
Start-Sleep 2
}
Else
{
ForEach ($WIM in $WIMs)
{
$TempWIM = $WIM.FullName
Write-Output "Copying $TempWIM to $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs\install.wim..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path $TempWIM -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs" -PassThru | Set-ItemProperty -Name IsReadOnly -Value $false
Start-Sleep 2
}
}
If ($TmpESDConvertWIM -eq $Null)
{
# Do Nothing
}
ElseIf (Test-Path $TmpESDConvertWIM)
{
Write-Output "Deleting $TmpESDConvertWIM..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Remove-Item -Path $TmpESDConvertWIM -Force
Write-Output ""
}
Else
{
# This shouldn't be possible
}
Dismount-DiskImage -ImagePath $ISO | Out-Null
Write-Output ""
}
Function Add-PackageIntoWindowsImage
{
Param(
[string]$ImageMountFolder,
[string]$PackagePath,
[string]$TempImagePath,
[bool]$DismountImageOnCompletion = $true
)
try
{
Add-WindowsPackage -Path $ImageMountFolder -PackagePath $PackagePath
Write-Output ""
Write-Output ""
}
catch
{
$theError = $_
Write-Output $theError
throw $theError
}
# Force setting DismountImageOnCompletion to $false
$DismountImageOnCompletion = $false
If ($DismountImageOnCompletion -eq $True)
{
# Dismount the image to avoid PSFX/non-PSFX update compression issues in RS5+
Write-Output "Saving $TempImagePath..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
DisMount-WindowsImage -Path $ImageMountFolder -Save -CheckIntegrity
Write-Output ""
Write-Output ""
Start-Sleep 2
# Re-mount the image
Write-Output "Mounting $TempImagePath in $ImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Mount-WindowsImage -ImagePath $TempImagePath -Index 1 -Path $ImageMountFolder -CheckIntegrity
Write-Output ""
Write-Output ""
}
}
Function UpdateMenu
{
Param(
[Parameter(Mandatory=$true)]
[string]
$MenuTitle,
[Parameter(Mandatory=$true)]
[string[]]
$MenuItems,
[Parameter(Mandatory=$false)]
[switch]
$ShowOnlyLastFolder,
[Parameter(Mandatory=$true)]
[int]
$selection
)
Clear-Host
$UpA = [char]0x2191
$DownA = [char]0x2193
$Count = $MenuItems.Length
$HelperText = " $UpA, $DownA, Num (1-$Count), Enter to select:"
Write-Host -ForegroundColor White "`n $MenuTitle "
Write-Host -ForegroundColor White ("-"*($MenuTitle.Length + 4))
$itemCount = 0
foreach($item in $MenuItems){
If ($ShowOnlyLastFolder -eq $true){
$line = [string]$(Split-Path -Path $item -Leaf)
} Else {
$line = $item
}
If ($selection -eq $itemCount) {
$itemCount++
Write-Host -BackgroundColor White -ForegroundColor Black "$itemCount ] $line"
} Else {
$itemCount++
Write-Host -ForegroundColor White "$itemCount ] $line"
}
}
$viewSelection = $selection+1
Write-Host -ForegroundColor White ("-"*($MenuTitle.Length + 4))
If ($HelperText) {
Write-Host -ForegroundColor Yellow $HelperText
}
Write-Host -ForegroundColor White ">>: $viewSelection" -NoNewline
}
Function Select-MenuItem
{
Param(
[Parameter(Mandatory=$true)]
[string]
$MenuTitle,
[Parameter(Mandatory=$true)]
[string[]]
$MenuItems,
[Parameter(Mandatory=$false)]
[switch]
$ShowOnlyLastFolder
)
Clear-Host
#Menu input type defines
$ENTER = 13
$UPARROW = 38
$DOWNARROW = 40
$LEFTARROW = 37
$RIGHTARROW = 39
$BACKSPACE = 8
$DELETE = 46
#init selection variables
$selection = 0
$ExitEvent = $false
$UserInput = $null
Do {
# clear key input before getting new up/down/enter etc
$host.UI.RawUI.FlushInputBuffer()
UpdateMenu -MenuTitle $MenuTitle -MenuItems $MenuItems -selection $selection -ShowOnlyLastFolder
$key = ($host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp")).VirtualKeyCode
#Below lines useful for debugging
#Write-Host -ForegroundColor Magenta $key
#[Threading.Thread]::Sleep( 800 )
switch ($key) {
$UPARROW {
if($selection -gt 0){
$selection--
$UserInput = $null
}
}
$DOWNARROW {
if($selection -lt ($MenuItems.Count - 1) ) {
$selection++
$UserInput = $null
}
}
{(48..57) -contains $_ } {
#Number 0-9 key hit
$num = $key - 48
#$MenuItems.Count
$tempUI = $UserInput
If ( ($UserInput -eq $null) -or (($UserInput.Length -gt 0) -and ($MenuItems.Count -lt 10)) ) {
$tempUI = [string]"$num"
} Else {
$tempUI = $UserInput + [string]"$num"
}
#Below lines useful for debugging
#Write-Host -ForegroundColor Magenta "tempUI: $tempUI"
#[Threading.Thread]::Sleep( 1000 )
$UINum = [int]$tempUI
If( ($UINum -le 0) -or ($UINum -gt $MenuItems.Count) ) {
#out of range, reset
$UserInput = [string]($selection + 1)
} Else {
$UserInput = $tempUI
$selection = $UINum - 1
}
}
$ENTER {
$ExitEvent = $true
}
$BACKSPACE {
#Do back space stuff
If ( $UserInput.Length -eq 1 ) {
$UserInput = $null
$selection = 0
}
If ( $UserInput.Length -gt 1 ) {
$UserInput = $UserInput.Substring(0, $UserInput.Length-1)
$selection = ([int]$UserInput) - 1
}
}
}
} While ( -not $ExitEvent )
Return $selection
}
Function Select-USBDrive
{
$usbDisks = Get-Disk | Where-Object BusType -eq USB | Where-Object isOffline -ne True | Sort-Object Size
$DriveNumArray = @($usbDisks | Select-Object -ExpandProperty Number)
$MenuArray = @()
$usbDisks |
Select-Object -Property Number, FriendlyName, Size |
ForEach-Object {
$VolumeLabel = (Get-Disk -Number $_.Number | Get-Partition | Get-Volume).FileSystemLabel
$MenuArray += "DISK:$("{0:D3}" -f $_.Number) ($("{0:G5} GB" -f ($_.Size /1GB))) [$VolumeLabel] $($_.FriendlyName) "
}
If ($DriveNumArray.Count -lt 1)
{
Write-output " -- No USB key Found." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Return $null
}
$SelectIndex = Select-MenuItem -MenuTitle "Select USB Drive to format" -MenuItems $MenuArray
$diskNumToFlash = $DriveNumArray[$SelectIndex]
$diskName = $MenuArray[$SelectIndex]
Write-Output $diskNumToFlash
}
Function New-RegKey
{
Param($key)
$key = $key -replace ':',''
$parts = $key -split '\\'
$tempkey = ''
$parts | ForEach-Object {
$tempkey += ($_ + "\")
If ( (Test-Path "Registry::$tempkey") -eq $false)
{
New-Item "Registry::$tempkey" | Out-Null
}
}
}
Function TattooRegistry
{
Param(
[string]$ImageMountFolder,
[string]$RefImage,
[string]$SplitImage
)
$TempPath = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp"
LoadRegistryHive -MountPoint "HKLM\Mount" -RegistryHive "$ImageMountFolder\Windows\system32\config\SOFTWARE"
Start-Sleep 2
$SDARegKey = "HKLM:\Mount\Microsoft\Surface\SDA"
New-RegKey $SDARegKey
Start-Sleep 2
$ISORegValue = Get-ItemProperty $SDARegKey ISO -ErrorAction SilentlyContinue
$OSSKURegValue = Get-ItemProperty $SDARegKey OSSKU -ErrorAction SilentlyContinue
$DotNet35RegValue = Get-ItemProperty $SDARegKey DotNet35 -ErrorAction SilentlyContinue
$ServicingStackRegValue = Get-ItemProperty $SDARegKey ServicingStackUpdate -ErrorAction SilentlyContinue
$CumulativeUpdateRegValue = Get-ItemProperty $SDARegKey CumulativeUpdate -ErrorAction SilentlyContinue
$CumulativeDotNetUpdateRegValue = Get-ItemProperty $SDARegKey CumulativeDotNetUpdate -ErrorAction SilentlyContinue
$AdobeFlashUpdateRegValue = Get-ItemProperty $SDARegKey AdobeFlashUpdate -ErrorAction SilentlyContinue
$Office365RegValue = Get-ItemProperty $SDARegKey Office365 -ErrorAction SilentlyContinue
$DeviceRegValue = Get-ItemProperty $SDARegKey Device -ErrorAction SilentlyContinue
$DriverRegValue = Get-ItemProperty $SDARegKey Drivers -ErrorAction SilentlyContinue
$ImageRegValue = Get-ItemProperty $SDARegKey Image -ErrorAction SilentlyContinue
$OSVersionRegValue = Get-ItemProperty $SDARegKey OSVersion -ErrorAction SilentlyContinue
$ReleaseIDRegValue = Get-ItemProperty $SDARegKey ReleaseID -ErrorAction SilentlyContinue
$SDAVersionRegValue = Get-ItemProperty $SDARegKey SDAVersion -ErrorAction SilentlyContinue
$ISOFileName = $ISO.Substring($ISO.LastIndexOf("\") + 1)
If ($ISORegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name ISO -PropertyType STRING -Value $ISOFilename | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name ISO -Value $ISOFileName
}
If ($OSSKURegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name OSSKU -PropertyType STRING -Value $OSSKU | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name OSSKU -Value $OSSKU
}
If ($DotNet35 -eq $true)
{
If ($DotNet35RegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name DotNet35 -PropertyType STRING -Value $DotNet35 | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name DotNet35 -Value $DotNet35
}
}
If ($ServicingStack -eq $true)
{
$PathToScan = "$TempPath\Servicing"
$FileName = (Get-ChildItem -Path $PathToScan).Name
If ($ServicingStackRegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name ServicingStackUpdate -PropertyType STRING -Value $FileName | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name ServicingStackUpdate -Value $FileName
}
}
If ($CumulativeUpdate -eq $true)
{
$PathToScan = "$TempPath\Cumulative"
$FileName = (Get-ChildItem -Path $PathToScan).Name
If ($CumulativeUpdateRegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name CumulativeUpdate -PropertyType STRING -Value $FileName | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name CumulativeUpdate -Value $FileName
}
}
If ($CumulativeDotNetUpdate -eq $true)
{
$PathToScan = "$TempPath\DotNet"
$FileName = (Get-ChildItem -Path $PathToScan).Name
If ($CumulativeDotNetUpdateRegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name CumulativeDotNetUpdate -PropertyType STRING -Value $FileName | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name CumulativeDotNetUpdate -Value $FileName
}
}
If ($AdobeFlashUpdate -eq $true)
{
$PathToScan = "$TempPath\Adobe"
$FileName = (Get-ChildItem -Path $PathToScan).Name
If ($AdobeFlashUpdateRegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name AdobeFlashUpdate -PropertyType STRING -Value $FileName | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name AdobeFlashUpdate -Value $FileName
}
}
If ($Office365 -eq $true)
{
$PathToScan = "$TempPath\Office365"
$FileName = (Get-ChildItem -Path $PathToScan -Recurse | Where-Object { ($_.PSIsContainer) -and ($_.Name -like "16.*") }).Name
If ($Office365RegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name Office365 -PropertyType STRING -Value $FileName | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name Office365 -Value $FileName
}
}
If ($Device)
{
If ($DeviceRegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name Device -PropertyType STRING -Value $Device | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name Device -Value $Device
}
}
If (($Device) -or ($LocalDriverPath))
{
If ($LocalDriverPath)
{
$TempLocalDriverPath = (Get-Item $LocalDriverPath) -is [System.IO.DirectoryInfo]
If ($TempLocalDriverPath -eq $False)
{
$FileName = (Get-ChildItem -Path $LocalDriverPath).Name
}
Else
{
$FileName = (Get-ChildItem -Path $LocalDriverPath -Recurse | Where-Object { $_.Name -like "*.msi" }).Name
}
}
Else
{
If (Test-Path "$TempPath\$Device")
{
$TempLocalDriverPath = (Get-ChildItem -Path "$TempPath\$Device")
$FileName = (Get-ChildItem -Path "$Temppath\$Device" -Recurse | Where-Object { $_.Name -like "*.msi" }).Name
}
}
If ($DriverRegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name Drivers -PropertyType STRING -Value $FileName | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name Drivers -Value $FileName
}
}
If (($RefImage) -or ($SplitImage))
{
If ($ImageRegValue -eq $null)
{
If (Test-path $RefImage)
{
If (Test-Path $SplitImage)
{
$SplitImageName = (Get-Item -Path $SplitImage).Name
If ($ImageRegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name Image -PropertyType STRING -Value $SplitImageName | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name Image -Value $SplitImageName
}
}
Else
{
$RefImageName = (Get-Item -Path $RefImage).Name
If ($ImageRegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name Image -PropertyType STRING -Value $RefImageName | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name Image -Value $RefImageName
}
}
}
}
}
If ($OSVersionRegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name OSVersion -PropertyType STRING -Value $Build | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name OSVersion -Value $Build
}
If ($ReleaseIDRegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name ReleaseID -PropertyType STRING -Value $global:ReleaseID | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name ReleaseID -Value $global:ReleaseID
}
If ($SDAVersionRegValue -eq $null)
{
New-ItemProperty -Path $SDARegKey -Name SDAVersion -PropertyType STRING -Value $SDAVersion | Out-Null
}
Else
{
Set-ItemProperty -Path $SDARegKey -Name SDAVersion -Value $SDAVersion
}
Start-Sleep 5
Write-Output "Unloading the registry key..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
UnloadRegistryHive -MountPoint "HKLM\Mount"
Start-Sleep 2
}
Function Update-Win10WIM
{
Param(
[string]$SourcePath,
[string]$SourceName,
[bool]$ServicingStack,
[bool]$CumulativeUpdate,
[bool]$DotNet35,
[bool]$CumulativeDotNetUpdate,
[bool]$AdobeFlashUpdate,
[bool]$UpdateBootWIM,
[string]$ImageMountFolder,
[string]$BootImageMountFolder,
[string]$WinREImageMountFolder,
[string]$TempFolder,
[string]$WindowsKitsInstall,
[bool]$MakeUSBMedia,
[bool]$MakeISOMedia
)
# Variables
$TmpImage = "$TempFolder\tmp_install.wim"
$TmpWinREImage = "$TempFolder\tmp_winre.wim"
$TmpBootImage = "$TempFolder\tmp_boot.wim"
$ServicingStackPath = "$TempFolder\Servicing"
$CumulativeUpdatePath = "$TempFolder\Cumulative"
$CumulativeDotNetPath = "$TempFolder\DotNet"
$AdobeFlashUpdatePath = "$TempFolder\Adobe"
$Office365Path = "$TempFolder\Office365"
$DeviceDriverPath = "$TempFolder\$Device"
$VC2013x86Path = "$TempFolder\VCRuntimes\2013\vcredist_x86.exe"
$VC2013x64Path = "$TempFolder\VCRuntimes\2013\vcredist_x64.exe"
$VC2019x86Path = "$TempFolder\VCRuntimes\2019\vc_redist.x86.exe"
$VC2019x64Path = "$TempFolder\VCRuntimes\2019\vc_redist.x64.exe"
$ProUnattendXMLPath = "$WorkingDirPath\Win10Pro_Unattend.xml"
$EntUnattendXMLPath = "$WorkingDirPath\Win10Ent_Unattend.xml"
$HubUnattendXMLPath = "$WorkingDirPath\Win10Hub_Unattend.xml"
$OfficeAuditXMLPath = "$WorkingDirPath\Win10_Audit_Office.xml"
$NoOfficeAuditXMLPath = "$WorkingDirPath\Win10_Audit_NoOffice.xml"
$InstallOfficeScriptPath = "$WorkingDirPath\InstallOffice.ps1"
$SetTaskBarPinsScriptPath = "$WorkingDirPath\SetTaskBarPins.ps1"
$SysprepToOOBEScriptPath = "$WorkingDirPath\SysprepToOOBE.ps1"
<#
$SourceName = Switch ($SourceName)
{
Pro {"Windows 10 Pro"}
Enterprise {"Windows 10 Enterprise"}
}
#>
Write-Output ""
Write-Output ""
Write-Output ""
Write-Output ""
Write-Output ""
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * Updating install.wim *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Sleep 2
If ($InstallWIM)
{
# Export the reference image to a new (temporary) WIM - this will leave the original "install.wim" untouched when finished
If (Test-Path "$SourcePath\install.wim")
{
Write-Output "Exporting $SourcePath\install.wim to $TmpImage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Export-WindowsImage -SourceImagePath "$SourcePath\install.wim" -SourceIndex $global:OriginalOSIndex -DestinationImagePath $TmpImage -CheckIntegrity
#Export-WindowsImage -SourceImagePath "$SourcePath\install.wim" -SourceName "$SourceName" -DestinationImagePath $TmpImage -CheckIntegrity
Write-Output ""
Write-Output ""
}
Else
{
Write-Output "No WIM file found in $SourcePath, aborting." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
# Mount the image
Write-Output "Mounting $TmpImage in $ImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Mount-WindowsImage -ImagePath $TmpImage -Index 1 -Path $ImageMountFolder -CheckIntegrity
Write-Output ""
Write-Output ""
If ($DotNet35 -eq $True)
{
# Cleanup the image BEFORE installing .NET to prevent errors
Write-Output "Running image cleanup on $ImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
& $DISMFile /Image:$ImageMountFolder /Cleanup-Image /StartComponentCleanup /ResetBase
Write-Output ""
Write-Output ""
# Dismount the image
Write-Output "Saving $TmpImage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
DisMount-WindowsImage -Path $ImageMountFolder -Save -CheckIntegrity
Write-Output ""
Write-Output ""
Start-Sleep 10
# Re-mount the image
Write-Output "Mounting $TmpImage in $ImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Mount-WindowsImage -ImagePath $TmpImage -Index 1 -Path $ImageMountFolder -CheckIntegrity
Write-Output ""
Write-Output ""
# Add .NET Framework 3.5 to the image
Write-Output "Adding .NET Framework 3.5 to $ImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Enable-WindowsOptionalFeature -Path $ImageMountFolder -FeatureName NetFx3 -All -Source "$TempFolder\sxs" -LimitAccess
Write-Output ""
Write-Output ""
Start-Sleep 2
}
# Add servicing stack (SSU), if exists.
If ($ServicingStack -eq $true)
{
$SSU = Get-ChildItem -Path $ServicingStackPath
If (!($SSU.Exists))
{
$ServicingStack = $False
}
Else
{
# Add required Servicing Stack updates
Write-Output "Adding Servicing Stack updates to $ImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-PackageIntoWindowsImage -ImageMountFolder $ImageMountFolder -PackagePath $ServicingStackPath -TempImagePath $TmpImage -DismountImageOnCompletion $True
Start-Sleep 2
}
}
If ($CumulativeUpdate -eq $true)
{
$CU = Get-ChildItem -Path $CumulativeUpdatePath
If (!($CU.Exists))
{
$CumulativeUpdate = $False
}
Else
{
# Add monthly Cumulative update
Write-Output "Adding Cumulative updates to $ImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
try
{
Add-PackageIntoWindowsImage -ImageMountFolder $ImageMountFolder -PackagePath $CumulativeUpdatePath -TempImagePath $TmpImage -DismountImageOnCompletion $False
Start-Sleep 2
}
catch
{
$theError = $_
Write-Output "$theError" | Receive-Output -Color Red -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If ($theError.Exception -like "*0x8007007e*")
{
Write-Output "This failure is a known issue with combined cumulative update, we can ignore." | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
ElseIf ($theError.Exception -like "*0x800f0823*")
{
Write-Output "This failure is a known issue with combined cumulative update, we need to try install same package again" | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Re-installing the cumulative update..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-PackageIntoWindowsImage -ImageMountFolder $ImageMountFolder -PackagePath $CumulativeUpdatePath -TempImagePath $TmpImage -DismountImageOnCompletion $False
Start-Sleep 2
}
else
{
throw
}
}
}
}
If ($CumulativeDotNetUpdate -eq $true)
{
$CUDN = Get-ChildItem -Path $CumulativeDotNetPath
If (!($CUDN.Exists))
{
$CumulativeDotNetUpdate = $False
}
Else
{
# Add monthly Cumulative update
Write-Output "Adding Cumulative .NET updates to $ImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-PackageIntoWindowsImage -ImageMountFolder $ImageMountFolder -PackagePath $CumulativeDotNetPath -TempImagePath $TmpImage -DismountImageOnCompletion $False
Start-Sleep 2
}
}
If ($AdobeFlashUpdate -eq $true)
{
$AFU = Get-ChildItem -Path $AdobeFlashUpdatePath
If (!($AFU.Exists))
{
$AdobeFlashUpdate = $False
}
Else
{
# Add Adobe Flash updates
Write-Output "Adding Adobe Flash updates to $ImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-PackageIntoWindowsImage -ImageMountFolder $ImageMountFolder -PackagePath $AdobeFlashUpdatePath -TempImagePath $TmpImage -DismountImageOnCompletion $False
Start-Sleep 2
}
}
If ($Office365 -eq $True)
{
# Copy Office 365 bits to device
If (Test-Path "$ImageMountFolder\Windows\Temp\Office365")
{
Write-Output "$ImageMountFolder\Windows\Temp\Office365..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$ImageMountFolder\Windows\Temp\Office365" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$ImageMountFolder\Windows\Temp\Office365" -Force
}
If (!(Test-Path "$ImageMountFolder\Windows\Temp\Office365"))
{
New-Item -Path "$ImageMountFolder\Windows\Temp\Office365" -ItemType Directory | Out-Null
}
If (!($Architecture -eq "ARM64"))
{
Write-Output "Copying Office365 files to $ImageMountFolder\Windows\Temp..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path $InstallOfficeScriptPath -Destination "$Office365Path\InstallOffice.ps1" -Force -ErrorAction Continue
& xcopy.exe /herky "$Office365Path" "$ImageMountFolder\Windows\Temp\Office365"
Write-Output ""
}
}
If ($Device)
{
$MSIFiles = Get-ChildItem -Path $DeviceDriverPath -Recurse
# Add drivers/firmware to WIM
Write-Output "Adding Driver updates for $Device to $ImageMountFolder from $DeviceDriverPath..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsDriver -Path $ImageMountFolder -Driver "$DeviceDriverPath" -Recurse
Write-Output ""
Write-Output ""
# Copy VC++ Runtimes
If (Test-Path "$ImageMountFolder\Windows\Temp\VCRuntimes\2013")
{
Write-Output "Deleting $ImageMountFolder\Windows\Temp\VCRuntimes\2013..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$ImageMountFolder\Windows\Temp\VCRuntimes\2013" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$ImageMountFolder\Windows\Temp\VCRuntimes\2013" -Force
}
If (!(Test-Path "$ImageMountFolder\Windows\Temp\VCRuntimes\2013"))
{
New-Item -Path "$ImageMountFolder\Windows\Temp\VCRuntimes\2013" -ItemType Directory | Out-Null
}
If (Test-Path "$ImageMountFolder\Windows\Temp\VCRuntimes\2019")
{
Write-Output "Deleting $ImageMountFolder\Windows\Temp\VCRuntimes\2019..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$ImageMountFolder\Windows\Temp\VCRuntimes\2019" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$ImageMountFolder\Windows\Temp\VCRuntimes\2019" -Force
}
If (!(Test-Path "$ImageMountFolder\Windows\Temp\VCRuntimes\2019"))
{
New-Item -Path "$ImageMountFolder\Windows\Temp\VCRuntimes\2019" -ItemType Directory | Out-Null
}
}
If (!($Architecture -eq "ARM64"))
{
Write-Output "Copying VC++ Runtime binaries to $ImageMountFolder\Windows\Temp..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path $VC2013x86Path -Destination "$ImageMountFolder\Windows\Temp\VCRuntimes\2013"
Copy-Item -Path $VC2013x64Path -Destination "$ImageMountFolder\Windows\Temp\VCRuntimes\2013"
Copy-Item -Path $VC2019x86Path -Destination "$ImageMountFolder\Windows\Temp\VCRuntimes\2019"
Copy-Item -Path $VC2019x64Path -Destination "$ImageMountFolder\Windows\Temp\VCRuntimes\2019"
Write-Output ""
}
Write-Output "Copying files to disk for unattended installation..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path $SysprepToOOBEScriptPath -Destination "$ImageMountFolder\Windows\Temp\SysprepToOOBE.ps1" -Force -ErrorAction Continue
If ($Office365 -eq $true)
{
Copy-Item -Path $OfficeAuditXMLPath -Destination "$ImageMountFolder\Windows\System32\sysprep\unattend.xml" -Force -ErrorAction Continue
Copy-Item -Path $SetTaskBarPinsScriptPath -Destination "$ImageMountFolder\Windows\Temp\SetTaskBarPins.ps1" -Force -ErrorAction Continue
}
Else
{
Copy-Item -Path $NoOfficeAuditXMLPath -Destination "$ImageMountFolder\Windows\System32\sysprep\unattend.xml" -Force -ErrorAction Continue
}
If ($Device -eq "SurfaceHub2")
{
Copy-Item -Path $HubUnattendXMLPath -Destination "$ImageMountFolder\Windows\Temp\Reseal.xml" -Force -ErrorAction Continue
}
Else
{
If ($OSSKU -like "*Pro*")
{
Copy-Item -Path $ProUnattendXMLPath -Destination "$ImageMountFolder\Windows\Temp\Reseal.xml" -Force -ErrorAction Continue
}
ElseIf ($OSSKU -like "*Enterprise*")
{
Copy-Item -Path $EntUnattendXMLPath -Destination "$ImageMountFolder\Windows\Temp\Reseal.xml" -Force -ErrorAction Continue
}
Write-Output ""
}
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * Updating winre.wim *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Sleep 2
# Copy WinRE Image to temp location
Write-Output "Copying WinRE image to $TmpWinREImage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Move-Item -Path "$ImageMountFolder\Windows\System32\Recovery\winre.wim" -Destination $TmpWinREImage
Write-Output ""
Write-Output ""
# Mount the temp WinRE Image
Write-Output "Mounting $TmpWinREImage to $WinREImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Mount-WindowsImage -ImagePath $TmpWinREImage -Index 1 -Path $WinREImageMountFolder -CheckIntegrity
Write-Output ""
Write-Output ""
# Add servicing stack (SSU), if exists.
If ($ServicingStack)
{
$SSU = Get-ChildItem -Path $ServicingStackPath
If (!($SSU.Exists))
{
$ServicingStack = $False
}
Else
{
# Add Servicing Stack updates to the WinRE image
Write-Output "Adding Servicing Stack updates to $WinREImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-PackageIntoWindowsImage -ImageMountFolder $WinREImageMountFolder -PackagePath $ServicingStackPath -TempImagePath $TmpWinREImage -DismountImageOnCompletion $True
Start-Sleep 2
}
}
If ($CumulativeUpdate)
{
$CU = Get-ChildItem -Path $CumulativeUpdatePath
If (!($CU.Exists))
{
$CumulativeUpdate = $False
}
Else
{
try
{
# Add monthly Cumulative updates to the WinRE image
Write-Output "Adding Cumulative updates to $WinREImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-PackageIntoWindowsImage -ImageMountFolder $WinREImageMountFolder -PackagePath $CumulativeUpdatePath -TempImagePath $TmpWinREImage -DismountImageOnCompletion $False
Start-Sleep 2
}
catch
{
$theError = $_
Write-Output "$theError" | Receive-Output -Color Red -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If ($theError.Exception -like "*0x8007007e*")
{
Write-Output "This failure is a known issue with combined cumulative update, we can ignore." | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
ElseIf ($theError.Exception -like "*0x800f0823*")
{
Write-Output "This failure is a known issue with combined cumulative update, we need to try install same package again" | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Re-installing the cumulative update..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-PackageIntoWindowsImage -ImageMountFolder $WinREImageMountFolder -PackagePath $CumulativeUpdatePath -TempImagePath $TmpWinREImage -DismountImageOnCompletion $False
Start-Sleep 2
}
else
{
throw
}
}
}
}
If ($Device)
{
$MSIFiles = Get-ChildItem -Path $DeviceDriverPath -Recurse
If ($SurfaceDevices.$Device)
{
# Add system-level drivers to WIM
Write-Output "Adding Driver updates for $Device to $WinREImageMountFolder from $DeviceDriverPath..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$Drivers = $SurfaceDevices.$Device.Drivers.Driver
ForEach ($Driver in $Drivers)
{
$TempDriverName = $Driver.name
ForEach ($MSIFile in $MSIFiles)
{
If ($MSIFile.Name -eq $TempDriverName)
{
Add-WindowsDriver -Path $WinREImageMountFolder -Driver $MSIFile.FullName
}
}
}
}
Write-Output ""
Write-Output ""
}
# Cleanup the WinRE image
Write-Output "Running image cleanup on $TmpWinREImage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
& $DISMFile /Image:$WinREImageMountFolder /Cleanup-Image /StartComponentCleanup /ResetBase
Write-Output ""
Write-Output ""
Start-Sleep 2
# Dismount the WinRE image
Write-Output "Saving $TmpWinREImage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
DisMount-WindowsImage -Path $WinREImageMountFolder -Save -CheckIntegrity
Write-Output ""
Write-Output ""
Start-Sleep 2
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * Saving winre.wim *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Sleep 2
# Export the new WinRE image back to original location
Write-Output "Exporting $TmpWinREImage to $ImageMountFolder\Windows\System32\Recovery\winre.wim..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Export-WindowsImage -SourceImagePath $TmpWinREImage -SourceIndex "1" -DestinationImagePath "$ImageMountFolder\Windows\System32\Recovery\winre.wim" -CheckIntegrity
Start-Sleep 2
Write-Output ""
Write-Output ""
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * Saving install.wim *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Sleep 2
# Validate Windows WIM build number
$Build = (Get-Item $ImageMountFolder\Windows\System32\ntoskrnl.exe).VersionInfo.ProductVersion
If (($global:ReleaseId -eq "1909") -and ($Build -match "18362"))
{
$Build = $Build -replace "18362", "18363"
}
If (($global:ReleaseId -eq "2009") -and ($Build -match "19041"))
{
$Build = $Build -replace "19041", "19042"
}
If ($Device)
{
$RefImage = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\$Device-Install-$Build-$OSSKU-$Now.wim"
$SplitImage = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\$Device-Install-$Build-$OSSKU-$Now--Split.swm"
}
Else
{
$RefImage = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Generic-Install-$Build-$OSSKU-$Now.wim"
$SplitImage = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Generic-Install-$Build-$OSSKU-$Now--Split.swm"
}
Write-Output "Adding registry tattoo..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
TattooRegistry -ImageMountFolder $ImageMountFolder -RefImage $RefImage -SplitImage $SplitImage
Start-Sleep 2
# Dismount the reference image
Write-Output "Saving $TmpImage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
DisMount-WindowsImage -Path $ImageMountFolder -Save -CheckIntegrity
Start-Sleep 2
Write-Output ""
Write-Output ""
# Export the image to a new WIM
Write-Output "Exporting $TmpImage to $RefImage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Export-WindowsImage -SourceImagePath $TmpImage -SourceIndex 1 -DestinationImagePath $RefImage -CheckIntegrity
Start-Sleep 2
Write-Output ""
Write-Output ""
$TempRefImageSize = Get-Item $RefImage
$RefImageSize = ($TempRefImageSize.Length /1GB)
If ($RefImageSize -ge "4")
{
$SplitWIM = $true
# Split the WIM to fit on FAT32-formatted media (splitting at ~3GB for simplicity)
Write-Output "Splitting $RefImage into 3GB files as $SplitImage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Split-WindowsImage -ImagePath $RefImage -SplitImagePath $SplitImage -FileSize 3096 -CheckIntegrity
Start-Sleep 2
Write-Output ""
Write-Output ""
}
Start-Sleep 5
# Remove temporary WIMs
If (Test-Path -path $TmpImage)
{
Remove-Item -Path $TmpImage -Force
}
If (Test-Path -path $TmpWinREImage)
{
Remove-Item -Path $TmpWinREImage -Force
}
If ($SplitWIM -eq $True)
{
If ($KeepOriginalWIM -eq $True)
{
#Don't delete original .wim file
}
ElseIf (Test-Path -path $RefImage)
{
Remove-Item -Path $RefImage
}
}
}
If ($UpdateBootWIM -eq $True)
{
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * Updating boot.wim *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Sleep 2
# Copy boot.wim for editing
Write-Output "Copying $SourcePath\boot.wim to $TmpBootImage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item "$SourcePath\boot.wim" $TempFolder
Attrib -r "$TempFolder\boot.wim"
Rename-Item -Path "$TempFolder\boot.wim" -NewName "$TmpBootImage"
Write-Output ""
Write-Output ""
# Mount index 1 of the boot image (WinPE)
Write-Output "Mounting $TmpBootImage to $BootImageMountFolder using Index 1..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Mount-WindowsImage -ImagePath $TmpBootImage -Index 1 -Path $BootImageMountFolder -CheckIntegrity
Start-Sleep 2
Write-Output ""
Write-Output ""
Write-Output "WindowsKitsInstall : $WindowsKitsInstall" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Mounted $TmpBootImage to $BootImageMountFolder using Index 1..." | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
# Add Servicing stack (SSU), if exists.
If ($ServicingStack)
{
$SSU = Get-ChildItem -Path $ServicingStackPath
If (!($SSU.Exists))
{
$ServicingStack = $False
}
Else
{
# Add required Servicing Stack updates
Write-Output "Adding Servicing Stack updates to $BootImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-PackageIntoWindowsImage -ImageMountFolder $BootImageMountFolder -PackagePath $ServicingStackPath -TempImagePath $TmpBootImage -DismountImageOnCompletion $True
Start-Sleep 2
}
}
If ($CumulativeUpdate)
{
$CU = Get-ChildItem -Path $CumulativeUpdatePath
If (!($CU.Exists))
{
$CumulativeUpdate = $False
}
Else
{
# Add SSU and monthly Cumulative update (Windows 10 CU is Uno and Windows 11 is OnePackage - Meaning both SSU and latest CU is part of same .msu).
Start-Sleep 1
try
{
Write-Output "Adding Cumulative updates to $BootImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-PackageIntoWindowsImage -ImageMountFolder $BootImageMountFolder -PackagePath $CumulativeUpdatePath -TempImagePath $TmpBootImage -DismountImageOnCompletion $False
}
catch
{
$theError = $_
Write-Output "$theError" | Receive-Output -Color Red -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If ($theError.Exception -like "*0x8007007e*")
{
Write-Output "This failure is a known issue with combined cumulative update, we can ignore." | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
ElseIf ($theError.Exception -like "*0x800f0823*")
{
Write-Output "This failure is a known issue with combined cumulative update, we need to try install same package again" | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Re-installing the cumulative update..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-PackageIntoWindowsImage -ImageMountFolder $BootImageMountFolder -PackagePath $CumulativeUpdatePath -TempImagePath $TmpBootImage -DismountImageOnCompletion $False
Start-Sleep 2
}
else
{
throw
}
}
Start-Sleep 2
}
}
If ($CumulativeDotNetUpdate)
{
$CUDN = Get-ChildItem -Path $CumulativeDotNetPath
If (!($CUDN.Exists))
{
$CumulativeDotNetUpdate = $False
}
Else
{
# Add monthly Cumulative update
Write-Output "Adding Cumulative .NET updates to $BootImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-PackageIntoWindowsImage -ImageMountFolder $BootImageMountFolder -PackagePath $CumulativeDotNetPath -TempImagePath $TmpBootImage -DismountImageOnCompletion $False
Start-Sleep 2
}
}
If ($Device)
{
$MSIFiles = Get-ChildItem -Path $DeviceDriverPath -Recurse
If ($SurfaceDevices.$Device)
{
# Add system-level drivers to WIM
Write-Output "Adding Driver updates for $Device to $BootImageMountFolder from $DeviceDriverPath..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$Drivers = $SurfaceDevices.$Device.Drivers.Driver
ForEach ($Driver in $Drivers)
{
$TempDriverName = $Driver.name
ForEach ($MSIFile in $MSIFiles)
{
If ($MSIFile.Name -eq $TempDriverName)
{
Add-WindowsDriver -Path $BootImageMountFolder -Driver $MSIFile.FullName
}
}
}
}
Write-Output ""
Write-Output ""
}
# Add support for deployment components
If ($Architecture -eq "x64")
{
$WinPEOCPath = "$WindowsKitsInstall\Windows Preinstallation Environment\amd64\WinPE_OCs"
}
ElseIf ($Architecture -eq "ARM64")
{
$WinPEOCPath = "$WindowsKitsInstall\Windows Preinstallation Environment\arm64\WinPE_OCs"
}
Write-Output "Adding WMI..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\WinPE-WMI.cab" | Out-Null
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\en-us\WinPE-WMI_en-us.cab" | Out-Null
Write-Output "Adding PE Scripting..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\WinPE-Scripting.cab" | Out-Null
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\en-us\WinPE-Scripting_en-us.cab" | Out-Null
Write-Output "Adding Enhanced Storage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\WinPE-EnhancedStorage.cab" | Out-Null
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\en-us\WinPE-EnhancedStorage_en-us.cab" | Out-Null
Write-Output "Adding Bitlocker support..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\WinPE-SecureStartup.cab" | Out-Null
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\en-us\WinPE-SecureStartup_en-us.cab" | Out-Null
Write-Output "Adding .NET..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\WinPE-NetFx.cab" | Out-Null
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\en-us\WinPE-NetFx_en-us.cab" | Out-Null
Write-Output "Adding PowerShell..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\WinPE-PowerShell.cab" | Out-Null
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\en-us\WinPE-PowerShell_en-us.cab" | Out-Null
Write-Output "Adding Storage WMI..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\WinPE-StorageWMI.cab" | Out-Null
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\en-us\WinPE-StorageWMI_en-us.cab" | Out-Null
Write-Output "Adding DISM support..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\WinPE-DismCmdlets.cab" | Out-Null
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\en-us\WinPE-DismCmdlets_en-us.cab" | Out-Null
Write-Output "Adding Secure Boot support..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\WinPE-SecureBootCmdlets.cab" | Out-Null
Write-Output "Adding Secure Startup support..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\WinPE-DismCmdlets.cab" | Out-Null
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\en-us\WinPE-DismCmdlets_en-us.cab" | Out-Null
Write-Output "Adding WinRE support..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\WinPE-WinReCfg.cab" | Out-Null
Add-WindowsPackage -Path $BootImageMountFolder -PackagePath "$WinPEOCPath\en-us\WinPE-WinReCfg_en-us.cab" | Out-Null
If (($MakeUSBMedia) -or ($MakeISOMedia))
{
Write-Output "Copying scripts to $BootImageMountFolder..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path "$WorkingDirPath\UsbImage\CreatePartitions-UEFI.txt" -Destination $BootImageMountFolder
Copy-Item -Path "$WorkingDirPath\UsbImage\CreatePartitions-UEFI_Source.txt" -Destination $BootImageMountFolder
Copy-Item -Path "$WorkingDirPath\UsbImage\Imaging.ps1" -Destination $BootImageMountFolder
Copy-Item -Path "$WorkingDirPath\UsbImage\Install.cmd" -Destination $BootImageMountFolder
Copy-Item -Path "$WorkingDirPath\UsbImage\startnet.cmd" -Destination "$BootImageMountFolder\Windows\System32" -Force
}
Write-Output ""
Write-Output ""
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * Saving boot.wim *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Sleep 2
# Variable
$WinPEBuild = (Get-Item $BootImageMountFolder\Windows\System32\ntoskrnl.exe).VersionInfo.ProductVersion
If ($Device)
{
$RefBootImage = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\$Device-Boot-$WinPEBuild-$Now.wim"
}
Else
{
$RefBootImage = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Generic-Boot-$WinPEBuild-$Now.wim"
}
# Dismount the boot image
Write-Output "Saving $TmpBootImage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
DisMount-WindowsImage -Path $BootImageMountFolder -Save -CheckIntegrity
Start-Sleep 2
Write-Output ""
Write-Output ""
# Export the image to a new WIM
Write-Output "Exporting $TmpBootImage to $RefBootImage..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Export-WindowsImage -SourceImagePath $TmpBootImage -SourceIndex 1 -DestinationImagePath $RefBootImage -CheckIntegrity
Start-Sleep 2
Write-Output ""
Write-Output ""
# Remove the temporary WIM
If (Test-Path -path $TmpBootImage)
{
Remove-Item -Path $TmpBootImage -Force
Write-Output ""
Write-Output ""
}
}
# Make a USB key or ISO
If (($MakeUSBMedia) -or ($MakeISOMedia))
{
If (Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media")
{
Write-Output "Deleting $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media" -Force
}
If (!(Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media"))
{
New-Item -path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media" -ItemType "directory" | Out-Null
}
If (Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\fwfiles")
{
Write-Output "Deleting $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\fwfiles\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\fwfiles" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\fwfiles" -Force
}
If (!(Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\fwfiles"))
{
New-Item -path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\fwfiles" -ItemType "directory" | Out-Null
}
If ($Architecture -eq "x64")
{
$Arch = "amd64"
}
ElseIf ($Architecture -eq "ARM64")
{
$Arch = "arm64"
}
Write-Output "Creating WinPE media in $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
& xcopy.exe /herky "$WindowsKitsInstall\Windows Preinstallation Environment\$Arch\Media" "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media"
Copy-Item -Path "$WindowsKitsInstall\Deployment Tools\$Arch\Oscdimg\efisys.bin" -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\fwfiles"
Copy-Item -Path "$WindowsKitsInstall\Deployment Tools\$Arch\Oscdimg\etfsboot.com" -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\fwfiles"
If (!(Test-Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media\Sources"))
{
New-Item -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media\sources" -ItemType Directory | Out-Null
}
Copy-Item -Path $RefBootImage -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media\sources\boot.wim"
Copy-Item -Path "$WorkingDirPath\UsbImage\CreatePartitions-UEFI.txt" -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media"
Copy-Item -Path "$WorkingDirPath\UsbImage\CreatePartitions-UEFI_Source.txt" -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media"
Copy-Item -Path "$WorkingDirPath\UsbImage\Imaging.ps1" -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media"
Copy-Item -Path "$WorkingDirPath\UsbImage\Install.cmd" -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media"
Copy-Item -Path "$WorkingDirPath\UsbImage\startnet.cmd" -Destination "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media"
If ($MakeUSBMedia)
{
Write-Output "Insert USB drive 16GB+ in size, and press ENTER to view the drive selection menu" | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " Note that choosing a USB drive on the next screen WILL FORMAT THE DRIVE " | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
If (!($Automated)) { PAUSE }
Start-Sleep 5
# Find USB Drive that the image will be copied to.
$TempUSB = Select-USBDrive
Write-Output ""
If (!($TempUSB))
{
Write-Output "No USB key found, skipping..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
Else
{
$USB = Get-Disk | Where-Object {$_.Number -eq $TempUSB} | Get-Partition | Get-Volume
If ($USB)
{
$USBVolumeLabel = @($USB.FileSystemLabel)
}
$USBDiskName = Get-Disk |
Where-Object Number -eq $TempUSB |
ForEach-Object { "DISK:$("{0:D3}" -f $_.Number) ($("{0:G5} GB" -f ($_.Size /1GB))) $($_.FriendlyName)"}
$UserInput = Read-Host -Prompt "`n`nAre you sure you want to format: [$USBVolumeLabel] on ($USBDiskName) (Y/N)?"
If ( $UserInput -ne "y" )
{
Write-Output " -- Aborting Operation" | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
Else
{
$USBSize = $USB.Size /1GB
Get-Disk -Number $TempUSB | Clear-Disk -RemoveData -Confirm:$false
Initialize-Disk -Number $TempUSB -PartitionStyle MBR -ErrorAction SilentlyContinue
Write-Output ""
Write-Output ""
Write-Output "DEBUG: USB disk size:" | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "DEBUG: $USBSize" | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
Write-Output ""
If ($USBSize -ge "30")
{
$NewUSBDriveLetter = New-Partition -DiskNumber $TempUSB -Size 32GB -AssignDriveLetter | Format-Volume -FileSystem FAT32 -NewFileSystemLabel $Device
}
ElseIf ($USBSize -lt "14")
{
Write-Output "USB drive appears to be smaller than 16GB, skipping..." | Receive-Output -Color Yellow -LogLevel 2 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
}
Else
{
$NewUSBDriveLetter = New-Partition -DiskNumber $TempUSB -Size 14GB -AssignDriveLetter | Format-Volume -FileSystem FAT32 -NewFileSystemLabel $Device
}
$NewUSBDriveLetter = $NewUSBDriveLetter.DriveLetter + ":"
Write-Output "Copying WinPE Media contents to $NewUSBDriveLetter..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
& bootsect.exe /nt60 $NewUSBDriveLetter /force /mbr
& xcopy /herky "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media" $NewUSBDriveLetter
If ($SplitWIM -eq $True)
{
$SplitWIMs = Get-ChildItem -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture" -Filter *install*$Now*.swm -Recurse
ForEach ($TempWIM in $SplitWIMs)
{
$TempSplitWIM = $TempWIM.FullName
Write-Output "Copying $TempSplitWIM to $NewUSBDriveLetter..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path "$TempSplitWIM" -Destination "$NewUSBDriveLetter\Sources" -Force
}
}
Else
{
Write-Output "Copying $RefImage to $NewUSBDriveLetter..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path "$RefImage" -Destination "$NewUSBDriveLetter\Sources" -Recurse
}
}
}
}
If ($MakeISOMedia)
{
$oscdimg = "$WindowsKitsInstall\Deployment Tools\$Arch\Oscdimg\oscdimg.exe"
$efisys = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\fwfiles\efisys.bin"
$etfsboot = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\fwfiles\etfsboot.com"
$MediaSource = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp\Media"
$args = "-l$Device -bootdata:2#p0,e,b$etfsboot#pEF,e,b$efisys -m -u1 -udfver102 $MediaSource $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\$Device-$Build-$Now.iso"
If ($SplitWIM -eq $True)
{
$SplitWIMs = Get-ChildItem -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture" -Filter *install*$Now*.swm -Recurse
ForEach ($TempWIM in $SplitWIMs)
{
$TempSplitWIM = $TempWIM.FullName
Write-Output "Copying $TempSplitWIM to $MediaSource..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path "$TempSplitWIM" -Destination "$MediaSource\Sources" -Force
}
}
Else
{
## Copy the install.wim to Media destination folder.
If ($InstallWIM)
{
Write-Output "Copying $RefImage to $MediaSource..." | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Copy-Item -Path "$RefImage" -Destination "$MediaSource\Sources" -Recurse
}
}
Start-Process -FilePath $oscdimg -ArgumentList $args -NoNewWindow -Wait
}
}
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * Image modifications complete! *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Sleep 2
Set-Location -Path "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture"
Write-Output "Finalized image files can be found here:" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
If ($CreateISO)
{
If (Test-Path("$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\$Device-$Build-$Now.iso"))
{
Write-Output "ISO: $DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\$Device-$Build-$Now.iso" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
}
}
If ($SplitWIM -eq $True)
{
Write-Output "Install: $SplitImage" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
Else
{
Write-Output "Install: $RefImage" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
Write-Output "Boot: $RefBootImage" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
}
function ConvertArchitecture($arch) {
switch ($arch) {
"x64" { return "amd64" }
"arm64" { return "arm64" }
default { return "Unknown architecture" }
}
}
###########################
# Begin script processing #
###########################
Clear-Host
# Get current working directory
$Invocation = (Get-Variable MyInvocation).Value
$WorkingDirPath = Split-Path $Invocation.MyCommand.Path
If (!($DestinationFolder))
{
$DestinationFolder = $WorkingDirPath
}
# Get script start time (will be used to determine how long execution takes)
$Script_Start_Time = (Get-Date).ToShortDateString()+", "+(Get-Date).ToLongTimeString()
$Now = Get-Date -Format yyyy-MM-dd_HH-mm-ss
# Start logging
$SourcePath = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs"
$TempFolder = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp"
$LogFilePath = "$DestinationFolder\Logs"
$LogFileName = "Log--$OSSKU-$Architecture--$Now.log"
Start-Log -FilePath $LogFilePath -FileName $LogFileName
Write-Output "Script start: $Script_Start_Time" | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If ($Device)
{
# Read WinPEXML file
[string]$XmlPath = "$WorkingDirPath\WinPE_Drivers.xml"
[Xml]$WinPEXML = Get-Content $XmlPath
$SurfaceDevices = $WinPEXML.Surface.Devices
}
# Necessary variables not passed into script directly
$DISMFile = "$WindowsKitsInstall\Deployment Tools\amd64\DISM\dism.exe"
$ADKWinPEFile = "$WindowsKitsInstall\Windows Preinstallation Environment\amd64\en-us\winpe.wim"
$Mount = "$env:TEMP\Mount"
$ScratchMountFolder = "$Mount\Scratch"
if(Test-Path -Path $Mount)
{
try
{
If ((Get-ChildItem -Path $Mount -File -Force -Recurse | Select-Object -First 1 | Measure-Object).Count -gt 0)
{
Write-Output "Previous interrupted execution detected. $Mount must be empty for execution to continue." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
}
catch
{
Write-Output "Previous interrupted execution detected. $Mount must be empty for execution to continue." | Receive-Output -Color Red -BGColor Black -LogLevel 3 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
}
## This is hack to avoid any existing mount points
UnloadRegistryHive -MountPoint "HKLM\Mount"
# Leave blank space at top of window to not block output by progress bars
AddHeaderSpace
<#
# STEPS for setting up correct ADK tools
# 1. Get the OS version from install.wim from ISO.
# 2. Check whether $WindowsKitsInstall (passed by user) path is valid. If valid check whether the version of ADK matches with OS version of ISO.
If Yes Goto 6
# 3. Check whether any ADK installed or not by checking registry (HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots" -Name "KitsRoot10)
If Yes Goto 6
# 4. Uninstall the current ADK and WinPE, if exists
# 5. Download & Install the matching ADK and WinPE version.
# 6. Use ADK found or installed by one of above steps.
#>
# Check for admin rights and ADK install
PrereqCheck
# Identifiy the Windows Version in question by reading information from ISO.
$WindowsOSVersionFull = Get-WindowsOSVersionFromISO -ISO $ISO
Write-Output "OS Version from setup.exe: $global:FullOSVersionFromSetupEXE" | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$WindowsOSVersionFull = $global:FullOSVersionFromSetupEXE
$ADKRootRegistryKey = "HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots"
$ConvertedArch = ConvertArchitecture($Architecture).Trim()
$IsValidADKFound = $false
$WindowsOSVersionMajorMinorBuild = $WindowsOSVersionFull.Substring(0, $WindowsOSVersionFull.LastIndexOf('.'))
# Check whether user provided ADK path is valid or not.
Write-Output "Checking WindowsKitsInstall: $WindowsKitsInstall" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If (Test-Path -Path "$WindowsKitsInstall")
{
Write-Output "ADK Path [$WindowsKitsInstall] is valid, verify version" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
$DISMFile = "$WindowsKitsInstall\Deployment Tools\amd64\DISM\dism.exe"
$ADKWinPEFile = "$WindowsKitsInstall\Windows Preinstallation Environment\$ConvertedArch\en-us\winpe.wim"
if ((Test-Path -Path $DISMFile) -and (Test-Path -Path $ADKWinPEFile))
{
$global:InstalledWinPEVersion = (& $DISMFile /Get-WimInfo /WimFile:$ADKWinPEFile /index:1 | Select-String "Version ").ToString().Split(":")[1].Trim()
Write-Output "Check vesion: $WindowsOSVersionMajorMinorBuild and $global:InstalledWinPEVersion" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If ($WindowsOSVersionMajorMinorBuild -eq $global:InstalledWinPEVersion)
{
$IsValidADKFound = $true
$global:InstalledADKRoot = $WindowsKitsInstall
}
}
}
# If user provided the ADK path is not valid, then check registry entry to find whether ADK kit installed on machine or not.
If ($IsValidADKFound -eq $false)
{
Write-Output "Checking registry key: $ADKRootRegistryKey" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If (!(Test-Path -Path $ADKRootRegistryKey))
{
Write-Output "Not found: $ADKRootRegistryKey"
$ADKRootRegistryKey = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Roots"
Write-Output "Attempting check in Wow6432Node: $ADKRootRegistryKey"
}
If (Test-Path -Path $ADKRootRegistryKey)
{
$ADKRootPath = Get-ItemPropertyValue -Path $ADKRootRegistryKey -Name "KitsRoot10"
Write-Output "Found registry key: $ADKRootRegistryKey" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "ADK root from registry: $ADKRootPath" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If (Test-Path -Path $ADKRootPath)
{
$InstalledADKRootFromRegistry = "$ADKRootPath\Assessment and Deployment Kit"
$DISMFile = "$InstalledADKRootFromRegistry\Deployment Tools\amd64\DISM\dism.exe"
$ADKWinPEFile = "$InstalledADKRootFromRegistry\Windows Preinstallation Environment\$ConvertedArch\en-us\winpe.wim"
Write-Output "Checking for existence of ADK and WinPE" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
if ((Test-Path -Path $DISMFile) -and (Test-Path -Path $ADKWinPEFile))
{
$global:InstalledWinPEVersion = (& $DISMFile /Get-WimInfo /WimFile:$ADKWinPEFile /index:1 | Select-String "Version ").ToString().Split(":")[1].Trim()
Write-Output "Check vesion: $WindowsOSVersionMajorMinorBuild and $global:InstalledWinPEVersion" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If ($WindowsOSVersionMajorMinorBuild -eq $global:InstalledWinPEVersion)
{
$IsValidADKFound = $true
$global:InstalledADKRoot = $InstalledADKRootFromRegistry
}
}
}
}
}
# Uninstall existing ADK installations, if any.
If ($IsValidADKFound -eq $false)
{
Write-Output "No known valid ADK/WinPE installation found" | Receive-Output -Color Red -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
# Uninstall existing ADK and WinPE and install latest version of ADK and WinPE Addon based on OS version.
ConfigureADKTools -OSFullVersion $WindowsOSVersionFull
$IsValidADKFound = $true
}
# Above steps will ensures that either existing ADK or newly installed ADK is valid.
$WindowsKitsInstall = "$global:InstalledADKRoot"
Write-Output "Using ADK Root: $WindowsKitsInstall" | Receive-Output -Color Green -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
# Necessary variables not passed into script directly
$DISMFile = "$WindowsKitsInstall\Deployment Tools\amd64\DISM\dism.exe"
$ADKWinPEFile = "$WindowsKitsInstall\Windows Preinstallation Environment\$ConvertedArch\en-us\winpe.wim"
Write-Output "SDA version: $SDAVersion" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * Parameters passed to script: *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " * *" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " *********************************************" | Receive-Output -Color Cyan -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "ISO path: $ISO" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "OS SKU: $OSSKU" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Architecture: $Architecture" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Output: $DestinationFolder" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " .NET 3.5: $DotNet35" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " Servicing Stack: $ServicingStack" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " Cumulative Update: $CumulativeUpdate" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " Cumulative DotNet Updates: $CumulativeDotNetUpdate" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " Adobe Flash Player Updates: $AdobeFlashUpdate" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " Out-Of-Band Updates: $OOBUpdate" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " Office 365 install: $Office365" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " ADK installation Root: $WindowsKitsInstall" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
If ($Device)
{
Write-Output " Device drivers: $Device" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
If ($UseLocalDriverPath -eq $True)
{
Write-Output " Use Local driver path: $LocalDriverPath" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
Write-Output " Create USB key: $CreateUSB" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " Create ISO: $CreateISO" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " Automated: $Automated" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output " " | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Start-Sleep 2
If (!($Automated))
{
Write-Output "Verifty the parameters passed to script and hit Enter to continue or Press Ctrl + C to cancel" | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
PAUSE
}
# Pull Windows 10 version and SKU from ISO provided by script param, returns OSVersion and WinPEVersion variable as well
Get-OSWIMFromISO -ISO $ISO -OSSKU $OSSKU -DestinationFolder $DestinationFolder -Architecture $Architecture -WindowsKitsInstall $WindowsKitsInstall -ScratchMountFolder $ScratchMountFolder
Start-Sleep 2
Write-Output "Windows Version: $global:WindowsVersion" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "OSVersion: $global:OSVersion" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "ReleaseId: $global:ReleaseId" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
Start-Sleep 5
If ($global:OSVersion.Trim() -ne $global:WinPEVersion.Trim())
{
If (($global:WinPEVersion.Trim() -eq "10.0.19041") -and ($Windows10Versions -contains $global:OSVersion.Trim()))
{
Write-Output "WinPE ver: $global:WinPEVersion supports OS ver: $global:OSVersion, Hence proceed..." | Receive-Output -Color Yellow -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
}
Else
{
Write-Output "OSVersion: $global:OSVersion and WinPEVersion: $global:WinPEVersion are not matching" | Receive-Output -Color Red -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Exit
}
}
# Variables needed after Get-OSWIMFromISO finishes, passed to Update-Win10WIM
$SourcePath = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\SourceWIMs"
$TempFolder = "$DestinationFolder\$OSSKU\$global:WindowsVersion\$global:OSVersion\$Architecture\Temp"
$ImageMountFolder = "$Mount\OSImage"
$BootImageMountFolder = "$Mount\BootImage"
$WinREImageMountFolder = "$Mount\WinREImage"
If (Test-Path "$ImageMountFolder")
{
Write-Output "Deleting $ImageMountFolder\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$ImageMountFolder" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$ImageMountFolder" -Force
}
If (!(Test-Path -path $ImageMountFolder))
{
New-Item -path $ImageMountFolder -ItemType Directory | Out-Null
}
If (Test-Path "$BootImageMountFolder")
{
Write-Output "Deleting $BootImageMountFolder\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$BootImageMountFolder" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$BootImageMountFolder" -Force
}
If (!(Test-Path -path $BootImageMountFolder))
{
New-Item -path $BootImageMountFolder -ItemType Directory | Out-Null
}
If (Test-Path "$WinREImageMountFolder")
{
Write-Output "Deleting $WinREImageMountFolder\..." | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Get-ChildItem -Path "$WinREImageMountFolder" -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path "$WinREImageMountFolder" -Force
}
If (!(Test-Path -path $WinREImageMountFolder))
{
New-Item -path $WinREImageMountFolder -ItemType Directory | Out-Null
}
If ($BootWIM -eq $true)
{
$UpdateBootWIM = $True
}
Else
{
$UpdateBootWIM = $False
}
# If installing DotNet 3.5, the latest updates are also required - override any user parameters
If ($DotNet35 -eq $True)
{
$ServicingStack = $True
$CumulativeUpdate = $True
$CumulativeDotNetUpdate = $True
}
# Latest Servicing Stack is likely needed (if it exists) for the latest Cumulative Update to install successfully
If ($CumulativeUpdate -eq $True)
{
$ServicingStack = $True
}
# Download any components requested
If ($Device)
{
Get-LatestDrivers -TempFolder $TempFolder -Device $Device
}
If ($Office365 -eq $True)
{
Get-Office365 -TempFolder $TempFolder
}
# We always need the VC Runtimes for our devices
Get-LatestVCRuntimes -TempFolder $TempFolder
If ($ServicingStack -eq $True)
{
Get-ServicingStackUpdates -TempFolder $TempFolder
}
If (!($Automated)) { PAUSE }
If ($CumulativeUpdate -eq $True)
{
Get-CumulativeUpdates -TempFolder $TempFolder
}
If (!($Automated)) { PAUSE }
If ($DotNet35 -eq $True)
{
Get-CumulativeDotNetUpdates -TempFolder $TempFolder
}
If (!($Automated)) { PAUSE }
If ($AdobeFlashUpdate -eq $True)
{
Get-AdobeFlashUpdates -TempFolder $TempFolder
}
If (!($Automated)) { PAUSE }
If ($OOBUpdate -eq $True)
{
Get-OOBUpdates -TempFolder $TempFolder
}
If (!($Automated)) { PAUSE }
# Add Servicing Stack / Cumulative updates and necessary drivers to install.wim, winre.wim, and boot.wim
Update-Win10WIM -SourcePath $SourcePath -SourceName $OSSKU -ServicingStack $ServicingStack -CumulativeUpdate $CumulativeUpdate -DotNet35 $DotNet35 -CumulativeDotNetUpdate $CumulativeDotNetUpdate -AdobeFlashUpdate $AdobeFlashUpdate -ImageMountFolder $ImageMountFolder -BootImageMountFolder $BootImageMountFolder -WinREImageMountFolder $WinREImageMountFolder -TempFolder $TempFolder -WindowsKitsInstall $WindowsKitsInstall -UpdateBootWIM $UpdateBootWIM -MakeUSBMedia $CreateUSB -MakeISOMedia $CreateISO
# Determine ending time
$Script_End_Time = (Get-Date).ToShortDateString()+", "+(Get-Date).ToLongTimeString()
$Script_Time_Taken = New-TimeSpan -Start $Script_Start_Time -End $Script_End_Time
# How long did this take?
Write-Output "Script start: $Script_Start_Time" | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output "Script end: $Script_End_Time" | Receive-Output -Color Gray -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"
Write-Output ""
Write-Output "Execution time: $Script_Time_Taken seconds" | Receive-Output -Color White -LogLevel 1 -LineNumber "$($Invocation.MyCommand.Name):$( & {$MyInvocation.ScriptLineNumber})"