Just a detail : the 2 command files restart themselves to get admin privilges even if they are started as Administrator. You could replace statement 6 by : Code: reg query HKU\S-1-5-19 >nul 2>&1 ||(powershell start -verb runas '%0' & exit /b)
If a problem happens with runas, there is a risk of entering an infinite loop. I ran tne script on a W7 VM as a standard user and getting the required privileges failed (since there is a pause before the call to powershell, you can abort the script). The following instructions detect the problem : Code: rem Getting Administrator privileges reg query HKU\S-1-5-19 >nul 2>&1 ||(if defined ? (rem ? variable defined means this sequence is entered a second time : possible infinite loop echo Getting Administrator privileges failed.&echo Press any key to exit&pause>nul&exit/b) powershell -c "start cmd -ArgumentList '/c set ?=?&call \"%~f0\"' -verb runas" &exit/b)&rem escape " with \ for script name - set ? variable set ?=
I usually put this in my self-elevating scripts: Code: :: self-elevate passing args and preventing loop set "args="%~f0" %*" & call set "args=%%args:"=\""%%" reg query HKU\S-1-5-19>nul||(if "%?%" neq "y" powershell -c "start cmd -ArgumentList '/c set ?=y&call %args%' -verb runas" &exit) or even safer (without quotes substitution): Code: :: self-elevate passing args and preventing loop set "args="%~f0" %*" & reg query HKU\S-1-5-19>nul 2>nul || if "%_%" neq "y" ( powershell -c "$Env:_='y';$ErrorActionPreference=0;start cmd -Arg \"/c call $Env:args\" -verb runas" && exit)
Elevation can be done by one instruction : powershell start -verb runas '%0' & exit /b But first you test if you need elevation : reg query HKU\S-1-5-19 That's fine. But what happens if the elevation fails? When the script is reentered you are still not privileged and the process is iterated and you enter an infinte loop. For this reason a variable is passed; if it is defined when the script is reentered, this means that the elevation failed and it is better to abort the script. If your are administrator vs standard user, the elevation should never fail, but users are unpredictable... Error processing is important when programming.
I adapted the script to introduce a gui interface with powershell. With this interface the user chooses the usb drive, the iso image and wether he wants to create swm files or a esd file. If there is only one swm file, it is deleted and the wim file is kept. If 7zip is installed, it is used instead of the delivered file. Finally, the usb device is created. Update : Complete rewrite of the original script The storage cmdlets (get-disk, etc...) are not supported on Windows 7. They are replaced with Get-WmiObject. Spoiler Code: <# : @echo off rem Getting Administrator privileges reg query HKU\S-1-5-19 >nul 2>&1 ||(if defined ? (rem ? variable defined means this sequence is entered a second time : possible infinite loop echo Getting Administrator privileges failed.&echo Press any key to exit&pause>nul&set "?="&exit/b) powershell "start cmd -ArgumentList '/c set ?=?&\"%~f0\"' -verb runas" &exit/b)&rem escape " with \ set "?=" rem Got Administrator privileges powershell -noprofile "$ScriptDir='%~dp0';iex ((Get-Content('%~f0')) -join \"`n\")"&exit/b #> $swmsize=4000 # max split wim size $ScriptDir=$ScriptDir.TrimEnd("\") # remove trailing \ $pswindow = $host.ui.rawui # create a reference to the console’s UI.RawUI child object $pswindow.foregroundcolor = "Yellow";$pswindow.backgroundcolor = "Blue" $Title="Create Win 10 ISO with multiple $swmsize MB install.swm files or install.esd" $pswindow.windowtitle = $Title # Add-Type -AssemblyName System.Windows.Forms # Load the class System.Windows.Forms # # Filebrowser dialog object $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{ Multiselect = $false # One file can be chosen Filter = 'ISO images (*.iso)|*.iso' # Select only iso files } # Function MsgBox {[System.Windows.Forms.MessageBox]::Show($Args[0],$Title,$Args[1],$Args[2])} # clear-host ########################## # Define GUI objects # ########################## $Form=New-Object System.Windows.Forms.Form # Create the screen form (window) $Form.TopMost = $True # Set the window title and size $Form.Text=$Title $Form.Width=420 ; $Form.Height=300 $Form.StartPosition = "CenterScreen" $Form.ControlBox=$False $Form.SizeGripStyle = "Hide" [System.Windows.Forms.Application]::EnableVisualStyles() # # Create a drop-down list and fill it $USBDiskList=New-Object System.Windows.Forms.ComboBox $USBDiskList.DropDownStyle=[System.Windows.Forms.ComboBoxStyle]::DropDownList $USBDiskList.Location=New-Object System.Drawing.Point(20,45) $USBDiskList.Size=New-Object System.Drawing.Size(365,20) $USBDiskList.Font='Consolas,10' $Form.Controls.Add($SelectUSBDiskList) # $USBDisk=New-Object System.Windows.Forms.Label # Put the USB Disk label on the form $USBDisk.Location=New-Object System.Drawing.Point(20,25) $USBDisk.Text="USB Disk" $USBDisk.Font='Default Font,9' $USBDisk.Size=New-Object System.Drawing.Size(400,20) $Form.Controls.Add($USBDisk) # $ISOImage=New-Object System.Windows.Forms.Label # Put the ISO Image label on the form $ISOImage.Location=New-Object System.Drawing.Point(20,90) $ISOImage.Font='Default Font,9' $ISOImage.Size=New-Object System.Drawing.Size(350,20) $ISOImage.text="ISO Image" $Form.Controls.Add($ISOImage) # $ISOFile=New-Object System.Windows.Forms.Label # Put the ISO file name on the form $ISOFile.Location=New-Object System.Drawing.Point(20,110) $ISOFile.Text=" " $ISOFile.Font='Consolas,10' $ISOFile.Size=New-Object System.Drawing.Size(365,23) $ISOFile.Backcolor = [System.Drawing.Color]::White $ISOFile.BorderStyle = [System.Windows.Forms.BorderStyle]::FixedSingle $Form.Controls.Add($ISOFile) # $SelectISOButton=New-Object System.Windows.Forms.Button # Put the Select ISO button on the form $SelectISOButton.Location=New-Object System.Drawing.Point(145,220) $SelectISOButton.Text="Select ISO" $SelectISOButton.Font='Default Font,9' $SelectISOButton.Size=New-Object System.Drawing.Size(120,26) $Form.Controls.Add($SelectISOButton) # $CancelButton=New-Object System.Windows.Forms.Button # Put the Cancel button on the form $CancelButton.Location=New-Object System.Drawing.Point(270,220) $CancelButton.Text="Cancel" $CancelButton.Font='Default Font,9' $CancelButton.Size=New-Object System.Drawing.Size(120,26) $Form.Controls.Add($CancelButton) $CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel # $CreateDiskButton=New-Object System.Windows.Forms.Button # Put the Create Disk button on the form $CreateDiskButton.Location=New-Object System.Drawing.Point(20,220) $CreateDiskButton.Text="Create USB Disk" $CreateDiskButton.Font='Default Font,9' $CreateDiskButton.Size=New-Object System.Drawing.Size(120,26) $Form.Controls.Add($CreateDiskButton) $CreateDiskButton.Enabled = $false $CreateDiskButton.DialogResult = [System.Windows.Forms.DialogResult]::OK # $SplitCheckbox = New-Object System.Windows.Forms.RadioButton $SplitCheckbox.Location = New-Object System.Drawing.Point(20,160) $SplitCheckbox.Name = "SplitCheckbox" $SplitCheckbox.Size = New-Object System.Drawing.Size(150,26) $SplitCheckbox.Text = "Create install.swm files" $SplitCheckbox.Checked=$True $Form.Controls.Add($SplitCheckbox) # $ESDCheckbox = New-Object System.Windows.Forms.RadioButton $ESDCheckbox.Location = New-Object System.Drawing.Point(260,160) $ESDCheckbox.Name = "ESDCheckbox" $ESDCheckbox.Size = New-Object System.Drawing.Size(150,26) $ESDCheckbox.Text = "Create install.esd file" $ESDCheckbox.Checked=$False $Form.Controls.Add($ESDCheckbox) # $SelectISOButton.Add_Click({ If ($FileBrowser.ShowDialog() -ne "Cancel"){ # if Cancel, just ignore $Global:ImagePath = $FileBrowser.filename # return the file name $ISOFile.Text= Split-Path -Path $ImagePath -leaf # extract filename and extension (iso) if (($ISOFile.Text).length -gt 44) {$ISOFile.Text = $ImagePath.PadRight(100," ").substring(0,43)+"..."} $CreateDiskButton.Enabled = $true $CreateDiskButton.Focus()}}) # $USBDisks=@() # array with USB disk number $Disks=$False;$First=$True While(!$Disks){if(!$First){ if((MsgBox "Please plug in your USB disk first then click OK to continue." "OKCancel" "Information") -eq "Cancel"){exit}} $First=$False;$Disks=Get-CimInstance Win32_Diskdrive| Where-Object {$_.InterfaceType -eq "USB" -and $_.size -ne $Null} } Foreach ($USBDisk in $Disks) { $FriendlyName=($USBDisk.Caption).PadRight(100," ").substring(0,37) $USBDisks+=$USBDisk.Index $USBDiskList.Items.Add(("{0,-38}{1,7:n2} GB" -f $FriendlyName,($USBDisk.Size/1GB)))|out-null $partitions = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='$($USBDisk.DeviceID)'} WHERE AssocClass = Win32_DiskDriveToDiskPartition" Get-WmiObject -Query $Partitions | ForEach-Object { $Partition = $_ $Volumes = "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='$($partition.DeviceID)'} WHERE AssocClass = Win32_LogicalDiskToPartition" Get-WmiObject -Query $Volumes | ForEach-Object { $Volume = $_ $USBDisks+=$USBDisk.DiskNumber $USBDiskList.Items.Add(("{0,-1}{1,1} {2,-34}{3,7:n2} GB" -f " ", ($Volume.DeviceID), $Volume.VolumeName.PadRight(100," ").substring(0,33), ($Volume.Size/1GB)))|out-null}}} $form.Controls.Add($USBDiskList) $USBDiskList.SelectedIndex=0 # if($form.ShowDialog() -eq "Cancel") {exit} #################################### # Create ISO dir and ISO image # #################################### # # At this point the mounted USB disk and the iso image file path are defined # if ($SplitCheckbox.checked){$Split=$True} else {$Split=$False} $USB=$USBDisks[$USBDiskList.SelectedIndex] # Clear-Host # Detect OS Architecture if([Environment]::Is64BitOperatingSystem) {$wimlib="$ScriptDir\bin\bin64\wimlib-imagex.exe"}else {$wimlib="$ScriptDir\bin\wimlib-imagex.exe"} $zip="$Env:ProgramFiles\7-zip\7z.exe" if(!(test-path $Zip)){$zip="$ScriptDir\bin\7z.exe"} "`r`nCreating work ISO folder`r`n" $ISO="$ScriptDir\ISO" remove-item "$ISO" -Recurse -force -ErrorAction SilentlyContinue new-item "$ISO" -ItemType "directory" # "`r`nExtracting ISO image to work ISO folder`r`n" & $zip x -y "-o$ISO" $ImagePath # "`r`nOptimizing Boot.wim`r`n" & "$wimlib" optimize "$ISO\Sources\boot.wim" $IsoName="Win10_With_Install_win.ISO" If((Get-Item "$ISO\Sources\Install.wim").Length/1MB -ge $swmsize){ if($Split){ "`r`nSplitting install.wim to multiple $swmsize MB install.swm files`r`n" & "$wimlib" split "$ISO\Sources\Install.wim" "$ISO\Sources\Install.swm" $swmsize if (Test-Path "$ISO\Sources\Install2.swm") { "`r`nDeleting install.wim`r`n" remove-item "$ISO\Sources\Install.wim" $IsoName="Win10_With_Install_SWM.ISO" }else{remove-item "$ISO\Sources\Install.swm" -force -ErrorAction SilentlyContinue} } else{ "`r`nCompressing install.wim to install.esd with LZMS compression" "`r`nThis can take some time, depending on the hardware and used source ISO!`r`n`r`n" & "$wimlib" export "$ISO\Sources\Install.wim" ALL "$ISO\Sources\Install.esd" --compress=LZMS --solid "`r`nDeleting install.wim`r`n" $IsoName="Win10_With_Install_ESD.ISO" remove-item "$ISO\Sources\Install.wim"} } # "`r`nSet bootmenupolicy Legacy in BCD`r`n" # & bcdedit /store "$ISO\boot\bcd" /set '{default}' bootmenupolicy Legacy *>$Null remove-item "$ISO\boot\bcd.*" -force & bcdedit /store "$ISO\EFI\Microsoft\boot\bcd" /set '{default}' bootmenupolicy Legacy *>$Null remove-item "$ISO\EFI\Microsoft\boot\bcd.*" -force # "`r`nCreating ISO...`r`n" Set-Location "$ISO" & "$ScriptDir\bin\cdimage.exe" -bootdata:2#p0,e,b".\boot\etfsboot.com"#pEF,e,b".\efi\Microsoft\boot\efisys.bin" -o -m -u2 -udfver102 -lWin10_ISO . $IsoName remove-item "$ScriptDir\$IsoName" -force -ErrorAction SilentlyContinue Move-item "$ISO\$IsoName" "$ScriptDir\$IsoName" Set-Location "$ScriptDir" # ########################## # Create the USB key # ########################## Clear-Host "`r`nClear the USB key, create the fat32 partition and retreive the drive letter`r`n" Stop-Service ShellHWDetection -erroraction silentlycontinue|out-null Try { # Try using the storage cmdlets # Clear the USB stick Clear-Disk $usb -RemoveData -RemoveOEM -Confirm:$false set-disk $usb -partitionstyle mbr # Create the fat32 boot partition $DeviceLetter=(New-Partition -DiskNumber $usb -UseMaximumSize -AssignDriveLetter -IsActive | Format-Volume -FileSystem FAT32 -NewFileSystemLabel "BOOT").DriveLetter + ":" } Catch { # The storage cmdlets are not available : use diskpart @" Select disk $USB clean convert MBR rescan create partition primary format fs=fat32 label=boot quick assign active "@ | diskpart $DeviceLetter=( Get-CimInstance Win32_Diskdrive -filter "Index=$USB" | Get-CimAssociatedInstance -ResultClassName Win32_DiskPartition | Get-CimAssociatedInstance -ResultClassName Win32_LogicalDisk).DeviceID } Start-Service ShellHWDetection -erroraction silentlycontinue|out-null # # & "$ISO\boot\bootsect.exe" /nt60 $DeviceLetter /force /mbr Get-ChildItem $Deviceletter -ErrorAction SilentlyContinue if(!$?){ MsgBox "Errors occured while preparing the USB key`r`n.The ISO folder is still available." "OK" "ERROR"|Out-Null;exit} "`r`nCopying ISO folder on the $DeviceLetter USB key and clean up ISO folder`r`n" robocopy "$ISO" $DeviceLetter /E /move /R:0 /W:0 /NJH /NJS if($LastExitCode -gt 8){MsgBox "Errors occured while creating the USB key`r`n.The ISO folder is still available." "OK" "ERROR"|Out-Null;exit} MsgBox "The installation USB key completed successfully." "OK" "Information" |Out-Null;exit
How to know when you have updated your tool, as it doesn't have any version number, etc., mentioned in it?
During the Windows 10 clean install, will windows know how to create a new install.wim file from the 2x install.SWM files during installation?
@Enthousiast Just want to report this here so you can put some kind of read me/warning about this in op post as it may hep some others. I downloaded the tool but failed to run the batch script. After spending 10-15 min I finally figured out it was because I renamed the downloaded zip file to contain your name within brackets for reference(tool(mdl_enthousiast).zip) which were also incl within the extracted files path & the reason for failure of script to run.
I should put in a warning for not using ( or ) in the file path or folder name, isn't that a bit obvious to not do?
Well I always assumed win 10 is now advanced enough to ignore such old restrictions so didn't bother to think about things like not extracting & running the tool too deep in a directory &/or path with some special characters,spaces etc.
You are right and it's ridiculous to demand from users to not use spaces in paths in 2020 - 25 years later after long names have been introduced. But it's Windows we are talking about, so there's no one definitive solution. The onus is on us, the script writers. When a script needs to reload itself for various reasons such as gaining admin rights, there are a couple tricks to prevent issues and do it successfully even there where normal right-click run as admin fails (pff microsoft)! First, not using the script directly, but always launched via cmd, and very important, using the call command. And second, not passing paths directly between cmd side and powershell side - that's easily obtained by passing a variable instead. So this will work on most paths: Code: :::: Relaunch as Admin passing cmdline args set _=call "%~f0" %*& fltmc>nul||(powershell -nop -c start cmd -args'/d/x/r',$env:_ -verb runas &&exit/b ||pause&exit/b) A bit fancy in that it passes command line arguments as is, and also disables cmd autorun (/d) and enables cmd extensions (/x). But the real kickass is that @Enthousiast updated the script to use wimlib so there should not be a need for elevation anymore I think, it's only required for anything dism (grr microsoft).
It's just a f**king 3 line command tool, made because of manually running the command line to compress wim > esd or split the wim into 2 swm files, seemed to hard for 90% of the current members.
price to pay these days. you have to document everything in detailed steps, even instructions on how one wipes his own arse but we old dogs also assume everybody had our background to quickly get out of any issue, plus we often make rookie mistakes own your s**t