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.

 




2 thoughts on “PowerShell – Create USB Recovery Drive

  1. Tom

    Hi!
    My name is Tom.
    I am from Hungary.

    Your website is super.
    You are very skilled.
    I’m interested in what you’re doing, very good.
    I make similar things to myself, but not in such a profitable way.

    Bye
    Tom

    Reply
    1. admin Post author

      Thank you Tom for nice comments.
      Please note that all my scripts and applications are free, and not for profit.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *