Last Modified: Sat, 18 Nov 2023 19:09:12 +0000 ; Created: Sat, 18 Nov 2023 19:09:12 +0000
I had to search a number of powershell and other methods for editing a Hyper-V BIOS GUID / UUID when using the Windows 11 Pro Hyper-V software. The few examples I found did not work with the newer Hyper-V APIs. Credits
Modified code to change the Hyper-V BIOSGUID
function New-VMBIOSGUID
{
<#
.SYNOPSIS
Changes the BIOSGUID for Hyper-V guests running on Hyper-V versions 8/2012 or later.
.DESCRIPTION
Changes the BIOSGUID for Hyper-V guests running on Hyper-V versions 8/2012 or later.
A GUID can be supplied. If not, one is automatically generated.
If the virtual machine is running, this script will attempt to shut it down prior to the operation. Once the replacement is complete, the virtual machine will be turned back on.
.PARAMETER VM
The name or virtual machine object (from Get-VM) of the virtual machine whose BIOSGUID is to be changed.
.PARAMETER NewID
The new GUID to assign to the virtual machine. If empty, a new GUID will be automatically generated.
.PARAMETER ComputerName
The Hyper-V host that owns the virtual machine to be modified.
.PARAMETER Timeout
Number of seconds to wait when shutting down the guest before assuming the shutdown failed and ending the script.
Default is 300 (5 minutes).
If the virtual machine is off, this parameter has no effect.
.PARAMETER Force
Suppresses prompts. If this parameter is not used, you will be prompted to shut down the virtual machine if it is running and you will be prompted to replace the BIOSGUID.
Force can shut down a running virtual machine. It cannot affect a virtual machine that is saved or paused.
.PARAMETER WhatIf
Performs normal WhatIf operations by displaying the change that would be made. However, the new BIOSGUID is automatically generated on each run. The one that WhatIf displays will not be used.
.NOTES
Version 1.0
February 29, 2016
Author: Eric Siron
(c) 2016 Altaro Software
This script comes with no warranty, express or implied. Neither Altaro Software nor Eric Siron are liable for any damages, intentional or otherwise, that arise from its use in any capacity.
.INPUTS
Microsoft.HyperV.PowerShell.VirtualMachine or System.String
System.GUID
.EXAMPLE
New-VMBIOSGUID -VM svtest
Replaces the BIOS GUID on the virtual machine named svtest with an automatically-generated ID.
.EXAMPLE
New-VMBIOSGUID svtest
Exactly the same as example 1; uses positional parameter.
.EXAMPLE
Get-VM svtest | New-VMBIOSGUID
Exactly the same as example 1 and 2; uses the pipeline.
.EXAMPLE
New-VMBIOSGUID svtest -Force
Exactly the same as examples 1, 2, and 3; prompts suppressed.
.EXAMPLE
New-VMBIOSGUID svtest -NewID $Guid
Replaces the BIOS GUID of svtest with the supplied ID. These IDs can be generated with [System.Guid]::NewGuid().
.EXAMPLE
New-VMBIOSGUID svtest -WhatIf
Shows how the BIOS GUID will be changed. TIP: Use this to view the current BIOS GUID without changing it.
#>
#requires -Version 4
#requires -Modules Hyper-V
#requires -RunAsAdministrator
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='High')]
param
(
[Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=1)][PSObject]$VM,
[Parameter()][System.GUID]$NewID,
[Parameter()][String]$ComputerName = $env:COMPUTERNAME,
[Parameter()][UInt32]$Timeout = 300,
[Parameter()][Switch]$Force
)
begin
{
<# adapted from http://blogs.msdn.com/b/taylorb/archive/2008/06/18/hyper-v-wmi-rich-error-messages-for-non-zero-returnvalue-no-more-32773-32768-32700.aspx #>
function Process-WMIJob
{
param
(
[Parameter(ValueFromPipeline=$true)][System.Management.ManagementBaseObject]$WmiResponse,
[Parameter()][String]$WmiClassPath = $null,
[Parameter()][String]$MethodName = $null,
[Parameter()][String]$VMName,
[Parameter()][String]$ComputerName
)
process
{
$ErrorCode = 0
if($WmiResponse.ReturnValue -eq 4096)
{
$Job = [WMI]$WmiResponse.Job
while ($Job.JobState -eq 4)
{
Write-Progress -Activity ('Modifying virtual machine {0}' -f $VMName, $ComputerName) -Status ('{0}% Complete' -f $Job.PercentComplete) -PercentComplete $Job.PercentComplete
Start-Sleep -Milliseconds 100
$Job.PSBase.Get()
}
if($Job.JobState -ne 7)
{
if ($Job.ErrorDescription -ne "")
{
throw $Job.ErrorDescription
}
else
{
$ErrorCode = $Job.ErrorCode
}
Write-Progress $Job.Caption "Completed" -Completed $true
}
}
elseif ($WmiResponse.ReturnValue -ne 0)
{
$ErrorCode = $WmiResponse.ReturnValue
}
if($ErrorCode -ne 0)
{
if($WmiClassPath -and $MethodName)
{
$PSWmiClass = [WmiClass]$WmiClassPath
$PSWmiClass.PSBase.Options.UseAmendedQualifiers = $true
$MethodQualifiers = $PSWmiClass.PSBase.Methods[$MethodName].Qualifiers
$IndexOfError = [System.Array]::IndexOf($MethodQualifiers["ValueMap"].Value, [String]$ErrorCode)
if($IndexOfError -ne "-1")
{
throw('Error Code: {0}, Method: {1}, Error: {2}' -f $ErrorCode, $MethodName, $MethodQualifiers["Values"].Value[$IndexOfError])
}
else
{
throw('Error Code: {0}, Method: {1}, Error: Message Not Found' -f $ErrorCode, $MethodName)
}
}
}
}
}
}
process
{
Write-Verbose -Message 'Validating input...'
$VMName = ''
$InputType = $VM.GetType()
if($InputType.FullName -eq 'System.String')
{
$VMName = $VM
}
elseif($InputType.FullName -eq 'Microsoft.HyperV.PowerShell.VirtualMachine')
{
$VMName = $VM.Name
$ComputerName = $VM.ComputerName
}
else
{
throw('You must supply a virtual machine name or object.')
}
if($NewID -ne $null)
{
try
{
$NewID = [System.Guid]::Parse($NewID)
}
catch
{
throw('Provided GUID cannot be parsed. Supply a valid GUID or leave empty to allow an ID to be automatically generated.')
}
}
Write-Verbose -Message ('Establishing WMI connection to Virtual Machine Management Service on {0}...' -f $ComputerName)
$VMMS = Get-WmiObject -Namespace "root\virtualization\v2" -Class Msvm_VirtualSystemManagementService -ComputerName $ComputerName
Write-Verbose -Message 'Acquiring an empty paramater object for the ModifySystemSettings function...'
$ModifySystemSettingsParams = $VMMS.GetMethodParameters('ModifySystemSettings')
Write-Verbose -Message ('Establishing WMI connection to virtual machine {0}' -f $VMName)
$VMObject = Get-WmiObject -Namespace "root\virtualization\v2" -Class Msvm_ComputerSystem -Filter "ElementName = '$VMName'"
if($VMObject -eq $null)
{
throw('Virtual machine {0} not found on computer {1}' -f $VMName, $ComputerName)
}
Write-Verbose -Message ('Verifying that {0} is off...' -f $VMName)
$OriginalState = $VMObject.EnabledState
if($OriginalState -ne 3)
{
if($OriginalState -eq 2 -band ($Force.ToBool() -bor $PSCmdlet.ShouldProcess($VMName, 'Shut down')))
{
$ShutdownComponent = $VMObject.GetRelated('Msvm_ShutdownComponent')
Write-Verbose -Message 'Initiating shutdown...'
Process-WMIJob -WmiResponse $ShutdownComponent.InitiateShutdown($true, 'Change BIOSGUID') -WmiClassPath $ShutdownComponent.ClassPath -MethodName 'InitiateShutdown' -VMName $VMName -ComputerName $ComputerName -ErrorAction Stop
# the InitiateShutdown function completes as soon as the guest's integration services respond; it does not wait for the power state change to complete
Write-Verbose -Message ('Waiting for virtual machine {0} to shut down...' -f $VMName)
$TimeoutCounterStarted = [datetime]::Now
$TimeoutExpiration = [datetime]::Now + [timespan]::FromSeconds($Timeout)
while($VMObject.EnabledState -ne 3)
{
$ElapsedPercent = [UInt32]((([datetime]::Now - $TimeoutCounterStarted).TotalSeconds / $Timeout) * 100)
if($ElapsedPercent -ge 100)
{
throw('Timeout waiting for virtual machine {0} to shut down' -f $VMName)
}
else
{
Write-Progress -Activity ('Waiting for virtual machine {0} on {1} to stop' -f $VMName, $ComputerName) -Status ('{0}% timeout expiration' -f ($ElapsedPercent)) -PercentComplete $ElapsedPercent
Start-Sleep -Milliseconds 250
$VMObject.Get()
}
}
}
elseif($OriginalState -ne 2)
{
throw('Virtual machine must be turned off to replace the BIOS GUID. It is not in a state this script can work with.' -f $VMName)
}
}
Write-Verbose -Message ('Retrieving all current settings for virtual machine {0}' -f $VMName)
$CurrentSettingsDataCollection = $VMObject.GetRelated('Msvm_VirtualSystemSettingData')
Write-Verbose -Message 'Extracting the settings data object from the settings data collection object...'
$CurrentSettingsData = $null
foreach($SettingsObject in $CurrentSettingsDataCollection)
{
$CurrentSettingsData = [System.Management.ManagementObject]($SettingsObject)
}
if($NewID -eq $null)
{
Write-Verbose 'Generating new GUID...'
$NewID = [System.Guid]::NewGuid()
}
$OriginalGUID = $CurrentSettingsData.BIOSGUID
Write-Verbose -Message ('Orginal BIOS GUID: {0}' -f $OriginalGUID)
Write-Verbose -Message 'Changing BIOSGUID in data object...'
$CurrentSettingsData['BIOSGUID'] = "{$($NewID.Guid.ToUpper())}"
Write-Verbose -Message ('New BIOS GUID: {0}' -f $CurrentSettingsData.BIOSGUID)
Write-Verbose -Message 'Assigning modified data object as parameter for ModifySystemSettings function...'
$ModifySystemSettingsParams['SystemSettings'] = $CurrentSettingsData.GetText([System.Management.TextFormat]::CimDtd20)
if($Force.ToBool() -bor $PSCmdlet.ShouldProcess($VMName, ('Change BIOSGUID from {0} to {1}' -f $OriginalGUID, "{$($NewID.Guid.ToUpper())}")))
{
Write-Verbose -Message ('Instructing Virtual Machine Management Service to modify settings for virtual machine {0}' -f $VMName)
Process-WMIJob -WmiClassPath $VMMS.ClassPath ($VMMS.InvokeMethod('ModifySystemSettings', $ModifySystemSettingsParams, $null))
Process-WMIJob -WmiResponse ($VMMS.InvokeMethod('ModifySystemSettings', $ModifySystemSettingsParams, $null)) -WmiClassPath $VMMS.ClassPath -MethodName 'ModifySystemSettings' -VMName $VMName -ComputerName $ComputerName
}
$VMObject.Get()
if($OriginalState -ne $VMObject.EnabledState)
{
Write-Verbose -Message ('Returning {0} to its original running state.' -f $VMName)
Process-WMIJob -WmiResponse $VMObject.RequestStateChange($OriginalState) -WmiClassPath $VMObject.ClassPath -MethodName 'RequestStateChange' -VMName $VMName -ComputerName $ComputerName -ErrorAction Stop
}
}
}
Convert VMware UUID To Windows GuestParam ( [Parameter(Mandatory=$True)] [String] $rawUUID ) # Create an array of each half (hyphen delimiter) $octets = $rawUUID.Split("-") # Create an array of each two-character byte (space delimiter) $bytes = $octets[0].Split(" ") + $octets[1].Split(" ") # Build the final string, piecing together byte by byte $prettyUUID = $bytes[3] + $bytes[2] + $bytes[1] + $bytes[0] + "-" + $bytes[5] + $bytes[4] + "-" + $bytes[7] + $bytes[6] + "-" + $bytes[8] + $bytes[9] + "-" + $bytes[10] + $bytes[11] + $bytes[12] + $bytes[13] + $bytes[14] + $bytes[15] Return $prettyUUID |
|