Free PowerShell Script for Hyper-V: Rename-ClusterVM

Building off of the earlier Get-ClusterVM script, I’ve come up with a Rename-ClusterVM PowerShell script.

It sometimes comes as a surprise to people to learn that what Failover Cluster Manager shows you for a virtual machine (in the Roles node) isn’t actually the virtual machine at all. It’s an object known as a “cluster group”. Inside that are two resource objects. The first is a “virtual machine” object and the second is a “virtual machine configuration” object. None of these is the actual virtual machine.

The distinction usually isn’t all that important. But, every once in a while, you run afoul of the system. A common place this occurs is when a VM is exported and then imported as a copy (clone). Hyper-V allows you to have multiple virtual machines with the same name because it tracks them by their IDs. Failover Clustering uses IDs, but doesn’t allow duplicate names. So when a clone is made highly available, Failover Cluster Manager automatically assigns its group and resources with unique names. It ties everything together using the real virtual machine’s identifier.

There are two takeaways from this. The first is that because the virtual machine’s name isn’t connected with its resources, renaming a virtual machine has no effect on the cluster group or resources.

Second, you can wind up with cluster groups and resources that are disjointed from their virtual machine.

So, building on the previous Get-ClusterVM script, I’ve designed a Rename-ClusterVM PowerShell script that handles all of these issues.

The Script – Rename-ClusterVM

Its most basic usage is to rename a virtual machine. Give it the current name, the new name, and the cluster name (or the name of any of the nodes), and it will handle everything from there. You can also pipe in a VmID or the VMObject from Get-VM.

If you specify an ID or provide a VMObject, then only that virtual machine is renamed. If there are groups or resources in the cluster with the name that you specified, they will be renamed so that the specified VM’s resources are all synchronized. The conflicting VMs will receive a name that has the date stamp for when the rename took place and a numerical identifier.

If your goal is just to synchronize resource names with a VM that you already renamed, then don’t specify a new name (or just use the existing one). The script will handle everything from there.

If you specify a name instead of an ID or a VMObject, then the script will operate on all virtual machines it finds with that name. If you instruct the script to Sequentialize, then each virtual machine will be renamed with an ascending prefix (-2, -3, -4, etc.). Otherwise, each VM will have the same name. The cluster group and resources will always be given unique names that end in (2), (3), (4), etc. They’ll be ordered by the creation date of the virtual machines.

You may have noticed that Virtual Machine Manager has its own method for naming cluster resources. If you would prefer that convention over Hyper-V’s, use the NamingMethod parameter with a value of VMM.  If you don’t like VMM’s, then either leave NamingMethod off or specify FC, and Failover Clustering’s default naming method will be used.

The script has full support for Get-Help, Confirm, WhatIf, and Verbose. Because of the order of operations and in the intent of favoring a lean script, WhatIf can tell you if there are resources in conflict but it cannot tell you what they are or what they will be renamed to. It can be run from any system that has the Hyper-V and Failover Clusters modules from 2012 or later installed. Your user account must have the necessary permissions to perform virtual machine renames and change objects in the targeted cluster.

Odd Behavior Warning: If you open a PowerShell prompt and immediately run Get-Help on this script, it will return the error “Unable to find type [Microsoft.HyperV.PowerShell.VirtualMachine]”. If you first execute any script or cmdlet that uses this type (like Get-VM), then you don’t get the message. This is only an issue with Get-Help. The script itself will operate just fine without needing to do anything else first.

<#
.SYNOPSIS
	Renames a clustered virtual machine, the related cluster group, and all related cluster resources.

.DESCRIPTION
	Renames a virtual machine and its associated cluster group and resources. Can work with multiple VMs with the same name. By "renaming" a VM using its current name, the names of all related cluster resources will be synchronized to the virtual machine's name.

	If a VmID or VMObject is specified, only that virtual machine will be renamed. If a VMName is specified, then all VMs with that name will be renamed. Use the <Sequential> parameter to have the function automatically append distinguishing specifiers.

	Be aware that when run against remote computers, the results of Get-VM are not always consistent with what's on the host. Therefore, it may sometimes appear that a VM was not renamed.

	In the event of unresolvable conflicts with existing resource names, the existing resources will be renamed with a timestamp.

.PARAMETER VMName
	The name of the virtual machine to be renamed.

.PARAMETER VMObject
	The virtual machine object to be renamed.

.PARAMETER VmID
	Virtual machine identifier of the VM to be renamed.

.PARAMETER NewName
	A string that contains the new name for the virtual machine. If not specified, the cluster group and resources are still changed

.PARAMETER IgnoreSameName
	When set to True, allows you to rename the cluster resources of an existing VM.
	When set to False, stops if 

.PARAMETER Cluster
	The cluster to scan for virtual machines.

	Accepts a string that contains the cluster or any node name.
	Accepts a Cluster object (from Get-Cluster).
	Accepts a VMHost object (from Get-VMHost).

.PARAMETER NamingMethod
	Choose FC or VMM. The default is FC.

	If FC is selected, the cluster resource will be renamed using the default Failover Clustering convention.
		Example: "Virtual machine VMName" and "Virtual machine configuration VMName"
	If VMM is selected, the cluster resource will be renamed using System Center Virtual Machine Manager's convention.
		Example: "SCVMM VMName" and "SCVMM VMName Configuration"

.PARAMETER Sequentialize
	If this is set and multiple instances of <VMName> are found, each successive VM will be named with an ascending decorator.
		EXAMPLE: vmdupe, vmdupe-1, vmdupe-2 and "Virtual machine vmdupe", "Virtual machine vmdupe-1", "Virtual machine vmdupe-2" and "Virtual machine configuration vmdupe", "Virtual machine configuration vmdupe-1", "Virtual machine configuration vmdupe-3"

	VM names are ordered by their creation date.

	If not set, each duplicate VM will take the same new name.

	Cluster resources are always named sequentially by VM creation date, as cluster resources cannot have duplicate names.

	Cannot be used with VMObject or VmID.

.PARAMETER PassThru
	Specifies that the VM object is to be passed through to the pipeline.

.OUTPUTS
	None by default; Microsoft.HyperV.PowerShell.VirtualMachine if –PassThru is specified.

.EXAMPLE
	C:PS> .Rename-ClusterVM -VMName vmold -NewName vmnew

	Description
	-----------
	Renames a virtual machine named "vmold" on the cluster the current computer is a member of to "vmnew". Cluster resources are named in the Failover Clustering style.

.EXAMPLE
	C:PS> .Rename-ClusterVM vmold vmnew clhv1

	Description
	-----------
	Renames a virtual machine named "vmold" to "vmnew" on the cluster named "clhv1". Cluster resources are named in the Failover Clustering style.

.EXAMPLE
	C:PS> Get-VM -Name "vmold" | .Rename-ClusterVM -NewName "vmnew" -NamingMethod VMM

	Description
	-----------
	Renames the virtual machine named "vmold" on the current host to "vmnew". Cluster resources are named in the SCVMM style.

.EXAMPLE
	C:PS> Get-VM -Name "vmsame" | .Rename-ClusterVM -NewName "vmsame" -NamingMethod VMM

	Description
	-----------
	The virtual machine named "vmsame" keeps its name. Its cluster resources are named in the SCVMM style.

.EXAMPLE
	C:PS> Rename-VM "vmCloned" 

.NOTES
	Author: Eric Siron
	Copyright: (C) 2014 Altaro Software
	Version 1.0.1
	Authored Date: December 13, 2019

.LINK
	https://www.altaro.com/hyper-v/free-powershell-script-for-hyper-v-rename-clustervm/
#>

#requires -Version 3
#requires -Modules Hyper-V, FailoverClusters

[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium", DefaultParameterSetName="ByName")]
param(
		[Parameter(Position=1, HelpMessage="Specify the VM to rename", ValueFromPipelineByPropertyName=$true, Mandatory=$true, ParameterSetName="ByName")]
		[Alias("Name", "VM")]
		[String]$VMName = "",

		[Parameter(Position=1, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, ParameterSetName="ByVMObject")]
		[Microsoft.HyperV.PowerShell.VirtualMachine]$VMObject,

		[Parameter(Position=1, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, ParameterSetName="ByVmID")]
		[Guid]$VmID,

		[Parameter(Position=2, ValueFromPipelineByPropertyName=$true)]
		[String]$NewName="",

		[Parameter(ValueFromPipelineByPropertyName=$true)]
		[Alias("ComputerName", "ClusterName" , "HostName", "VMHost")]	# it's OK if the node name is specified rather than the cluster name
		[String]$Cluster = $env:COMPUTERNAME,

		[Parameter(ValueFromPipelineByPropertyName=$true)]
		[ValidateSet("FC", "VMM")]
		[String]$NamingMethod="FC",

		[Parameter(ParameterSetName="ByName")]
		[Switch]$Sequentialize,

		[Parameter()]
		[Switch]$PassThru=$false
)

BEGIN {
	Set-StrictMode -Version Latest
	$VMSuffixPattern = "-{0}"
	$ClusterSuffixPattern = " ({0})"
}

PROCESS {

	function Rename-ClusterVM
	{

		if ($PSCmdlet.ParameterSetName -eq "ByVMObject")
		{
			$VmID = $VMObject.VmID
		}

		$SourceVMArray = @()	# will contain VM(s) to rename
		$ClusterResourceArray = @() # will contain the attached resource
		$ConfigResourceArray = @() # will contain the associated configuration resource

		Write-Verbose "Searching cluster $cluster for Virtual Machine resources"
		$ClusteredVMResources = Get-ClusterResource -Cluster $Cluster | where { $_.ResourceType -like "Virtual Machine*" } |
			select -Property OwnerNode, OwnerGroup,
				@{Name="VmID";Expression={ (Get-ClusterParameter -InputObject $_ | where { $_.Name -eq "VmID" } | select -Property Value).Value } },
				@{Name="ClusterResource";Expression={$_} }

		$ClusteredVMResources = $ClusteredVMResources | where { $_.VmID -ne $null }
		Write-Verbose "Matching cluster resources to supplied VM(s)"
		$FoundVM = $false
		$FoundVMConfig = $false
		foreach ($ClusteredVM in $ClusteredVMResources)	# doesn't use pipeline continuation so that we can take advantage of "break"
		{
			$CurrentVM = Hyper-VGet-VM -Id $ClusteredVM.VmId -ComputerName $ClusteredVM.OwnerNode
			if($VmID -eq $CurrentVM.VmId -or $CurrentVM.Name -eq $VMName)	# $VmID only present if VMObject or VmID; $VMName only present if ByName
			{
				switch($ClusteredVM.ClusterResource.ResourceType)
				{
					"Virtual Machine" {
						$SourceVMArray += $CurrentVM
						$ClusterResourceArray += $ClusteredVM.ClusterResource
						$FoundVM = $true
					}
					"Virtual Machine Configuration" {
						$ConfigResourceArray += $ClusteredVM.ClusterResource
						$FoundVMConfig = $true
					}
				}
				if($VmID -and $FoundVM -and $FoundVMConfig)
				{
					break # if a specific VM was pushed in, not a name, then we are only going to rename that one object, so no need to continue scanning
				}
			}
		}

		Write-Verbose "Verifying that at least one qualifying VM was found"
		if($SourceVMArray.Count -eq 0)
		{
			throw ("No VM matching the source was found on cluster $Cluster. It may not be highly available.")
		}

		# prepare resource string sets
		switch($NamingMethod)
		{
			"VMM" {
				$VMGroupPattern = "SCVMM {0}{1} Resources"
				$VMResourcePattern  = "SCVMM {0}{1}"
				$VMConfigResourcePattern = "SCVMM {0}{1} Configuration"
			}
			default {
				$VMGroupPattern = "{0}{1}"
				$VMResourcePattern = "Virtual Machine {0}{1}"
				$VMConfigResourcePattern = "Virtual Machine Configuration {0}{1}"
			}
		}

		$SourceVMArray = Sort-Object -InputObject $SourceVMArray -Property CreationTime
		if($NewName.Length -eq 0)
		{
			$NewName = $SourceVMArray[0].VMName	# presumably, user is either trying to sequentialize or synchronize, not rename
		} 

		1..($SourceVMArray.Count) | foreach {
			$SourceVM = $SourceVMArray[($_ -1 )]
			$SourceGroup = Get-ClusterGroup -Cluster $Cluster -VmID $SourceVM.VmID
			$SourceResource = $ClusterResourceArray[($_ -1)]
			$SourceConfigResource = $ConfigResourceArray[($_ -1)]
			$VMSuffix = ""
			$ClusterSuffix = ""
			$ConflictMessage = ""
			$GroupConflict = $false
			$ResourceConflict = $false
            $ConflictCheckVMResource = $null
			$ConfigConflict = $false
			$ConflictFlag = $false

			Write-Verbose "Starting rename process on $($SourceVM.VMName)"

			# determine suffixes
			if($_ -gt 1)
			{
				$VMSuffix = $VMSuffixPattern  -f $_
				$ClusterSuffix = $ClusterSuffixPattern -f $_
			}

			$NewGroupName = $VMGroupPattern -f $NewName, $VMSuffix
			if($Sequentialize)
			{
				$NewVMName = $NewGroupName
			}
			else
			{
				$NewVMName = $NewName
			}
			$VMResource = $VMResourcePattern -f $NewName, $ClusterSuffix
			$VMConfigResource = $VMConfigResourcePattern -f $NewName, $ClusterSuffix

			Write-Verbose "Checking for conflicting cluster group object"
			try
			{
				$ConflictCheckGroup = Get-ClusterGroup -Name $NewGroupName -Cluster $Cluster -ErrorAction Stop # Instead of returning an empty set, this throws an error if the target isn't found.
				if($SourceGroup.Id -ne $ConflictCheckGroup.Id)
				{
					$ResourceConflict = $true
					$ConflictFlag = $true
					Write-Verbose "Conflicting group object found"
				}
			}
			catch { }

			Write-Verbose "Checking for conflicting cluster virtual machine objects"
			try
			{
				$ConflictCheckVMResource = Get-ClusterResource -Name $VMResource -Cluster $Cluster -ErrorAction Stop # Instead of returning an empty set, this throws an error if the target isn't found.
				if($SourceResource.Id -ne $ConflictCheckVMResource.Id)
				{
					$ResourceConflict = $true
					$ConflictFlag = $true
					Write-Verbose "Conflicting cluster virtual machine object found"
				}
			}
			catch { }

			Write-Verbose "Checking for conflicting cluster virtual machine configuration objects"
			try
			{
				$ConflictCheckConfigResource = Get-ClusterResource -Name $VMConfigResource -Cluster $Cluster -ErrorAction Stop # Instead of returning an empty set, this throws an error if the target isn't found.
				if($SourceConfigResource.Id -ne $ConflictCheckConfigResource.Id)
				{
					$ConfigConflict = $true
					$ConflictFlag  = $true
					Write-Verbose "Conflicting cluster virtual machine configuration object found"
				}
			}
			catch { }

			if($ConflictFlag)
			{
				$ConflictMessage = ". Detected conflicting resources will be renamed"
			}

			if($PSCmdlet.ShouldProcess($SourceVM.VMName, "Rename VM to '$NewVMName', Name VM cluster resource as '$VMResource', Name VM configuration cluster resource as '$VMConfigResource'$ConflictMessage"))
			{
				Rename-VM -VM $SourceVM -NewName $NewVMName -Passthru:$Passthru
				Write-Verbose "VM renamed to $NewVMName"
				if($GroupConflict)
				{
					$ConflictCheckGroup.Name = $NewGroupName + " " + (Get-Date -Format yyMMddHHmmss)
					Write-Verbose "Conflicting cluster group renamed to $($ConflictCheckGroup.Name)"
				}
				$SourceGroup.Name = $NewGroupName
				Write-Verbose "Cluster group renamed to $NewGroupName"

				if($ResourceConflict)
				{
					$ConflictCheckVMResource.Name = $VMResourcePattern -f $NewName, ($ClusterSuffixPattern -f ((Get-Date -Format yyMMddHHmmss) + $VMSuffix))
					Write-Verbose "Conflicting VM resource renamed to $($ConflictCheckVMResource.Name)"
				}
				$SourceResource.Name = $VMResource
				Write-Verbose "Cluster virtual machine resource renamed to $VMResource"

				if($ConfigConflict)
				{
					$ConflictCheckConfigResource.Name = $VMConfigResourcePattern -f $NewName, ($ClusterSuffixPattern -f ((Get-Date -Format yyMMddHHmmss) + $VMSuffix))
					Write-Verbose "Conflicting VM configuration resource renamed to $($ConflictCheckConfigResource.Name)"
				}
				$SourceConfigResource.Name = $VMConfigResource
				Write-Verbose "Cluster virtual machine configuration resource renamed to $VMConfigResource"
			}
		}
	}

	Rename-ClusterVM
}

END {}

 

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!

4 thoughts on "Free PowerShell Script for Hyper-V: Rename-ClusterVM"

  • Renaud BOURGAIN says:

    Hello Eric,

    I’m almost in the same configuration, i have a Cluster Failoover Hyper-V and i would like to rename the Network Interface.
    For the moment i don’t found a command who do what i want.
    For the Disk i have rename i’ had used this command :
    If ($CDCsv.name[$i] -eq “Disque de cluster 1”) {
    $DiskName1 = $CDCsv.Name[$i]
    $BDNum = $i
    Write-Host “$DiskName1”
    (Get-ClusterResource $DiskName1).Name = “Disque du cluster 1”
    }
    ElseIf ($CDCsv.Name[$i] -eq “Disque de cluster 2”) {
    $DiskName2 = $CDCsv.Name[$i]
    $SDNum = $i
    Write-Host “$DiskName2”
    (Get-ClusterResource $DiskName2).Name = “Disque du cluster 2”
    }
    $i
    }

    could you help me for the Network Interface plz.

  • Dennis Mutsaers says:

    On Windows Server 2016 I get the following error. What can I do to make the script working again?

    PS C:Scripts> .Rename-ClusterVM.ps1 -VMName VM01 -Cluster Cluster01 -NamingMethod VMM
    Get-VM : Cannot validate argument on parameter ‘Id’. The argument is null. Provide a valid value for the argument, and
    then try running the command again.
    At C:ScriptsRename-ClusterVM.ps1:168 char:36
    $CurrentVM = Hyper-VGet-VM -Id $ClusteredVM.VmId -Comput …
    ~~~~~~~~~~~~~~~~~
    CategoryInfo : InvalidData: (:) [Get-VM], ParameterBindingValidationException
    FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.HyperV.PowerShell.Commands.GetVM

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.