Partager via


Ajouter une mise à jour à Windows RE

Vous pouvez appliquer une mise à jour, que ce soit une mise à jour cumulative (LCU) ou une mise à jour dynamique (DU) du système d’exploitation sécurisée, à une image Windows RE. Les étapes de mise à jour d’une image Windows RE diffèrent si vous mettez à jour une image hors connexion ou une image sur un système en cours d’exécution.

  • Lorsque vous ajoutez une LCU : avant d’ajouter une mise à jour, mais aussi après l’avoir ajouté, vérifiez le numéro de version WinRE pour vous assurer que la mise à jour a bien été appliquée.
  • Lorsque vous ajoutez un package DU : après avoir ajouté le package, vérifiez la liste des packages dans l’image pour vous assurer que le package a bien été ajouté à l’image.

Mettre à jour une image hors connexion

Les étapes ci-dessous expliquent comment ajouter un package mis à jour à une image Windows RE hors connexion.

Monter votre image Windows

L’image Windows RE (winre.wim) est incluse dans l’image Windows (install.wim) et finit par être copiée dans la partition d’outils Windows RE sur le PC ou le périphérique de destination. Pour modifier l’image WinRE, vous devez d’abord monter l’image Windows, puis monter l’image Windows RE. Une fois les deux images montées, vous pouvez apporter vos modifications, démonter l’image WinRE, puis démonter l’image Windows.

  • Créez un répertoire de montage et montez install.wim :

    md C:\mount\windows
    Dism /Mount-Wim /WimFile:C:\images\install.wim /index:1 /MountDir:C:\mount\windows 
    

Monter votre image WinRE

  • Montez le fichier image Windows RE à partir de votre image montée.

    Md c:\mount\winre 
    Dism /Mount-Wim /WimFile:C:\mount\windows\Windows\System32\Recovery\winre.wim /index:1 /MountDir:C:\mount\winre 
    

    Conseil

    Si winre.wim ne peut pas être affiché sous le répertoire spécifié, utilisez la commande suivante pour rendre le fichier visible :

    attrib -h -a -s C:\mount\windows\Windows\System32\Recovery\winre.wim
    

Ajouter une mise à jour à WinRE

Une fois votre image de récupération montée, vous pouvez ajouter la mise à jour pour résoudre le problème.

  1. Téléchargez la mise à jour requise et ajoutez-la à votre image de récupération.

    Dism /Add-Package /Image:C:\mount\winre /PackagePath:"c:\downloadedupdate\update.msu" 
    

    or

    Dism /Add-Package /Image:C:\mount\ /PackagePath:"c:\downloadedupdate\dynamicupdate.cab" 
    
  2. Si vous avez ajouté une mise à jour dynamique, vérifiez que le package que vous avez ajouté se trouve dans votre image :

     Dism /Image:C:\mount\ /Get-Packages
    

    Dans la liste, cherchez la mise à jour que vous venez d’ajouter. Le package doit inclure le numéro de la base de connaissances et indiquera State en tant que Installed s’il a bien été ajouté :

    Package Identity : Package_for_KB5021041~31bf3856ad364e35~amd64~~22621.815.1.0
    State : Installed
    Release Type : Update
    
  3. Nettoyez votre image de récupération et réinitialisez la base des composants remplacés :

    dism /image:C:\mount /cleanup-image /StartComponentCleanup /ResetBase 
    

Démontez vos images

  1. Fermez toutes les applications qui peuvent accéder aux fichiers à partir de l’image, y compris l’Explorateur de fichiers.

  2. Validez les modifications et démontez l’image système Windows RE :

    Dism /Unmount-Image /MountDir:"C:\mount\winre" /Commit 
    

    Ce processus peut prendre quelques minutes.

  3. Validez les modifications et démontez l’image système Windows :

    Dism /Unmount-Image /MountDir:"C:\mount\windows" /Commit 
    

    Où C correspond à la lettre du lecteur qui contient l’image. Ce processus peut prendre quelques minutes.

L’image Windows RE est maintenant mise à jour et fait partie de l’image Windows plus grande.

Appliquer la mise à jour à un PC en cours d’exécution

Vous pouvez appliquer le correctif à l’image de récupération Windows sur un PC en cours d’exécution. Les étapes suivantes vous guident tout au long du montage d’une image de récupération Windows, de l’application d’une mise à jour, puis de l’enregistrement des modifications :

  1. Ouvrez une invite de commandes en tant qu’administrateur.

  2. Créez un répertoire dans lequel vous pouvez monter l’image de récupération pour y apporter des modifications.

    md c:\mount 
    
  3. Montez votre image de récupération avec REAgentC :

    ReAgentC.exe /mountre /path c:\mount 
    
  4. Une fois votre image de récupération montée, vous pouvez télécharger et ajouter la mise à jour à l’image Windows RE.

    Dism /Add-Package /Image:C:\mount\ /PackagePath:"c:\downloadedupdate\update.msu" 
    

    or

    Dism /Add-Package /Image:C:\mount\ /PackagePath:"c:\downloadedupdate\dynamicupdate.cab" 
    
  5. Si vous avez ajouté une mise à jour dynamique, vérifiez que le package que vous avez ajouté se trouve dans votre image :

     Dism /Image:C:\mount\ /Get-Packages
    

    Dans la liste, cherchez la mise à jour que vous venez d’ajouter. Le package doit inclure le numéro de la base de connaissances et indiquera State en tant que Installed s’il a bien été ajouté :

    Package Identity : Package_for_KB5021041~31bf3856ad364e35~amd64~~22621.815.1.0
    State : Installed
    Release Type : Update
    
  6. Nettoyez votre image de récupération et réinitialisez la base des composants remplacés :

    dism /image:C:\mount /cleanup-image /StartComponentCleanup /ResetBase 
    
  7. Démontez l’image de récupération Windows :

    ReAgentC.exe /unmountre /path c:\mount /commit 
    
  8. Si le disque du PC est protégé par BitLocker ou Device Encryption : utilisez ReagentC pour désactiver, puis réactiver Windows RE. Cela garantit que l’image Windows RE mise à jour est activée et correctement configurée pour votre installation Windows :

    reagentc /disable
    reagentc /enable 
    

L’image Windows RE sur le PC en cours d’exécution est maintenant mise à jour.

Vérifier la version de l’image WinRE

Vous pouvez vérifier le numéro de version de l’image WinRE sur les installations Windows en ligne et hors connexion. Cela peut être utile pour vérifier la version WinRE avant l’ajout d’une mise à jour, puis après avoir son application pour vous assurer qu’elle a bien été ajoutée.

Remarque

Le numéro de version WinRE change uniquement après l’ajout d’une LCU. Si vous ajoutez un package DU, utilisez DISM /get-packages tel que décrit dans les étapes ci-dessus pour vous assurer que le package a été ajouté à l’image.

Valider la version de Windows RE dans un système d’exploitation Windows en ligne

  1. Utilisez ReagentC pour rechercher l’emplacement de l’image WinRE installée en exécutant la commande suivante :

    reagentc /info
    

    Cette commande retourne l’emplacement Windows RE, qui est similaire à l’exemple suivant :

    \\?\GLOBALROOT\device\harddisk0\partition4\Recovery\WindowsRE
    
  2. Utilisez DISM pour obtenir des informations de version sur l’image winre.wim située dans le chemin d’accès retourné par ReagentC à l’étape précédente. Veillez à ajouter le nom de l’image WinRE et le numéro d’index de l’image au chemin d’accès lorsque vous exécutez la commande :

    Dism /Get-ImageInfo /ImageFile:\\?\GLOBALROOT\device\harddisk0\partition4\Recovery\WindowsRE\winre.wim /index:1
    

    Notez la version indiquée. Par exemple :

    Version : 10.0.22621
    ServicePack Build : 1
    ServicePack Level : 0
    

    Assurez-vous que la valeur ServicePackBuild est supérieure ou égale à l’UBR pour la mise à jour que vous avez ajoutée. Par exemple, pour Windows 11, version 22H2, la mise à jour de sécurité de novembre affiche 819 comme SerivcePack Build, car le numéro de version complet de cette mise à jour est 22621.819.

    • Si la version indiquée est une version antérieure, cela signifie que l’image Windows RE n’est pas à jour.
    • Si la version indiquée est identique ou ultérieure, aucune action n’est nécessaire.

Valider la version de Windows RE dans un système d’exploitation Windows hors connexion

  1. Montez l’image Windows dans un dossier local, par exemple C:\mount\windows :

    mkdir C:\mount\windows
    Dism /Mount-Image /ImageFile:C:\mount\install.wim /Index:1 /MountDir:C:\mount\windows
    
  2. Utilisez DISM pour obtenir des informations de version sur l’image winre.wim située dans le dossier Windows\system32\recovery. Veillez à spécifier le numéro d’index de l’image lorsque vous exécutez la commande :

    Dism /Get-ImageInfo /ImageFile:c:\mount\windows\windows\system32\recovery\winre.wim /index:1
    

    Notez la version indiquée. Par exemple :

    Version : 10.0.22621
    ServicePack Build : 1
    ServicePack Level : 0
    

    Assurez-vous que la valeur ServicePackBuild est supérieure ou égale à l’UBR pour la mise à jour que vous avez ajoutée. Par exemple, pour Windows 11, version 22H2, la mise à jour de sécurité de novembre affiche 819 comme SerivcePack Build, car le numéro de version complet de cette mise à jour est 22621.819.

    • Si la version indiquée est une version antérieure, cela signifie que l’image Windows RE n’est pas à jour.
    • Si la version indiquée est identique ou ultérieure, aucune action n’est nécessaire.

Étendre la partition Windows RE

L’exemple de script ci-dessous peut être utilisé pour agrandir la partition de récupération pour une mise à jour réussie de l’environnement de récupération Windows (WinRE). Il est recommandé d’avoir 250 Mo d’espace libre dans la partition de récupération pour une installation réussie des mises à jour WinRE. Il est possible d’utiliser l’exemple de script ci-dessous sur les périphériques qui n’ont peut-être pas suffisamment d’espace libre dans la partition de récupération afin d’étendre cette dernière de 250 Mo.

Redémarrez votre machine avant d’exécuter le script. Cela est essentiel, car il peut y avoir des opérations de partition en attente intermédiaires sur votre ordinateur qui devront être finalisées avant que le script puisse agrandir en toute sécurité la partition WinRE. Une fois votre machine redémarrée, ouvrez PowerShell en tant qu’administrateur et exécutez le chemin d’accès mkdir <vers le nouveau répertoire de sauvegarde> pour créer un répertoire de sauvegarde que le script peut utiliser au cas où la restauration de la partition d’origine échoue. Notez l'emplacement de ce répertoire de sauvegarde, car le script vous demandera votre chemin de sauvegarde. Si vous déployez cette solution à grande échelle, vous pouvez contourner l'invite du script en utilisant les paramètres suivants :

« <fichier de script> -SkipConfirmation $true -BackupFolder<dossier de sauvegarde> »

Par exemple,

Resize_script.ps1 -SkipConfirmation $true -BackupFolder c:\winre_backup

Param (
[Parameter(Mandatory=$false,HelpMessage="Skip confirmation")][bool]$SkipConfirmation=$false,
[Parameter(Mandatory=$true,HelpMessage="Path to backup old WinRE partition content to")][string]$BackupFolder
)
# ------------------------------------
# Helper functions
# ------------------------------------
# Log message
function LogMessage([string]$message)
{
  $message = "$message"
  Write-Host $message
}
# Extract numbers from string
function ExtractNumbers([string]$str)
{
  $cleanString = $str -replace "[^0-9]"
  return [long]$cleanString
}
# Display partition info using fsutil
# Return an array, the first value is total size and the second value is free space
function DisplayPartitionInfo([string[]]$partitionPath)
{
  $volume = Get-WmiObject -Class Win32_Volume | Where-Object { $partitionPath -contains $_.DeviceID }
  LogMessage("  Partition capacity: " +  $volume.Capacity)
  LogMessage("  Partition free space: " + $volume.FreeSpace)
  return $volume.Capacity, $volume.FreeSpace
} 
# Display WinRE status
function DisplayWinREStatus()
{
  # Get WinRE partition info
  $WinREInfo = Reagentc /info
  foreach ($line in $WinREInfo)
  {
    $params = $line.Split(':')
    if ($params.Count -lt 2)
    {
      continue
    }
    if (($params[1].Trim() -ieq "Enabled") -Or (($params[1].Trim() -ieq "Disabled")))
    {
      $Status = $params[1].Trim() -ieq "Enabled"
      LogMessage($line.Trim())
    }
    if ($params[1].Trim() -like "\\?\GLOBALROOT*")
    {
      $Location = $params[1].Trim()
      LogMessage($line.Trim())
    }
  }
  
  return $Status, $Location
}
# ------------------------------------
# Main execution
# ------------------------------------
# Clear the error
$Error.Clear()
# ------------------------------------
# Examining the system to collect required info 
# for the execution
# Need to check WinRE status, collect OS and WinRE
# partition info
# ------------------------------------
LogMessage("Start time: $([DateTime]::Now)")
LogMessage("Examining the system...")
$NeedShrink = $true
$NeedCreateNew = $false
$NeedBackup = $false
# Get WinRE partition info
$InitialWinREStatus = DisplayWinREStatus
$WinREStatus = $InitialWinREStatus[0]
$WinRELocation = $InitialWinREStatus[1]
if (!$WinREStatus)
{
  LogMessage("Error: WinRE Disabled")
  exit 1
}
# Get System directory and ReAgent xml file
$system32Path = [System.Environment]::SystemDirectory
LogMessage("System directory: " + $system32Path)
$ReAgentXmlPath = Join-Path -Path $system32Path -ChildPath "\Recovery\ReAgent.xml"
LogMessage("ReAgent xml: " + $ReAgentXmlPath)
if (!(Test-Path  $ReAgentXmlPath))
{
  LogMessage("Error: ReAgent.xml cannot be found")
  exit 1
}
# Get OS partition
LogMessage("")
LogMessage("Collecting OS and WinRE partition info...")
$OSDrive = $system32Path.Substring(0,1)
$OSPartition = Get-Partition -DriveLetter $OSDrive
# Get WinRE partition
$WinRELocationItems = $WinRELocation.Split('\\')
foreach ($item in $WinRELocationItems)
{
    if ($item -like "harddisk*")
  {
    $OSDiskIndex = ExtractNumbers($item)
  }
  if ($item -like "partition*")
  {
    $WinREPartitionIndex = ExtractNumbers($item)
  }
}
LogMessage("OS Disk: " + $OSDiskIndex)
LogMessage("OS Partition: " + $OSPartition.PartitionNumber)
LogMessage("WinRE Partition: " + $WinREPartitionIndex)
$WinREPartition = Get-Partition -DiskNumber $OSDiskIndex -PartitionNumber $WinREPartitionIndex
$diskInfo = Get-Disk -number $OSDiskIndex
$diskType = $diskInfo.PartitionStyle
LogMessage("Disk PartitionStyle: " + $diskType)
# Display WinRE partition size info
LogMessage("WinRE partition size info")
$WinREPartitionSizeInfo = DisplayPartitionInfo($WinREPartition.AccessPaths)
LogMessage("WinRE Partition Offset: " + $WinREPartition.Offset)
LogMessage("WinRE Partition Type: " + $WinREPartition.Type)
LogMessage("OS partition size: " + $OSPartition.Size)
LogMessage("OS partition Offset: " + $OSPartition.Offset)
$OSPartitionEnds = $OSPartition.Offset + $OSPartition.Size
LogMessage("OS partition ends at: " + $OSPartitionEnds)
LogMessage("WinRE partition starts at: " + $WinREPartition.Offset)
$WinREIsOnSystemPartition = $false
if ($diskType -ieq "MBR")
{
  if ($WinREPartition.IsActive)
  {
    LogMessage("WinRE is on System partition")
    $WinREIsOnSystemPartition = $true
  }
}
if ($diskType -ieq "GPT")
{
  if ($WinREPartition.Type -ieq "System")
  {
    LogMessage("WinRE is on System partition")
    $WinREIsOnSystemPartition = $true
  }
}
# Checking the BackupFolder parameter
if ($PSBoundParameters.ContainsKey('BackupFolder'))
{
  LogMessage("")
  LogMessage("Backup Directory: [" + $BackupFolder + "]")
  
  $Needbackup = $true
  
  if ($WinREIsOnSystemPartition)
  {
    $Needbackup = $false
    LogMessage("WinRE is on System partition which will be preserved. No need to backup content")
  }
  else
  {
    if (Test-path $BackupFolder)
    {
      $items = Get-ChildItem -Path $BackupFolder
      if ($items)
      {
        LogMessage("Error: Existing backup directory is not empty")
        exit 1
      }
    }
    else
    {
      LogMessage("Creating backup directory...")
      try 
      {
        $item = New-Item -Path $BackupFolder -ItemType Directory -ErrorAction Stop
        if ($item)
        {
          LogMessage("Backup directory created")
        }
        else
        {
          LogMessage("Error: Failed to create backup directory [" + $BackupFolder + "]")
          exit 1
        }
      } catch 
      {
        LogMessage("Error: An error occurred: $_")
        exit 1
      }
    }
  }
}
# ------------------------------------
# Verify whether we meet requirements of execution
# - WinRE cannot be on OS partition for the extension
# - WinRE partition must be the next partition after OS partition
# - If WinRE partition already have >=250MB free space, no need to do repartition
# - If there is enough unallocated space to grow the WinRE partition size, skip shrinking OS
# 
# However, if the WinRE partition is before the OS partition, there is no chance to extend it
# As a result, it's better to create a new WinRE partition after the OS partition
# ------------------------------------
# Perform a few checks
LogMessage("")
LogMessage("Verifying if the WinRE partition needs to be extended or not...")
if (!(($diskType -ieq "MBR") -Or ($diskType -ieq "GPT")))
{
  LogMessage("Error: Got an unexpected disk partition style: " +$diskType)
  exit 1
}
# WinRE partition must be after OS partition for the repartition
if ($WinREPartitionIndex -eq $OSPartition.PartitionNumber)
{
  LogMessage("WinRE and OS are on the same partition, should not perform extension")
  exit 0
}
$supportedSize = Get-PartitionSupportedSize -DriveLetter $OSDrive
# if there is enough free space, skip extension
if ($WinREPartitionSizeInfo[1] -ge 250MB)
{
  LogMessage("More than 250 MB of free space was detected in the WinRE partition, there is no need to extend the partition")
  exit 0
}
if ($WinREPartition.Offset -lt $OSPartitionEnds)
{
  LogMessage("WinRE partition is not after OS partition, cannot perform extension")
  LogMessage("Need to create a new WinRE partition after OS partition")
  $NeedCreateNew = $true
  $NeedShrink = $true
  
  # Calculate the size of repartition
  # Will create a new WinRE partition with current WinRE partition size + 250 MB
  # The OS partition size will be shrunk by the new WinRE partition size
  $targetWinREPartitionSize = $WinREPartitionSizeInfo[0] + 250MB
  $shrinkSize = [Math]::Ceiling($targetWinREPartitionSize / 1MB) * 1MB
  $targetOSPartitionSize = $OSPartition.Size - $shrinkSize
  if ($targetOSPartitionSize -lt $supportedSize.SizeMin)
  {
    LogMessage("Error: The target OS partition size after shrinking is smaller than the supported minimum size, cannot perform the repartition")
    exit 1
  }
}
else
{
  if ($WinREIsOnSystemPartition)
  {
    LogMessage("WinRE parititon is after the OS partition and it's also System partition")
    LogMessage("Error: Got unexpected disk layout, cannot proceed")
    exit 1
  }
  if (!($WinREPartitionIndex -eq ($OSPartition.PartitionNumber + 1)))
  {
    LogMessage("Error: WinRE partition is not right after the OS partition, cannot extend WinRE partition")
    exit 1
  }
  # Calculate the size of repartition
  # Will shrink OS partitition by 250 MB
  $shrinkSize = 250MB
  $targetOSPartitionSize = $OSPartition.Size - $shrinkSize
  $targetWinREPartitionSize = $WinREPartitionSizeInfo[0] + 250MB
  $UnallocatedSpace = $WinREPartition.Offset - $OSPartitionEnds;
  # If there is unallocated space, consider using it
  if ($UnallocatedSpace -ge 250MB)
  {
    $UnallocatedSpace = $WinREPartition.Offset - $OSPartitionEnds;
    LogMessage("Found unallocated space between OS and WinRE partition: " + $UnallocatedSpace)
    LogMessage("There is already enough space to extend WinRE partition without shrinking the OS partition")
    $NeedShrink = $false
    $targetOSPartitionSize = 0
  }
  else
  {
    $shrinkSize = [Math]::Ceiling((250MB - $UnallocatedSpace)/ 1MB) * 1MB
    if ($shrinkSize > 250MB)
    {
      $shrinkSize = 250MB
    }
    $targetOSPartitionSize = $OSPartition.Size - $shrinkSize
    if ($targetOSPartitionSize -lt $supportedSize.SizeMin)
    {
      LogMessage("Error: The target OS partition size after shrinking is smaller than the supported minimum size, cannot perform the repartition")
      exit 1
    }
  }
}
# ------------------------------------
# Report execution plan and ask for user confirmation to continue
# ------------------------------------
# Report the changes planned to be executed, waiting for user confirmation
LogMessage("")
LogMessage("Summary of proposed changes")
if ($NeedCreateNew)
{
  LogMessage("Note: WinRE partition is before OS partition, need to create a new WinRE partition after OS partition")
  LogMessage("Will shrink OS partition by " + $shrinkSize)
  LogMessage("  Current OS partition size: " + $OSPartition.Size)
  LogMessage("  Target OS partition size after shrinking: " + $targetOSPartitionSize)
  LogMessage("New WinRE partition will be created with size: ", $targetWinREPartitionSize)
  if ($WinREIsOnSystemPartition)
  {
    LogMessage("Existing WinRE partition is also system partition, it will be preserved")
  }
  else
  {
    LogMessage("Existing WinRE partition will be deleted")
    LogMessage("  WinRE partition: Disk [" + $OSDiskIndex + "] Partition [" + $WinREPartitionIndex + "]")
    LogMessage("  Current WinRE partition size: " + $WinREPartitionSizeInfo[0])
  }
}
else
{
  if ($NeedShrink)
  {
    LogMessage("Will shrink OS partition by " + $shrinkSize)
    LogMessage("  Current OS partition size: " + $OSPartition.Size)
    LogMessage("  Target OS partition size after shrinking: " + $targetOSPartitionSize)
    if ($UnallocatedSpace -ge 0)
    {
      LogMessage("Unallocated space between OS and WinRE partition that will be used towards the new WinRE partition: " + $UnallocatedSpace)
    }
  }
  else
  {
    LogMessage("Will use 250MB from unallocated space between OS and WinRE partition")
  }
  LogMessage("Will extend WinRE partition size by 250MB")
  LogMessage("  WinRE partition: Disk [" + $OSDiskIndex + "] Partition [" + $WinREPartitionIndex + "]")
  LogMessage("  Current WinRE partition size: " + $WinREPartitionSizeInfo[0])
  LogMessage("  New WinRE partition size:     " + $targetWinREPartitionSize)
  LogMessage("WinRE will be temporarily disabled before extending the WinRE partition and enabled automatically in the end")
  if ($UnallocatedSpace -ge 100MB)
  {
    LogMessage("Warning: More than 100MB of unallocated space was detected between the OS and WinRE partitions")
    LogMessage("Would you like to proceed by using the unallocated space between the OS and the WinRE partitions?")
  }
}
if ($Needbackup)
{
  LogMessage("")
  LogMessage("The contents of the old WinRE partition will be backed up to [" + $BackupFolder + "]")
}
LogMessage("")
LogMessage("Please reboot the device before running this script to ensure any pending partition actions are finalized")
LogMessage("")
if ($SkipConfirmation)
{
  LogMessage("User chose to skip confirmation")
  LogMessage("Proceeding with changes...")
}
else
{
  $userInput = Read-Host -Prompt "Would you like to proceed? Y for Yes and N for No"
    
  if ($userInput -ieq "Y")
  {
    LogMessage("Proceeding with changes...")
  }
  elseif ($userInput -ieq "N")
  {
    LogMessage("Canceling based on user request, no changes were made to the system")
    exit 0
  }
  else
  {
    LogMessage("Error: Unexpected user input: [" + $userInput + "]") 
    exit 0
  }
}
LogMessage("")
LogMessage("Note: To prevent unexpected results, please do not interrupt the execution or restart your system")
# ------------------------------------
# Do the actual execution
# The main flow is:
# 1. Check whether ReAgent.xml has stage location and clear it for repartiion
# 2. Disable WinRE as WinRE partition will be deleted
# 3. Perform the repartition to create a larger WinRE partition
# 4. Re-enable WinRE
# ------------------------------------
LogMessage("")
# Load ReAgent.xml to clear Stage location
LogMessage("Loading [" + $ReAgentXmlPath + "] ...")
$xml = [xml](Get-Content -Path $ReAgentXmlPath)
$node = $xml.WindowsRE.ImageLocation
if (($node.path -eq "") -And ($node.guid -eq "{00000000-0000-0000-0000-000000000000}") -And ($node.offset -eq "0") -And ($node.id -eq "0"))
{
  LogMessage("Stage location info is empty")
}
else
{
  LogMessage("Clearing stage location info...")
  $node.path = ""
  $node.offset = "0"
  $node.guid= "{00000000-0000-0000-0000-000000000000}"
  $node.id="0"
  # Save the change
  LogMessage("Saving changes to [" + $ReAgentXmlPath + "]...")
  $xml.Save($ReAgentXmlPath)
}
# Disable WinRE
LogMessage("Disabling WinRE...")
reagentc /disable
if (!($LASTEXITCODE -eq 0))
{
  LogMessage("Warning: encountered an error when disabling WinRE: " + $LASTEXITCODE)
  exit $LASTEXITCODE
}
# Verify WinRE is under C:\Windows\System32\Recovery\WinRE.wim
$disableWinREPath = Join-Path -Path $system32Path -ChildPath "\Recovery\WinRE.wim"
LogMessage("Verifying that WinRE wim exists in downlevel at default location")
if (!(Test-Path $disableWinREPath))
{
  LogMessage("Error: Cannot find " + $disableWinREPath)
  
  # Re-enable WinRE
  LogMessage("Re-enabling WinRE on error...")
  reagentc /enable
  if (!($LASTEXITCODE -eq 0))
  {
    LogMessage("Warning: encountered an error when enabling WinRE: " + $LASTEXITCODE)
  }
  exit 1
}
# ------------------------------------
# Perform the repartition
# 1. Resize the OS partition
# 2. Delete the WinRE partition
# 3. Create a new WinRE partition
# ------------------------------------
LogMessage("Performing repartition to extend the WinRE partition ...")
# 1. Resize the OS partition
if ($NeedShrink)
{
  LogMessage("Shrinking the OS partition to create a larger WinRE partition")
  LogMessage("Resizing the OS partition to: [" + $targetOSPartitionSize + "]...")
  Resize-Partition -DriveLetter $OSDrive -Size $targetOSPartitionSize
  if ($Error.Count -gt 0) {
    LogMessage("Error: Resize-Partition encountered errors: " + $Error[0].Exception.Message)
    
    # Re-enable WinRE
    LogMessage("Re-enabling WinRE on error...")
    reagentc /enable
    if (!($LASTEXITCODE -eq 0))
    {
      LogMessage("Warning: encountered an error when enabling WinRE: " + $LASTEXITCODE)
    }
    exit 1
  }
  $OSPartitionAfterShrink = Get-Partition -DriveLetter $OSDrive
  LogMessage("Target partition size: " + $targetOSPartitionSize)
  LogMessage("Size of OS partition after shrinking: " + $OSPartitionAfterShrink.Size)
}
# 2. Delete the WinRE partition
LogMessage("")
if ($WinREIsOnSystemPartition)
{
  LogMessage("Existing WinRE partition is System partition, skipping deletion")
}
else
{  
  # If requested by user, backup rest of the content on WinRE partition to backup directory
  if ($Needbackup)
  {
    $sourcePath = $WinREPartition.AccessPaths[0] 
    LogMessage("Copying content on WinRE partition from [" + $sourcePath + "] to [" + $BackupFolder + "]...")
    
    # Copy-Item may have access issue with certain system folders, enumerate the children items and exlcude them
    $items = Get-ChildItem -LiteralPath $sourcePath -Force
    foreach ($item in $items)
    {
      if ($item.Name -ieq "System Volume Information")
      {
        continue
      }
      $sourceItemPath = Join-Path -Path $sourcePath -ChildPath $item.Name
      $destItemPath = Join-Path -Path $BackupFolder -ChildPath $item.Name
      try 
      {
        LogMessage("Copying [" + $sourceItemPath + "] to [" + $destItemPath + "]...")
        Copy-Item -LiteralPath $sourceItemPath -Destination $destItemPath -Recurse -Force
      } catch 
      {
        LogMessage("Error: An error occurred during copy: $_")
        exit 1
      }
    }
    
    LogMessage("Backup completed")
    LogMessage("")
  }
  LogMessage("Deleting WinRE partition: Disk [" + $OSDiskIndex + "] Partition [" + $WinREPartitionIndex + "]...")
  Remove-Partition -DiskNumber $OSDiskIndex -PartitionNumber $WinREPartitionIndex -Confirm:$false
  if ($Error.Count -gt 0) {
    LogMessage("Error: Remove-Partition encountered errors: " + $Error[0].Exception.Message)
    exit 1
  }
}
# A short sleep for the partition change
Sleep 5
# 3. Create a new WinRE partition
LogMessage("")
LogMessage("Creating new WinRE partition...")
LogMessage("Target size: " + $targetWinREPartitionSize)
if ($diskType -ieq "GPT")
{
  $partition = New-Partition -DiskNumber $OSDiskIndex -Size $targetWinREPartitionSize -GptType "{de94bba4-06d1-4d40-a16a-bfd50179d6ac}"
  
  $newPartitionIndex = $partition.PartitionNumber
  # A short sleep to make sure the partition is ready for formatting
  Sleep 2
  LogMessage("Formating the partition...")
  $result = Format-Volume -Partition $partition -FileSystem NTFS -Confirm:$false
  if ($Error.Count -gt 0) {
    LogMessage("Error: Format-Volume encountered errors: " + $Error[0].Exception.Message)
    exit 1
  }
}
else
{
  #$partition = New-Partition -DiskNumber $OSDiskIndex -Size $targetWinREPartitionSize -MbrType 0x27
  $targetWinREPartitionSizeInMb = [int]($targetWinREPartitionSize/1MB)
  $diskpartScript = 
@"
select disk $OSDiskIndex
create partition primary size=$targetWinREPartitionSizeInMb id=27
format quick fs=ntfs label="Recovery"
set id=27
"@
  $TempPath = $env:Temp
  $diskpartSciptFile = Join-Path -Path $TempPath -ChildPath "\ExtendWinRE_MBR_PowershellScript.txt"
  
  LogMessage("Creating temporary diskpart script to create Recovery partition on MBR disk...")
  LogMessage("Temporary diskpart script file: " + $diskpartSciptFile)
  $diskpartScript | Out-File -FilePath $diskpartSciptFile -Encoding ascii
  
  LogMessage("Executing diskpart script...")
  try 
  {
    $diskpartOutput = diskpart /s $diskpartSciptFile
    
    if ($diskpartOutput -match "DiskPart successfully") 
    {
      LogMessage("Diskpart script executed successfully")
    }
    else
    {
      LogMessage("Error executing diskpart script:" + $diskpartOutput)
      exit 1
    }
    LogMessage("Deleting temporary diskpart script file...")
    Remove-Item $diskpartSciptFile
  }
  catch 
  {
    LogMessage("Error executing diskpart script: $_")
    exit 1
  }
  
  $vol = Get-Volume -FileSystemLabel "Recovery"
  $newPartitionIndex = (Get-Partition | Where-Object { $_.AccessPaths -contains $vol.Path } ).PartitionNumber
}
if ($Error.Count -gt 0) 
{
  LogMessage("Error: New-Partition encountered errors: " + $Error[0].Exception.Message)
  exit 1
}
LogMessage("New Partition index: " + $newPartitionIndex)
# Re-enable WinRE
LogMessage("Re-enabling WinRE...")
reagentc /enable
if (!($LASTEXITCODE -eq 0))
{
  LogMessage("Warning: encountered an error when enabling WinRE: " + $LASTEXITCODE)
  exit $LASTEXITCODE
}
# In the end, Display WinRE status to verify WinRE is enabled correctly
LogMessage("")
LogMessage("WinRE Information:")
$FinalWinREStatus = DisplayWinREStatus
$WinREStatus = $FinalWinREStatus[0]
$WinRELocation = $FinalWinREStatus[1]
if (!$WinREStatus)
{
  LogMessage("Warning: WinRE Disabled")
}
$WinRELocationItems = $WinRELocation.Split('\\')
foreach ($item in $WinRELocationItems)
{
  if ($item -like "partition*")
  {
    $WinREPartitionIndex = ExtractNumbers($item)
  }
}
LogMessage("WinRE Partition Index: " + $WinREPartitionIndex)
$WinREPartition = Get-Partition -DiskNumber $OSDiskIndex -PartitionNumber $WinREPartitionIndex
$WinREPartitionSizeInfoAfter = DisplayPartitionInfo($WinREPartition.AccessPaths)
LogMessage("")
LogMessage("OS Information:")
$OSPartition = Get-Partition -DriveLetter $OSDrive
LogMessage("OS partition size: " + $OSPartition.Size)
LogMessage("OS partition Offset: " + $OSPartition.Offset)
if (!($WinREPartitionIndex -eq $newPartitionIndex))
{
  LogMessage("Warning: WinRE is installed to partition [" + $WinREPartitionIndex +"], but the newly created Recovery partition is [" + $newPartitionIndex + "]")
}
LogMessage("End time: $([DateTime]::Now)")
if ($NeedBackup)
{
  LogMessage("")
  LogMessage("The contents of the old WinRE partition has been backed up to [" + $BackupFolder + "]")
}
LogMessage("")
LogMessage("Successfully completed the operation")