Windows ISO With Install.esd/Install.swm Creation Tool

Discussion in 'Windows 10' started by Enthousiast, Jan 29, 2019.

  1. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,423
    60
    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)
     
  2. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    47,274
    94,761
    450
    Will replace the line.
     
  3. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    47,274
    94,761
    450
    Download is available at the OP (first post of this thread)!
     
  4. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,423
    60
    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 ?=
    
     
  5. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,836
    5,693
    60
    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)
    
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  6. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,423
    60
    My solution is based on yours.
     
  7. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    47,274
    94,761
    450
    #27 Enthousiast, Oct 19, 2019
    Last edited: Oct 19, 2019
    (OP)
    Does any of the 5 lines of code actually need elevation at all? Didn't test that.
     
  8. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,423
    60
    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.
     
  9. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,423
    60
    #29 rpo, Oct 20, 2019
    Last edited: Dec 8, 2019
    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.
    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
    
     
  10. skagarwals

    skagarwals MDL Novice

    May 12, 2016
    4
    0
    0
    How to know when you have updated your tool, as it doesn't have any version number, etc., mentioned in it?
     
  11. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    47,274
    94,761
    450
    There is not really anything to update.
     
  12. maxama123

    maxama123 MDL Senior Member

    Oct 22, 2009
    489
    184
    10
    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?
     
  13. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    47,274
    94,761
    450
    Windows can handle swm since vista was officially released on multiple cd's, containing swm files.
     
  14. whitestar_999

    whitestar_999 MDL Addicted

    Dec 9, 2011
    713
    318
    30
    @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.
     
  15. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    47,274
    94,761
    450
    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?
     
  16. whitestar_999

    whitestar_999 MDL Addicted

    Dec 9, 2011
    713
    318
    30
    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. :)
     
  17. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,836
    5,693
    60
    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).
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  18. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    47,274
    94,761
    450
    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.
     
  19. AveYo

    AveYo MDL Expert

    Feb 10, 2009
    1,836
    5,693
    60
    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 :)
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  20. Enthousiast

    Enthousiast MDL Tester

    Oct 30, 2009
    47,274
    94,761
    450
    These days you own something online till you refresh the webpage, it seems.