VMware Cloud Community
daphnissov
Immortal
Immortal
Jump to solution

Ideas to script a save-and-restore process of a VM's instanceUuid (vc.uuid)?

This is somewhat of a follow-up question to the post here to which a satisfactory solution was discovered, but I now must go deeper. My first post was about changing the instanceUuid (also known as vc.uuid in the VMX file) from PowerCLI, and it is possible as outlined thanks to the idea from LucD. However, I now need some ideas on a solution that is able to backup and restore that instanceUuid. Here's a scenario as to why this is necessary:

  1. MyVM01 has instanceUuid set to "ABC".
  2. Some external solution runs on a periodic basis and changes that instanceUuid of MyVM01 to "XYZ".
  3. On a scheduled interval, a script needs to run that restores the MyVM01's instanceUuid to "ABC".
  4. instanceUuid cannot be randomly re-generated as provided in the first post--it must be retained as "ABC".

The backup portion could be accomplished by just reading the instanceUuid out and saving to a file, but there needs to be a way to more or less manually edit the VMX file in situ to restore the given instanceUuid to what it previously was. As discussed in the previous thread, it's not possible to use VMware.Vim.VirtualMachineConfigSpec because the API will not permit an arbitrary instanceUuid to be set, only a new one to be generated by setting the parameter to null. The VM is always in a powered off state, so reloading of that VMX should not be necessary.

Is there PowerCLI code that anyone can think of that could directly edit a VMX without going through the API? Any other ideas are certainly welcome, but it would seem we are reduced to this only. Thanks to all who can offer thoughts or ideas.

1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

There was an error in the vCenter calculated property as well.

Try this new version

####Set variables####

$vcenters = 'llvc02.domain.com'

$username = 'myuser@domain.com'

$password = 'password'

$foldername = 'MyFolder'

$UuidFile = 'D:\uuids.csv'

#####################

$spec = New-Object VMware.Vim.VirtualMachineConfigSpec

$spec.instanceUuid =  ''

$report = @()

foreach ($vcenter in $vcenters){

    Connect-VIServer $vcenter -User $username -Password $password -Force

    Get-Folder -Name $foldername -Server $vcenter | Get-Template | Set-Template -ToVM

    foreach ($vm in (Get-Folder $foldername | Get-VM )){

        $vm.Extensiondata.ReconfigVM($spec)

        $vm = Get-VM -Name $vm.Name

        $newinstanceUuid = $vm.extensiondata.config.InstanceUUID

        Write-Output "New vCenter ID for $($vm.Name) is $($newinstanceUuid)."

        $report += $vm | Select-Object Name,

            @{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID}},

            @{Name="vCenter"; Expression={$_.Uid.Split('@')[1].Split(':')[0]}}

    }

    Get-Folder -Name $foldername -Server $vcenter | Get-VM | Set-VM -ToTemplate -Confirm:$false | Out-Null

}

$report | Export-Csv -Path $UuidFile -NoTypeInformation -UseCulture


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

31 Replies
LucD
Leadership
Leadership
Jump to solution

You can handle the VMX file as a regular file, search in it and change content.

See for example Re: how can modify/change key in VMX file


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
daphnissov
Immortal
Immortal
Jump to solution

Thanks, Luc, for pointing me in that direction. I will look to that as a model for what I'm trying to accomplish and will let you know. It appears it'll do the trick.

0 Kudos
daphnissov
Immortal
Immortal
Jump to solution

LucD​ is it possible to edit a VMX file in place rather than downloading it via a PS drive and uploading it after replacing content?

0 Kudos
daphnissov
Immortal
Immortal
Jump to solution

And second question is about writing out values for each VM to a CSV. Here's what I have so far:

foreach ($vmname in (Get-Folder $foldername | Get-VM )){

    $spec = New-Object VMware.Vim.VirtualMachineConfigSpec

    $spec.instanceUuid =  ''

    $vm = Get-VM -Name $vmName

    $vm.Extensiondata.ReconfigVM_Task($spec)

    $newinstanceUuid = (Get-VM $vmname).extensiondata.config.InstanceUUID

    Write-Output "New vCenter ID for $vmname is $newinstanceUuid."

    $vmname | Select-Object Name,@{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID},@{Name="vCenter"; Expression={$_.Uid.Substring($_.Uid.IndexOf('@')+1).Split(":")[0]}}} | Export-Csv -Append -Path $UuidFile -NoTypeInformation}

The last line of the foreach loop is contiguous. I need to write out to a CSV file for later parsing the VM name, InstanceUuid, and vCenter to which it belongs. The Name and InstanceUuid work fine. When I add the expression that derives the vCenter name I get the error

Select-Object : The "Expression" key has a type, System.Object[], that is not valid; expected types are {System.String, System.Management.Automation.ScriptBlock}.

I take it this is not a valid object. There may be an easier way, so could you suggest how I might accomplish this simple task? Also, if you can offer any critique of the code that would make it more efficient and all-around "better" then please do so.

LucD
Leadership
Leadership
Jump to solution

1. Afaik you can't edit a .vmx file in place

2. I made a couple of changes to the script

  • move the static part outside the ForEach loop
  • Used UpdaetViewData instead of a Get-VM, to get the new InstanceUuid property value
  • Collected the output in an array, since the ForEach doesn't place anything on the pipeline

$spec = New-Object VMware.Vim.VirtualMachineConfigSpec

$spec.instanceUuid =  ''

$report = foreach ($vm in (Get-Folder $foldername | Get-VM )){

    $vm.Extensiondata.ReconfigVM_Task($spec)

    $vm.ExtensionData.UpdateViewData('Config.InstanceUUID')

    $newinstanceUuid = $vm.extensiondata.config.InstanceUUID

    Write-Output "New vCenter ID for $vmname is $newinstanceUuid."

    $vm | Select-Object Name,

        @{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID},

        @{Name="vCenter"; Expression={$_.Uid.Uid.Split('@')[1].Split(':')[0]}}

}

$report | Export-Csv -Append -Path $UuidFile -NoTypeInformation -UseCulture

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
daphnissov
Immortal
Immortal
Jump to solution

Thanks for your help, Luc. I will have to use the copy-replace mechanism for changing strings in the VMX. Once replaced, do you know if it needs to be reloaded for vCenter to parse any changes? Or any other methods invoked for the changes made to take effect.

About your script changes, that does look better and I've learned quite a bit from the few changes. However, I'm not having success incorporating that into what I had before. I know it's something simple.

####Set variables####

$vcenters = 'llvc02.domain.com'

$username = 'myuser@domain.com'

$password = 'password'

$foldername = 'MyFolder'

$UuidFile = 'D:\uuids.csv'

#####################

foreach ($vcenter in $vcenters){

Connect-VIServer $vcenter -User $username -Password $password -Force

Get-Folder -Name $foldername -Server $vcenter | Get-Template | Set-Template -ToVM

$spec = New-Object VMware.Vim.VirtualMachineConfigSpec

$spec.instanceUuid =  ''

$report = foreach ($vm in (Get-Folder $foldername | Get-VM )){

    $vm.Extensiondata.ReconfigVM_Task($spec)

    $vm.ExtensionData.UpdateViewData('Config.InstanceUUID')

    $newinstanceUuid = $vm.extensiondata.config.InstanceUUID

    Write-Output "New vCenter ID for $vm is $newinstanceUuid."

    $vm | Select-Object Name,@{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID}},@{Name="vCenter"; Expression={$_.Uid.Uid.Split('@')[1].Split(':')[0]}}

    $report | Export-Csv -Append -Path $UuidFile -NoTypeInformation -UseCulture}

}

Get-Folder -Name $foldername -Server $vcenter | Get-VM | Set-VM -ToTemplate -Confirm:$false | Out-Null

Specifically, the information as expected is not getting written to the CSV file, and Write-Output is not having any effect.

Also, how do you preserve the color formatting when pasting here?

0 Kudos
LucD
Leadership
Leadership
Jump to solution

See KB1026043 for reloading a VMX file.

You will need an SSH to the hosting ESXi node for that.

The reloadVirtualMachineFromPath method unfortunately requires the VM to be powered off.

Try like this.

The $report array needs to be outside the ForEach loop, else it's still empty when you export.

####Set variables####

$vcenters = 'llvc02.domain.com'

$username = 'myuser@domain.com'

$password = 'password'

$foldername = 'MyFolder'

$UuidFile = 'D:\uuids.csv'

#####################

$spec = New-Object VMware.Vim.VirtualMachineConfigSpec

$spec.instanceUuid =  ''

foreach ($vcenter in $vcenters){

    Connect-VIServer $vcenter -User $username -Password $password -Force

    Get-Folder -Name $foldername -Server $vcenter | Get-Template | Set-Template -ToVM

    $report = foreach ($vm in (Get-Folder $foldername | Get-VM )){

        $vm.Extensiondata.ReconfigVM_Task($spec)

        $vm.ExtensionData.UpdateViewData('Config.InstanceUUID')

        $newinstanceUuid = $vm.extensiondata.config.InstanceUUID

        Write-Output "New vCenter ID for $($vm.Name) is $($newinstanceUuid)."

        $vm | Select-Object Name,

            @{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID}},

            @{Name="vCenter"; Expression={$_.Uid.Uid.Split('@')[1].Split(':')[0]}}

    }

    $report | Export-Csv -Append -Path $UuidFile -NoTypeInformation -UseCulture

}

Get-Folder -Name $foldername -Server $vcenter | Get-VM | Set-VM -ToTemplate -Confirm:$false | Out-Null


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
daphnissov
Immortal
Immortal
Jump to solution

1.) I had to put the last Get-Folder line inside the foreach loop as the last step. This way, it can iterate on all the folders when there are multiple vCenters.

$spec = New-Object VMware.Vim.VirtualMachineConfigSpec

$spec.instanceUuid =  ''

foreach ($vcenter in $vcenters){

    Connect-VIServer $vcenter -User $username -Password $password -Force

    Get-Folder -Name $foldername -Server $vcenter | Get-Template | Set-Template -ToVM

$report = foreach ($vm in (Get-Folder $foldername | Get-VM )){

        $vm.Extensiondata.ReconfigVM_Task($spec)

        $vm.ExtensionData.UpdateViewData('Config.InstanceUUID')

        $newinstanceUuid = $vm.extensiondata.config.InstanceUUID

        Write-Output "New vCenter ID for $($vm.Name) is $($newinstanceUuid)."

        $vm | Select-Object Name,

            @{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID}},

            @{Name="vCenter"; Expression={$_.Uid.Uid.Split('@')[1].Split(':')[0]}}

    }

    $report | Export-Csv -Append -Path $UuidFile -NoTypeInformation -UseCulture

Get-Folder -Name $foldername -Server $vcenter | Get-VM | Set-VM -ToTemplate -Confirm:$false | Out-Null

}

2.) I still am seeing no console output from Write-Output.

3.) More importantly, the uuids.csv file generated from the $report array doesn't contain any values:

"Type","Value"

"Task","task-170794"

,

,

"Task","task-170795"

,

,

0 Kudos
daphnissov
Immortal
Immortal
Jump to solution

Also, these VMs will be powered off, so the reloadVirtualMachineFromPath method should work. I'll need to figure that out in just a bit.

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Ok, I think I know what happens, when you call a methood with the _Task suffix, that is the same as running a cmdlet with the RunAsync switch.

It runs in the background, and the script continues.

Change line

$vm.Extensiondata.ReconfigVM_Task($spec)

into

$vm.Extensiondata.ReconfigVM($spec)


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
LucD
Leadership
Leadership
Jump to solution

The reload is quite straightforward, something like this

$vm = Get-VM -Name MyVM

$vm.ExtensionData.reloadVirtualMachineFromPath($vm.ExtensionData.Config.Files.VmPathName)


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

daphnissov
Immortal
Immortal
Jump to solution

Still no Write-Output, still no data in CSV, only this now:

"Length"

"74"

"71"

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Can you try leaving out the Write-Output line altogether?

Just comment it out


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
daphnissov
Immortal
Immortal
Jump to solution

Ah, ok that does produce output in the file, but no vCenter associated with each of the VMs:

"Name","InstanceUUID","vCenter"

"CentOS 7.2","5005c055-e3e1-26ec-6f3e-67bbe9534a6b",

"W2K12R2","5005a95a-eea6-8005-2a8a-dc4afafd4df1",

0 Kudos
LucD
Leadership
Leadership
Jump to solution

There was an error in the vCenter calculated property as well.

Try this new version

####Set variables####

$vcenters = 'llvc02.domain.com'

$username = 'myuser@domain.com'

$password = 'password'

$foldername = 'MyFolder'

$UuidFile = 'D:\uuids.csv'

#####################

$spec = New-Object VMware.Vim.VirtualMachineConfigSpec

$spec.instanceUuid =  ''

$report = @()

foreach ($vcenter in $vcenters){

    Connect-VIServer $vcenter -User $username -Password $password -Force

    Get-Folder -Name $foldername -Server $vcenter | Get-Template | Set-Template -ToVM

    foreach ($vm in (Get-Folder $foldername | Get-VM )){

        $vm.Extensiondata.ReconfigVM($spec)

        $vm = Get-VM -Name $vm.Name

        $newinstanceUuid = $vm.extensiondata.config.InstanceUUID

        Write-Output "New vCenter ID for $($vm.Name) is $($newinstanceUuid)."

        $report += $vm | Select-Object Name,

            @{Name="InstanceUUID"; Expression={$_.extensiondata.config.InstanceUUID}},

            @{Name="vCenter"; Expression={$_.Uid.Split('@')[1].Split(':')[0]}}

    }

    Get-Folder -Name $foldername -Server $vcenter | Get-VM | Set-VM -ToTemplate -Confirm:$false | Out-Null

}

$report | Export-Csv -Path $UuidFile -NoTypeInformation -UseCulture


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

daphnissov
Immortal
Immortal
Jump to solution

Ahh, yes! That's it! I see what you did there. You've created a variable called $report and initialized it as an array. The second foreach is largely as it was, except each time you add content into $report you're appending to the array. Finally, $report is output piped to CSV. This is very educational to see, so thank you.

What is an ideal way to suppress much of the output that's produced from the loops calling Get-VM? I'm speaking of blocks like this:

PowerState              : PoweredOff

Version                 : v11

Notes                   :

Guest                   : CentOS 7.2:CentOS 4/5 or later (64-bit)

NumCpu                  : 1

CoresPerSocket          : 1

MemoryMB                : 2048

MemoryGB                : 2

VMHostId                : HostSystem-host-29

VMHost                  : host01.domain.com

VApp                    :

FolderId                : Folder-group-v16621

Folder                  : My Folder

ResourcePoolId          : ResourcePool-resgroup-27

ResourcePool            : Resources

PersistentId            : 5005c055-e3e1-26ec-6f3e-67bbe9534a6b

UsedSpaceGB             : 2.7035106951370835304260253906

ProvisionedSpaceGB      : 10.890099778771400451660156250

DatastoreIdList         : {Datastore-datastore-34}

HARestartPriority       : ClusterRestartPriority

HAIsolationResponse     : AsSpecifiedByCluster

DrsAutomationLevel      : AsSpecifiedByCluster

VMSwapfilePolicy        : Inherit

VMResourceConfiguration : CpuShares:Normal/1000 MemShares:Normal/20480

GuestId                 : centos64Guest

Name                    : CentOS 7.2-vRA

CustomFields            : {[SRM-com.vmware.vcDr:::protected, ]}

ExtensionData           : VMware.Vim.VirtualMachine

Id                      : VirtualMachine-vm-16623

Uid                     : /VIServer=domain\user@vc.domain.com:443/VirtualMachine=VirtualMachine-vm-16623/

Client                  : VMware.VimAutomation.ViCore.Impl.V1.VimClient

Piping it to Out-Null doesn't work since it throws the data into a black hole. I only want to silence this information while still being able to print an errors that would occur.

Also, can you share your secret as to how you're pasting Powershell with color markup like what ISE displays? Is it via an Add-On for the ISE? I think I saw reference to one out there.

0 Kudos
LucD
Leadership
Leadership
Jump to solution

PowerShell output is split in streams, you cna redirect individual streams

For example, the following will suppress regular output (stream 1), but still show errors (and all the other streams).

Test by using a name for a VM that exist and one that doesn't exist

Get-VM -Name xyz 1> $null

Yes, you can use an ISE plugin, one example is the one from Falchion

And there is also one for the VSC, see Copy as HTML


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
daphnissov
Immortal
Immortal
Jump to solution

Thanks, I'll try one of those extensions. It makes it so much easier to read when pasted here.

Every place I try and specify 1> $null it affects the running of the script because each instance of Get-VM is used further down. Either the Write-Output contains no IDs, doesn't run at all, and the output uuids.csv file is empty. I tried the occurrence in lines 17 and 19 with foul luck. Is this just not going to be possible the way the script is constructed?

0 Kudos
LucD
Leadership
Leadership
Jump to solution

My suggestions was for line 26 where you had the Out-Null, replace that with the stream 1 redirection.

The other lines shouldn't be producing any output on the console


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos