Preparing Your Virtual Environment for Windows 11: Tackling EFI, Secure Boot, and TPM Part – 4 Automating the last mile for scale

Ok so I’ve covered all the prep work for getting the virtual environment to take Windows 11, but these tasks can be time consuming if there are hundreds or thousands of VMs that need to be upgraded.  

This post will cover some of the ways to add some automation.  The goal for this post is not just to scale this application but to provide ideas for your next big”Thing” that you have to do.   I’ll help explain some building blocks that I used to complete my puzzle, but your puzzle is going to look different.

Here we go.

SCCM/MDT is your friend. 

Most larger orgs have some sort of imaging solution and if you are a MSFT shop than there is a good chance you are already using Configuration Manager or MDT some places even both. If you are using either one, then you know where I’m going with this.  If not, then you will.

To tackle the first the MBR to GPT conversion at scale, using a task sequence is the first thing that comes to mind to hit many devices at once . It allows for you to reboot these devices automatically into Windows PE and then run commands and then reboot the device back into windows.

Leveraging the steps discussed Part – 2 Converting to GPT/EFI, we create a simple task sequence that

  1. Restart’s the computer in WinPE
  2. Runs the following command:  MBR2GPT.exe /convert /disk:0
  3. Reboot the computer.

You can get fancy and can also incorporate some automation to proactively fix the MBR partition and/or the BCD store.

Now that we have our task sequence to handle the disk let’s just deploy it right?  Well there is a VM side to this as well.  That we need to fix otherwise the device won’t boot, or have TPM right.   Here is where we can mix the best of SCCM and powershell to automate everything. 

First, we are going to deploy the TS to a collection as “available and we are going to note the Deployment ID in SCCM and the Package ID.  We are going to use it later. Then dump all the devices into the collection.

Calling the Task Sequence Via PowerShell

To call the TS via PowerShell I’m going to use the following code:

Note: that this PowerShell calls a SCCM task sequence calling the task sequence from MDT will look different and I’ll try to post it in the comments for reference.

SCCM Deployment Info
#TS DeploymentID
$TSID = Deployment ID
#TS PackageID
$TSPID =Package ID

#Import Virtual Device List
$VMs = get-content 'C:\temp\list.txt'
#Begin loop
foreach ($vm in $VMs) {
$objDateTimeCurrent = Get-Date
# Retrieve the PackageID and AdvertisementID from the machine actual policy
$SoftwareDistributionPolicy = Get-WmiObject -Namespace "root\ccm\policy\machine\actualconfig" -computername $VM -Class "CCM_SoftwareDistribution" | Where-Object { $_.ADV_AdvertisementID -eq $TSID -and $_.PKG_PackageID -eq $TSPID } | Select-Object -Property PKG_PackageID, ADV_AdvertisementID
 
        # Retrieve the ScheduleID used for triggering a new required assignment for task sequence
$ScheduleID = Get-WmiObject -Namespace "root\ccm\scheduler" -computername $VM -Class "CCM_Scheduler_History" | Where-Object { $_.ScheduleID -like "*$($SoftwareDistributionPolicy.PKG_PackageID)*" } | Select-Object -ExpandProperty ScheduleID
# Check if the RepeatRunBehavior is set to RerunAlways, if not change the value
$TaskSequencePolicy = Get-WmiObject -Namespace "root\ccm\policy\machine\actualconfig" -computername $VM -Class "CCM_TaskSequence" | Where-Object { $_.ADV_AdvertisementID -like $SoftwareDistributionPolicy.ADV_AdvertisementID }
# Set the mandatory assignment property to true mimicing it contains assignments
$TaskSequencePolicy.Get()
$TaskSequencePolicy.ADV_MandatoryAssignments = $true
$TaskSequencePolicy.Put() | Out-Null
 
            # Invoke the mandatory assignment
$Run = Invoke-WmiMethod -Namespace "root\ccm" -computername $VM -Class "SMS_Client" -Name "TriggerSchedule" -ArgumentList $ScheduleID
Write-Host $VM "is running TS -"   $objDateTimeCurrent -ForegroundColor Cyan
}

Now we just add VMware PowerShell that was covered in Part – 3 Upgrading Hardware and adding TPM , making sure that you add the Vcenter parts after the GPT conversation and include the “for each loop.

BAM!  Automation! 

Moving The EFI Partition

The last piece of the puzzle is to handle the tricky issue of expanding the disk after the MBR partition is moved from the old MBR partition on the disk. This is a common hurdle, and we’ll use PowerShell to address it.

This is how you do it via PowerShell

##Create a new EFI System Partition at the beginning of the disk
# and configure it as the primary boot partition.
#
# Define variables for the new EFI partition size.
# EFI partitions are typically 100-300MB, but 500MB is a safe, modern size.
$newEfiSizeInMB = 500

#Identify the Partitions
Write-Host "Identifying partitions on the disk..."

# Find the main Windows partition (usually the largest one)
$windowsPartition = Get-Partition | Where-Object { $_.Type -eq 'Basic' -and $_.Size -gt 10GB } | Sort-Object -Property Size -Descending | Select-Object -First 1
if (-not $windowsPartition) {
    Write-Error "Could not find a suitable Windows partition. Exiting."
  exit
}
# Find the old MBR System Reserved partition (typically 50-100MB)
# We will identify it by its size and type, assuming it's the only one of its kind.
$oldSystemPartition = Get-Partition | Where-Object { $_.Type -eq Basic -and $_.Size -gt 50MB -and $_.Size -lt 101MB } | Select-Object -First 

if (-not $oldSystemPartition) {
Write-Error "Could not find the old System Reserved partition. Exiting."
    exit
}

Write-Host "Found Windows partition on Disk $($windowsPartition.DiskNumber), Partition Number $($windowsPartition.PartitionNumber)."
Write-Host "Found old MBR System Reserved partition on Disk $($oldSystemPartition.DiskNumber), Partition Number $($oldSystemPartition.PartitionNumber)."

# Remove the old MBR System Reserved Partition
# This will free up the space at the beginning of the disk.
Write-Host "Removing the old System Reserved partition to free up space at the start of the disk..."
Remove-Partition -DiskNumber $oldSystemPartition.DiskNumber -PartitionNumber $oldSystemPartition.PartitionNumber -Confirm:$false

# Create the New EFI System Partition
# This will be created in the now-unallocated space at the beginning of the disk.
# The offset is set to 1MB to align the partition correctly.
Write-Host "Creating the new EFI partition at the beginning of the disk with a size of $newEfiSizeInMB MB..."
$newEfiPartition = New-Partition -DiskNumber $windowsPartition.DiskNumber -Size "$newEfiSizeInMB" -GptType "{C12A7328-F81F-11D2-BA4B-00A0C93EC93B}" -Offset "1MB"

if (-not $newEfiPartition) {
    Write-Error "Failed to create the new EFI partition. Exiting."
    exit
}

Write-Host "New EFI partition created. Partition Number: $($newEfiPartition.PartitionNumber)."

# Format the New EFI Partition
# EFI partitions must be FAT32.
Write-Host "Formatting the new EFI partition with FAT32..."
Format-Volume -Partition $newEfiPartition -FileSystem FAT32 -NewFileSystemLabel "EFI" -Confirm:$false

# Assign a temporary drive letter and rebuild boot files
$tempDriveLetter = "S"

Write-Host "Assigning temporary drive letter '$tempDriveLetter'..."
Set-Partition -DiskNumber $newEfiPartition.DiskNumber -PartitionNumber $newEfiPartition.PartitionNumber -NewDriveLetter $tempDriveLetter

Write-Host "Rebuilding BCD boot files..."
# The /s switch points to the EFI partition, and /f UEFI specifies the firmware type.
bcdboot "$($windowsPartition.DriveLetter):\Windows" /s "$($tempDriveLetter):" /f UEFI

Write-Host "Boot files successfully updated."

#Remove the temporary drive letter from the new EFI partition
Write-Host "Removing temporary drive letter '$tempDriveLetter'..."
Set-Partition -DiskNumber $newEfiPartition.DiskNumber -PartitionNumber $newEfiPartition.PartitionNumber -NewDriveLetter $null

Write-Host "Process complete. Please restart your computer to boot from the new EFI partition."


And there you have it.  Obviously, there is more advanced logging, threading, and you can even optimize the script a little by creating functions, but you get the idea.  

I hope this content sparks some new ideas on how to do more than just prep for an OS upgrade but maybe solve another problem.  What are some of the other ways you’ve automated tasks?  Drop them in the comments I’d love to hear about them.

Discover more from IT Engineered

Subscribe now to keep reading and get access to the full archive.

Continue reading