ElevenBuilder/Elevenbuilder.ps1
hax d0e336d0a2 Elevenbuilder.ps1 aktualisiert
Signed-off-by: hax <hax@lainlounge.xyz>
2025-02-09 03:50:37 +00:00

581 lines
26 KiB
PowerShell
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<#
.SYNOPSIS
Tiny11 Image Creator Full Workflow (ISO Download, Mount, Customization, and ISO Creation)
.DESCRIPTION
This script uses DISM and other tools to create a customized Windows 11 image.
It now supports obtaining the Windows 11 ISO from a user-supplied path or by autodownloading
(using massgrave.dev as the source) if no ISO is provided. The ISO is then mounted and assigned
a free drive letter. The script then copies installation files, processes install.wim and boot.wim,
applies registry tweaks and removals, and finally creates an ISO using oscdimg.exe.
.PARAMETER ScratchDisk
A drive letter (e.g. "D") or path where the working files will be stored.
.PARAMETER ISOPath
(Optional) Full path to a Windows 11 ISO file. If not provided, the script will prompt for
the desired language and autodownload the ISO.
.PARAMETER Language
(Optional) Desired language code for the ISO download (e.g. "en-US", "de-DE"). Only used if ISOPath is not provided.
.NOTES
- This script requires administrative privileges.
- It assumes that your original workflow (registry tweaks, application removals, etc.) must remain intact.
- Some API endpoints (for downloading the ISO) are hypothetical and may need adjustment.
#>
param (
[ValidatePattern('^[c-zC-Z]$')]
[string]$ScratchDisk,
[string]$ISOPath, # Full path to a Windows 11 ISO (optional)
[string]$Language # Desired language code (e.g., "en-US", "de-DE")
)
#region Helper Functions (ISO download and mount)
function Get-Win11DownloadLink {
<#
.SYNOPSIS
Queries the API (via massgrave.dev) for the proper ISO download link.
.PARAMETER Language
The desired language code.
.OUTPUTS
The download URL as a string.
#>
param(
[Parameter(Mandatory = $true)]
[string]$Language
)
# Adjust the endpoint and parameters as required.
$apiBase = "https://api.gravesoft.dev/msdl/"
$endpoint = "getDownloadLink" # Hypothetical endpoint.
$url = "$apiBase$endpoint?language=$Language"
Write-Host "Querying download link for Windows 11 ISO for language: $Language" -ForegroundColor Cyan
try {
$response = Invoke-RestMethod -Uri $url -Method Get
if ($response -and $response.downloadUrl) {
Write-Host "Download URL obtained: $($response.downloadUrl)" -ForegroundColor Green
return $response.downloadUrl
}
else {
Write-Error "API did not return a valid download URL."
return $null
}
}
catch {
Write-Error "Error calling the download API: $_"
return $null
}
}
function Get-Windows11ISO {
<#
.SYNOPSIS
Returns the path to a Windows 11 ISO. If a valid ISOPath is provided, that file is used.
Otherwise, prompts (or uses the provided language) and downloads the ISO.
.PARAMETER ISOPath
User-supplied ISO path.
.PARAMETER Language
Desired language code.
.OUTPUTS
The full path to the Windows 11 ISO.
#>
param(
[string]$ISOPath,
[string]$Language
)
# Use provided ISO if valid
if ($ISOPath -and (Test-Path $ISOPath -PathType Leaf)) {
Write-Host "Using provided ISO: $ISOPath" -ForegroundColor Green
return $ISOPath
}
# If no ISO path, prompt for language (if not provided)
if (-not $Language) {
$Language = Read-Host "Enter your desired Windows 11 language (e.g., en-US, de-DE)"
}
$downloadLink = Get-Win11DownloadLink -Language $Language
if (-not $downloadLink) {
Write-Error "Could not retrieve a valid download link. Exiting."
exit 1
}
$DownloadPath = "$env:TEMP\Windows11_$Language.iso"
Write-Host "Downloading Windows 11 ISO from $downloadLink ..." -ForegroundColor Cyan
try {
Invoke-WebRequest -Uri $downloadLink -OutFile $DownloadPath
Write-Host "Download complete: $DownloadPath" -ForegroundColor Green
return $DownloadPath
}
catch {
Write-Error "Failed to download the ISO: $_"
exit 1
}
}
function Mount-ISOAndAssignDriveLetter {
<#
.SYNOPSIS
Mounts an ISO image and assigns a free drive letter if none is already assigned.
.PARAMETER ISOPath
The full path to the ISO file.
.OUTPUTS
The drive letter (e.g., "E:") where the ISO is mounted.
#>
param(
[Parameter(Mandatory = $true)]
[string]$ISOPath
)
if (-not (Test-Path $ISOPath)) {
Write-Error "The ISO file '$ISOPath' does not exist."
return
}
Write-Host "Mounting ISO image: $ISOPath" -ForegroundColor Cyan
$mountedImage = Mount-DiskImage -ImagePath $ISOPath -PassThru
if (-not $mountedImage) {
Write-Error "Failed to mount ISO image."
return
}
Start-Sleep -Seconds 3 # Wait for the volume to become available
$diskImage = Get-DiskImage -ImagePath $ISOPath
if (-not $diskImage) {
Write-Error "Unable to retrieve disk image information."
return
}
$disk = $diskImage | Get-Disk
if (-not $disk) {
Write-Error "Unable to retrieve disk information for the mounted image."
return
}
$diskNumber = $disk.Number
# Retrieve the first partition (most ISOs contain a single partition)
$partition = Get-Partition -DiskNumber $diskNumber | Select-Object -First 1
if (-not $partition) {
Write-Error "No partition found on the mounted ISO."
return
}
# If no drive letter is assigned, choose a free one (from C: to Z:)
if (-not $partition.DriveLetter) {
$freeLetters = [char[]](67..90) | ForEach-Object { [char]$_ }
$usedLetters = (Get-Volume | Where-Object { $_.DriveLetter } | Select-Object -ExpandProperty DriveLetter)
$availableLetters = $freeLetters | Where-Object { $usedLetters -notcontains $_ }
if ($availableLetters.Count -eq 0) {
Write-Error "No free drive letters available."
return
}
$freeLetter = $availableLetters | Select-Object -First 1
Write-Host "Assigning drive letter '$freeLetter' to the mounted ISO." -ForegroundColor Yellow
Set-Partition -DiskNumber $diskNumber -PartitionNumber $partition.PartitionNumber -NewDriveLetter $freeLetter
$driveLetter = "$freeLetter`:"
}
else {
$driveLetter = "$($partition.DriveLetter):"
}
Write-Host "ISO mounted at drive letter: $driveLetter" -ForegroundColor Green
return $driveLetter
}
#endregion Helper Functions
#region Environment Setup
function Setup-Environment {
<#
.SYNOPSIS
Performs pre-flight checks, sets the scratch disk, adjusts execution policy,
ensures admin rights, starts logging, and creates necessary directories.
#>
# Set ScratchDisk (if not provided, use the script folder)
if (-not $ScratchDisk) {
$global:ScratchDisk = $PSScriptRoot.TrimEnd('\')
} else {
$global:ScratchDisk = "$ScratchDisk`:" # Append colon if needed.
}
Write-Output "Scratch disk set to $global:ScratchDisk"
# Check and adjust execution policy
if ((Get-ExecutionPolicy) -eq 'Restricted') {
Write-Host "Your current PowerShell Execution Policy is 'Restricted'. Changing it to 'RemoteSigned'..."
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Confirm:$false
}
# Ensure script is running as administrator
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
$principal = New-Object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent())
if (-not $principal.IsInRole($adminRole)) {
Write-Host "Restarting the script with elevated privileges..."
$arguments = "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`""
Start-Process powershell -Verb RunAs -ArgumentList $arguments
exit
}
# Start logging and set window title
Start-Transcript -Path "$global:ScratchDisk\tiny11.log"
$Host.UI.RawUI.WindowTitle = "Tiny11 Image Creator"
Clear-Host
Write-Host "Welcome to the Tiny11 Image Creator! Release: 05-06-24" -ForegroundColor Cyan
# Create required directories
$global:tiny11Folder = Join-Path $global:ScratchDisk "tiny11"
$global:sourcesFolder = Join-Path $global:tiny11Folder "sources"
$global:mountPath = Join-Path $global:ScratchDisk "scratchdir"
New-Item -ItemType Directory -Force -Path $global:sourcesFolder | Out-Null
New-Item -ItemType Directory -Force -Path $global:mountPath | Out-Null
}
#endregion Environment Setup
#region Obtain & Mount Installation Media
function Get-InstallationMedia {
<#
.SYNOPSIS
Obtains the Windows 11 ISO (using a provided path or by downloading it) and mounts it.
.OUTPUTS
The drive letter where the installation media is mounted.
#>
# Get the ISO (download if necessary)
$global:win11ISO = Get-Windows11ISO -ISOPath $ISOPath -Language $Language
if (-not $global:win11ISO) {
Write-Error "Failed to obtain a Windows 11 ISO. Exiting."
exit
}
# Mount the ISO and retrieve the drive letter
$mediaDrive = Mount-ISOAndAssignDriveLetter -ISOPath $global:win11ISO
if (-not $mediaDrive) {
Write-Error "Failed to mount the Windows 11 ISO. Exiting."
exit
}
Write-Output "Installation media mounted at: $mediaDrive"
return $mediaDrive
}
#endregion Obtain & Mount Installation Media
#region Process install.wim Image
function Process-InstallImage {
<#
.SYNOPSIS
Processes the Windows installation image (install.wim). This includes:
- Validating that the necessary installation files exist (or converting install.esd)
- Copying installation files from the installation media to the working folder
- Mounting the install.wim, gathering image information, applying customizations,
removing apps, tweaking registries, and finally unmounting the image.
#>
param(
[string]$DriveLetter # Installation media drive letter
)
# Validate Windows installation files
if ((Test-Path "$DriveLetter\sources\boot.wim") -eq $false -or (Test-Path "$DriveLetter\sources\install.wim") -eq $false) {
if (Test-Path "$DriveLetter\sources\install.esd") {
Write-Host "Found install.esd, converting to install.wim..."
Get-WindowsImage -ImagePath "$DriveLetter\sources\install.esd"
$index = Read-Host "Please enter the image index to convert from install.esd"
Write-Host "Converting install.esd to install.wim. This may take a while..."
Export-WindowsImage -SourceImagePath "$DriveLetter\sources\install.esd" `
-SourceIndex $index `
-DestinationImagePath "$global:ScratchDisk\tiny11\sources\install.wim" `
-CompressionType Maximum -CheckIntegrity
} else {
Write-Host "Cannot find Windows OS installation files on the installation media."
exit
}
}
else {
Write-Host "Copying Windows installation files from $DriveLetter..."
Copy-Item -Path "$DriveLetter\*" -Destination "$global:tiny11Folder" -Recurse -Force | Out-Null
# Remove install.esd if present
Set-ItemProperty -Path "$global:tiny11Folder\sources\install.esd" -Name IsReadOnly -Value $false -ErrorAction SilentlyContinue
Remove-Item "$global:tiny11Folder\sources\install.esd" -ErrorAction SilentlyContinue
Write-Host "Copy complete!"
}
Start-Sleep -Seconds 2
Clear-Host
Write-Host "Retrieving image information from install.wim..."
Get-WindowsImage -ImagePath (Join-Path $global:sourcesFolder "install.wim")
$index = Read-Host "Please enter the desired image index"
Write-Host "Mounting install.wim image. This may take a while..."
$global:wimFilePath = Join-Path $global:sourcesFolder "install.wim"
& takeown "/F" $global:wimFilePath
& icacls $global:wimFilePath "/grant" "$($adminGroup.Value):(F)"
try {
Set-ItemProperty -Path $global:wimFilePath -Name IsReadOnly -Value $false -ErrorAction Stop
} catch {
# Suppress errors
}
New-Item -ItemType Directory -Force -Path $global:mountPath > $null
Mount-WindowsImage -ImagePath $global:wimFilePath -Index $index -Path $global:mountPath
# Retrieve system UI language from the mounted image
$imageIntl = & dism /English /Get-Intl "/Image:$global:mountPath"
$languageLine = $imageIntl -split '\n' | Where-Object { $_ -match 'Default system UI language : ([a-zA-Z]{2}-[a-zA-Z]{2})' }
if ($languageLine) {
$languageCode = $Matches[1]
Write-Host "Default system UI language code: $languageCode"
} else {
Write-Host "Default system UI language code not found."
}
# Retrieve architecture information from the image
$imageInfo = & dism /English /Get-WimInfo "/wimFile:$global:sourcesFolder\install.wim" "/index:$index"
$lines = $imageInfo -split '\r?\n'
foreach ($line in $lines) {
if ($line -like '*Architecture : *') {
$architecture = $line -replace 'Architecture : ',''
if ($architecture -eq 'x64') {
$architecture = 'amd64'
}
Write-Host "Architecture: $architecture"
break
}
}
if (-not $architecture) {
Write-Host "Architecture information not found."
}
Write-Host "Install image mounted. Proceeding with application removals and customizations..."
# Remove unwanted applications (bloatware) via DISM
$packages = & dism /English "/image:$global:mountPath" '/Get-ProvisionedAppxPackages' |
ForEach-Object {
if ($_ -match 'PackageName : (.*)') {
$matches[1]
}
}
$packagePrefixes = 'Clipchamp.Clipchamp_', 'Microsoft.BingNews_', 'Microsoft.BingWeather_', 'Microsoft.GamingApp_', 'Microsoft.GetHelp_', 'Microsoft.Getstarted_', 'Microsoft.MicrosoftOfficeHub_', 'Microsoft.MicrosoftSolitaireCollection_', 'Microsoft.People_', 'Microsoft.PowerAutomateDesktop_', 'Microsoft.Todos_', 'Microsoft.WindowsAlarms_', 'microsoft.windowscommunicationsapps_', 'Microsoft.WindowsFeedbackHub_', 'Microsoft.WindowsMaps_', 'Microsoft.WindowsSoundRecorder_', 'Microsoft.Xbox.TCUI_', 'Microsoft.XboxGamingOverlay_', 'Microsoft.XboxGameOverlay_', 'Microsoft.XboxSpeechToTextOverlay_', 'Microsoft.YourPhone_', 'Microsoft.ZuneMusic_', 'Microsoft.ZuneVideo_', 'MicrosoftCorporationII.MicrosoftFamily_', 'MicrosoftCorporationII.QuickAssist_', 'MicrosoftTeams_', 'Microsoft.549981C3F5F10_'
$packagesToRemove = $packages | Where-Object {
$packageName = $_
$packagePrefixes -contains ($packagePrefixes | Where-Object { $packageName -like "$_*" })
}
foreach ($package in $packagesToRemove) {
& dism /English "/image:$global:mountPath" '/Remove-ProvisionedAppxPackage' "/PackageName:$package"
}
# Remove Microsoft Edge and its components
Write-Host "Removing Microsoft Edge..."
Remove-Item -Path "$global:mountPath\Program Files (x86)\Microsoft\Edge" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -Path "$global:mountPath\Program Files (x86)\Microsoft\EdgeUpdate" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -Path "$global:mountPath\Program Files (x86)\Microsoft\EdgeCore" -Recurse -Force -ErrorAction SilentlyContinue
if ($architecture -eq 'amd64') {
$folderPath = Get-ChildItem -Path "$global:mountPath\Windows\WinSxS" -Filter "amd64_microsoft-edge-webview_31bf3856ad364e35*" -Directory | Select-Object -ExpandProperty FullName
if ($folderPath) {
& takeown '/f' $folderPath '/r' | Out-Null
& icacls $folderPath "/grant" "$($adminGroup.Value):(F)" '/T' '/C' | Out-Null
Remove-Item -Path $folderPath -Recurse -Force | Out-Null
} else {
Write-Host "Edge WebView folder not found."
}
}
elseif ($architecture -eq 'arm64') {
$folderPath = Get-ChildItem -Path "$global:mountPath\Windows\WinSxS" -Filter "arm64_microsoft-edge-webview_31bf3856ad364e35*" -Directory | Select-Object -ExpandProperty FullName
if ($folderPath) {
& takeown '/f' $folderPath '/r' | Out-Null
& icacls $folderPath "/grant" "$($adminGroup.Value):(F)" '/T' '/C' | Out-Null
Remove-Item -Path $folderPath -Recurse -Force | Out-Null
} else {
Write-Host "Edge WebView folder not found."
}
}
& takeown '/f' "$global:mountPath\Windows\System32\Microsoft-Edge-Webview" '/r' | Out-Null
& icacls "$global:mountPath\Windows\System32\Microsoft-Edge-Webview" '/grant' "$($adminGroup.Value):(F)" '/T' '/C' | Out-Null
Remove-Item -Path "$global:mountPath\Windows\System32\Microsoft-Edge-Webview" -Recurse -Force | Out-Null
# Remove OneDrive
Write-Host "Removing OneDrive..."
& takeown '/f' "$global:mountPath\Windows\System32\OneDriveSetup.exe" | Out-Null
& icacls "$global:mountPath\Windows\System32\OneDriveSetup.exe" '/grant' "$($adminGroup.Value):(F)" '/T' '/C' | Out-Null
Remove-Item -Path "$global:mountPath\Windows\System32\OneDriveSetup.exe" -Force | Out-Null
Write-Host "Application removal complete!"
Start-Sleep -Seconds 2
Clear-Host
# Load registry hives from the mounted image and apply tweaks
Write-Host "Loading registry hives from the mounted image..."
reg load HKLM\zCOMPONENTS "$global:mountPath\Windows\System32\config\COMPONENTS" | Out-Null
reg load HKLM\zDEFAULT "$global:mountPath\Windows\System32\config\default" | Out-Null
reg load HKLM\zNTUSER "$global:mountPath\Users\Default\ntuser.dat" | Out-Null
reg load HKLM\zSOFTWARE "$global:mountPath\Windows\System32\config\SOFTWARE" | Out-Null
reg load HKLM\zSYSTEM "$global:mountPath\Windows\System32\config\SYSTEM" | Out-Null
Write-Host "Applying registry tweaks to bypass system requirements..."
& reg add 'HKLM\zDEFAULT\Control Panel\UnsupportedHardwareNotificationCache' '/v' 'SV1' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null
& reg add 'HKLM\zDEFAULT\Control Panel\UnsupportedHardwareNotificationCache' '/v' 'SV2' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null
& reg add 'HKLM\zNTUSER\Control Panel\UnsupportedHardwareNotificationCache' '/v' 'SV1' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null
& reg add 'HKLM\zNTUSER\Control Panel\UnsupportedHardwareNotificationCache' '/v' 'SV2' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\LabConfig' '/v' 'BypassCPUCheck' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\LabConfig' '/v' 'BypassRAMCheck' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\LabConfig' '/v' 'BypassSecureBootCheck' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\LabConfig' '/v' 'BypassStorageCheck' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\LabConfig' '/v' 'BypassTPMCheck' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\MoSetup' '/v' 'AllowUpgradesWithUnsupportedTPMOrCPU' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
Write-Host "Registry tweaks complete. Unloading registry hives..."
reg unload HKLM\zCOMPONENTS | Out-Null
reg unload HKLM\zDEFAULT | Out-Null
reg unload HKLM\zNTUSER | Out-Null
reg unload HKLM\zSOFTWARE | Out-Null
reg unload HKLM\zSYSTEM | Out-Null
Write-Host "Performing component cleanup on the image..."
Repair-WindowsImage -Path $global:mountPath -StartComponentCleanup -ResetBase
Write-Host "Unmounting install.wim image (saving changes)..."
Dismount-WindowsImage -Path $global:mountPath -Save
Clear-Host
Write-Host "Exporting updated install.wim..."
Export-WindowsImage -SourceImagePath (Join-Path $global:sourcesFolder "install.wim") -SourceIndex $index `
-DestinationImagePath (Join-Path $global:sourcesFolder "install2.wim") -CompressionType Fast
Remove-Item -Path (Join-Path $global:sourcesFolder "install.wim") -Force | Out-Null
Rename-Item -Path (Join-Path $global:sourcesFolder "install2.wim") -NewName "install.wim" -Force | Out-Null
Write-Host "Install image processing complete. Proceeding with boot.wim..."
}
#endregion Process install.wim Image
#region Process boot.wim Image
function Process-BootImage {
<#
.SYNOPSIS
Processes the boot image (boot.wim). This includes mounting the boot image,
applying necessary tweaks (if any), and then unmounting the image.
#>
Write-Host "Mounting boot.wim image..."
$global:wimFilePath = Join-Path $global:sourcesFolder "boot.wim"
& takeown "/F" $global:wimFilePath | Out-Null
& icacls $global:wimFilePath "/grant" "$($adminGroup.Value):(F)" | Out-Null
Set-ItemProperty -Path $global:wimFilePath -Name IsReadOnly -Value $false
Mount-WindowsImage -ImagePath $global:wimFilePath -Index 2 -Path $global:mountPath
Write-Host "Boot image mounted. Loading registry from boot image..."
reg load HKLM\zCOMPONENTS "$global:mountPath\Windows\System32\config\COMPONENTS" | Out-Null
reg load HKLM\zDEFAULT "$global:mountPath\Windows\System32\config\default" | Out-Null
reg load HKLM\zNTUSER "$global:mountPath\Users\Default\ntuser.dat" | Out-Null
reg load HKLM\zSOFTWARE "$global:mountPath\Windows\System32\config\SOFTWARE" | Out-Null
reg load HKLM\zSYSTEM "$global:mountPath\Windows\System32\config\SYSTEM" | Out-Null
Write-Host "Applying tweaks to boot image registry..."
& reg add 'HKLM\zDEFAULT\Control Panel\UnsupportedHardwareNotificationCache' '/v' 'SV1' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null
& reg add 'HKLM\zDEFAULT\Control Panel\UnsupportedHardwareNotificationCache' '/v' 'SV2' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null
& reg add 'HKLM\zNTUSER\Control Panel\UnsupportedHardwareNotificationCache' '/v' 'SV1' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null
& reg add 'HKLM\zNTUSER\Control Panel\UnsupportedHardwareNotificationCache' '/v' 'SV2' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\LabConfig' '/v' 'BypassCPUCheck' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\LabConfig' '/v' 'BypassRAMCheck' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\LabConfig' '/v' 'BypassSecureBootCheck' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\LabConfig' '/v' 'BypassStorageCheck' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\LabConfig' '/v' 'BypassTPMCheck' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
& reg add 'HKLM\zSYSTEM\Setup\MoSetup' '/v' 'AllowUpgradesWithUnsupportedTPMOrCPU' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null
Write-Host "Tweaks for boot image applied. Unloading registry hives..."
reg unload HKLM\zCOMPONENTS | Out-Null
reg unload HKLM\zDEFAULT | Out-Null
reg unload HKLM\zNTUSER | Out-Null
reg unload HKLM\zSOFTWARE | Out-Null
reg unload HKLM\zSYSTEM | Out-Null
Write-Host "Unmounting boot image (saving changes)..."
Dismount-WindowsImage -Path $global:mountPath -Save
}
#endregion Process boot.wim Image
#region Finalize ISO Creation
function Finalize-ISO {
<#
.SYNOPSIS
Uses oscdimg.exe to create the final Tiny11 ISO from the customized image.
#>
Write-Host "Copying unattended file for bypassing MS account on OOBE..."
Copy-Item -Path "$PSScriptRoot\autounattend.xml" -Destination "$global:tiny11Folder\autounattend.xml" -Force | Out-Null
Write-Host "Creating final ISO image..."
$ADKDepTools = "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\$hostarchitecture\Oscdimg"
$localOSCDIMGPath = "$PSScriptRoot\oscdimg.exe"
if ([System.IO.Directory]::Exists($ADKDepTools)) {
Write-Host "Using oscdimg.exe from the system ADK."
$OSCDIMG = Join-Path $ADKDepTools "oscdimg.exe"
} else {
Write-Host "ADK folder not found. Using bundled oscdimg.exe."
if (-not (Test-Path -Path $localOSCDIMGPath)) {
Write-Host "Downloading oscdimg.exe..."
$url = "https://msdl.microsoft.com/download/symbols/oscdimg.exe/3D44737265000/oscdimg.exe"
Invoke-WebRequest -Uri $url -OutFile $localOSCDIMGPath
if (-not (Test-Path $localOSCDIMGPath)) {
Write-Error "Failed to download oscdimg.exe."
exit 1
}
} else {
Write-Host "oscdimg.exe already exists locally."
}
$OSCDIMG = $localOSCDIMGPath
}
# Define boot data (adjust paths if necessary)
$bootData = "2#p0,e,b$global:tiny11Folder\boot\etfsboot.com#pEF,e,b$global:tiny11Folder\efi\microsoft\boot\efisys.bin"
$isoOutput = Join-Path $PSScriptRoot "tiny11.iso"
& "$OSCDIMG" '-m' '-o' '-u2' '-udfver102' "-bootdata:$bootData" "$global:tiny11Folder" "$isoOutput"
Write-Host "ISO creation complete: $isoOutput" -ForegroundColor Green
}
#endregion Finalize ISO Creation
#region Cleanup
function Cleanup-Environment {
<#
.SYNOPSIS
Cleans up temporary folders used during image processing.
#>
Write-Host "Performing cleanup..."
Remove-Item -Path "$global:tiny11Folder" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -Path "$global:mountPath" -Recurse -Force -ErrorAction SilentlyContinue
Stop-Transcript
Write-Host "Cleanup complete."
}
#endregion Cleanup
#region Main Flow
function Main {
# Step 1: Setup environment (parameters, admin check, logging, directories)
Setup-Environment
# Step 2: Obtain and mount installation media (ISO)
$mediaDrive = Get-InstallationMedia
# Step 3: Process the install.wim image (copy files, convert ESD if needed, apply tweaks)
Process-InstallImage -DriveLetter $mediaDrive
# Step 4: Process the boot.wim image
Process-BootImage
# Step 5: Finalize ISO creation using oscdimg.exe
Finalize-ISO
# Step 6: Cleanup temporary folders and stop logging
Cleanup-Environment
Write-Host "Tiny11 image creation completed. Press Enter to exit."
Read-Host
exit
}
# Start the main flow
Main
#endregion Main Flow