diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..8d2f77bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.log +scratchdir/ +tiny11/ +oscdimg.exe diff --git a/README.md b/README.md index 72ed8787..60b246fc 100644 --- a/README.md +++ b/README.md @@ -23,24 +23,34 @@ This script generates a significantly reduced Windows 11 image. However, **it's ## Instructions: 1. Download Windows 11 from the [Microsoft website](https://www.microsoft.com/software-download/windows11) or [Rufus](https://github.com/pbatard/rufus) -2. Mount the downloaded ISO image using Windows Explorer. +2. Mount the downloaded ISO image using Windows Explorer, **or** provide the `.iso` file path when prompted — the script can mount it automatically. 3. Open **PowerShell 5.1** as Administrator. -5. Change the script execution policy : +4. Change the script execution policy : ```powershell Set-ExecutionPolicy Bypass -Scope Process ``` > Using `-Scope Process` you keep your original policy intact as this change only lasts for the current PowerShell session. -6. Start the script : +5. Start the script : ```powershell -C:/path/to/your/tiny11/script.ps1 -ISO -SCRATCH -``` -> You can see of the script by running the `get-help` command. +C:/path/to/your/tiny11/script.ps1 -ISO -SCRATCH +``` +> `inputletter` is the drive that the ISO is mounted to, `outputletter` is the drive where the temporary files will be written to. +> Only put the actual letter of the drive, do not put a colon (:) after. +> Example: `C:\Users\TomBob\Downloads\tiny11maker.ps1 -ISO E -SCRATCH D` +> The finished image will be written to the same folder that the script is executed from. +> +> You can see the help of the script by running the `get-help` command. + +**Custom app selection** — add `-Custom` to choose which apps to remove interactively: +```powershell +C:/path/to/your/tiny11/script.ps1 -ISO E -SCRATCH D -Custom +``` +Without `-Custom`, all packages listed in `removePackage.txt` are removed (default). -6. Select the drive letter where the image is mounted (only the letter, no colon (:)) -7. Select the SKU that you want the image to be based. -8. Sit back and relax :) -9. When the image is completed, you will see it in the folder where the script was extracted, with the name tiny11.iso +6. Select the SKU that you want the image to be based. +7. Sit back and relax :) +8. When the image is completed, you will see it in the folder where the script was extracted, with the name tiny11.iso --- @@ -77,6 +87,7 @@ C:/path/to/your/tiny11/script.ps1 -ISO -SCRATCH
  • Tablet PC Math
  • Edge
  • OneDrive
  • +
  • Search Highlights
  • @@ -97,6 +108,25 @@ You will be asked during image creation if you want to enable .net 3.5 support! --- +## Customizing what gets removed +Edit `removePackage.txt` to change the default removal list, or run with `-Custom` for an interactive selector before the build starts. + +## Adding back a feature +If you want to add or remove something after installing the tiny11maker iso (not possible with core), +use the following in a admin powershell terminal. +```powershell +DISM /Online /Get-Capabilities | findstr /i Hello +``` +You can of course replace the word "Hello" with the feature you are looking for. +This will return something like `Capability Identity : Hello.Face.20134~~~~0.0.1.0`. +Copy the ending and run the following. +```powershell +DISM /Online /Add-Capability /CapabilityName:Hello.Face.20134~~~~0.0.1.0 +``` +You should replace the Capability with the one returned of course. + +--- + ## Known issues: - Although Edge is removed, there are some remnants in the Settings, but the app in itself is deleted. - You might have to update Winget before being able to install any apps, using Microsoft Store. @@ -109,7 +139,7 @@ You will be asked during image creation if you want to enable .net 3.5 support! - ~~disabling telemetry~~ (Implemented in the 04-29-24 release!) - ~~more ad suppression~~ (Partially implemented in the 09-06-25 release!) - improved language and arch detection -- more flexibility in what to keep and what to delete +- ~~more flexibility in what to keep and what to delete~~ (Use `-Custom` or edit `removePackage.txt`) - maybe a GUI??? And that's pretty much it for now! diff --git a/autounattend.xml b/autounattend.xml index 08897e97..adae8478 100644 --- a/autounattend.xml +++ b/autounattend.xml @@ -3,6 +3,11 @@ + true + true + Home + 3 + true true @@ -28,8 +33,10 @@ + true - + + Never diff --git a/removePackage.txt b/removePackage.txt new file mode 100644 index 00000000..5ea38138 --- /dev/null +++ b/removePackage.txt @@ -0,0 +1,55 @@ +AppUp.IntelManagementandSecurityStatus +Clipchamp.Clipchamp +DolbyLaboratories.DolbyAccess +DolbyLaboratories.DolbyDigitalPlusDecoderOEM +Microsoft.549981C3F5F10 +Microsoft.BingNews +Microsoft.BingSearch +Microsoft.BingWeather +Microsoft.Copilot +Microsoft.Edge.GameAssist +Microsoft.GamingApp +Microsoft.GetHelp +Microsoft.Getstarted +Microsoft.Microsoft3DViewer +Microsoft.MicrosoftEdge.Stable_8wekyb3d8bbwe!App +Microsoft.MicrosoftOfficeHub +Microsoft.MicrosoftSolitaireCollection +Microsoft.MicrosoftStickyNotes +Microsoft.MixedReality.Portal +Microsoft.MSPaint +Microsoft.Office.OneNote +Microsoft.OfficePushNotificationUtility +Microsoft.OutlookForWindows +Microsoft.Paint +Microsoft.People +Microsoft.PowerAutomateDesktop +Microsoft.SkypeApp +Microsoft.StartExperiencesApp +Microsoft.Todos +Microsoft.Wallet +Microsoft.Windows.Copilot +Microsoft.Windows.CrossDevice +Microsoft.Windows.DevHome +Microsoft.Windows.Teams +Microsoft.WindowsAlarms +Microsoft.WindowsCamera +microsoft.windowscommunicationsapps +Microsoft.WindowsFeedbackHub +Microsoft.WindowsMaps +Microsoft.WindowsNotepad +Microsoft.WindowsSoundRecorder +Microsoft.WindowsTerminal +Microsoft.Xbox.TCUI +Microsoft.XboxApp +Microsoft.XboxGameOverlay +Microsoft.XboxGamingOverlay +Microsoft.XboxIdentityProvider +Microsoft.XboxSpeechToTextOverlay +Microsoft.YourPhone +Microsoft.ZuneMusic +Microsoft.ZuneVideo +MicrosoftCorporationII.MicrosoftFamily +MicrosoftCorporationII.QuickAssist +MicrosoftTeams +MSTeams diff --git a/tiny11Coremaker.ps1 b/tiny11Coremaker.ps1 index 439768ab..1d2d6646 100644 --- a/tiny11Coremaker.ps1 +++ b/tiny11Coremaker.ps1 @@ -19,7 +19,7 @@ if (! $myWindowsPrincipal.IsInRole($adminRole)) { Write-Host "Restarting Tiny11 image creator as admin in a new window, you can close this one." $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"; - $newProcess.Arguments = $myInvocation.MyCommand.Definition; + $newProcess.Arguments = "-NoProfile -ExecutionPolicy Bypass -NoExit -File `"$($myInvocation.MyCommand.Definition)`""; $newProcess.Verb = "runas"; [System.Diagnostics.Process]::Start($newProcess); exit @@ -332,11 +332,11 @@ Rename-Item -Path $mainOSDrive\scratchdir\Windows\WinSxS_edit -NewName $mainOSDr Write-Host "Complete!" Write-Host "Loading registry..." -reg load HKLM\zCOMPONENTS $ScratchDisk\scratchdir\Windows\System32\config\COMPONENTS | Out-Null -reg load HKLM\zDEFAULT $ScratchDisk\scratchdir\Windows\System32\config\default | Out-Null -reg load HKLM\zNTUSER $ScratchDisk\scratchdir\Users\Default\ntuser.dat | Out-Null -reg load HKLM\zSOFTWARE $ScratchDisk\scratchdir\Windows\System32\config\SOFTWARE | Out-Null -reg load HKLM\zSYSTEM $ScratchDisk\scratchdir\Windows\System32\config\SYSTEM | Out-Null +reg load HKLM\zCOMPONENTS $mainOSDrive\scratchdir\Windows\System32\config\COMPONENTS | Out-Null +reg load HKLM\zDEFAULT $mainOSDrive\scratchdir\Windows\System32\config\default | Out-Null +reg load HKLM\zNTUSER $mainOSDrive\scratchdir\Users\Default\ntuser.dat | Out-Null +reg load HKLM\zSOFTWARE $mainOSDrive\scratchdir\Windows\System32\config\SOFTWARE | Out-Null +reg load HKLM\zSYSTEM $mainOSDrive\scratchdir\Windows\System32\config\SYSTEM | Out-Null Write-Host "Bypassing system requirements(on the system image):" & '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 @@ -380,7 +380,7 @@ Write-Host "Disabling Sponsored Apps:" & 'reg' 'add' 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\CloudContent' '/v' 'DisableCloudOptimizedContent' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null Write-Host "Enabling Local Accounts on OOBE:" & 'reg' 'add' 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\OOBE' '/v' 'BypassNRO' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null -Copy-Item -Path "$PSScriptRoot\autounattend.xml" -Destination "$ScratchDisk\scratchdir\Windows\System32\Sysprep\autounattend.xml" -Force | Out-Null +Copy-Item -Path "$PSScriptRoot\autounattend.xml" -Destination "$mainOSDrive\scratchdir\Windows\System32\Sysprep\autounattend.xml" -Force | Out-Null Write-Host "Disabling Reserved Storage:" & 'reg' 'add' 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\ReserveManager' '/v' 'ShippedWithReserves' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null Write-Host "Disabling BitLocker Device Encryption" @@ -393,6 +393,8 @@ reg delete "HKEY_LOCAL_MACHINE\zSOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVe reg delete "HKEY_LOCAL_MACHINE\zSOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge Update" /f | Out-Null Write-Host "Disabling OneDrive folder backup" & 'reg' 'add' "HKLM\zSOFTWARE\Policies\Microsoft\Windows\OneDrive" '/v' 'DisableFileSyncNGSC' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null +Write-Output "Disabling Search Highlights:" +& 'reg' 'add' "HKLM\zSoftware\Microsoft\Windows\CurrentVersion\SearchSettings" '/v' 'IsDynamicSearchBoxEnabled' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null Write-Host "Disabling Telemetry:" & 'reg' 'add' 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\AdvertisingInfo' '/v' 'Enabled' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null & 'reg' 'add' 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\Privacy' '/v' 'TailoredExperiencesWithDiagnosticDataEnabled' '/t' 'REG_DWORD' '/d' '0' '/f' | Out-Null @@ -417,7 +419,7 @@ Write-Host "Prevents installation of Teams:" & 'reg' 'add' 'HKLM\zSOFTWARE\Policies\Microsoft\Teams' '/v' 'DisableInstallation' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null Write-Host "Prevent installation of New Outlook": & 'reg' 'add' 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Windows Mail' '/v' 'PreventRun' '/t' 'REG_DWORD' '/d' '1' '/f' | Out-Null -$tasksPath = "C:\scratchdir\Windows\System32\Tasks" +$tasksPath = "$mainOSDrive\scratchdir\Windows\System32\Tasks" Write-Host "Deleting scheduled task definition files..." @@ -556,7 +558,7 @@ if ([System.IO.Directory]::Exists($ADKDepTools)) { $OSCDIMG = $localOSCDIMGPath } -& "$OSCDIMG" '-m' '-o' '-u2' '-udfver102' "-bootdata:2#p0,e,b$ScratchDisk\tiny11\boot\etfsboot.com#pEF,e,b$ScratchDisk\tiny11\efi\microsoft\boot\efisys.bin" "$ScratchDisk\tiny11" "$PSScriptRoot\tiny11.iso" +& "$OSCDIMG" '-m' '-o' '-u2' '-udfver102' "-bootdata:2#p0,e,b$mainOSDrive\tiny11\boot\etfsboot.com#pEF,e,b$mainOSDrive\tiny11\efi\microsoft\boot\efisys.bin" "$mainOSDrive\tiny11" "$PSScriptRoot\tiny11.iso" # Finishing up Write-Host "Creation completed! Press any key to exit the script..." diff --git a/tiny11maker.ps1 b/tiny11maker.ps1 index c24757f2..645ab293 100644 --- a/tiny11maker.ps1 +++ b/tiny11maker.ps1 @@ -8,31 +8,42 @@ The only executable included is oscdimg.exe, which is provided in the Windows ADK and it is used to create bootable ISO images. .PARAMETER ISO - Drive letter given to the mounted iso (eg: E) + Drive letter of the mounted Windows 11 ISO (e.g. E), or omit to be prompted for an ISO path or drive letter. .PARAMETER SCRATCH Drive letter of the desired scratch disk (eg: D) + NOTE: The SCRATCH drive must support file/folder security (i.e., must be, e.g., NTFS filesystem). + +.PARAMETER Custom + Enable interactive selection of which apps to remove. Without this flag, all packages listed in + removePackage.txt are removed (default behaviour). .EXAMPLE + .\tiny11maker.ps1 .\tiny11maker.ps1 E D .\tiny11maker.ps1 -ISO E -SCRATCH D .\tiny11maker.ps1 -SCRATCH D -ISO E - .\tiny11maker.ps1 + .\tiny11maker.ps1 -ISO E -SCRATCH D -Custom - *If you ordinal parameters the first one must be the mounted iso. The second is the scratch drive. + *If you use ordinal parameters the first one must be the mounted iso. The second is the scratch drive. prefer the use of full named parameter (eg: "-ISO") as you can put in the order you want. .NOTES Auteur: ntdevlabs - Date: 09-07-25 + Date: 11-06-26 #> #---------[ Parameters ]---------# param ( - [ValidatePattern('^[c-zC-Z]$')][string]$ISO, - [ValidatePattern('^[c-zC-Z]$')][string]$SCRATCH + [string]$ISO, + [ValidatePattern('^[c-zC-Z]$')][string]$SCRATCH, + [switch]$Custom ) +$ErrorActionPreference = 'Stop' +$WarningPreference = 'Continue' +$InformationPreference = 'Continue' + if (-not $SCRATCH) { $ScratchDisk = $PSScriptRoot -replace '[\\]+$', '' } else { @@ -57,18 +68,205 @@ function Set-RegistryValue { function Remove-RegistryValue { param ( - [string]$path - ) - try { - & 'reg' 'delete' $path '/f' | Out-Null - Write-Output "Removed registry value: $path" - } catch { - Write-Output "Error removing registry value: $_" - } + [string]$path + ) + try { + & 'reg' 'delete' $path '/f' | Out-Null + Write-Output "Removed registry value: $path" + } catch { + Write-Output "Error removing registry value: $_" + } +} + +function Show-PackageSelector { + param( + [string[]]$Items + ) + + $selected = @{} + for ($i = 0; $i -lt $Items.Count; $i++) { + $selected[$i] = $true + } + + while ($true) { + Clear-Host + Write-Host "Select packages to REMOVE from the image:" -ForegroundColor Cyan + Write-Host "Toggle items by entering numbers separated by commas. Commands: all, none" -ForegroundColor DarkGray + Write-Host "Tip: use ranges like 1-5 or combinations like 1,3,7-9" -ForegroundColor DarkGray + Write-Host "use: q / quit / exit to abort - use: 'done' if the selection is ready to proceed" -ForegroundColor DarkGreen + Write-Host "" + + for ($i = 0; $i -lt $Items.Count; $i++) { + $mark = if ($selected[$i]) { '[X]' } else { '[ ]' } + $num = ($i + 1).ToString().PadLeft(3) + Write-Host "$num $mark $($Items[$i])" + } + + Write-Host "" + $selectionInput = Read-Host "Enter selection" + if (-not $selectionInput) { continue } + + $selectionInput = $selectionInput.Trim() + $lower = $selectionInput.ToLowerInvariant() + if ($lower -in @('q', 'quit', 'exit')) { + Write-Host "Exiting selection and keeping current choices." -ForegroundColor Yellow + break + } + if ($lower -eq 'done') { break } + if ($lower -eq 'all') { + for ($i = 0; $i -lt $Items.Count; $i++) { $selected[$i] = $true } + continue + } + if ($lower -eq 'none') { + for ($i = 0; $i -lt $Items.Count; $i++) { $selected[$i] = $false } + continue + } + + $tokens = $selectionInput -split '[, ]+' | Where-Object { $_ -ne '' } + foreach ($t in $tokens) { + if ($t -match '^\d+$') { + $idx = [int]$t - 1 + if ($idx -ge 0 -and $idx -lt $Items.Count) { + $selected[$idx] = -not $selected[$idx] + } else { + Write-Host "Number out of range: $t" -ForegroundColor DarkYellow + Start-Sleep -Seconds 1 + } + } elseif ($t -match '^(\d+)-(\d+)$') { + $start = [int]$Matches[1] - 1 + $end = [int]$Matches[2] - 1 + if ($start -lt 0) { $start = 0 } + if ($end -ge $Items.Count) { $end = $Items.Count - 1 } + if ($start -le $end) { + for ($j = $start; $j -le $end; $j++) { + $selected[$j] = -not $selected[$j] + } + } else { + Write-Host "Invalid range: $t" -ForegroundColor DarkYellow + Start-Sleep -Seconds 1 + } + } else { + Write-Host "Ignored token: $t" -ForegroundColor DarkYellow + Start-Sleep -Seconds 1 + } + } + } + + $result = for ($i = 0; $i -lt $Items.Count; $i++) { + if ($selected[$i]) { $Items[$i] } + } + return ,$result +} + +function Test-PrefixSelected { + param( + [string[]]$SelectedPrefixes, + [string]$Prefix + ) + return $SelectedPrefixes -contains $Prefix +} + +function Test-Prerequisites { + Write-Output "Checking prerequisites..." + + if (-not (Get-Command 'dism.exe' -ErrorAction SilentlyContinue)) { + Write-Error "DISM was not found. Install the Windows Assessment and Deployment Kit (ADK) or run on a Windows edition that includes deployment tools." + exit 1 + } + + foreach ($cmd in @('Mount-WindowsImage', 'Dismount-WindowsImage', 'Get-WindowsImage', 'Export-WindowsImage')) { + if (-not (Get-Command $cmd -ErrorAction SilentlyContinue)) { + Write-Error "Required cmdlet '$cmd' was not found. Install the DISM PowerShell module (usually included with ADK)." + exit 1 + } + } + + if (-not (Test-Path "$PSScriptRoot\removePackage.txt")) { + Write-Error "removePackage.txt was not found in $PSScriptRoot" + exit 1 + } + + Write-Output "Prerequisites OK." +} + +function Initialize-Oscdimg { + param([string]$HostArchitecture) + + Write-Output "Checking for prerequisite oscdimg.exe..." + $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-Output "Will be using oscdimg.exe from system ADK." + return "$ADKDepTools\oscdimg.exe" + } + + Write-Output "ADK folder not found. Creating/using local copy of oscdimg.exe." + $url = "https://msdl.microsoft.com/download/symbols/oscdimg.exe/3D44737265000/oscdimg.exe" + + if (-not (Test-Path -Path $localOSCDIMGPath)) { + Write-Output "Downloading oscdimg.exe..." + Invoke-WebRequest -Uri $url -OutFile $localOSCDIMGPath + + if (-not (Test-Path $localOSCDIMGPath)) { + Write-Error "Failed to download oscdimg.exe." + exit 1 + } + Write-Output "oscdimg.exe downloaded successfully." + } else { + Write-Output "oscdimg.exe already exists locally." + } + + return $localOSCDIMGPath +} + +function Resolve-WindowsSource { + param([string]$IsoParameter) + + $script:ImagePath = $null + $script:MountedByScript = $false + $driveLetter = $null + + if ($IsoParameter) { + if ($IsoParameter -match '^[c-zC-Z]$') { + $driveLetter = $IsoParameter + ":" + Write-Output "Using mounted drive $driveLetter" + return $driveLetter + } + if ((Test-Path $IsoParameter -PathType Leaf) -and ($IsoParameter -match '\.iso$')) { + $script:ImagePath = $IsoParameter + $vol = Mount-DiskImage -ImagePath $script:ImagePath -Access ReadOnly -PassThru | Get-Volume + $driveLetter = $vol.DriveLetter + ":" + $script:MountedByScript = $true + Write-Output "Mounted $($script:ImagePath) at $driveLetter" + return $driveLetter + } + Write-Error "Invalid -ISO value. Provide a drive letter (e.g. E) or a path to a .iso file." + exit 1 + } + + do { + $input = Read-Host "Enter Windows 11 ISO path or mounted drive letter" + $input = $input.Trim() -replace '"', '' + if ($input -match '^[c-zC-Z]$') { + $driveLetter = $input + ":" + Write-Output "Using mounted drive $driveLetter" + } elseif ((Test-Path $input -PathType Leaf) -and ($input -match '\.iso$')) { + $script:ImagePath = $input + $vol = Mount-DiskImage -ImagePath $script:ImagePath -Access ReadOnly -PassThru | Get-Volume + $driveLetter = $vol.DriveLetter + ":" + $script:MountedByScript = $true + Write-Output "Mounted $($script:ImagePath) at $driveLetter" + } else { + Write-Output "Invalid input. Provide a drive letter (e.g. E) or a path to a .iso file." + $driveLetter = $null + } + } while (-not $driveLetter) + + return $driveLetter } #---------[ Execution ]---------# -# Check if PowerShell execution is restricted if ((Get-ExecutionPolicy) -eq 'Restricted') { Write-Output "Your current PowerShell Execution Policy is set to Restricted, which prevents scripts from running. Do you want to change it to RemoteSigned? (yes/no)" $response = Read-Host @@ -80,19 +278,23 @@ if ((Get-ExecutionPolicy) -eq 'Restricted') { } } -# Check and run the script as admin if required $adminSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544") $adminGroup = $adminSID.Translate([System.Security.Principal.NTAccount]) -$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent() -$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID) -$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator -if (! $myWindowsPrincipal.IsInRole($adminRole)) -{ +$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent() +$myWindowsPrincipal = new-object System.Security.Principal.WindowsPrincipal($myWindowsID) +$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator +if (! $myWindowsPrincipal.IsInRole($adminRole)) { Write-Output "Restarting Tiny11 image creator as admin in a new window, you can close this one." - $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"; - $newProcess.Arguments = $myInvocation.MyCommand.Definition; - $newProcess.Verb = "runas"; - [System.Diagnostics.Process]::Start($newProcess); + $argList = @( + '-NoProfile', '-ExecutionPolicy', 'Bypass', '-NoExit', '-File', "`"$PSCommandPath`"" + ) + if ($ISO) { $argList += @('-ISO', $ISO) } + if ($SCRATCH) { $argList += @('-SCRATCH', $SCRATCH) } + if ($Custom) { $argList += '-Custom' } + $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell" + $newProcess.Arguments = $argList -join ' ' + $newProcess.Verb = "runas" + [System.Diagnostics.Process]::Start($newProcess) | Out-Null exit } @@ -100,28 +302,18 @@ if (-not (Test-Path -Path "$PSScriptRoot/autounattend.xml")) { Invoke-RestMethod "https://raw.githubusercontent.com/ntdevlabs/tiny11builder/refs/heads/main/autounattend.xml" -OutFile "$PSScriptRoot/autounattend.xml" } -# Start the transcript and prepare the window Start-Transcript -Path "$PSScriptRoot\tiny11_$(get-date -f yyyyMMdd_HHmms).log" $Host.UI.RawUI.WindowTitle = "Tiny11 image creator" Clear-Host -Write-Output "Welcome to the tiny11 image creator! Release: 09-07-25" +Write-Output "Welcome to the tiny11 image creator! Release: 11-06-26" $hostArchitecture = $Env:PROCESSOR_ARCHITECTURE +Test-Prerequisites +$OSCDIMG = Initialize-Oscdimg -HostArchitecture $hostArchitecture + New-Item -ItemType Directory -Force -Path "$ScratchDisk\tiny11\sources" | Out-Null -do { - if (-not $ISO) { - $DriveLetter = Read-Host "Please enter the drive letter for the Windows 11 image" - } else { - $DriveLetter = $ISO - } - if ($DriveLetter -match '^[c-zC-Z]$') { - $DriveLetter = $DriveLetter + ":" - Write-Output "Drive letter set to $DriveLetter" - } else { - Write-Output "Invalid drive letter. Please enter a letter between C and Z." - } -} while ($DriveLetter -notmatch '^[c-zC-Z]:$') +$DriveLetter = Resolve-WindowsSource -IsoParameter $ISO 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") -eq $true) { @@ -132,16 +324,20 @@ if ((Test-Path "$DriveLetter\sources\boot.wim") -eq $false -or (Test-Path "$Driv Write-Output 'Converting install.esd to install.wim. This may take a while...' Export-WindowsImage -SourceImagePath $DriveLetter\sources\install.esd -SourceIndex $index -DestinationImagePath $ScratchDisk\tiny11\sources\install.wim -Compressiontype Maximum -CheckIntegrity } else { - Write-Output "Can't find Windows OS Installation files in the specified Drive Letter.." - Write-Output "Please enter the correct DVD Drive Letter.." - exit + Write-Output "Can't find Windows OS Installation files in the specified source." + Write-Output "Please provide a valid Windows 11 ISO or mounted drive." + exit 1 } } Write-Output "Copying Windows image..." Copy-Item -Path "$DriveLetter\*" -Destination "$ScratchDisk\tiny11" -Recurse -Force | Out-Null -Set-ItemProperty -Path "$ScratchDisk\tiny11\sources\install.esd" -Name IsReadOnly -Value $false > $null 2>&1 -Remove-Item "$ScratchDisk\tiny11\sources\install.esd" > $null 2>&1 +if ($MountedByScript -and $ImagePath) { + Dismount-DiskImage -ImagePath $ImagePath -ErrorAction SilentlyContinue | Out-Null + Write-Output "Source ISO unmounted after copy." +} +Set-ItemProperty -Path "$ScratchDisk\tiny11\sources\install.esd" -Name IsReadOnly -Value $false -ErrorAction 'Continue' > $null 2>&1 +Remove-Item "$ScratchDisk\tiny11\sources\install.esd" -ErrorAction 'Continue' > $null 2>&1 Write-Output "Copy complete!" Start-Sleep -Seconds 2 Clear-Host @@ -158,8 +354,7 @@ $wimFilePath = "$ScratchDisk\tiny11\sources\install.wim" try { Set-ItemProperty -Path $wimFilePath -Name IsReadOnly -Value $false -ErrorAction Stop } catch { - # This block will catch the error and suppress it. - Write-Error "$wimFilePath not found" + Write-Error "$wimFilePath not found" } New-Item -ItemType Directory -Force -Path "$ScratchDisk\scratchdir" > $null Mount-WindowsImage -ImagePath $ScratchDisk\tiny11\sources\install.wim -Index $index -Path $ScratchDisk\scratchdir @@ -180,7 +375,6 @@ $lines = $imageInfo -split '\r?\n' foreach ($line in $lines) { if ($line -like '*Architecture : *') { $architecture = $line -replace 'Architecture : ','' - # If the architecture is x64, replace it with amd64 if ($architecture -eq 'x64') { $architecture = 'amd64' } @@ -202,79 +396,58 @@ $packages = & 'dism' '/English' "/image:$($ScratchDisk)\scratchdir" '/Get-Provis } } -$packagePrefixes = 'AppUp.IntelManagementandSecurityStatus', -'Clipchamp.Clipchamp', -'DolbyLaboratories.DolbyAccess', -'DolbyLaboratories.DolbyDigitalPlusDecoderOEM', -'Microsoft.BingNews', -'Microsoft.BingSearch', -'Microsoft.BingWeather', -'Microsoft.Copilot', -'Microsoft.Windows.CrossDevice', -'Microsoft.GamingApp', -'Microsoft.GetHelp', -'Microsoft.Getstarted', -'Microsoft.Microsoft3DViewer', -'Microsoft.MicrosoftOfficeHub', -'Microsoft.MicrosoftSolitaireCollection', -'Microsoft.MicrosoftStickyNotes', -'Microsoft.MixedReality.Portal', -'Microsoft.MSPaint', -'Microsoft.Office.OneNote', -'Microsoft.OfficePushNotificationUtility', -'Microsoft.OutlookForWindows', -'Microsoft.Paint', -'Microsoft.People', -'Microsoft.PowerAutomateDesktop', -'Microsoft.SkypeApp', -'Microsoft.StartExperiencesApp', -'Microsoft.Todos', -'Microsoft.Wallet', -'Microsoft.Windows.DevHome', -'Microsoft.Windows.Copilot', -'Microsoft.Windows.Teams', -'Microsoft.WindowsAlarms', -'Microsoft.WindowsCamera', -'microsoft.windowscommunicationsapps', -'Microsoft.WindowsFeedbackHub', -'Microsoft.WindowsMaps', -'Microsoft.WindowsSoundRecorder', -'Microsoft.WindowsTerminal', -'Microsoft.Xbox.TCUI', -'Microsoft.XboxApp', -'Microsoft.XboxGameOverlay', -'Microsoft.XboxGamingOverlay', -'Microsoft.XboxIdentityProvider', -'Microsoft.XboxSpeechToTextOverlay', -'Microsoft.YourPhone', -'Microsoft.ZuneMusic', -'Microsoft.ZuneVideo', -'MicrosoftCorporationII.MicrosoftFamily', -'MicrosoftCorporationII.QuickAssist', -'MSTeams', -'MicrosoftTeams', -'Microsoft.WindowsTerminal', -'Microsoft.549981C3F5F10' - -$packagesToRemove = $packages | Where-Object { - $packageName = $_ - $packagePrefixes -contains ($packagePrefixes | Where-Object { $packageName -like "*$_*" }) +$packagePrefixes = Get-Content -Path "$PSScriptRoot\removePackage.txt" | + Where-Object { $_.Trim() -ne '' } | + ForEach-Object { $_.Trim() } + +if ($Custom) { + try { + $selectedPrefixes = Show-PackageSelector -Items $packagePrefixes + } catch { + Write-Warning "Interactive selector failed or was interrupted. Defaulting to all prefixes." + $selectedPrefixes = $packagePrefixes + } +} else { + $selectedPrefixes = $packagePrefixes +} + +if (-not $selectedPrefixes -or $selectedPrefixes.Count -eq 0) { + Write-Output "No package prefixes selected for removal. Skipping Appx package removal step." + $packagesToRemove = @() +} else { + Write-Output "Selected package prefixes to remove:" + $selectedPrefixes | ForEach-Object { Write-Output " - $_" } + + $packagesToRemove = $packages | Where-Object { + $pkg = $_ + $match = $false + foreach ($pref in $selectedPrefixes) { + if ($pkg -like "*$pref*") { $match = $true; break } + } + $match + } } + foreach ($package in $packagesToRemove) { + Write-Output "Removing provisioned package: $package" & 'dism' '/English' "/image:$($ScratchDisk)\scratchdir" '/Remove-ProvisionedAppxPackage' "/PackageName:$package" } -Write-Output "Removing Edge:" -Remove-Item -Path "$ScratchDisk\scratchdir\Program Files (x86)\Microsoft\Edge" -Recurse -Force | Out-Null -Remove-Item -Path "$ScratchDisk\scratchdir\Program Files (x86)\Microsoft\EdgeUpdate" -Recurse -Force | Out-Null -Remove-Item -Path "$ScratchDisk\scratchdir\Program Files (x86)\Microsoft\EdgeCore" -Recurse -Force | Out-Null -& 'takeown' '/f' "$ScratchDisk\scratchdir\Windows\System32\Microsoft-Edge-Webview" '/r' | Out-Null -& 'icacls' "$ScratchDisk\scratchdir\Windows\System32\Microsoft-Edge-Webview" '/grant' "$($adminGroup.Value):(F)" '/T' '/C' | Out-Null -Remove-Item -Path "$ScratchDisk\scratchdir\Windows\System32\Microsoft-Edge-Webview" -Recurse -Force | Out-Null +$removeEdge = (-not $Custom) -or (Test-PrefixSelected $selectedPrefixes 'Microsoft.MicrosoftEdge.Stable_8wekyb3d8bbwe!App') +if ($removeEdge) { + Write-Output "Removing Edge:" + Remove-Item -Path "$ScratchDisk\scratchdir\Program Files (x86)\Microsoft\Edge" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null + Remove-Item -Path "$ScratchDisk\scratchdir\Program Files (x86)\Microsoft\EdgeUpdate" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null + Remove-Item -Path "$ScratchDisk\scratchdir\Program Files (x86)\Microsoft\EdgeCore" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null + & 'takeown' '/f' "$ScratchDisk\scratchdir\Windows\System32\Microsoft-Edge-Webview" '/r' | Out-Null + & 'icacls' "$ScratchDisk\scratchdir\Windows\System32\Microsoft-Edge-Webview" '/grant' "$($adminGroup.Value):(F)" '/T' '/C' | Out-Null + Remove-Item -Path "$ScratchDisk\scratchdir\Windows\System32\Microsoft-Edge-Webview" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null +} + Write-Output "Removing OneDrive:" & 'takeown' '/f' "$ScratchDisk\scratchdir\Windows\System32\OneDriveSetup.exe" | Out-Null & 'icacls' "$ScratchDisk\scratchdir\Windows\System32\OneDriveSetup.exe" '/grant' "$($adminGroup.Value):(F)" '/T' '/C' | Out-Null -Remove-Item -Path "$ScratchDisk\scratchdir\Windows\System32\OneDriveSetup.exe" -Force | Out-Null +Remove-Item -Path "$ScratchDisk\scratchdir\Windows\System32\OneDriveSetup.exe" -Force -ErrorAction SilentlyContinue | Out-Null Write-Output "Removal complete!" Start-Sleep -Seconds 2 Clear-Host @@ -330,11 +503,17 @@ Set-RegistryValue 'HKLM\zSYSTEM\ControlSet001\Control\BitLocker' 'PreventDeviceE Write-Output "Disabling Chat icon:" Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Windows Chat' 'ChatIcon' 'REG_DWORD' '3' Set-RegistryValue 'HKLM\zNTUSER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced' 'TaskbarMn' 'REG_DWORD' '0' -Write-Output "Removing Edge related registries" -Remove-RegistryValue "HKEY_LOCAL_MACHINE\zSOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge" -Remove-RegistryValue "HKEY_LOCAL_MACHINE\zSOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge Update" + +if ($removeEdge) { + Write-Output "Removing Edge related registries" + Remove-RegistryValue "HKEY_LOCAL_MACHINE\zSOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge" + Remove-RegistryValue "HKEY_LOCAL_MACHINE\zSOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge Update" +} + Write-Output "Disabling OneDrive folder backup" Set-RegistryValue "HKLM\zSOFTWARE\Policies\Microsoft\Windows\OneDrive" "DisableFileSyncNGSC" "REG_DWORD" "1" +Write-Output "Disabling Search Highlights:" +Set-RegistryValue 'HKLM\zSoftware\Microsoft\Windows\CurrentVersion\SearchSettings' 'IsDynamicSearchBoxEnabled' 'REG_DWORD' '0' Write-Output "Disabling Telemetry:" Set-RegistryValue 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\AdvertisingInfo' 'Enabled' 'REG_DWORD' '0' Set-RegistryValue 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\Privacy' 'TailoredExperiencesWithDiagnosticDataEnabled' 'REG_DWORD' '0' @@ -346,38 +525,51 @@ Set-RegistryValue 'HKLM\zNTUSER\Software\Microsoft\InputPersonalization\TrainedD Set-RegistryValue 'HKLM\zNTUSER\Software\Microsoft\Personalization\Settings' 'AcceptedPrivacyPolicy' 'REG_DWORD' '0' Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\DataCollection' 'AllowTelemetry' 'REG_DWORD' '0' Set-RegistryValue 'HKLM\zSYSTEM\ControlSet001\Services\dmwappushservice' 'Start' 'REG_DWORD' '4' -## Prevents installation of DevHome and Outlook -Write-Output "Prevents installation of DevHome and Outlook:" -Set-RegistryValue 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate' 'workCompleted' 'REG_DWORD' '1' -Set-RegistryValue 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\OutlookUpdate' 'workCompleted' 'REG_DWORD' '1' -Set-RegistryValue 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\DevHomeUpdate' 'workCompleted' 'REG_DWORD' '1' -Remove-RegistryValue 'HKLM\zSOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate' -Remove-RegistryValue 'HKLM\zSOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\DevHomeUpdate' -Write-Output "Disabling Copilot" -Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsCopilot' 'TurnOffWindowsCopilot' 'REG_DWORD' '1' -Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Edge' 'HubsSidebarEnabled' 'REG_DWORD' '0' -Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Explorer' 'DisableSearchBoxSuggestions' 'REG_DWORD' '1' -Write-Output "Prevents installation of Teams:" -Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Teams' 'DisableInstallation' 'REG_DWORD' '1' -Write-Output "Prevent installation of New Outlook": -Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Windows Mail' 'PreventRun' 'REG_DWORD' '1' +Set-RegistryValue 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'RotatingLockScreenEnabled' 'REG_DWORD' '0' +Set-RegistryValue 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'RotatingLockScreenOverlayEnabled' 'REG_DWORD' '0' +Set-RegistryValue 'HKLM\zNTUSER\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager' 'SubscribedContent-338387Enabled' 'REG_DWORD' '0' + +$response = Read-Host "Prevent Windows from automatically installing device drivers? (Y/n)" +if ($response -eq '' -or $response.ToLower() -eq 'y') { + Set-RegistryValue 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\DriverSearching' 'SearchOrderConfig' 'REG_DWORD' '0' +} + +$removeDevHome = (-not $Custom) -or (Test-PrefixSelected $selectedPrefixes 'Microsoft.Windows.DevHome') +$removeOutlook = (-not $Custom) -or (Test-PrefixSelected $selectedPrefixes 'Microsoft.OutlookForWindows') +$removeCopilot = (-not $Custom) -or (Test-PrefixSelected $selectedPrefixes 'Microsoft.Windows.Copilot') -or (Test-PrefixSelected $selectedPrefixes 'Microsoft.Copilot') +$removeTeams = (-not $Custom) -or (Test-PrefixSelected $selectedPrefixes 'Microsoft.Windows.Teams') -or (Test-PrefixSelected $selectedPrefixes 'MicrosoftTeams') -or (Test-PrefixSelected $selectedPrefixes 'MSTeams') + +if ($removeDevHome -or $removeOutlook) { + Write-Output "Prevents installation of DevHome and Outlook:" +} +if ($removeOutlook) { + Set-RegistryValue 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate' 'workCompleted' 'REG_DWORD' '1' + Set-RegistryValue 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\OutlookUpdate' 'workCompleted' 'REG_DWORD' '1' + Remove-RegistryValue 'HKLM\zSOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate' + Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Windows Mail' 'PreventRun' 'REG_DWORD' '1' +} +if ($removeDevHome) { + Set-RegistryValue 'HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\DevHomeUpdate' 'workCompleted' 'REG_DWORD' '1' + Remove-RegistryValue 'HKLM\zSOFTWARE\Microsoft\WindowsUpdate\Orchestrator\UScheduler_Oobe\DevHomeUpdate' +} +if ($removeCopilot) { + Write-Output "Disabling Copilot" + Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\WindowsCopilot' 'TurnOffWindowsCopilot' 'REG_DWORD' '1' + Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Edge' 'HubsSidebarEnabled' 'REG_DWORD' '0' + Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Windows\Explorer' 'DisableSearchBoxSuggestions' 'REG_DWORD' '1' +} +if ($removeTeams) { + Write-Output "Prevents installation of Teams:" + Set-RegistryValue 'HKLM\zSOFTWARE\Policies\Microsoft\Teams' 'DisableInstallation' 'REG_DWORD' '1' +} Write-Host "Deleting scheduled task definition files..." $tasksPath = "$ScratchDisk\scratchdir\Windows\System32\Tasks" -# Application Compatibility Appraiser Remove-Item -Path "$tasksPath\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser" -Force -ErrorAction SilentlyContinue - -# Customer Experience Improvement Program (removes the entire folder and all tasks within it) Remove-Item -Path "$tasksPath\Microsoft\Windows\Customer Experience Improvement Program" -Recurse -Force -ErrorAction SilentlyContinue - -# Program Data Updater Remove-Item -Path "$tasksPath\Microsoft\Windows\Application Experience\ProgramDataUpdater" -Force -ErrorAction SilentlyContinue - -# Chkdsk Proxy Remove-Item -Path "$tasksPath\Microsoft\Windows\Chkdsk\Proxy" -Force -ErrorAction SilentlyContinue - -# Windows Error Reporting (QueueReporting) Remove-Item -Path "$tasksPath\Microsoft\Windows\Windows Error Reporting\QueueReporting" -Force -ErrorAction SilentlyContinue Write-Host "Task files have been deleted." Write-Host "Unmounting Registry..." @@ -439,46 +631,25 @@ Write-Output "The tiny11 image is now completed. Proceeding with the making of t Write-Output "Copying unattended file for bypassing MS account on OOBE..." Copy-Item -Path "$PSScriptRoot\autounattend.xml" -Destination "$ScratchDisk\tiny11\autounattend.xml" -Force | Out-Null Write-Output "Creating 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-Output "Will be using oscdimg.exe from system ADK." - $OSCDIMG = "$ADKDepTools\oscdimg.exe" -} else { - Write-Output "ADK folder not found. Will be using bundled oscdimg.exe." - $url = "https://msdl.microsoft.com/download/symbols/oscdimg.exe/3D44737265000/oscdimg.exe" - - if (-not (Test-Path -Path $localOSCDIMGPath)) { - Write-Output "Downloading oscdimg.exe..." - Invoke-WebRequest -Uri $url -OutFile $localOSCDIMGPath - - if (Test-Path $localOSCDIMGPath) { - Write-Output "oscdimg.exe downloaded successfully." - } else { - Write-Error "Failed to download oscdimg.exe." - exit 1 - } - } else { - Write-Output "oscdimg.exe already exists locally." - } - - $OSCDIMG = $localOSCDIMGPath -} & "$OSCDIMG" '-m' '-o' '-u2' '-udfver102' "-bootdata:2#p0,e,b$ScratchDisk\tiny11\boot\etfsboot.com#pEF,e,b$ScratchDisk\tiny11\efi\microsoft\boot\efisys.bin" "$ScratchDisk\tiny11" "$PSScriptRoot\tiny11.iso" -# Finishing up Write-Output "Creation completed! Press any key to exit the script..." Read-Host "Press Enter to continue" Write-Output "Performing Cleanup..." Remove-Item -Path "$ScratchDisk\tiny11" -Recurse -Force | Out-Null Remove-Item -Path "$ScratchDisk\scratchdir" -Recurse -Force | Out-Null -Write-Output "Ejecting Iso drive" -Get-Volume -DriveLetter $DriveLetter[0] | Get-DiskImage | Dismount-DiskImage -Write-Output "Iso drive ejected" -Write-Output "Removing oscdimg.exe..." -Remove-Item -Path "$PSScriptRoot\oscdimg.exe" -Force -ErrorAction SilentlyContinue + +if (-not $MountedByScript) { + try { + $letter = $DriveLetter.TrimEnd(':') + Get-Volume -DriveLetter $letter -ErrorAction Stop | Get-DiskImage | Dismount-DiskImage -ErrorAction SilentlyContinue | Out-Null + Write-Output "Source drive $DriveLetter ejected." + } catch { + Write-Output "Source drive was not mounted or could not be ejected." + } +} + Write-Output "Removing autounattend.xml..." Remove-Item -Path "$PSScriptRoot\autounattend.xml" -Force -ErrorAction SilentlyContinue @@ -505,17 +676,6 @@ if (Test-Path -Path "$ScratchDisk\scratchdir") { } else { Write-Output "scratchdir folder does not exist. No action needed." } -if (Test-Path -Path "$PSScriptRoot\oscdimg.exe") { - Write-Output "oscdimg.exe still exists. Attempting to remove it again..." - Remove-Item -Path "$PSScriptRoot\oscdimg.exe" -Force -ErrorAction SilentlyContinue - if (Test-Path -Path "$PSScriptRoot\oscdimg.exe") { - Write-Output "Failed to remove oscdimg.exe." - } else { - Write-Output "oscdimg.exe removed successfully." - } -} else { - Write-Output "oscdimg.exe does not exist. No action needed." -} if (Test-Path -Path "$PSScriptRoot\autounattend.xml") { Write-Output "autounattend.xml still exists. Attempting to remove it again..." Remove-Item -Path "$PSScriptRoot\autounattend.xml" -Force -ErrorAction SilentlyContinue @@ -528,8 +688,6 @@ if (Test-Path -Path "$PSScriptRoot\autounattend.xml") { Write-Output "autounattend.xml does not exist. No action needed." } -# Stop the transcript Stop-Transcript exit -