Save to My DOJO
Table of contents
Several computing functions require the ability to uniquely identify a computer. These functions will generally rely on various signatures presented by hardware components. For a physical machine, it’s expected that these numbers will be constants. For virtual machines, almost nothing is constant. Nothing is truly unique, either, as duplicates can be made through simple file copy operations. Sometimes, items are duplicated that should be unique. All of these situations can be avoided by using Hyper-V’s export and import functionality, but, things happen. This post includes a free script that easily modifies the BIOS GUID of a virtual machine.
The particular field that this script modifies (VM BIOS GUID) is also known as the system’s UUID (universally unique identifier). I know that it is used when you attempt to PXE boot a computer. If you’re here, you likely already know the reason that you care about it, so I’m not going to spend a lot of time on that particular topic. If you want to see what a computer’s UUID is, open an elevated PowerShell prompt and run the following (this works on any Windows computer, whether virtual or physical):
gwmi Win32_ComputerSystem | select UUID
I used short-hand aliases since this is something you’re typing interactively. gwmi is short for Get-WMIObject, Win32_ComputerSystem is being given to the positional ClassName parameter, select is an alias for Select-Object, and UUID is being supplied to the positional Property parameter.
The output of the above PowerShell prompt is the computer’s UUID. On a virtual machine, it is also the BIOS GUID. That is the field that this script modifies.
Warning 1: Changes to this field are irreversible without restoring from backup. Modification of the field is likely to trigger software activation events. Other side effects, potentially damaging, may occur. Use this script at your own risk.
Warning 2: The BIOS GUID cannot be modified while the virtual machine is on. It must be in an Off state (not Saved or Paused). This script will turn off a running virtual machine (you are prompted first). It will not change anything on saved or paused VMs.
The following safety measures are in place:
- The script is marked as High impact, which means that it will prompt before doing anything unless you supply the -Force parameter or have your confirmation preference set to a dangerous level. It will prompt up to two times: once if the virtual machine is running (because the VM must be off before the change can occur) and when performing the change.
- The script will only accept a single virtual machine at a time. Of course, it can operate within a foreach block so this barrier can be overcome. The intent was to prevent severe accidents such as Get-VM | New-VMBIOSGUID.
- If a running virtual machine does not shut down within the allotted time, the script exits. The default wait time is 5 minutes, overridable by specifying the Timeout parameter. The timeout is measured in seconds. If the virtual machine’s guest shutdown process was properly triggered, it will continue to attempt to shut down and this script will not try to turn it back on.
- If a guest’s shutdown integration service does not respond (which includes guests that don’t have a shutdown integration service) the script will exit without making changes.
Tip: If you just want to know what a virtual machine’s BIOSGUID is, and the above PowerShell doesn’t work/isn’t what you want to do, use this function with the -WhatIf parameter. However, because the BIOSGUID is automatically generated each time the script is run, the one that WhatIf displays will not be the one that is actually applied should you run the script again. When running without WhatIf, the GUID shown in the confirmation prompt is the GUID that will be applied.
New-VMBIOSGUID
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 rootvirtualizationv2 -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 rootvirtualizationv2 -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 } } }
Script Discussion
Changing the BIOSGUID has never been something that I needed to do, but I saw someone else requesting it. I noticed that all existing documentation was written for version 1 of the Hyper-V WMI namespace, which doesn’t exist as of 2012 R2. Whereas a number of WMI-based Hyper-V functions can be updated from the v1 namespace to the v2 namespace just by changing the -Namespace parameter from rootvirtualization to rootvirtualizationv2, these functions have completely different names and are not accessed through associators. So, none of those older guides will work for current virtual machines; mine will not work on 2008 R2 and earlier.
There aren’t any supported non-WMI techniques to change a Hyper-V virtual machine’s BIOSGUID/UUID. Up through 2012 R2, you can modify the .XML file directly. From what I’ve seen of the new virtual machine file format in 2016, you should be able to perform something similar using a binary file editor. However, if you don’t want to get on the bad side of Microsoft support, WMI is the way to go.
For a more detailed discussion of the BIOSGUID on Hyper-V, I found an article by John Howard. Most of the how-to there is useless because it’s for the older version, but you can learn more about the field itself than what I explained.
Not a DOJO Member yet?
Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!
73 thoughts on "PowerShell Script: Change the BIOS GUID of a Hyper-V Virtual Machine"
Could this script be modified to set the serial number or asset tag? As you observed in 2016 thanks to the new binary format these are much more difficult to change.
Sure. It can easily serve as a template for modifying any read/write attribute of the Msvm_VirtualSystemSettingData class, which includes the serial number and asset tags. It looks like I have a new script set to write.
— and then I noticed that the documentation for those fields says they are read-only. Get-Member shows get and set methods for them. I’ll have to do some work to see if they can be changed or not.
Hello. Great script. If we are able to provide remote access to our Hyper-V management, can we hire you to run the script for us remotely? We have two Windows 2016 Terminal Server servers. SUNTS01-2016 and SUNTS02-2016.
SUNTS01-2016 was cloned over to SUNTS02-2016 but the unique UUID was not created for some reason. TS01 is a production machine and cannot be taken offline. TS02 needs to be put into production very soon (mission critical) but the same-UUID is preventing certain software from running.
Can you help?
… I could, I suppose…. but why would you pay someone to use this? My hourly rate is not worth it.
Eric,
I have been trying to run this script with no luck, is there something that I need to be doing other than saving the code and running with the sample command
New-VMBIOSGUID -VM svtest
Im not seeing any feedback from the powershell terminal. Any tip for where I can look would be great!
I don’t usually write my scripts to emit output. It can cause issues with automation routines. Use -Verbose.
You need to import this as a function into powershell.
Copy this script and name for example new-biosguid.ps1
Then in powershell
. .newbiosguid.ps1
After that you can do the new-vmbiosguid “servername” -whatif
Minor update to the script for Hyper-V 2016 all updates to Nov 2017
Script error:
Get-WmiObject : Invalid namespace “rootvirtualizationv2”
At C:Userssu_kritschgDocumentsnewguid.ps1:180 char:11
… $VMMS = Get-WmiObject -Namespace rootvirtualizationv2 -Class Msvm …
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidArgument: (:) [Get-WmiObject], ManagementException
FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
Fix: (line 180)
$VMMS = Get-WmiObject -Namespace “rootvirtualizationV2” -Class Msvm_VirtualSystemManagementService -ComputerName $ComputerName
Just added “” to rootvirtualizationV2
Thanks for this, big help, major retailer, 500 stores with 2x Hyper-V VMs, needed to change GUID on all of them due to a requirement in Cylance
Interesting, I’ve never had that problem before. Unless spaces are present, PowerShell has always accepted non-quoted strings for me. I’ll modify the source. Thanks!
As a tip, I’d recommend using single-quotes any time you don’t need in-script variable substitution. When run in large batches, you can see the performance difference.
How is the -Force command not working. I am still getting prompts.
I’ll look into it.
Any luck on getting the -Force switch to work? I am getting prompted as well when running this.
Interesting. I upgraded my cluster from 2012-R2 to 2016. One VM (maybe more but NOT all of them) “lost it’s UUID).
I had a problem with software activation because of this. I figured I would use this to reset it back. The scripts SHOWED the old UUID and what I would be changing it to (identical). Yet even afterwards within Windows it shows a blank UUID!
Did you ever get a resolution on this? We have this exact same issue.
Noah and Shaun,
I had the exact same behavior after going from 2012R2 to 2016. Make sure you update the VM configuration version – that’s what made this work for me. Although I did change the UUID through this script, I don’t think I had to.
HTH,
Peter
Here’s a link to updating the VM configuration version….
https://docs.microsoft.com/en-us/windows-server/virtualization/hyper-v/deploy/upgrade-virtual-machine-version-in-hyper-v-on-windows-or-windows-server
Hi there,
Could you please help out a Powershell noob. I have no idea how to run the script.
I have copied your script and saved it as UUID.ps1
How do I run it?
Cheers.
Another Poor soul that needs to change the ChassisSerialNumber/BIOS Serial number due to cloned machines, but software licenses not liking duplicate numbers.
Per a link from this thread: https://social.technet.microsoft.com/Forums/lync/en-US/fb7f169c-9d0e-4512-8302-c5d111a0f147/hyperv-bios-serial-modification-in-windows-10?forum=winserverhyperv)
Which I just realized is you posting on MS’s forum I tried ChassisSerialNumber, but it errors out:
CategoryInfo : OperationStopped: (‘Seria…7-FE116D9ED2A1):String) [], RuntimeException
FullyQualifiedErrorId : ‘SerialTest-2’ failed to modify settings. (Virtual machine ID 584446E9-9482-480
D-A327-FE116D9ED2A1)
‘SerialTest-2’ encountered an unexpected error: Invalid parameter (0x80041008). (Virtual machine ID 58444
6E9-9482-480D-A327-FE116D9ED2A1)
Too bad HyperV doesn’t have a built in way to do this when cloning.
So I guess my question is, did you ever get that one off working?
Just found your other script at:
https://www.altaro.com/hyper-v/powershell-script-change-advanced-settings-hyper-v-virtual-machines/
Also try this if you get a chance: https://www.altaro.com/hyper-v/free-tool-advanced-settings-editor-hyper-v-virtual-machines/
It is perfect!!!
Thank you for such a wonderful script.
Hyper-V configuration version 9.0 on Windows 10 1903
The script returned
”
Get-WmiObject : Invalid namespace “rootvirtualizationv2″
At line:1 char:1
+ Get-WmiObject -Namespace rootvirtualizationv2 -Class Msvm_VirtualSyst …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-WmiObject], ManagementException
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
”
I was able to fix this by replacing “rootvirtualizationv2” by “root/virtualization/v2” in the script