PowerShell – Create USB Recovery Drive

This is the third post in my series about Windows Recovery Environment (Windows RE). Here, I will suggest to you to make a recovery drive and subsequently create a backup ISO image of your bootable USB recovery drive, mainly if you purchase a new computer and it is running smoothly.

The Recovery Drive creates a FAT32 formatted USB that boots in both MBR and UEFI with SecureBoot. It helps you to troubleshoot and fix problems with your PC, even if it won’t start, because the (USB) media contains a bootable copy of Windows RE, and therefore gives you access to all troubleshooting and recovery tools. No product key is required when using the Recovery USB Drive to reinstall Windows 10.

In Windows 10 OS, you can use a command-line tool named RecoverDrive.exe to create a system recovery USB drive. To make a USB recovery drive in Windows 10, please run PowerShell as Admin, and do the following:

  1. Type ‘RecoveryDrive.exe’ on the command line.
  2. In a new window that will appear; please select ‘Back up system files to the recovery drive’.
  3. Select Next.
  4. Choose the USB drive you want to use and click next.
  5. Select Create and let the process complete.
  6. Select Finish

Most guides out there will suggest to you that once you create the USB recovery drive, you will not be able to use it for anything else; that you should remove the USB recovery drive from your computer, label or mark it as such and keep it somewhere safe.

But there is an additional option, described in this post that enables you to use PowerShell script and create a backup ISO image of your bootable USB recovery drive, and whenever you need it again, just restore the backup image to the same or another USB stick.

To use this option, please do not remove (just created) USB recovery drive from your computer as soon as you finished step 6 listed above; close the ‘RecoveryDrive.exe’ window only, and run the following PowerShell script which will produce an ISO File:

Example: CreateOrMount-WinREisoFile.ps1 -Path C:\WinRe\WinRE.iso -Title “WinRE” -Force

The following two pictures display the process of creating a backup ISO image of your bootable USB recovery drive.

Picture 01: closing the RecoverDrive window and changing the current directory to the one containing my PowerShell script.

Picture 02: running a PS script to create a backup ISO image of a USB recovery drive.

Here is the PowerShell script:


<# Name: CreateOrMount-WinREisoFile.ps1 
Author: Alex Dujakovic 
Date: June 2017 
Description: Creates ISO File from USB WinRE Recovery Drive 
Mounts ISO File and creates USB WinRE Drive 
Examples: Mounts ISO File and creates USB WinRE Drive 
CreateOrMount-WinREisoFile.ps1 -ImagePath C:\Test\Win10.iso -USBDriveLetter Z -label 'WinRE' 
Creates ISO File from USB WinRE Recovery Drive 
CreateOrMount-WinREisoFile.ps1 -Path C:\Test\WinRE.iso -Title "WinRE" -Force 
CreateOrMount-WinREisoFile.ps1 -Path C:\Test\WinRE.iso -Force Tested on Windows 10 only 
#> 
            
[CmdletBinding(SupportsShouldProcess=$True,DefaultParametersetName="MountISO")]

param( 
[Parameter(ParameterSetName="MountISO", Mandatory=$True, Position=0)] 
[string]$ImagePath, 

[Parameter(ParameterSetName="MountISO", Position=1)] 
[string]$USBDriveLetter,

[Parameter(ParameterSetName="MountISO", Position=2)] 
[string]$label = "WinRE", 

[Parameter(ParameterSetName="CreateISO", Mandatory=$True, Position=0)] 
[string]$Path,
[Parameter(ParameterSetName="CreateISO", Position=1)] 
[string]$Title = "WinRE", 
[Parameter(ParameterSetName="CreateISO", Position=2)]  
[switch]$Force
)
# -------------------------|Functions|------------------------------------

Function New-IsoFile  
{  
  <# .Synopsis Creates a new .iso file .Description The New-IsoFile cmdlet creates a new .iso file containing content from chosen folders .Example New-IsoFile "c:\tools","c:Downloads\utils" This command creates a .iso file in $env:temp folder (default location) that contains c:\tools and c:\downloads\utils folders. The folders themselves are included at the root of the .iso image. .Example New-IsoFile -FromClipboard -Verbose Before running this command, select and copy (Ctrl-C) files/folders in Explorer first. .Example dir c:\WinPE | New-IsoFile -Path c:\temp\WinPE.iso -BootFile "${env:ProgramFiles(x86)}\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\efisys.bin" -Media DVDPLUSR -Title "WinPE" This command creates a bootable .iso file containing the content from c:\WinPE folder, but the folder itself isn't included. Boot file etfsboot.com can be found in Windows ADK. Refer to IMAPI_MEDIA_PHYSICAL_TYPE enumeration for possible media types: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366217(v=vs.85).aspx .Notes NAME: New-IsoFile AUTHOR: Chris Wu LASTEDIT: 03/23/2016 14:46:50 #>  
  
  [CmdletBinding(DefaultParameterSetName='Source')]Param( 
    [parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true, ParameterSetName='Source')]$Source,  
    [parameter(Position=2)][string]$Path = "$env:temp\$((Get-Date).ToString('yyyyMMdd-HHmmss.ffff')).iso",  
    [ValidateScript({Test-Path -LiteralPath $_ -PathType Leaf})][string]$BootFile = $null, 
    [ValidateSet('CDR','CDRW','DVDRAM','DVDPLUSR','DVDPLUSRW','DVDPLUSR_DUALLAYER','DVDDASHR','DVDDASHRW','DVDDASHR_DUALLAYER','DISK','DVDPLUSRW_DUALLAYER','BDR','BDRE')][string] $Media = 'DVDPLUSRW_DUALLAYER', 
    [string]$Title = (Get-Date).ToString("yyyyMMdd-HHmmss.ffff"),  
    [switch]$Force, 
    [parameter(ParameterSetName='Clipboard')][switch]$FromClipboard 
  ) 
 
  Begin {  
    ($cp = new-object System.CodeDom.Compiler.CompilerParameters).CompilerOptions = '/unsafe' 
    if (!('ISOFile' -as [type])) {  
      Add-Type -CompilerParameters $cp -TypeDefinition @' 
public class ISOFile  
{ 
  public unsafe static void Create(string Path, object Stream, int BlockSize, int TotalBlocks)  
  {  
    int bytes = 0;  
    byte[] buf = new byte[BlockSize];  
    var ptr = (System.IntPtr)(&bytes);  
    var o = System.IO.File.OpenWrite(Path);  
    var i = Stream as System.Runtime.InteropServices.ComTypes.IStream;  
  
    if (o != null) { 
      while (TotalBlocks-- > 0) {  
        i.Read(buf, BlockSize, ptr); o.Write(buf, 0, bytes);  
      }  
      o.Flush(); o.Close();  
    } 
  } 
}  
'@  
    } 
  
$i = 0

    if ($BootFile) { 
      if('BDR','BDRE' -contains $Media) { Write-Warning "Bootable image doesn't seem to work with media type $Media" } 
      ($Stream = New-Object -ComObject ADODB.Stream -Property @{Type=1}).Open()  # adFileTypeBinary 
      $Stream.LoadFromFile((Get-Item -LiteralPath $BootFile).Fullname) 
      ($Boot = New-Object -ComObject IMAPI2FS.BootOptions).AssignBootImage($Stream) 
    } 
    $MediaType = @('UNKNOWN','CDROM','CDR','CDRW','DVDROM','DVDRAM','DVDPLUSR','DVDPLUSRW','DVDPLUSR_DUALLAYER','DVDDASHR','DVDDASHRW','DVDDASHR_DUALLAYER','DISK','DVDPLUSRW_DUALLAYER','HDDVDROM','HDDVDR','HDDVDRAM','BDROM','BDR','BDRE') 
    ($Image = New-Object -com IMAPI2FS.MsftFileSystemImage -Property @{VolumeName=$Title}).ChooseImageDefaultsForMediaType($MediaType.IndexOf($Media)) 
  
    if (!($Target = New-Item -Path $Path -ItemType File -Force:$Force -ErrorAction SilentlyContinue)) { Write-Error -Message "Cannot create file $Path. Use -Force parameter to overwrite if the target file already exists."; break } 
  }  
 
  Process { 
    $i++
    $Activity = "Creating ISO File"
    $CurFolderText = '"Item: $($i.ToString().PadLeft($Script:IsoItemsCollection.Count.ToString().Length)) of $($Script:IsoItemsCollection.Count) | $($item.FullName)"'
    $CurFolderBlock = [ScriptBlock]::Create($CurFolderText)
   
    if($FromClipboard) { 
      if($PSVersionTable.PSVersion.Major -lt 5) { Write-Error -Message 'The -FromClipboard parameter is only supported on PowerShell v5 or higher'; break } 
      $Source = Get-Clipboard -Format FileDropList 
    } 

    foreach($item in $Source) { 
    Write-Host " " 
    Write-Host "       Processing:$item"    
      if($item -isnot [System.IO.FileInfo] -and $item -isnot [System.IO.DirectoryInfo]) { 
        $item = Get-Item -LiteralPath $item 
      } 
  if($item){     
  try { 

    $CurFolderpPercent = $i / $Script:IsoItemsCollection.Count * 100
    $Task = "Adding item files into ISO container"
    Write-Progress -Id 1 -Activity $Activity -Status (& $CurFolderBlock) -CurrentOperation $Task -PercentComplete $CurFolderpPercent 

    $Image.Root.AddTree($item. FullName, $true) 
  }
  catch { Write-Error -Message ($_.Exception.Message.Trim() + ' Try a different media type.') } 
  } 
  } 
  } # End of Process
 
  End {  
    if ($Boot) { $Image.BootImageOptions=$Boot }  
    $Result = $Image.CreateResultImage()  
    [ISOFile]::Create($Target.FullName,$Result.ImageStream,$Result.BlockSize,$Result.TotalBlocks) 
    $Target 
    Write-Host "Done! Script is finished executing." -ForegroundColor Green
  } 
}

Function Mount-ISOFile{

    Param(
    [Parameter(Mandatory=$True)]
    [string]$ImagePath,
    [Parameter(Mandatory=$True)]
    [string]$USBDriveLetter,
    [string]$label = "WinRE"
    )

Begin{
    
    Function Get-PBRFile($Drive){
    
    $partitionStyle = (Get-Content -Path "$($Drive)\sources\`$PBR_Diskpart.txt"|
    Select-String -Pattern "convert" |
    ForEach-Object{$_.ToString().Replace("convert " ,$null)})
    return $partitionStyle.Trim()
    }

    Function do_CopyFiles($diskLetter, $folder){

    $FOF_CREATEPROGRESSDLG = "&H0&"
    $objShell = New-Object -ComObject "Shell.Application"
    $objFolder = $objShell.NameSpace($diskLetter)
 
    if(Test-Path -Path $folder){
	$folder = $folder + "\*.*"
        $objFolder.CopyHere($folder, $FOF_CREATEPROGRESSDLG)
    }  
    else{
        [System.Windows.Forms.MessageBox]::Show("Folder does not exist!")
    }
}

# Get USB drive and ISO file size - validate
$Disk = Get-Disk|Where-Object {$_.BusType -eq 'USB'} 
$USBDriveSize = ($Disk | Where-Object Number -eq ($Disk.Number) | Select -ExpandProperty Size)
$ISOFileSize = (Get-ChildItem $ImagePath).Length

If($ISOFileSize -gt $USBDriveSize)
{
Write-Warning "There is not enough free space on USB Drive!!!"
Write-Host "USB Drive capacity: $(($USBDriveSize/1GB).ToString("0.00 GB"))."
Write-Host "ISO File size: $(($ISOFileSize/1GB).ToString("0.00 GB"))."
Write-Warning "A USB drive with a $(($ISOFileSize/1GB).ToString("0.00 GB")) capacity is needed."
Break
}

#Mount ISO File
If(-not(Get-DiskImage -ImagePath $ImagePath | Get-Volume))
    {
        $ISOFileMounted = $true
    Try{  
        Write-Host "Mounting ISO File, please wait ..." -ForegroundColor Green      
        Mount-DiskImage -ImagePath $ImagePath -ErrorAction Stop | Out-Null
    }
    Catch{
        Write-Host "Error: $_.Exception.GetType().FullName; Error: $_.Description" -ForegroundColor Yellow
        Break    
    }
}
Else
   {
     Write-Host "ISO File already mounted" -ForegroundColor Green
}# End of If/Else

$ISODrive = ((Get-DiskImage -ImagePath $ImagePath | Get-Volume).DriveLetter) + ":"

Write-Host "ISO File mounted to $ISODrive" -ForegroundColor Green

$partitionStyle = (Get-PBRFile -Drive $ISODrive)

} # End of Begin
Process{

 $Disk = Get-Disk|Where-Object {$_.BusType -eq 'USB'} 

 Get-Disk -Number ($Disk.Number)| Get-Partition | Remove-Partition -Confirm:$False -ErrorAction SilentlyContinue

 Clear-Disk -Number $Disk.Number -RemoveData -RemoveOEM -Confirm:$false -ErrorAction SilentlyContinue

 Switch($partitionStyle){

    "MBR" {Initialize-Disk -Number $Disk.Number -PartitionStyle MBR -ErrorAction SilentlyContinue}
    "GPT" {Initialize-Disk -Number $Disk.Number -PartitionStyle GPT -ErrorAction SilentlyContinue}

 }
 
 $partition = New-Partition -DiskNumber $Disk.Number -DriveLetter $USBDriveLetter -UseMaximumSize -IsActive

 Format-Volume -Partition $partition -FileSystem FAT32 -NewFileSystemLabel "$($label)"

 Write-Host "USB drive formated, preparing to copy files, please wait ..." -ForegroundColor Green

 $USBDriveLetter = $USBDriveLetter  + ":"

 do_CopyFiles -diskLetter $USBDriveLetter -folder $ISODrive

} # End of Process
End{
     If($ISOFileMounted)
        {
            Write-Host "Dismounting image ..." -ForegroundColor Green
            Dismount-DiskImage -ImagePath $ImagePath
        }
        Write-Host "Recovery USB Drive creation complete!" -ForegroundColor Green
    }
} 
# ---------------------------|End Of Functions|----------------------------------

switch ($PsCmdlet.ParameterSetName){ 
"MountISO"{ 
    Mount-ISOFile -ImagePath $ImagePath -USBDriveLetter $USBDriveLetter -label $label 
 } 
"CreateISO"{
    $Disk= Get-Disk | Where-Object { $_.BusType –eq ‘USB’ }
    $DriveLetter = "$(($Disk | Get-Partition).DriveLetter):\"
    Write-Host "Collecting info about USB Recovery Drive, please wait ..." -ForegroundColor Green
    $Script:IsoItemsCollection = (Get-ChildItem $DriveLetter)
    Write-Host  "Creating ISO File - Adding Folders/Files to ISO File" -ForegroundColor Green
    Dir $DriveLetter | New-IsoFile -Path $Path -Title $Title -Force
 } 
}

And whenever you need USB Recovery drive, just restore the backup image to the same or another USB stick. Run the following PowerShell command:

Example: CreateOrMount-WinREisoFile.ps1 -ImagePath C:\WinRe\WinRE.iso –USBDriveLetter Z -label “WinRE”

Picture 03: recreating USB Recovery drive form an ISO file.

Just as an extra note, the process of making USB recovery drive creates additional files in your computer’s C:\Windows\System32\Recovery folder. These files ($PBR_Diskpart.txt, $PBR_ResetConfig.xml and ReAgent.xml) are present on USB media as well and my Powershell script reads one of them to configure a proper partition style for a bootable USB stick.

Picture 04: additional files created in the computer’s C:\Windows\System32\Recovery folder.

At the end, it is important to remember that a Recovery Drive is BIT specific, and if you create a Recovery Drive in a 64-bit version of Windows 10, you can’t use that drive to boot up and repair a 32-bit version of Windows 10.

The PowerShell script described here is available in download section, under Powershell.

 




Wireless Support WinRE 10 – Part Two

In my blog “Wireless Support WinRE 10 – Part One”, I explored an idea of using Windows 10 WinRE.wim image and an optional component (Feature Pack) called WinPE-WiFi-Package to provide support for adding wireless drivers to WinRE. My understanding is that WinRE.wim is just a modified version of a WinPE image, so I can use all the tolls and scripts available for customization of WinPE images.
Here, I will use PowerShell script to automate process of creating WinRE with wireless support.

How this script – Create-BootableWinReUSBDrive.ps1 automate process
1. You need to have Windows Assessment and Deployment Kit (Windows ADK) installed
2. Download WinRE-Wireless.zip package from the download section, under Application folder. After you download the file, please do the following:
3. Unzip the file (in my example C:\ WinRE-Wireless).
4. Connect your external USB Drive to your computer.
5. Find the PowerShell script named: Create-BootableWinReUSBDrive.ps1 (Important: there are two files: Create-BootableWinReUSBDrive.ps1 and WinRE-Build.ps1).
6. Ran as administrator the script named Create-BootableWinReUSBDrive.ps1, which opens a form as an interface to collect all necessary info for the second script named WinRE-Build.ps1 and create Media files that are necessary to make the external USB drive bootable.

Again, this is a two steps process, as described in my previous blogs and shown in the picture below:

Picture 01: in the first step you make your selections and start creating Bootable Media files.
During the first step, please make your selection: select the 32 or 64 Bit Architecture, custom (HTA or PowerShell) tool for WinRE boot option menus, the WinRE.wim file source folder, and the source folder named WinRE-Wireless by clicking ‘Browse Source’ button; once this folder is selected, please click ‘Start’ button to start creation of the Media files (i.e. the files and applications essential for booting USB drive). Important: do not close PowerShell Command line windows as shown below.

Picture 02: WinRE Wireless Media files being created, do not close command line window

Once all Media files are created, please proceed to the second step in order to make External USB Drive bootable. First click on ‘View Drive(s)’ button to display all available drives. Click the radio button to select the file system (FAT 32 or NTFS) to be used to format external USB drive(s). Finally, click ‘Run’ button to format selected USB drive(s) and copy all Media files.
Important: The column named ‘Partition Type’ with its green / red background color will help you distinguished the USB drive that reports itself as Fixed/Removable. The Removable USB drive should not be used with FAT32 option which creates two partitions.

Picture 03: Select a drive, click a radio button and finally click the ‘Run’ button to format the selected drive and copy Media files to the selected USB drive.

Finally, at the end of the second step, you will have a bootable USB drive with WinRE image, wireless support and custom menu option created. If you boot your computer using this USB drive you will see the menu option for Custom Tool (in my example HTA tool) and WinRE detecting Wi-Fi networks.

Picture 04: Custom menu option and Wi-Fi support

To download the WinRE-Wireless application, please go to the site’s download section, and expand the Application folder. In the compressed download file I’ve included a script to export WLAN profile(s).