PowerShell Script for Recovery Partitions - Resize_Recovery.ps1

garlin

Moderator
Staff member
Messages
7,083
Reaction score
3,511
When using NTLite to create a System Drive's new disk layout, it may cause a major problem by assigning the Recovery partition in sequential order, before the Windows partition. This becomes an issue if Windows Update runs out of disk space, while applying the Monthly Update to WinRE.

The normal practice for installing Windows is to create a standard UEFI-based disk layout:
dep-win10-partitions-uefi.png

You have two options with WinPE Setup:
- Create a disk layout, using fixed partition sizes (if known in advance), and which assign Recovery to the end.​
- Create a disk layout, which works for any size disk, but assigns Recovery in front of Windows.​

NTLite can perform both options, but most users choose the second option. Setup has a design flaw where you can only use a disk layout to extend one disk partition (normally Windows) to the disk's end, but unblocked by any other partition.

By not assigning Recovery to the end makes it more difficult to expand the partition, in case more disk space is required. A 3rd-party disk partitioning tool is required to fix this problem if the Recovery partition unexpectedly runs out of space.

When Setup finds a disk with no partitions, it creates a new layout which extends Windows to the end of the disk, before shrinking it to allow Recovery to follow Windows.

You may have seen Windows install guides which instruct you to create a list of WinPE-based RunSynchronous commands to duplicate Setup's behavior by first extending Windows partition, and then shrinking it.
Code:
cmd.exe /c echo select disk 0 >> X:\diskpartUEFI.txt
cmd.exe /c echo clean >> X:\diskpartUEFI.txt
cmd.exe /c echo convert gpt >> X:\diskpartUEFI.txt
cmd.exe /c echo create partition efi size=260 >> X:\diskpartUEFI.txt
cmd.exe /c echo format quick fs=fat32 label="System" >> X:\diskpartUEFI.txt
cmd.exe /c echo create partition msr size=128 >> X:\diskpartUEFI.txt
cmd.exe /c echo create partition primary >> X:\diskpartUEFI.txt
cmd.exe /c echo shrink desired=500 minimum=500 >> X:\diskpartUEFI.txt
cmd.exe /c echo format quick fs=ntfs label="Windows" >> X:\diskpartUEFI.txt
cmd.exe /c echo create partition primary >> X:\diskpartUEFI.txt
cmd.exe /c echo format quick fs=ntfs label="Recovery tools" >> X:\diskpartUEFI.txt
cmd.exe /c echo set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac" >> X:\diskpartUEFI.txt
cmd.exe /c echo gpt attributes=0x8000000000000001 >> X:\diskpartUEFI.txt
cmd.exe /c echo exit >> X:\diskpartUEFI.txt
cmd.exe /c diskpart.exe /s X:\diskpartUEFI.txt

This solution immediately creates two more problems:
1. You need to insert a long list of RunSynchronousCommands into your unattended file.​
2. If you have existing WinPE commands, they need to be shifted down and renumbered.​


I wrote a PowerShell script which takes of the problem by importing any unattended file and safely inserting the diskpart commands as needed. This script works on the XML level, so it will leave everything unrelated to the WinPE pass untouched.

Run the script against your unattended file:
Code:
 .\Resize_Recovery.ps1 .\autounattend.xml
Updating file: ".\autounattend.xml" -> UEFI, Recovery 800 MB

If you provide a filename that doesn't exist, a new barebones file will be created for you which will repartition the disk.
Code:
.\Resize_Recovery.ps1 unattend.xml
Creating new file: "unattend.xml" -> UEFI, Recovery 800 MB

The script will delete any previous <DiskConfiguration> XML block, and insert the new RunSynchronous commands under <Order>1 and <Order>2. All existing WinPE pass commands will be shifted down by (<Order> + 2).

By default, the script creates an UEFI-based disk layout using the entire disk, except for a 800 MB Recovery partition at the end.

There are several command-line options:
-UEFIChange disk layout to UEFI standard (default mode)
-MBRChange disk layout to MBR standard
-Disk #Select a different disk number
This is the disk number as reported by diskpart, after WinPE is booted.
-VisibleDon't hide the CMD window running diskpart, if you want to see the script running
filenameName of the unattended file
sizeSize of Recovery partition in MB
800 MB is the recommended default, 250 MB is the minimum.

Code:
 .\Resize_Recovery.ps1 -MBR autounattend.xml 1024 -Visible
Updating file: "autounattend.xml" -> [visible] MBR, Recovery 1024 MB

.\Resize_Recovery.ps1 -UEFI autounattend.xml 760
Updating file: "autounattend.xml" -> UEFI, Recovery 760 M

Whenever you provide an existing file, a backup copy [filename].bak is always created.


How do I use the script? If you need to use NTLite's Unattended mode, allow NTLite to create the autounattend.xml.

When it's done, run the script on autounattend.xml to update it. There will be a backup file in the same folder, in case you need to revert. You can use this script on any unattended file created by another tool, or written/edited by yourself.
 

Attachments

Technical notes in case you're wondering how to reduce 16 lines to only 2.

Myths about diskpart commands:
- volume labels are optional (Setup doesn't do them)​
- assigned letters are optional (Setup doesn't do them)​
- "set [id]" doesn't require quotes for [id]​
- "exit" isn't required by "diskpart /s", it's only for when you're running diskpart interactively​

1st pass, based on Microsoft's original guide on UEFI disks:
Code:
cmd.exe /c echo select disk 0 >> X:\diskpartUEFI.txt
cmd.exe /c echo clean >> X:\diskpartUEFI.txt
cmd.exe /c echo convert gpt >> X:\diskpartUEFI.txt
cmd.exe /c echo create partition efi size=260 >> X:\diskpartUEFI.txt
cmd.exe /c echo format quick fs=fat32 label="System" >> X:\diskpartUEFI.txt
cmd.exe /c echo create partition msr size=128 >> X:\diskpartUEFI.txt
cmd.exe /c echo create partition primary >> X:\diskpartUEFI.txt
cmd.exe /c echo shrink desired=500 minimum=500 >> X:\diskpartUEFI.txt
cmd.exe /c echo format quick fs=ntfs label="Windows" >> X:\diskpartUEFI.txt
cmd.exe /c echo create partition primary >> X:\diskpartUEFI.txt
cmd.exe /c echo format quick fs=ntfs label="Recovery tools" >> X:\diskpartUEFI.txt
cmd.exe /c echo set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac" >> X:\diskpartUEFI.txt
cmd.exe /c echo gpt attributes=0x8000000000000001 >> X:\diskpartUEFI.txt
cmd.exe /c echo exit >> X:\diskpartUEFI.txt
cmd.exe /c diskpart.exe /s X:\diskpartUEFI.txt
Everyone copies this example. WTF? 15 commands = 60 lines of actual XML

2nd pass, after I learned to collapse multiple echo commands down to 3 lines:
Code:
cmd /c @echo off & (echo select disk 0 & echo clean & echo convert gpt & echo create partition efi size=260 & echo format quick fs=fat32 label="System" & echo create partition msr size=16 & echo create partition primary) > X:\UEFI.txt
cmd /c @echo off & (echo shrink minimum=500 & echo format quick fs=ntfs label="Windows" & echo create partition primary & echo format quick fs=ntfs label="Recovery" & echo set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac") >> X:\UEFI.txt
cmd /c @echo off & (echo gpt attributes=0x8000000000000001) >> X:\UEFI.txt & diskpart /s X:\UEFI.txt
<Path></Path> enforces a 259 character limit on each command line.

3rd pass, after I learned diskpart does shortcuts:
Code:
cmd /c @echo off & (echo sel disk 0 & echo cle & echo con gpt & echo cre par efi size=100 & echo for quick fs=fat32 & echo cre par msr size=16 & echo cre par pri & echo shr minimum=1024 & echo for quick fs=ntfs) > X:\UEFI.txt
cmd /c @echo off & (echo cre par pri & echo for quick fs=ntfs & echo set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac" & echo gpt attributes=0x8000000000000001) >> X:\UEFI.txt & diskpart /s X:\UEFI.txt

4th pass, removed more echo's by using an array:
Code:
cmd /c (for %a in ("sel dis 0" "cle" "con gpt" "cre par efi size=100" "for quick fs=fat32" "cre par msr size=16" "cre par pri" "shr minimum=1024" "for quick fs=ntfs") do @echo %~a) > X:\UEFI.txt
cmd /c (for %a in ("cre par pri" "for quick fs=ntfs" "set id=de94bba4-06d1-4d40-a16a-bfd50179d6ac" "gpt attributes=0x8000000000000001") do @echo %~a) >> X:\UEFI.cmd & diskpart /s X:\UEFI.txt

5th pass, using an "invisible VBS" script to hide the flashing CMD window.
Code:
cmd /c echo A=Split(WScript.Arguments(0),","): For i=0 to UBound(A): B=B ^& """" ^& A(i) ^& """ ": Next: L="cmd /c (for %a in (" ^& B ^& ") do @echo %~a) | diskpart": CreateObject("WScript.Shell").Run L,0,True > HIDE.vbs
wscript HIDE.vbs "sel dis 0,cle,con gpt,cre par efi size=100,for quick fs=fat32,cre par msr size=16,cre par pri,shr minimum=1024,for quick fs=ntfs,cre par pri,for quick fs=ntfs,set id=de94bba4-06d1-4d40-a16a-bfd50179d6ac,gpt attributes=0x8000000000000001"
That last line is perfection.
 
Back
Top