<# .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 auto‑downloading (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 auto‑download 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