Create a WinPE usb disk to install Windows 10+

Discussion in 'Scripting' started by rpo, Jan 26, 2023.

  1. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,421
    60
    #1 rpo, Jan 26, 2023
    Last edited: Apr 17, 2024 at 18:06
    This script creates an optional USB boot drive and/or an ISO image to perform a fresh Windows installation.

    Prerequisite : Windows Kits with WinPE extention must be installed
    Admin rights are requested

    The proposed script sets the language as fr-FR; this can be change at the user's convenience.
    The work directory by defaut is c:\WinPE, it can be changed.
    The parameter arch specifies the WinPE architecture to create.
    It is recommanded to have Explorer++ available.

    The script formats the drive using a 1 GB fat32 partition assigning the drive letter "S" and the remaining space is formated as exFat.
    Only the fat32 partition is requested.

    Later, after booting on this drive, a powershell script displays a screen permitting the navigation on the pc's different devices to select the Windows installation ISO image or a folder with the content of the image (the ISO image or the installation folder can be located on the second partition of the disk).

    If the ISO image or the installation folder are not located on the USB boot disk, this disk may be dismounted since the process is running in ram.

    If the ISO image is selected, it is mounted on a virtual dvd drive.

    The execution of setup.exe is then launched to start the Windows installation.

    Remarks :
    - This WinPE doesn't depend on the Windows release level you want to install.
    - Only a fresh Windows installation may be performed.

    Downloadlink : https://drive.google.com/file/d/1jeJkS5WFUAhVevAXfMTJHYnA0Ixt3fjI/view?usp=sharing

    Code:
    <# : standard way of doing hybrid batch + powershell scripts
     @set "__ARGS__=%*" &powershell -noprofile "$ScriptPath='%~f0';iex((Get-Content('%~f0') -Raw))" &exit/b
    #>
    #************************************************************
    #* Define parameters                                        *
    #************************************************************
    $lang="fr-FR"  
    $target="c:\WinPE"
    $make_iso=1  
    $make_usb=0  
    $arch="amd64"
    $explorer="C:\Téléchargements\explorer++\x64\Explorer++.exe"
    #************************************************************
    $Host.UI.RawUI.BackgroundColor = "Blue"
    $Host.UI.RawUI.ForegroundColor = "White"
    Clear-Host
    #
    if($PSCommandPath){$ScriptPath=$PSCommandPath;$hybrid=$False}else{$hybrid=$True}
    #
    #(New-Object -ComObject shell.application).toggleDesktop()
    $title="Winpe install disk"
    $host.ui.rawui.windowtitle = $title
    #   Test Administrator privileges
    If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
    {
        $uac_error="Elevating UAC for Administrator Privileges failed"
        #   The following test is to avoid infinite looping if elevating UAC for Administrator Privileges failed
        if(!$hybrid){$Errorarg=$Args[0]}else{$errorarg=$env:__ARGS__}
        if ($errorarg -eq $uac_error) {
        read-host "$uac_error`r`nRight click on the script and select ""Run as administrator""`r`nPress Enter to exit...";exit}
    #   Restart the script to get Administrator privileges and exit
        if(!$hybrid){
            Start-Process powershell -args "-file `"$scriptpath`" `"$uac_error`"" -Verb runAs}
        else {                                                                              
            Start-Process "$ScriptPath" "$uac_error" -Verb runAs}
        exit
    }
    #
    Function info ($msg, $color){
        if(!$color){Write-Host $msg}
        Else {Write-Host $msg -ForeGroundColor $color}
        }
    #
    Function Pause ($msg){
        if (!$msg){$msg="Hit akey to resume..."}
        Write-Host $msg -NoNewLine
        [void][Console]::ReadKey("NoEcho,IncludeKeyDown")
    }
    #
    Function terminate {
        Message "End of process..." "OKOnly,SystemModal,Information" "Closing"|Out-Null
        [Environment]::Exit(1)
    }
    #
    Function REM ($msg,$fc){
        $strLine= "*" * $msg.length
        Write-Host "`r`n$strLine"
        if(!$fc){$fc="White"}
        Write-Host $msg -Foregroundcolor $FC
        Write-Host $strLine
    }
    #
    Function Fatal-Error ($msg){
        rem $msg "RED"
        terminate
    }
    #
    Add-Type -AssemblyName Microsoft.VisualBasic
    Function Message ($msg, $button, $titre){
    [Microsoft.VisualBasic.Interaction]::MsgBox($msg,$button,$titre)}
    ##
    Function Update_bcd ($bcdstore,$lang)
    {
    & bcdedit /store "$($bcdstore)" /set '{bootmgr}' locale $lang |Out-Null
    & bcdedit /store "$($bcdstore)" /set '{bootmgr}' displaybootmenu no
    & bcdedit /store "$($bcdstore)" /deletevalue '{bootmgr}'  toolsdisplayorder
    & bcdedit /store "$($bcdstore)" /set '{default}' bootmenupolicy Legacy |Out-Null
    & bcdedit /store "$($bcdstore)" /set '{default}' locale $lang |Out-Null
    & bcdedit /store "$($bcdstore)" /set '{default}' description "Windows Install Disk"|Out-Null
    remove-item "$($bcdstore).*" -force
    }
    #
    "Language                                 : $lang"
    "Working directory                        : $target"
    if($make_iso -eq 0){
    "ISO image                                : Don't create ISO image"
    }else{
    "ISO image                                : Create ISO image"
    }
    if($make_usb -eq 0){
    "Bootable USB device                      : Don't create bootable USB device"
    }else{
    "Bootable USB device                      : Create bootable USB device"
    }
    "Architecture (amd64 or  x86)             : $arch"
    "Explorer++ (path or null)                : $explorer"
    $msg= "Are the parameters correct?"
    If((Message $msg "OKCancel,SystemModal,Information" "Parameters") -eq  "Cancel"){terminate}
    #
    $KitsRoot="${env:ProgramFiles(x86)}\Windows Kits\10"
    if (!(Test-Path "$KitsRoot")){
        fatal-error "Windows Kits not installed - Aborting"}
    $WinPERoot="$KitsRoot\Assessment and Deployment Kit\Windows Preinstallation Environment"
    $DandIRoot="$KitsRoot\Assessment and Deployment Kit\Deployment Tools"
    $FWFILES="$DandIRoot\$arch\Oscdimg"
    #
    If(Test-Path "$target"){
        remove-item "$target" -Recurse -force -ErrorAction SilentlyContinue
        Remove-item "$target" -Recurse -force -ErrorAction SilentlyContinue}
    If(Test-Path "$target"){
        fatal-error "$target still present - Aborting process."}
    New-Item "$target"  -ItemType "Directory"
    New-Item "$target\mount"  -ItemType "Directory"
    #
    Copy-Item "$WinPERoot\$arch\media" "$target" -Recurse
    #
    Get-ChildItem "$target\media\" -Recurse -Directory |
    Foreach-Object{if(($_.Name -match "^..-..$") -and ($_.Name -ne "en-us") -and ($_.Name -ne $lang)){
     Remove-Item $_.FullName -Recurse}}
    #
    New-Item "$target\media\sources"  -ItemType "Directory"
    rem "Creating boot.wim"                                  
    #
    "Mount boot.wim"
    Copy-Item "$WinPERoot\$arch\en-us\winpe.wim" "$target\boot.wim" -Force
    Mount-WindowsImage -ImagePath "$target\boot.wim"  -Index 1 -Path "$target\mount"
    if($lang -ne "en-US"){
    "en-US language pack searching"
    $langpkg=(Get-WindowsPackage -Path "$target\mount").PackageName[0]
    "Add language pack"
    Add-WindowsPackage -Path "$target\mount" -PackagePath "$WinPERoot\$arch\WinPE_OCs\$lang\lp.cab"
    "Remove en-US language pack"
    Remove-WindowsPackage -Path "$target\mount" -PackageName $langpkg
    Dism  /Image:"$target\mount" /Set-AllIntl:$lang
    }
    rem "Add package to boot.wim"                            
    ForEach ($pkg in ("WMI","NetFx","Scripting","PowerShell","DismCmdlets","StorageWMI","SecureStartup")){
    "Add package $pkg"  
    Add-WindowsPackage -Path "$target\mount" -PackagePath "$WinPERoot\$arch\WinPE_OCs\WINPE-$($pkg).cab"
    Add-WindowsPackage -Path "$target\mount" -PackagePath "$WinPERoot\$arch\WinPE_OCs\$lang\WINPE-$($pkg)_$($lang).cab"
    }
    #
    # Update startnet.cmd in order to start Windows installation with setup.exe
    #
    $system32="$target\mount\windows\system32"
    Add-Content "$system32\startnet.cmd" '@powershell -exec bypass .\install_windows.ps1'
    #************************************************************
    @'
    if(Test-Path "explorer++.exe") {
    Start-Process explorer++.exe -WindowStyle Minimized } else {
    $id=(Start-Process powershell -PassThru `
        -ArgumentList "-NoExit -WindowStyle Minimized Get-Volume").id
    }
    "A minimized window opened to make easier browsing on the different devices."
    $fn=Read-Host "Paste the full path to the Windows installation directory or ISO here "
    if(Test-Path "explorer++.exe") {Stop-Process -Name explorer++  -ErrorAction SilentlyContinue}
    else {
    if($id -ne $Null){Stop-Process -ID $id -ErrorAction SilentlyContinue}}
    $fn=$fn.Replace('"','').replace("'","").Trim()
    if($fn -NotMatch "\.iso$"){
        $installdir=$fn
        if($fn -Match "\\$"){$installdir=$fn.Substring(0,($fn.length-1))+"\"}
    }else{
        $letter=(Mount-DiskImage "$fn" -PassThru|Get-Volume).DriveLetter
        if(!$letter){"Mount ISO image failed. Exiting...";pause;exit}
        $installdir="$($letter):"
    }
    $setup="$($installdir)\setup.exe"
    if(!(Test-Path "$setup")){"Install file setup.exe not found. Aborting..."
        pause;exit}
    Clear-Host
    & $setup
    '@|Out-File "$system32\install_windows.ps1"
    #
    # Put your personal stuff here if any
    #
    #************************************************************
    if($explorer){
        Copy-Item $explorer "$system32\"
        Copy-Item ((Split-Path $explorer)+"\explorer++*.dll") "$system32\"
    }
    $msg="Make sure all explorer windows are closed"
    $result=(Message $msg "OKCancel,SystemModal,Information" "Close explorer")
    "Unmount image"
    Dismount-WindowsImage -Path "$target\mount" -Save
    #
    Export-WindowsImage -SourceImagePath "$target\boot.wim" -SourceIndex 1 -DestinationImagePath "$target\media\sources\boot.wim"
    Remove-Item  "$target\boot.wim"
    Remove-Item  "$target\mount"
    rem "Modify BCD"                                        
    Update_bcd "$target\media\Boot\BCD" $lang
    Update_bcd "$target\media\EFI\Microsoft\Boot\BCD" $lang
    #
    if ($make_iso -ne 0){
    #
    rem "Create iso image"                                    
        $Oscdimg="$FWFILES\Oscdimg.exe"
        $ISOName="$target\winpe.iso"
        $ISOLabel = '-l"WinPE"'
        $etfsboot = "$FWFILES\etfsboot.com"
        $efisys   = "$FWFILES\efisys_noprompt.bin"
        $data     = '2#p0,e,b"{0}"#pEF,e,b"{1}"' -f $etfsboot, $efisys
        &$Oscdimg @("-m","-o","-u2","-bootdata:$data",'-u2','-udfver102',$ISOLabel,`
        "$target\media", "$ISOName")
    }
    #
    if($make_usb -ne 0) {
    #
    rem "Create USB device"                                
    #
    Function Get-Driveletter {
        $letters="DEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
        $assignedletters=@() # array of in use drive letters
    #    get in use drive letters
        Foreach ($Volume in (Get-Volume)) {$assignedletters+=$volume.driveletter}
    #   remove already assigned devices from the $letters list
        foreach ($assignedletter in $assignedletters){
            $letters=$letters| Where-Object {$_ -ne $assignedletter}}
        return $letters[0] # first available drive letter
    }
    Add-Type -AssemblyName System.Windows.Forms # Load the class System.Windows.Forms
    #
    $Form=New-Object System.Windows.Forms.Form # Create the screen form (window)
    $Form.TopMost = $True
    $Form.FormBorderStyle = "none"
    $Form.Add_KeyDown({if ($_.KeyCode -eq "Enter"){$OKButton.PerformClick()}})         # Enter = click OK button
    $Form.Add_KeyDown({if ($_.KeyCode -eq "Escape"){$CancelButton.PerformClick()}})    # Escape = click on Cancel button
    # Set the title and size of the window:
    $Form.Text=$Title
    $Form.Font='Consolas,9'                
    $Form.Width=420 ; $Form.Height=200
    $Form.StartPosition = "CenterScreen"
    $Form.SizeGripStyle = "Hide"
    [System.Windows.Forms.Application]::EnableVisualStyles()
    $form.ShowInTaskbar = $False
    #
    $USBDisk=New-Object System.Windows.Forms.Label # Put Selected USB Disk label on the form
    $USBDisk.Location=New-Object System.Drawing.Point(20,20)
    $USBDisk.Text="Selected USB Disk"
    $USBDisk.Size=New-Object System.Drawing.Size(200,20)
    $Form.Controls.Add($USBDisk)
    #
    $USBDiskList=New-Object System.Windows.Forms.ComboBox # Create a drop-down list and fill it
    $USBDiskList.DropDownStyle=[System.Windows.Forms.ComboBoxStyle]::DropDownList;
    $USBDiskList.Location=New-Object System.Drawing.Point(20,40)
    $USBDiskList.Size=New-Object System.Drawing.Size(363,22)
    $Form.Controls.Add($SelectUSBDiskList)
    #
    $CancelButton=New-Object System.Windows.Forms.Button # Put the Cancel button on the form
    $CancelButton.Location=New-Object System.Drawing.Point(270,120)
    $CancelButton.Text="Cancel"
    $CancelButton.Size=New-Object System.Drawing.Size(115,26)
    $Form.Controls.Add($CancelButton)
    $CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
    #
    $OKButton=New-Object System.Windows.Forms.Button # Put the Create Disk button on the form
    $OKButton.Location=New-Object System.Drawing.Point(20,120)
    $OKButton.Text="OK"
    $OKButton.Size=New-Object System.Drawing.Size(115,26)
    $Form.Controls.Add($OKButton)
    $OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
    #
    $USB=$Null
    $USBDisks=@() # array with USB disk number
    #
    $Disks=$Null;While (!$Disks){
        If(!($Disks=Get-Disk | Where-Object {($_.BusType -eq "USB") -and ($_.OperationalStatus -eq "Online")})){
            $msg="Plug in your USB disk first.`n`nClick OK when ready."
            if((Message $msg "OKCancel,SystemModal,Information" "Get Disk") -eq  "Cancel"){terminate}}}
    #
    Foreach ($USBDisk in $Disks) {
        $FriendlyName=($USBDisk.FriendlyName).PadRight(40," ").substring(0,39)
        $DiskNumber=$USBDisk.DiskNumber
        $USBDisks+="$DiskNumber|"
        $USBDiskList.Items.Add(("{0,-39}{1:n2} GB" -f $FriendlyName,($USBDisk.Size/1GB)))|out-null
        $Partitions = Get-Partition | Where-Object { $_.DiskNumber -eq $DiskNumber}
        If ($Partitions) {
            Foreach ($Partition in $Partitions) {
            $PartitionNumber=$Partition.PartitionNumber
            If(!$Partition.DriveLetter){$AccessPath="  "} Else {$AccessPath=$Partition.DriveLetter+":"}
            $driveletter=$Partition.DriveLetter
            $type=$Partition.Type  
            $Volumes=get-volume | Where-Object {$Partition.AccessPaths -contains  $_.path }
                Foreach ($Volume in $Volumes) {
                    $USBDisks+="$DiskNumber|$PartitionNumber|$driveletter|$type| "
                    $USBDiskList.Items.Add(("{0,0}({1,2}){2,-32}[{3:n2} GB]" -f " ",$AccessPath,$Volume.FileSystemLabel.PadRight(25," ").substring(0,25), ($Partition.Size/1GB)))|out-null
                }
            }
        }
    }
    $form.Controls.Add($USBDiskList)
    $USBDiskList.SelectedIndex=0
    #
    if($form.ShowDialog() -eq "Cancel") {
        Message "The script was cancelled." "OK,SystemModal,Information" "Cancel";terminate}
    #
    $usb=$partition=$driveletter=$Null
    $USB=$USBDisks[$USBDiskList.SelectedIndex].Split('|')[0]
    $partition=$USBDisks[$USBDiskList.SelectedIndex].Split('|')[1]
    $driveletter=$USBDisks[$USBDiskList.SelectedIndex].Split('|')[2]
    #
    if((!$partition) -or ($partition -eq "")){ # no partitions initialize disk
    $msg="WARNING!`n`nYour USB disk will be converted to MBR scheme,repartitioned and reformatted." + `
         "`n`nAll partitions and data currently stored in the USB disk will be erased.`r`n" + `
        "Are you sure you want to continue?"
    If((Message $msg "OKCancel,SystemModal,Information" "Disk clear") -eq  "Cancel"){terminate}
    #    Clear the USB disk # Try with the Clear-Disk cmdlet; if failing, invoke diskpart
    Try{Clear-Disk $USB -RemoveData -RemoveOEM -Confirm:$false -ErrorAction Stop}
    Catch{"Select disk $USB`nSelect disk $USB`nclean`nrescan`nexit`n"|diskpart}
    set-disk $usb -partitionstyle mbr
    Stop-Service ShellHWDetection -erroraction silentlycontinue|out-null
    #    Create partition(s)
    $new_partition=(New-Partition -DiskNumber $usb -Size  1GB -AssignDriveLetter)
    $driveletter=$new_partition.Driveletter
    $partition=$new_partition.PartitionNumber
    #    Create the NTFS partition
    New-Partition -DiskNumber $usb -UseMaximumSize -AssignDriveLetter |
    Format-Volume -FileSystem NTFS -NewFileSystemLabel "SETUP"
    }else{ # partition exists
    Stop-Service ShellHWDetection -erroraction silentlycontinue|out-null
    if ((!$driveletter) -or ($driveletter -eq "")){$driveletter=Get-Driveletter}
    #    Set-Partition -DiskNumber $usb -PartitionNumber $partition -NewDriveLetter $driveletter
    }  # end partition(s) creation
    @"
    Select disk $USB
    Select disk $USB
    Select partition $partition
    format quick fs=Fat32 label="BOOT"
    assign letter="$driveletter"
    active
    exit
    "@|diskpart
    Start-Service ShellHWDetection -erroraction silentlycontinue|out-null  
    & bootsect.exe /nt60 "$($driveletter):" /force /mbr
    rem "Copying files to the USB disk."
    Copy-Item "$target\media\*" "$($driveletter):\" -Recurse
    }
    terminate
    
     
  2. vigipirate

    vigipirate MDL Senior Member

    Feb 24, 2011
    350
    87
    10
  3. Chibi ANUBIS

    Chibi ANUBIS MDL Chibi Developer

    Apr 28, 2014
    1,237
    911
    60
    Oh ! I do the same for Ghost :D
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  4. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,421
    60
    #4 rpo, Jan 31, 2023
    Last edited: Feb 2, 2023
    (OP)
    I don't know Ghost. Only the film with Patrick Swayze, many, many years ago.
    I suppose you refer to the Symantec imaging software.
     
  5. rashxxl

    rashxxl MDL Novice

    Mar 29, 2023
    1
    0
    0
    Is it designed for UEFI or Legasy ?
     
  6. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,421
    60
    Shoud not matter
     
  7. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,421
    60
    An updated version is available (cf OP)
     
  8. vigipirate

    vigipirate MDL Senior Member

    Feb 24, 2011
    350
    87
    10
    hello sir
    need your help for this script I run this script as an administrator open a window here attach with parameter I don't know how to activate copy on usb key it's note that I don't want to copy to usb key create my iso well and that's all
    no explorer++ download
    link : https://ibb.co/R2tBcnV

    thank you
     
  9. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,421
    60
    This script creates a bootable WinPE USB disk, no activation is needed when the disk is booted.
    When booted, it executes a powershell script which asks you to select a Windows installation ISO image or a Windows installation directory (containing the content of the ISO). If a ISO is selected, it is mounted. Finally setup.exe is executed from the mounted ISO or the install directory to install Windows.
    This script can only to be used for a fresh Windows installation, not for an upgrade or in-place installation; you don't need to create a USB windows installation disk because the script can load a ISO installation image.
     
  10. maargueb

    maargueb MDL Member

    Jun 9, 2019
    200
    26
    10
    #10 maargueb, Apr 17, 2024 at 14:06
    Last edited: Apr 17, 2024 at 15:13
    Can you write a small user guide?
     
  11. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,421
    60
    An updated OP is available. Feel free to ask any question.
     
  12. maargueb

    maargueb MDL Member

    Jun 9, 2019
    200
    26
    10
    How do I use this script?
     
  13. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,421
    60
    Download and execute (after reading the op).
     
  14. 12 lb Turkey

    12 lb Turkey MDL Junior Member

    Nov 24, 2022
    80
    47
    0
    May I suggest adding the ability to integrate storage drivers into boot.wim? That would be handy for Intel RST devices.
     
  15. rpo

    rpo MDL Expert

    Jan 3, 2010
    1,447
    1,421
    60
    To be clear, the windows installation iso image or the install directory are left untouched. The main purpose of the script is to create a bootable usb disk which can permit a fresh windows 10/11 installation using a windows installation iso image located anywhere on a device (disk or usb stick) of the pc,.
     
  16. maargueb

    maargueb MDL Member

    Jun 9, 2019
    200
    26
    10
    I want a guide in French on how to use the script for beginners