PowerShell Script: Change the BIOS GUID of a Hyper-V Virtual Machine

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.

 

 

Altaro Hyper-V Backup
Share this post

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"

  • Matt says:

    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.

    • Eric Siron says:

      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?

  • Mark says:

    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!

    • Eric Siron says:

      I don’t usually write my scripts to emit output. It can cause issues with automation routines. Use -Verbose.

    • Ted Brown says:

      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

  • David Kritschgau says:

    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

    • Eric Siron says:

      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.

  • Dheeraj Mutha says:

    How is the -Force command not working. I am still getting prompts.

  • Noah Sumner says:

    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!

  • chipped says:

    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.

  • Mike King says:

    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?

  • SIREN says:

    It is perfect!!!
    Thank you for such a wonderful script.
    Hyper-V configuration version 9.0 on Windows 10 1903

  • Pierre Drum says:

    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

Leave a comment or ask a question

Your email address will not be published. Required fields are marked *

Your email address will not be published. Required fields are marked *

Notify me of follow-up replies via email

Yes, I would like to receive new blog posts by email

What is the color of grass?

Please note: If you’re not already a member on the Dojo Forums you will create a new account and receive an activation email.